Как найти минимум нелинейной, многомерной функции, используя метод Ньютона (код нелинейной алгебры) - PullRequest
12 голосов
/ 24 декабря 2008

Я пытаюсь провести некоторую оценку параметров и хочу выбрать оценки параметров, которые минимизируют квадратную ошибку в прогнозируемом уравнении для более чем 30 переменных . Если бы уравнение было линейным, я бы просто вычислил 30 частных производных, установил их все в ноль и использовал решатель линейных уравнений. Но, к сожалению, уравнение нелинейно , как и его производные.

Если бы уравнение было над одной переменной, я бы просто использовал метод Ньютона (также известный как Ньютон-Рафсон). Сеть богата примерами и кодом для реализации метода Ньютона для функций одной переменной .

Учитывая, что у меня около 30 переменных, как мне запрограммировать численное решение этой проблемы, используя метод Ньютона ? У меня есть уравнение в замкнутой форме, и я могу вычислить первую и вторую производные, но я не знаю, как действовать дальше. Я нашел большое количество обработок в сети, но они быстро попадают в тяжелую матричную нотацию. Я нашел что-то в меру полезное в Википедии, но у меня возникли проблемы с переводом в код.

Я беспокоюсь о том, чтобы сломаться, - это алгебра матриц и инверсии матриц. Я могу инвертировать матрицу с помощью решателя линейных уравнений, но я беспокоюсь о том, чтобы получить правильные строки и столбцы, избежать ошибок транспонирования и т. Д.

Чтобы быть вполне конкретным:

  • Я хочу работать с таблицами, отображая переменные в их значения. Я могу написать функцию такой таблицы, которая возвращает квадратную ошибку, указав такую ​​таблицу в качестве аргумента. Я также могу создавать функции, которые возвращают частную производную по любой данной переменной.

  • У меня есть разумная начальная оценка для значений в таблице, поэтому я не беспокоюсь о сходимости.

  • Я не уверен, как написать цикл, который использует оценку (таблицу значений для каждой переменной), функцию и таблицу функций с частными производными для получения новой оценки.

Это последнее, с чем я хотел бы помочь. Мы будем признательны за любую прямую помощь или указатели на хорошие источники.


Редактировать: Поскольку у меня есть первая и вторая производные в закрытой форме, я хотел бы воспользоваться ими и избежать более медленно сходящихся методов, таких как симплексный поиск.

Ответы [ 7 ]

4 голосов
/ 08 января 2009

Ссылка «Численные рецепты» была наиболее полезной. Я получил символическую дифференциацию оценки ошибок, чтобы получить 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 голосов
/ 16 марта 2010

Вы запрашиваете алгоритм минимизации функции. Существует два основных класса: локальный и глобальный. Ваша проблема - это метод наименьших квадратов, поэтому как локальные, так и глобальные алгоритмы минимизации должны сходиться к одному и тому же уникальному решению. Локальная минимизация намного эффективнее глобальной, поэтому выберите это.

Существует много локальных алгоритмов минимизации, но особенно хорошо подходит для задач наименьших квадратов Левенберг-Марквардт . Если у вас нет такого решателя под рукой (например, из MINPACK), то вы, вероятно, можете воспользоваться методом Ньютона:

x <- x - (hessian x)^-1 * grad x

где вы вычисляете обратную матрицу, умноженную на вектор, используя линейный решатель.

3 голосов
/ 24 декабря 2008

В качестве альтернативы использованию метода Ньютона Симплексный метод Нелдера-Мида идеально подходит для этой задачи и упоминается в «Числовых реципиентах» в C.

Rob

3 голосов
/ 24 декабря 2008

Вы можете найти то, что вам нужно, на веб-странице «Численные рецепты на Си». бесплатная версия доступна онлайн . Здесь (PDF) - глава, содержащая метод Ньютона-Рафсона, реализованный в C. Вы также можете посмотреть, что доступно в Netlib (LINPack и др.)

1 голос
/ 14 января 2009

Может быть, вы думаете, что у вас есть достаточно хорошее решение, но для меня самый простой способ подумать об этом - сначала понять его в случае с 1 переменной, а затем распространить его на случай матрицы.

В случае с 1 переменной, если вы разделите первую производную на вторую производную, вы получите (отрицательный) размер шага до следующей пробной точки, например, -V / A.

В случае N-переменной первая производная является вектором, а вторая производная является матрицей (гессианом). Вы умножаете вектор производной на значение, обратное второй производной, и в результате получается отрицательный шаговый вектор к следующей точке испытания, например, -V * (1 / А)

Полагаю, вы можете получить матрицу Гессиана со второй производной. Вам понадобится рутина, чтобы инвертировать его. Их много в различных пакетах линейной алгебры, и они довольно быстрые.

(Для читателей, которые не знакомы с этой идеей, предположим, что две переменные - это 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-я производная), она получит ответ за 1 шаг. С другой стороны, если 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);
}
1 голос
/ 08 января 2009

Поскольку у вас уже есть частные производные, как насчет общего подхода с градиентным спуском?

0 голосов
/ 14 января 2009

Мое мнение состоит в том, чтобы использовать стохастический оптимизатор, например, метод Particle Swarm.

...