Я не могу получить 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 для подбора кривой, чтобы получить хорошее соответствие, но мне нужно иметь возможность автоматизировать это в C # с помощью Visual Studio, так как процесс будетбыть сделано много раз с различными измерениями.Я нашел несколько многообещающих пакетов 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