0

我想在 JAVA 中实现 Levenberg Marquardt 拟合,并发现 apache commons math 是合适的。由于我想拟合一个函数,而我没有计算梯度或雅可比行列式的导数,因此我需要来自 GNU octave 的 dfdp.m 之类的东西来计算数值导数。有人已经这样做了吗?

4

1 回答 1

0

我自己做的,以防其他人需要,这是方法

dfdp.m 代码

m=size(x,1); if (m==1), m=size(x,2); end  %# PAK: in case #cols > #rows
n=length(p);      %dimensions
ps=p; prt=zeros(m,n);del=zeros(n,1);       % initialise Jacobian to Zero
for j=1:n
      del(j)=dp(j) .*p(j);    %cal delx=fract(dp)*param value(p)
      if p(j)==0
           del(j)=dp(j);     %if param=0 delx=fraction
      end
      p(j)=ps(j) + del(j);
      if del(j)~=0, f1=feval(func,x,p);   %FJ ~= not equal (!=)  ...> p is now (p + dp*p)
           if dp(j) < 0, prt(:,j)=(f1-f)./del(j);
           else
                p(j)=ps(j) - del(j);  %FJ ...> p is now (p - dp*p)
                prt(:,j)=(f1-feval(func,x,p))./(2 .*del(j)); %FJ 2 steps from (ps + del) to (ps - del)
           end
      end
      p(j)=ps(j);     %restore p(j)
end

JAVA代码

private static class GhoosProblem {
 private double[][] data;
 private double[] dp;

 public GhoosProblem(double[][] datapoints, double[] delta_p) {
  data = datapoints;
  //dp= fractional increment of p for numerical derivatives
  //dp(j)>0 central differences calculated
  //dp(j)<0 one sided differences calculated
  //dp(j)=0 sets corresponding partials to zero; i.e. holds p(j) fixed
  dp = delta_p;
 }

 public MultivariateVectorFunction getModelFunction() {
  return new MultivariateVectorFunction() {
    public double[] value(double[] params) {
        double[] values = new double[data.length];
        for (int i = 0; i < values.length; ++i) {
            final double t = data[i][0];  // get the double value
            values[i] = params[0] *
                Math.pow(t, params[2]) *
                Math.exp(-params[1] * t);  // Ghoos function
        }
        return values;   // function values
    }
  };
 }
 public MultivariateMatrixFunction getModelFunctionJacobian2() {
    return new MultivariateMatrixFunction() {
        public double[][] value(double[] params) {
            double[][] jacobian = new double[data.length][params.length];

            final double a = params[0];
            final double b = params[2];
            final double c = params[1];

            for (int i = 0; i < jacobian.length; ++i) {
                final double t = data[i][0];  // get the double value

                jacobian[i][0] = Math.pow(t, b) * Math.exp(-c*t);
                jacobian[i][2] = a * Math.exp(-c*t) * Math.pow(t, b) * Math.log(t);
                jacobian[i][1] = a * Math.pow(t, b) * (-t*Math.exp(-c*t));
            }
        //System.out.println("Jacobian= "+ Arrays.deepToString(jacobian));
        return jacobian;
        }
    };
}

// compared to Ge2.m octave
public MultivariateMatrixFunction getModelFunctionJacobian() {
        return new MultivariateMatrixFunction() {
            public double[][] value(double[] params) {

        int m = data.length;    // cols
        int n = params.length;  // rows
        double[] p = params;
        double[] ps = params;
        double[] del = new double[n];
        double[] f = new double[n];
        double[] f1 = new double[n];
        BlockRealMatrix prt = new BlockRealMatrix(m, n);  // initializes to zeros

        f=feval(p);
        for (int j=0; j<n; ++j) {
            del[j]=dp[j] * p[j];  //delta_x=fractional(dp) * param value(p)
            if (p[j]==0)
                del[j]=dp[j];     //if param=0 delta_x=fractional(dp)
            p[j]=ps[j] + del[j];
            if (del[j]!=0) {
                f1=feval(p);      //p is now (p + dp*p)
                if (dp[j]<0)
                    prt.setColumn(j,(new ArrayRealVector(f1)).subtract(new ArrayRealVector(f)).mapDivideToSelf(del[j]).toArray());  // one sided diff
                else {
                    p[j]=ps[j] - del[j];  // p is now (p - dp*p)
                    prt.setColumn(j,(new ArrayRealVector(f1)).subtract(new ArrayRealVector(feval(p))).mapDivideToSelf(2*del[j]).toArray());  // central diff
                }
            }
            p[j]=ps[j];     //restore p(j)
        }//for
        //System.out.println("Jacobian= "+ Arrays.deepToString(prt.getData()));
        return prt.getData();  //jacobian, dimension is (m x n)
        }
    };
}

    public double[] feval(double[] params) {
        double[] values = new double[data.length];
        for (int i = 0; i < values.length; ++i) {
            final double t = data[i][0];  // get the double value
            values[i] = params[0] *
            Math.pow(t, params[2]) *
            Math.exp(-params[1] * t);  // Ghoos function
        }
        return values;
    }

}//GhoosProblem

抱歉,如果代码的识别效果不好!

相关部分是 getModelFunctionJacobian() -Function

我已将分析导数部分重命名为 getModelFunctionJacobian2(),并在此处发布以供比较

完成这里是使用 GhoosFunction 的 levenberg marquardt 设置

public void fit() {

final double[][] dataPoints = {  //  x, y
    //{0.0/60,  0.0},   // never use {0, 0} => org.apache.commons.math3.exception.ConvergenceException: illegal state: unable to perform Q.R decomposition on the 17x3 jacobian matrix
    {15.0/60,   8.891104},
    {30.0/60,   13.21852},
    {45.0/60,   28.09051},
    {60.0/60,   43.0011},
    {75.0/60,   57.43561},
    {90.0/60,   67.06862},
    {105.0/60,  82.60239},
    {120.0/60,  72.4649},
    {135.0/60,  61.4},
    {150.0/60,  43.97924},
    {165.0/60,  30.6},
    {180.0/60,  20.77112},
    {195.0/60,  15.5},
    {210.0/60,  10.85442},
    {225.0/60,  9.33},
    {240.0/60,  7.260234},
};

    final double[] initialGuess = { 1.0, 1.0, 1.0 };  // p
    final double[] fract_change = { 1E-4, 1E-4, 1E-4 };  // dp should be below 0.0001

    final GhoosProblem problem = new GhoosProblem(dataPoints, fract_change);

    final int len = dataPoints.length;
    final double[] weights = new double[len];
    final double[] target = new double[len];

    for (int i = 0; i < len; i++){
        weights[i] = 1.0;// / dataPoints[i][1];
        target[i] = dataPoints[i][1];
}

final LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer()
                            .withCostRelativeTolerance(1E-4)  // stol in octave
                            .withParameterRelativeTolerance(1E-4);  // dp should be below 0.0001

    final Optimum optimum = optimizer.optimize(
            builder(problem)
                    .weight(new DiagonalMatrix(weights))
                    .target(target)
                    .start(initialGuess)
                    .maxIterations(100)
                    .build()
    );

    final RealVector solution = optimum.getPoint();

    solution.setEntry(0, solution.getEntry(0) / 60.0);  // go back to minutes
    System.out.println("solution= " + solution);
    System.out.println("CostRelativeTolerance= " + optimizer.getCostRelativeTolerance());
    System.out.println("ParameterRelativeTolerance= " + optimizer.getParameterRelativeTolerance());
    System.out.println("evaluations= " + optimum.getEvaluations());
    System.out.println("iterations= " + optimum.getIterations());
    //System.out.println("residuals= " + optimum.getResiduals());
    System.out.println("RMS= " + optimum.getRMS());
    System.out.println("sigma= " + optimum.getSigma(1E-10));

}//fit

public LeastSquaresBuilder builder(GhoosProblem problem){
    return new LeastSquaresBuilder()
            .checkerPair(new SimpleVectorValueChecker(1e-6, 1e-6))   // The SimpleVectorValueChecker Class (Simple implementation of the ConvergenceChecker) contains a method that uses the value of the function between two successive iterations of the optimisation algorithm to check if convergence has occured
            .maxEvaluations(Integer.MAX_VALUE)
            .maxIterations(Integer.MAX_VALUE)
            //.lazyEvaluation(true)
            .model(problem.getModelFunction(), problem.getModelFunctionJacobian());
}
于 2015-07-07T12:00:22.233 回答