在C#中使用自定义函数实现多元非线性回归

时间:2019-06-06 15:22:07

标签: c# .net visual-studio machine-learning regression

我无法获得Accord.Net来估计具有两个独立变量和一个输出的自定义函数的系数。它只输出与我给出的初始系数估算值相同的值。

一些背景知识:我正在尝试使用多元非线性回归来估计以下函数中系数的最佳值:

Z = A+1/(1/(X/256*B+3*C)+1/(Y/1024*D+2*E))

自变量为X和Y,输出为Z。对于要用来减少计算要求的系数,我有一个最佳猜测:

A = 20

B = 10000

C = 50

D = 50000

E = 60

我成功地使用了MatLab的“曲线拟合工具箱”(Curve Fitting Toolbox)来获得良好的拟合度,但是我需要能够通过Visual Studio在C#中实现此自动化,因为该过程将使用不同的测量多次进行。我找到了一些有前途的Nuget软件包,其中包含一些机器学习工具,最终我进入了Accord.Net。

  double[,] data =
  {
    { 0,    0,    89.05295649 },
    { 128,  0,    123.2124033 },
    { 255,  0,    124.1087122 },
    { 0,    512,  196.9215557 },
    { 128,  512,  4270.278414 },
    { 255,  512,  7149.23716  },
    { 0,    1023, 197.4947063 },
    { 128,  1023, 4672.482543 },
    { 255,  1023, 8360.098631 }
  };

  // Extract inputs and outputs
  double[][] inputs = new double[2][];

  inputs[0] = data.GetColumn(0);
  inputs[1] = data.GetColumn(1);

  double[] outputs = data.GetColumn(2);

  var nls = new NonlinearLeastSquares()
  {

    NumberOfParameters = 5,

    // Initialize to ideal values from A+1/(1/(X/256*B+3*C)+1/(Y/1024*D+2*E))
    StartValues = new[] { 20.0, 10000.0, 50.0, 50000.0, 60.0 },

    Function = (w, x) => w[0] + 1 / (1 / (x[0] / 256 * w[1] + 3 * w[2]) + 1 / (x[1] / 1024 * w[3] + 2 * w[4])),

    // Derivative in respect to the weights:
    Gradient = (w, x, r) =>
    {
      // w.r.t a: A https://www.wolframalpha.com/input/?i=diff+A%2B1%2F(1%2F(X%2F256*B%2B3*C)%2B1%2F(Y%2F1024*D%2B2*E))+w.r.t.+A
      r[0] = 1;
      // w.r.t b: B https://www.wolframalpha.com/input/?i=diff+A%2B1%2F(1%2F(X%2F256*B%2B3*C)%2B1%2F(Y%2F1024*D%2B2*E))+w.r.t.+B
      r[1] = x[0] * ((2048 * w[4] + w[3] * x[1]) * (2048 * w[4] + w[3] * x[1])) / (256 * ((3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]) * (3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1])));
      // w.r.t c: C https://www.wolframalpha.com/input/?i=diff+A%2B1%2F(1%2F(X%2F256*B%2B3*C)%2B1%2F(Y%2F1024*D%2B2*E))+w.r.t.+C
      r[2] = 3 * ((2048 * w[4] + w[3] * x[1]) * (2048 * w[4] + w[3] * x[1])) / ((3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]) * (3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]));
      // w.r.t c: D https://www.wolframalpha.com/input/?i=diff+A%2B1%2F(1%2F(X%2F256*B%2B3*C)%2B1%2F(Y%2F1024*D%2B2*E))+w.r.t.+D
      r[3] = x[1] * ((768 * w[2] + w[1] * x[0]) * (769 * w[2] + w[1] * x[0])) / 64 * ((3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]) * (3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]));
      // w.r.t c: E https://www.wolframalpha.com/input/?i=diff+A%2B1%2F(1%2F(X%2F256*B%2B3*C)%2B1%2F(Y%2F1024*D%2B2*E))+w.r.t.+E
      r[4] = 32 * ((768 * w[2] + w[1] * x[0]) * (768 * w[2] + w[1] * x[0])) / ((3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]) * (3072 * w[2] + 2048 * w[4] + 4 * w[1] * x[0] + w[3] * x[1]));
    },


    Algorithm = new LevenbergMarquardt()
    {
      MaxIterations = 20000,
      Tolerance = 0
    }

  };

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

  var prediction = regression.Transform(new double[] { 200, 350 });

我希望看到与以下内容更接近的内容:

A = 27.85

B = 9886.98

C = 56.87

D = 48581.00

E = 48.47

即使运行了所有20000次迭代,输出也只给出了原始的估计值。 Accord网站上的示例将其显示为具有简单得多的渐变的单个自变量,因此很难说我的问题是否适用于他们的NonLinearLeastSquares类。也许有更好的解决方案?我想坚持使用Nuget上的可用功能,因为这必须在多个地理上分开的系统上进行维护。

更新: 我使用Math.Pow方法更改了渐变中的平方项,这至少导致了系数的改变。但是,这是非常不准确的。我注意到无论我为起点选择什么,B系数仍然不会改变,好像我在r [1]梯度中使用的代码有问题。但是我看不出有什么问题。

    Gradient = (w, x, r) =>
    {
      r[0] = 1;
      //(X (2048 E + D Y)^2)/(256 (3072 C + 2048 E + 4 B X + D Y)^2)
      r[1] = x[0] * Math.Pow(2048.0 * w[4] + w[3] * x[1], 2.0) / (256 * Math.Pow(3072 * w[2] + 2048.0 * w[4] + 4 * w[1] * x[0] + w[3] * x[1], 2.0));
      //(3 (2048 E + D Y)^2)/(3072 C + 2048 E + 4 B X + D Y)^2
      r[2] = 3 * Math.Pow(2048.0 * w[4] + w[3] * x[1], 2.0) / Math.Pow(3072.0 * w[2] + 2048.0 * w[4] + 4 * w[1] * x[0] + w[3] * x[1], 2.0);
      //((768 C + B X)^2 Y)/(64 (3072 C + 2048 E + 4 B X + D Y)^2)
      r[3] = (Math.Pow(768 * w[2] + w[1] * x[0], 2.0) * x[1]) / (64 * Math.Pow(3072.0 * w[2] + 2048.0 * w[4] + 4 * w[1] * x[0] + w[3] * x[1], 2.0));
      //(32 (768 C + B X)^2)/(3072 C + 2048 E + 4 B X + D Y)^2
      r[4] = 32 * Math.Pow(768 * w[2] + w[1] * x[0], 2.0) / Math.Pow(3072.0 * w[2] + 2048.0 * w[4] + 4 * w[1] * x[0] + w[3] * x[1], 2.0);
    },

A: 44469.142086254556

B: -243110.88545066063

C: -303081.17150483071

D: 50000

E: -23237.027101692031

0 个答案:

没有答案