为什么我在这个例子中得到一个 System.ArithmeticException 对高斯进行非线性回归

Why am I getting a System.ArithmeticException in this example doing a non-linear regression to a Gaussian

我正在将一些我在 Matlab 中制作原型的代码移植到 C# 中,并且需要对我的示例数据执行高斯非线性回归。我目前正在尝试 Accord.Net 并在 example here.

之后想出了下面的代码

不幸的是,我遇到了这个异常,但不明白为什么:

System.ArithmeticException: Error calculation has produced a non-finite number. Please make sure that there are no constant columns in the input data."

错误信息:

Error calculation has produced a non-finite number. Please make sure that there are no constant columns in the input data.

这是我想出的代码:

    public static double[] FitGaussian()
    {
        var nls = CreateGaussianFitObject(new[] { 1.43e+04, 0.05093, 0.8098 }, 100, 0);

        double[,] data =
        {
            { -0.2,    12973.3071 },
            { -0.1,    13846.1569 },
            { -0,      14243.9094 },
            {  0.1,    14215.6044 },
            {  0.2,    13840.9077 },
        };

        // Extract inputs and outputs
        double[][] inputs = data.GetColumn(0).ToJagged();
        double[] outputs = data.GetColumn(1);

        var regression = nls.Learn(inputs, outputs);

        var result = regression.Coefficients;
        return result;
    }

    public static NonlinearLeastSquares CreateGaussianFitObject(double[] StartValues, int maxIterations, double tolerance)
    {
        Func<double, double, double, double> expFnc = (mu, sig, x) => m.Exp(-m.Pow((x - mu) / sig, 2));

        var nls = new NonlinearLeastSquares()
        {
            NumberOfParameters = 3,

            // Reference Gaussian Distributioon used in Matlab: f(x) =  a1*exp(-((x-b1)/c1)^2)
            //Function = (w, x) => w[0] * System.Math.Exp(-System.Math.Pow((x[0] - w[1]) / w[2], 2)),
            Function = (w, x) => w[0] * expFnc(w[1],w[2],x[0]),

            // Derivative in respect to the weights:
            Gradient = (w, x, r) =>
            {
                r[0] = expFnc(w[1], w[2], x[0]); // e^(-(x - b1)^2/c1^2)   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. a1
                r[1] = (2 * w[0] * (x[0] - w[1]) * expFnc(w[1], w[2], x[0])) / m.Pow(w[2],2); // (2 a1 (x - b1) e^(-(x - b1)^2/c1^2))/c1^2   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. b1
                r[2] = (2 * w[0] * m.Pow((x[0] - w[1]),2) * expFnc(w[1], w[2], x[0])) / m.Pow(w[2],3); // (2 a1 (x - b1)^2 e^(-(x - b1)^2/c1^2))/c1^3   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. c1
            },

            Algorithm = new LevenbergMarquardt()
            {
                MaxIterations = maxIterations,
                Tolerance = tolerance
            }
        };

        return nls;
    }

我仔细检查了梯度函数(我怀疑它是罪魁祸首),它似乎没问题。

对应的Matlab代码是这样的:

x = [-0.2,-0.1,0,0.1,0.2];
y = [12973.3071,13846.1569,14243.9094,14215.6044,13840.9077];
f = fit(x.',y.','gauss1');
fitObject = f;

给出了这个输出:

f = 

     General model Gauss1:
     f(x) =  a1*exp(-((x-b1)/c1)^2)
     Coefficients (with 95% confidence bounds):
       a1 =    1.43e+04  (1.419e+04, 1.441e+04)
       b1 =     0.05093  (0.03501, 0.06686)
       c1 =      0.8098  (0.7263, 0.8932)

如您所见,我什至尝试将 Matlab 的预期系数作为 C# 实现的起始值。

此外,我对上面引用的 example 感到困惑,因为在我看来他们切换了系数和输入(?)。

我忘记在构造函数NonlinearLeastSquares()中使用参数StartValues,导致了异常。它应该是:

    var nls = new NonlinearLeastSquares()
    {
        NumberOfParameters = 3,
        StartValues = StartValues,
        // Reference Gaussian Distributioon used in Matlab: f(x) =  a1*exp(-((x-b1)/c1)^2)
        //Function = (w, x) => w[0] * System.Math.Exp(-System.Math.Pow((x[0] - w[1]) / w[2], 2)),
        Function = (w, x) => w[0] * expFnc(w[1],w[2],x[0]),

        // Derivative in respect to the weights:
        Gradient = (w, x, r) =>
        {
            r[0] = expFnc(w[1], w[2], x[0]); // e^(-(x - b1)^2/c1^2)   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. a1
            r[1] = (2 * w[0] * (x[0] - w[1]) * expFnc(w[1], w[2], x[0])) / m.Pow(w[2],2); // (2 a1 (x - b1) e^(-(x - b1)^2/c1^2))/c1^2   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. b1
            r[2] = (2 * w[0] * m.Pow((x[0] - w[1]),2) * expFnc(w[1], w[2], x[0])) / m.Pow(w[2],3); // (2 a1 (x - b1)^2 e^(-(x - b1)^2/c1^2))/c1^3   <=>   diff a1*exp(-((x-b1)/c1)^2) w.r.t. c1
        },

        Algorithm = new LevenbergMarquardt()
        {
            MaxIterations = maxIterations,
            Tolerance = tolerance
        }
    };

显然 StartValues 设置为一个零数组,如果没有明确设置的话。这导致除以零,从而导致异常。我通过添加

发现了这一点
Debug.WriteLine($"Weights: w[0]: {w[0]}, w[1]: {w[1]}, w[2]: {w[2]}");
Debug.WriteLine($"Result: r[0]: {r[0]}, r[1]: {r[1]}, r[2]: {r[2]}");

到梯度的 lambda 表达式 属性。