Как найти минимум нелинейной многомерной функции с использованием метода Ньютона (код не линейной алгебры)
Я пытаюсь выполнить некоторую оценку параметров и хочу выбирать оценки параметров, которые минимизируют квадратную ошибку в предсказанном уравнении около 30 переменных. Если бы уравнение было линейным, я бы просто вычислил 30 частных производных, поставил их равными нулю и использовал решатель линейного уравнения. Но, к сожалению, уравнение нелинейное, а также его производные.
Если бы уравнение было по одной переменной, я бы просто использовал метод Ньютона (также известный как Ньютон-Рафсон). Веб богат примерами и кодами для реализации метода Ньютона для функций одной переменной.
Учитывая, что у меня около 30 переменных, , как я могу запрограммировать числовое решение этой проблемы с помощью метода Newton? У меня есть уравнение в замкнутой форме и могу вычислить первую и вторую производные, но я не знаю, как исходить оттуда. Я нашел большое количество обработок в Интернете, но они быстро попадают в тяжелую матричную нотацию. Я нашел что-то умеренно полезное в Википедии, но мне трудно перевести его в код.
В тех случаях, когда я беспокоюсь о разрыве, в матричной алгебре и матричных инверсиях. Я могу инвертировать матрицу с решателем линейного уравнения, но я беспокоюсь о том, чтобы получить правильные строки и столбцы, избегая ошибок транспонирования и т.д.
Чтобы быть конкретным:
-
Я хочу работать с таблицами, сопоставляя переменные с их значениями. Я могу написать функцию такой таблицы, которая возвращает квадратную ошибку, учитывая такую таблицу как аргумент. Я также могу создавать функции, возвращающие частную производную по любой заданной переменной.
-
У меня есть разумная стартовая оценка для значений в таблице, поэтому я не беспокоюсь о конвергенции.
-
Я не уверен, как написать цикл, который использует оценку (таблицу значений для каждой переменной), функцию и таблицу функций с частичной производной для создания новой оценки.
Последнее, с чем мне хотелось бы помочь. Любую прямую помощь или указатели на хорошие источники будут тепло оценены.
Edit: Поскольку у меня есть первая и вторая производные в закрытой форме, я хотел бы воспользоваться ими и избежать более медленных методов схождения, таких как поиск симплексов.
Ответы
Ответ 1
Возможно, вам удастся найти то, что вам нужно, в Numerical Recipes на веб-странице C. Существует свободная версия доступная в Интернете. Здесь (PDF) - это глава, содержащая метод Ньютона-Рафсона, реализованный на C. Вы также можете посмотреть, что доступно в Netlib (LINPack и др.).
Ответ 2
Ссылка на числовые рецепты была наиболее полезной. Я закончил символически дифференцировать мою оценку ошибки, чтобы произвести 30 частных производных, а затем использовал метод Ньютона, чтобы установить их все в ноль. Вот основные моменты кода:
__doc.findzero = [[function(functions, partials, point, [epsilon, steps]) returns table, boolean
Where
point is a table mapping variable names to real numbers
(a point in N-dimensional space)
functions is a list of functions, each of which takes a table like
point as an argument
partials is a list of tables; partials[i].x is the partial derivative
of functions[i] with respect to 'x'
epilson is a number that says how close to zero we're trying to get
steps is max number of steps to take (defaults to infinity)
result is a table like 'point', boolean that says 'converged'
]]
-- See Numerical Recipes in C, Section 9.6 [http://www.nrbook.com/a/bookcpdf.php]
function findzero(functions, partials, point, epsilon, steps)
epsilon = epsilon or 1.0e-6
steps = steps or 1/0
assert(#functions > 0)
assert(table.numpairs(partials[1]) == #functions,
'number of functions not equal to number of variables')
local equations = { }
repeat
if Linf(functions, point) <= epsilon then
return point, true
end
for i = 1, #functions do
local F = functions[i](point)
local zero = F
for x, partial in pairs(partials[i]) do
zero = zero + lineq.var(x) * partial(point)
end
equations[i] = lineq.eqn(zero, 0)
end
local delta = table.map(lineq.tonumber, lineq.solve(equations, {}).answers)
point = table.map(function(v, x) return v + delta[x] end, point)
steps = steps - 1
until steps <= 0
return point, false
end
function Linf(functions, point)
-- distance using L-infinity norm
assert(#functions > 0)
local max = 0
for i = 1, #functions do
local z = functions[i](point)
max = math.max(max, math.abs(z))
end
return max
end
Ответ 3
В качестве альтернативы использованию метода Ньютона метод Simplex Nelder-Mead идеально подходит для этой проблемы и упоминается в Numerical Recpies in C.
Rob
Ответ 4
Вы запрашиваете алгоритм минимизации функции. Существует два основных класса: локальный и глобальный. Ваша проблема - наименьшие квадраты, поэтому локальные и глобальные алгоритмы минимизации должны сходиться к одному и тому же уникальному решению. Локальная минимизация намного эффективнее глобальной, поэтому выберите ее.
Существует множество локальных алгоритмов минимизации, но особенно хорошо подходит для задач наименьших квадратов Levenberg-Marquardt. Если у вас нет такого решателя (например, из MINPACK), вы, вероятно, можете уйти с помощью метода Ньютона:
x <- x - (hessian x)^-1 * grad x
где вы вычисляете обратную матрицу, умноженную на вектор, используя линейный решатель.
Ответ 5
Поскольку у вас уже есть частные производные, как насчет общего подхода с градиентом-спуском?
Ответ 6
Возможно, вы думаете, что у вас есть достаточно хорошее решение, но для меня самый простой способ подумать об этом - сначала понять его в случае с 1-переменной, а затем распространить его на матричный случай.
В случае с 1-переменной, если вы разделите первую производную на вторую производную, вы получите (отрицательный) размер шага в следующую пробную точку, например. -V/А.
В случае N-переменной первая производная является вектором, а вторая производная является матрицей (гессиан). Вы умножаете вектор производной на обратную по отношению к второй производной, а результат - отрицательный шаг-вектор на следующую пробную точку, например. -V * (1/А)
Я предполагаю, что вы можете получить матрицу Гессиана 2-й производной. Вам понадобится рутина, чтобы инвертировать ее. Их много в различных пакетах линейной алгебры, и они довольно быстры.
(Для читателей, которые не знакомы с этой идеей, предположим, что две переменные являются x и y, а поверхность v (x, y). Тогда первой производной является вектор:
V = [ dv/dx, dv/dy ]
а вторая производная - матрица:
A = [dV/dx]
[dV/dy]
или
A = [ d(dv/dx)/dx, d(dv/dy)/dx]
[ d(dv/dx)/dy, d(dv/dy)/dy]
или
A = [d^2v/dx^2, d^2v/dydx]
[d^2v/dxdy, d^2v/dy^2]
который является симметричным.)
Если поверхность является параболической (постоянная 2-я производная), она ответит на один шаг. С другой стороны, если 2-я производная очень не постоянна, вы можете столкнуться с колебаниями. Сокращение каждого этапа пополам (или некоторой долей) должно сделать его стабильным.
Если N == 1, вы увидите, что он делает то же самое, что и в случае с 1-переменной.
Удачи.
Добавлено: Вам нужен код:
double X[N];
// Set X to initial estimate
while(!done){
double V[N]; // 1st derivative "velocity" vector
double A[N*N]; // 2nd derivative "acceleration" matrix
double A1[N*N]; // inverse of A
double S[N]; // step vector
CalculateFirstDerivative(V, X);
CalculateSecondDerivative(A, X);
// A1 = 1/A
GetMatrixInverse(A, A1);
// S = V*(1/A)
VectorTimesMatrix(V, A1, S);
// if S is small enough, stop
// X -= S
VectorMinusVector(X, S, X);
}
Ответ 7
Мое мнение заключается в использовании стохастического оптимизатора, например метода частиц Рога.