
Метод 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. Всем пока!
