Как определить, что метод Ньютона не работает

Я создаю базовый алгоритм метода Ньютона для задачи неограниченной оптимизации, и мои результаты от алгоритма не соответствуют моим ожиданиям. Это простая целевая функция, поэтому ясно, что алгоритм должен сходиться к (1,1). Это подтверждает алгоритм градиентного спуска, который я создал ранее, здесь:

def grad_descent(x, t, count, magnitude):
    xvalues.append(x)
    gradvalues.append(np.array([dfx1(x), dfx2(x)]))
    fvalues.append(f(x))   
    temp=x-t*dfx(x)
    x = temp
    magnitude = mag(dfx(x))    
    count+=1

    return xvalues, gradvalues, fvalues, count

Моя попытка создать алгоритм для метода Ньютона находится здесь:

def newton(x, t, count, magnitude):
  xvalues=[]
  gradvalues=[]
  fvalues=[]
  temp=x-f(x)/dfx(x)

  while count < 10:
    xvalues.append(x)
    gradvalues.append(dfx(x))
    fvalues.append(f(x))  

    temp=x-t*f(x)/dfx(x)
    x = temp
    magnitude = mag(dfx(x))    
    count+=1
    if count > 100:
      break
  return xvalues, gradvalues, fvalues, count

Вот целевая функция и функция градиента:

f = lambda x: 100*np.square(x[1]-np.square(x[0])) + np.square((1-x[0]))
dfx = lambda x: np.array([-400*x[0]*x[1]+400*np.power(x[0],3)+2*x[0]-2, 200*(x[1]-np.square(x[0]))])

Вот начальные условия. Обратите внимание, что альфа и бета не используются в методе Ньютона.

x0, t0, alpha, beta, count = np.array([-1.1, 1.1]), 1, .15, .7, 1
magnitude = mag(np.array([dfx1(x0), dfx2(x0)]))

Чтобы вызвать функцию:

xvalues, gradvalues, fvalues, iterations = newton(x0, t0, count, magnitude)

Это дает очень странные результаты. Вот первые 10 итераций значений x, значений градиента и решения функции для соответствующего входа x:

[array([-1.1,  1.1]), array([-0.99315589,  1.35545455]), array([-1.11651296,  1.11709035]), array([-1.01732476,  1.35478987]), array([-1.13070578,  1.13125051]), array([-1.03603697,  1.35903467]), array([-1.14368874,  1.14364506]), array([-1.05188162,  1.36561528]), array([-1.15600558,  1.15480705]), array([-1.06599492,  1.37360245])]
[array([-52.6, -22. ]), array([142.64160215,  73.81918332]), array([-62.07323963, -25.90216846]), array([126.11789251,  63.96803995]), array([-70.85773749, -29.44900758]), array([114.31050737,  57.13241151]), array([-79.48668009, -32.87577304]), array([104.93863096,  51.83206539]), array([-88.25737032, -36.308371  ]), array([97.03403558, 47.45145765])]
[5.620000000000003, 17.59584998020613, 6.156932949106968, 14.29937453260906, 6.7080172227439725, 12.305727666787176, 7.297442528545537, 10.926625703722639, 7.944104584786208, 9.89743708419569]  

Вот окончательный результат:

final_value = print('Final set of x values: ', xvalues[-1])
final_grad = print('Final gradient values: ', gradvalues[-1])
final_f = print('Final value of the object function with optimized inputs: ', fvalues[-1])
final_grad_mag = print('Final magnitude of the gradient with optimized inputs: ', mag(np.array([dfx1(xvalues[-1]), dfx2(xvalues[-1])])))
total_iterations = print('Total iterations: ', iterations)

здесь показан трехмерный график:

x = np.array([i[0] for i in xvalues])
y = np.array([i[1] for i in xvalues])
z = np.array(fvalues)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(x, y, z, label='Newton Method')
ax.legend()

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


person RocketSocks22    schedule 10.09.2018    source источник
comment
Я думаю, нам нужно увидеть функции f(x) и dfx(x). Кроме того, какова цель (и ценность) t в ваших расчетах? Короче говоря, для этого вопроса нужен минимальный воспроизводимый пример.   -  person user3386109    schedule 11.09.2018
comment
Просто добавил в f(x) и df(x). t установлен в 1 для этого конкретного алгоритма, поэтому он не имеет смысла.   -  person RocketSocks22    schedule 11.09.2018
comment
Я подозреваю, что это всего одна или две строки, но не могли бы вы закончить это основной программой, которая производит вывод, который вы показываете?   -  person Prune    schedule 11.09.2018
comment
Просто добавлен в вызов функции, начальные условия и вывод (анонимно случайно...) Заранее извиняюсь за неправильное форматирование вопроса. Я новичок в stackoverflow и все еще пытаюсь изучить веревки   -  person RocketSocks22    schedule 11.09.2018
comment
Правильны ли функция и гессиан? Мне кажется, что минимум для данной функции находится в точке (1,1), а не (-1,1).   -  person Hans Olsson    schedule 11.09.2018
comment
Вы правы, что он должен сходиться в (1, 1). Я исправил эту ошибку в исходном вопросе. Это подтверждается алгоритмом градиентного спуска.   -  person RocketSocks22    schedule 11.09.2018


Ответы (3)


Я думаю, что нашел часть проблемы. Я использовал неправильный алгоритм Ньютона. Раньше я использовал:
x{k+1} = x{k}-f(x)f (х)

Правильное обновление:
x{k+1} = x{k} - [f''(x{k} )]-1f'(x{k})

Когда я изменил это, результат все еще расходится, но он немного лучше. Новая функция здесь:

f = lambda x: 100*np.square(x[1]-np.square(x[0])) + np.square((1-x[0]))
dfx1 = lambda x: -400*x[0]*x[1]+400*np.power(x[0],3)+2*x[0]-2
dfx2 = lambda x: 200*(x[1]-np.square(x[0]))
dfx = lambda x: np.array([-400*x[0]*x[1]+400*np.power(x[0],3)+2*x[0]-2, 200*(x[1]-np.square(x[0]))])
dfx11 = lambda x: -400*(x[1])+1200*np.square(x[0])+2
dfx12 = lambda x: -400*x[0]
dfx21 = lambda x: -400*x[0]
dfx22 = lambda x: 200
hessian = lambda x: np.array(([dfx11(x0), dfx12(x0)], [dfx21(x0), dfx22(x0)]))
inv_hessian = lambda x: inv(np.array(([dfx11(x0), dfx12(x0)], [dfx21(x0), dfx22(x0)])))  

def newton(x, t, count, magnitude):
  xvalues=[]
  gradvalues=[]
  fvalues=[]
  temp = x-(inv_hessian(x).dot(dfx(x)))

  while count < 25:
    xvalues.append(x)
    gradvalues.append(dfx(x))
    fvalues.append(f(x))  

    temp = x-(inv_hessian(x).dot(dfx(x)))
    x = temp
    magnitude = mag(dfx(x))    
    count+=1
    if count > 100:
      break
  return xvalues, gradvalues, fvalues, count

Ближайшее решение подходит к схождению после первого шага, где оно переходит в (-1,05, 1,1). Однако все равно расходится. Я никогда не работал с методом Ньютона, поэтому я не уверен, так ли он точен, как должен получить алгоритм, или нет.

введите здесь описание изображения

person RocketSocks22    schedule 11.09.2018
comment
Единственное, что здесь верно, так это то, что корень производной является либо максимумом, либо минимумом функции. До сих пор неясно, действительно ли функция, ее производная и ее вторая производная действительны и связаны между собой. - person user3386109; 11.09.2018
comment
Что вы подразумеваете под «действительным и родственным»? - person RocketSocks22; 11.09.2018
comment
Связанные означает, что dfx на самом деле является производной от f, а f'' на самом деле является производной от dfx. Действительный означает плавный и непрерывный, а f'' не имеет нуля рядом с x. Если f'' действительно имеет ноль рядом с x, то в алгоритме есть проблема с делением на ноль. - person user3386109; 11.09.2018
comment
Я уверен, что производные верны. Что касается достоверности, я не знаю, как проверить наличие нулей для f ''. И какие ограничения квалифицируются как «около» x? ‹.01 разница? - person RocketSocks22; 11.09.2018
comment
Я думаю, что лучший подход — проверить f перед делением. Если значение меньше, чем эпсилон (например, 1e-12), создайте исключение. - person user3386109; 11.09.2018
comment
Аккуратные свойства арифметики с плавающей запятой: деление на числа, близкие к 0, является проблемой только в том случае, если они близки к 0 из-за некоторого процесса, который вызывает потерю точности. Вам все равно нужно будет проверить, что это не точно 0, но нет никакой причины выбирать 1e-1 против 1e-200 для проверки деления около 0 во время выполнения. - person Hans Musgrave; 11.09.2018
comment
Причина, по которой вы можете получить потерю точности из-за этого, заключается в том, что процесс, который произвел количество, близкое к 0, сам имел потерю точности. Затем разделение усиливает проблему. Классический пример — вычитание двух чисел, которые велики относительно их разности. - person Hans Musgrave; 11.09.2018
comment
Я понимаю, но f'' в данном случае не просто одно число. f'' - матрица Гессе 2x2. Как проверить, что матрица не близка к нулю? Кроме того, с обновленным алгоритмом f'' не делится, поэтому я не думаю, что это должно быть проблемой. - person RocketSocks22; 11.09.2018

Теперь я уверен, что с кодом Python что-то не так. Я решил реализовать алгоритм в Matlab, и, похоже, он работает нормально. Вот этот код:

clear; clc;
x=[-1.1, 1.1]';
t=1;
count=1;

xvalues=[];

temp = x - inv([(-400*x(2)+1200*x(1)^2+2), -400*x(1); -400*x(1), 200]);
disp(x-inv([(-400*x(2)+1200*x(1)^2+2), -400*x(1); -400*x(1), 200])*[-400*x(1)*x(2)+400*x(1)^3+2*x(1)-2; 200*(x(2)-x(1)^2)])

while count<10
    xvalues(count,:)= x;
    temp = x - inv([(-400*x(2)+1200*x(1)^2+2), -400*x(1); -400*x(1), 200]) * [-400*x(1)*x(2)+400*x(1)^3+2*x(1)-2; 200*(x(2)-x(1)^2)];    
    x = temp;
    count = count+1;
end

disp(xvalues)

Выход:

-1.1000    1.1000
   -1.0087    1.0091
   -0.2556   -0.5018
   -0.2446    0.0597
    0.9707   -0.5348
    0.9708    0.9425
    1.0000    0.9991
    1.0000    1.0000
    1.0000    1.0000
person RocketSocks22    schedule 12.09.2018

Так что я, наконец, понял, что происходит с этим. Все дело в том, в каких структурах данных Python хранит мои переменные. Таким образом, я установил все свои значения в «float32» и инициализировал итерируемые переменные. Рабочий код здесь:

Примечание: лямбда-функция — это анонимная функция, полезная для однострочных выражений.

f = lambda x: 100*np.square(x[1]-np.square(x[0])) + np.square((1-x[0]))
dfx = lambda x: np.array([-400*x[0]*x[1]+400*np.power(x[0],3)+2*x[0]-2, 200*(x[1]-np.square(x[0]))], dtype='float32')
dfx11 = lambda x: -400*(x[1])+1200*np.square(x[0])+2
dfx12 = lambda x: -400*x[0]
dfx21 = lambda x: -400*x[0]
dfx22 = lambda x: 200
hessian = lambda x: np.array([[dfx11(x), dfx12(x)], [dfx21(x), dfx22(x)]], dtype='float32')
inv_hessian = lambda x: inv(hessian(x))
mag = lambda x: math.sqrt(sum(i**2 for i in x))

def newton(x, t, count, magnitude):
  xvalues=[]
  gradvalues=[]
  fvalues=[]
  temp = np.zeros((2,1))

  while magnitude > .000005:
    xvalues.append(x)
    gradvalues.append(dfx(x))
    fvalues.append(f(x))      

    deltaX = np.array(np.dot(-inv_hessian(x), dfx(x)))
    temp = np.array(x+t*deltaX)
    x = temp
    magnitude = mag(deltaX)    
    count+=1
  return xvalues, gradvalues, fvalues, count

x0, t0, alpha, beta, count = np.array([[-1.1], [1.1]]), 1, .15, .7, 1
xvalues, gradvalues, fvalues, iterations = newton(x0, t0, count, magnitude)

final_value = print('Final set of x values: ', xvalues[-1])
final_grad = print('Final gradient values: ', gradvalues[-1])
final_f = print('Final value of the object function with optimized inputs: ', fvalues[-1])
final_grad_mag = print('Final magnitude of the gradient with optimized inputs: ', mag(np.array([dfx1(xvalues[-1]), dfx2(xvalues[-1])])))
total_iterations = print('Total iterations: ', iterations
print(xvalues)

Выход:

Final set of x values:  [[0.99999995]
 [0.99999987]]
Final gradient values:  [[ 9.1299416e-06]
 [-4.6193604e-06]]
Final value of the object function with optimized inputs:  [5.63044182e-14]
Final magnitude of the gradient with optimized inputs:  1.02320249276675e-05
Total iterations:  9
[array([[-1.1],
       [ 1.1]]), array([[-1.00869558],
       [ 1.00913081]]), array([[-0.25557778],
       [-0.50186648]]), array([[-0.24460602],
       [ 0.05971173]]), array([[ 0.97073805],
       [-0.53472879]]), array([[0.97083687],
       [0.94252417]]), array([[0.99999957],
       [0.99914868]]), array([[0.99999995],
       [0.99999987]])]
person RocketSocks22    schedule 16.09.2018