Метод BFGS, итерационный метод численной оптимизации, назван в честь его исследователей: Broyden, Fletcher, Goldfarb, Shanno. Относится к классу так называемых квазиньютоновских методов. В отличие от ньютоновских методов в квазиньютоновских не вычисляется напрямую гессиан функции, т.е. нет необходимости находить частные производные второго порядка. Вместо этого гессиан вычисляется приближенно, исходя из сделанных до этого шагов.
Существует несколько модификаций метода:
L-BFGS (ограниченное использование памяти) — используется в случае большого количества неизвестных.
L-BFGS-B — модификация с ограниченным использованием памяти в многомерном кубе.
Метод эффективен и устойчив, поэтому зачастую применяется в функциях оптимизации. Например в SciPy, популярной библиотеки для языка python, в функции optimize по умолчанию применяется BFGS, L-BFGS-B.
Алгоритм
Пусть задана некоторая функция и мы решаем задачу оптимизации: .
Где в общем случае является не выпуклой функцией, которая имеет непрерывные вторые производные.
Шаг №1
Инициализируем начальную точку ;
Задаем точность поиска > 0;
Определяем начальное приближение , где — обратный гессиан функции;
Каким нужно выбрать начальное приближение ?
К сожалению не существует общей формулы, которая хорошо бы работала во всех случаях. В качестве начального приближения можно взять гессиан функции, вычисленный в начальной точке . Иначе можно использовать хорошо обусловленную, невырожденную матрицу, на практике часто берут единичную матрицу.
Шаг №2
Находим точку, в направлении которой будем производить поиск, она определяется следующим образом:
Шаг №3
Вычисляем через рекуррентное соотношение:
Коэффициент находим используя линейный поиск (line search), где удовлетворяет условиям Вольфе (Wolfe conditions):
Константы и выбирают следующим образом: . В большинстве реализаций: и .
Фактически мы находим такое при котором значение функции минимально.
Шаг №4
Определяем вектора:
— шаг алгоритма на итерации, — изменение градиента на итерации.
Шаг №5
Обновляем гессиан функции, согласно следующей формуле:
где
— единичная матрица.
Замечание
Выражение вида является внешним произведением (outer product) двух векторов.
Пусть определены два вектора и , тогда их внешнее произведение эквивалентно матричному произведению . Например, для векторов на плоскости:
Шаг №6
Алгоритм продолжает выполнятся до тех пор пока истинно неравенство: .
Алгоритм схематически
Пример
Найти экстремум следующей функции:
В качестве начальной точки возьмем ;
Необходимая точность ;
Находим градиент функции :
Итерация №0:
Находим градиент в точке :
Проверка на окончание поиска:
Неравенство не выполняется, поэтому продолжаем процесс итераций.
Находим точку в направлении которой будем производить поиск:
где — единичная матрица.
Ищем такое 0, чтобы
Аналитически задачу можно свести к нахождению экстремума функции от одной переменной:
Тогда
Упростив выражение, находим производную:
Находим следующую точку :
Вычисляем значение градиента в т. :
Находим приближение гессиана:
Итерация 1:
Проверка на окончание поиска:
Неравенство не выполняется, поэтому продолжаем процесс итераций:
Находим > 0:
Тогда:
Вычисляем значение градиента в точке :
Итерация 2:
Проверка на окончание поиска:
Неравенство выполняется следовательно поиска заканчивается. Аналитически находим экстремум функции он достигается в точке .
Еще о методе
Каждая итерация может быть совершена со стоимостью ( плюс стоимость вычисления функции и оценки градиента). Здесь нет операций таких, как решение линейных систем или сложных математических операций. Алгоритм устойчив и имеет сверхлинейную сходимость, чего достаточно для большинства практических задач. Даже если методы Ньютона сходятся гораздо быстрее (квадратично), стоимость каждой итерации выше, поскольку необходимо решать линейные системы. Неоспоримое преимущество алгоритма, конечно, состоит в том, что нет необходимости вычислять вторые производные.
Очень мало теоретически известно о сходимости алгоритма для случая не выпуклой гладкой функции, хотя общепризнанно, что метод хорошо работает на практике.
Формула BFGS имеет самокорректирующиеся свойства. Если матрица не верно оценивает кривизну функции и если эта плохая оценка замедляет алгоритм, тогда апроксимация гессиана стремится исправить ситуацию за несколько шагов. Самокорректирующие свойства алгоритма работают только в том случае, если реализован соответствующий линейный поиск (соблюдены условия Вольфе).
Пример реализации на Python
Алгоритм реализован с использованием библиотек numpy и scipy. Линейный поиск был реализован посредством использования функция line_search() из модуля scipy.optimize. В примере наложено ограничение на количество итераций.
'''
Pure Python/Numpy implementation of the BFGS algorithm.
Reference: https://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm
'''
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import numpy.linalg as ln
import scipy as sp
import scipy.optimize
# Objective function
def f(x):
return x[0]**2 - x[0]*x[1] + x[1]**2 + 9*x[0] - 6*x[1] + 20
# Derivative
def f1(x):
return np.array([2 * x[0] - x[1] + 9, -x[0] + 2*x[1] - 6])
def bfgs_method(f, fprime, x0, maxiter=None, epsi=10e-3):
"""
Minimize a function func using the BFGS algorithm.
Parameters
----------
func : f(x)
Function to minimise.
x0 : ndarray
Initial guess.
fprime : fprime(x)
The gradient of `func`.
"""
if maxiter is None:
maxiter = len(x0) * 200
# initial values
k = 0
gfk = fprime(x0)
N = len(x0)
# Set the Identity matrix I.
I = np.eye(N, dtype=int)
Hk = I
xk = x0
while ln.norm(gfk) > epsi and k < maxiter:
# pk - direction of search
pk = -np.dot(Hk, gfk)
# Line search constants for the Wolfe conditions.
# Repeating the line search
# line_search returns not only alpha
# but only this value is interesting for us
line_search = sp.optimize.line_search(f, f1, xk, pk)
alpha_k = line_search[0]
xkp1 = xk + alpha_k * pk
sk = xkp1 - xk
xk = xkp1
gfkp1 = fprime(xkp1)
yk = gfkp1 - gfk
gfk = gfkp1
k += 1
ro = 1.0 / (np.dot(yk, sk))
A1 = I - ro * sk[:, np.newaxis] * yk[np.newaxis, :]
A2 = I - ro * yk[:, np.newaxis] * sk[np.newaxis, :]
Hk = np.dot(A1, np.dot(Hk, A2)) + (ro * sk[:, np.newaxis] *
sk[np.newaxis, :])
return (xk, k)
result, k = bfgs_method(f, f1, np.array([1, 1]))
print('Result of BFGS method:')
print('Final Result (best point): %s' % (result))
print('Iteration Count: %s' % (k))
Спасибо за интерес проявленный к моей статье. Надеюсь она была Вам полезна и Вы узнали много нового.
С вами был FUNNYDMAN. Всем пока!