我无法获得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