
Метод BFGS, итерационный метод численной оптимизации, назван в честь его исследователей: Broyden, Fletcher, Goldfarb, Shanno. Относится к классу так называемых квазиньютоновских методов. В отличие от ньютоновских методов в квазиньютоновских не вычисляется напрямую гессиан функции, т.е. нет необходимости находить частные производные второго порядка. Вместо этого гессиан вычисляется приближенно, исходя из сделанных до этого шагов.
Существует несколько модификаций метода:
L-BFGS (ограниченное использование памяти) — используется в случае большого количества неизвестных.
L-BFGS-B — модификация с ограниченным использованием памяти в многомерном кубе.
Метод эффективен и устойчив, поэтому зачастую применяется в функциях оптимизации. Например в SciPy, популярной библиотеки для языка python, в функции optimize по умолчанию применяется BFGS, L-BFGS-B.
Алгоритм
Пусть задана некоторая функция
Где в общем случае
Шаг №1
Инициализируем начальную точку
Задаем точность поиска
Определяем начальное приближение
Каким нужно выбрать начальное приближение
К сожалению не существует общей формулы, которая хорошо бы работала во всех случаях. В качестве начального приближения можно взять гессиан функции, вычисленный в начальной точке
Шаг №2
Находим точку, в направлении которой будем производить поиск, она определяется следующим образом:
Шаг №3
Вычисляем
Коэффициент
Константы
Фактически мы находим такое
Шаг №4
Определяем вектора:
Шаг №5
Обновляем гессиан функции, согласно следующей формуле:
где
Замечание
Выражение вида
Пусть определены два вектора
Шаг №6
Алгоритм продолжает выполнятся до тех пор пока истинно неравенство:
Алгоритм схематически

Пример
Найти экстремум следующей функции:
В качестве начальной точки возьмем
Необходимая точность
Находим градиент функции
Итерация №0:
Находим градиент в точке
Проверка на окончание поиска:
Неравенство не выполняется, поэтому продолжаем процесс итераций.
Находим точку в направлении которой будем производить поиск:
где
Ищем такое
Аналитически задачу можно свести к нахождению экстремума функции от одной переменной:
Тогда
Упростив выражение, находим производную:
Находим следующую точку
Вычисляем значение градиента в т.
Находим приближение гессиана:
Итерация 1:
Проверка на окончание поиска:
Неравенство не выполняется, поэтому продолжаем процесс итераций:
Находим
Тогда:
Вычисляем значение градиента в точке
Итерация 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. Всем пока!