前言
本文介绍了无约束问题中常用的两种算法,最速下降法和拟牛顿法(BFGS算法),最后通过matlab编程实现了以上两种算法,并对实际问题进行求解。
一、 最速下降法
简介
最速下降法,又叫梯度下降法,是一种求解无约束问题的常用算法,以负梯度方向作为下降方向的算法。
收敛速度
求解严格凸二次函数极小化问题时恰好具有线性收敛速度。
优缺点
优点:具有存储量少的特点,且算法简单易理解。
缺点:收敛速度较慢。
算法步骤
步1:给定初始点 x ( 0 ) ∈ R n x^{(0)} \in R^n x(0)∈Rn,精度 ϵ > 0 \epsilon > 0 ϵ>0. 令k=0.
步2:若 ∣ ∣ ∇ f ( x ( k ) ) ∣ ∣ ≤ ϵ ||\nabla f(x^{(k)})|| \leq \epsilon ∣∣∇f(x(k))∣∣≤ϵ,则算法终止. 得解 x ( k ) x^{(k)} x(k). 否则,计算 d ( k ) = − ∇ f ( x ( k ) ) d^{(k)} = -\nabla f(x^{(k)}) d(k)=−∇f(x(k)). 转步3.
步3:由线性搜索确定步长 α k \alpha_k αk.
步4:令 x ( k + 1 ) = x ( k ) + α k d ( k ) , k = k + 1. x^{(k+1)} = x^{(k)} + \alpha_kd^{(k)}, k=k+1. x(k+1)=x(k)+αkd(k),k=k+1.转步2.
matlab实现
说明:函数中使用的initpt函数(用于初始化 x 0 x_0 x0)、Wolfe_Powell函数(用于进行线性搜索)和gradfcn函数(用于求解问题的梯度)是为了自己编写的辅助函数,这几个函数的具体实现可见最下方的链接处。(现在才发现原来这里是没有matlab的代码块的…)
function [iter_num, time, value] = gradient_descent(nprob,n, x, epsilon, max_iter)
% 最速下降法 2020.06.15
% @author: 豆奶
% 函数功能:实现最速下降法
% 输入:
% nprob: 问题编号,这个是由于我需要对某些问题进行求解,因此有着问题编号
% n: 维度
% x: 自变量x
% epsilon: int,
% max_iter: 最大迭代次数
% 输出:
% iter_num: 迭代次数
% time: 计算时间
% value: 目标函数的梯度的模的最终值
t1=clock; % 计时开始
% 步1:初始化初始点x,精度epsilon>0.
if nargin==2
x = initpt(n, nprob); % 初始化x_0,这里利用自己编写的函数initpt进行初始化
epsilon = 1e-3;
max_iter = 500;
end
for iter_num = 0:max_iter
% 步2:若f的梯度的模小于epsilon,终止算法. 得解xk. 否则,计算dk = -\nabla f(xk).
if norm(gradfcn(n, x, nprob))<=epsilon
break;
end
d = -gradfcn(n, x, nprob); % 该函数为自己编写的用于获取求解问题的梯度
%tic
% 步3:由线性搜索确定步长alpha,这里采用Wolfe_Powell型线性搜索.
alpha = Wolfe_Powell(0.1, 0.9, 15, 0.2, 0.5, d, n, x, nprob);
%toc %利用tic toc可以计算程序运行时间
% 步4:更新xk
x = x + alpha*d;
end
% 获取输出值
t2=clock;
value = norm(gradfcn(n, x, nprob));
time = etime(t2,t1);
%fprintf('问题编号\t\t运行次数\t\t运行时间\t\t\t最后的梯度\n');
fprintf('\t%2d\t\t最速下降法\t\t%5d\t\t%2f\t\t%5d\n', nprob, iter_num, time, value);
return;
end
二、BFGS算法
简介
牛顿法具有二次收敛性,而当 ∇ 2 f ( x ( k ) ) \nabla^2 f(x^{(k)}) ∇2f(x(k))不正定时,算法产生的方向不能保证是f在 x ( k ) x^{(k)} x(k)处的下降方向. 当 ∇ 2 f ( x ( k ) ) \nabla^2 f(x^{(k)}) ∇2f(x(k))奇异时,牛顿方向甚至可能不存在。为了克服牛顿法的上述困难,于是有了修正牛顿法和拟牛顿法。下面讲述的BFGS算法便是拟牛顿法的一种。拟牛顿法的基本思想是在牛顿法中,采用 ∇ 2 f ( x ( k ) ) \nabla^2 f(x^{(k)}) ∇2f(x(k))的某个近似矩阵 B k B_k Bk取代 ∇ 2 f ( x ( k ) ) \nabla^2 f(x^{(k)}) ∇2f(x(k)).
收敛速度
在一定的条件下具有超线性收敛速度,且无须计算函数f的二阶导数.
优缺点
优点:收敛速度较快
缺点:算法需要存储矩阵 B k B_k Bk,因此在求解大规模问题时会遇到困难。
算法步骤
步1:取初始点 x ( 0 ) ∈ R n x^{(0)} \in R^n x(0)∈Rn,初始对称正定矩阵 B 0 ∈ R n × n B_0 \in R^{n×n} B0∈Rn×n. 精度 ϵ > 0 \epsilon > 0 ϵ>0. 令k=0.
步2:若 ∣ ∣ ∇ f ( x ( k ) ) ∣ ∣ ≤ ϵ ||\nabla f(x^{(k)})|| \leq \epsilon ∣∣∇f(x(k))∣∣≤ϵ,则算法终止. 得问题的解 x ( k ) ) x^{(k)}) x(k)).
步3:解线性方程组 B k d + ∇ f ( x ( k ) ) = 0 B_kd + \nabla f(x^{(k)}) = 0 Bkd+∇f(x(k))=0得解 d ( k ) d^{(k)} d(k).
步4:由线性搜索确定步长 α k \alpha_k αk(一般我使用Wolfe-Powell型线性搜索,因为Armijo线性搜索还需要进行修正才能保证 B k B_k Bk的正定性)
步5:令 x ( k + 1 ) = x ( k ) + α k d ( k ) x^{(k+1)} = x^{(k)} + \alpha_kd^{(k)} x(k+1)=x(k)+αkd(k). 若 ∣ ∣ ∇ f ( x ( k + 1 ) ) ≤ ϵ ||\nabla f(x^{(k+1)}) \leq \epsilon ∣∣∇f(x(k+1))≤ϵ,则得解 x ( k + 1 ) x^{(k+1)} x(k+1). 否则,由BFGS修正公式(如下)确定 B k + 1 B_{k+1} Bk+1.
步6:令k = k+1,转步3.
BFGS修正公式:
B k + 1 = B k − B k s ( k ) s ( k ) T B k s ( k ) T B k s ( k ) + y ( k ) y ( k ) T y ( k ) T s ( k ) B_{k+1} = B_k - \frac{B_ks^{(k)}s^{(k)T}B_k}{s^{(k)T}B_ks^{(k)}} + \frac{y^{(k)}y^{(k)T}}{y^{(k)T}s^{(k)}} Bk+1=Bk−s(k)TBks(k)Bks(k)s(k)TBk+y(k)Ts(k)y(k)y(k)T
其中, s ( k ) = x ( k + 1 ) − x ( k ) , y ( k ) = ∇ f ( k + 1 ) − ∇ f ( k ) s^{(k)} = x^{(k+1)} - x^{(k)}, y^{(k)} = \nabla f^{(k+1)} - \nabla f^{(k)} s(k)=x(k+1)−x(k),y(k)=∇f(k+1)−∇f(k).
function [iter_num, time, value] = BFGS(nprob,n, x, epsilon, max_iter)
% BFGS算法 2020.06.15
% @author: 豆奶
% 函数功能:实现BFGS算法
% 输入:
% nprob: 问题编号
% n: 维度
% x: 自变量x
% epsilon: int, 精度
% max_iter: 最大迭代次数
% 输出:
% iter_num: 迭代次数
% time: 计算时间
% value: 目标函数的梯度的模的最终值
t1=clock; % 计时开始
% 步1:取初始点x0, 初始对称矩阵B0. 精度
if nargin==2
x = initpt(n, nprob);
epsilon = 1e-3;
max_iter = 500;
end
B = eye(n); % 单位矩阵
% 步2:若f的梯度小于epsilon,得解x
if (norm(gradfcn(n, x, nprob))<=epsilon)
value = norm(gradfcn(n, x, nprob));
t2=clock;
time = etime(t2,t1);
return;
end
xk = x;
for k=0:max_iter
% 步3:解线性方程组,得解dk.
gx = gradfcn(n, xk, nprob);
dk = -linsolve(B+0.1*eye(n), gx);
%dk = -pinv(B)*gx; % (n, 1)
% 步4:由线性搜索确定步长alpha,这里采用Wolfe_Powell
alpha = Wolfe_Powell(0.01, 0.6, 15, 0.8, 0.6, dk, n, xk, nprob); % step 4
% 步5:更新xk,并判断是否满足终止条件,若不满足,则由BFGS修正公式确定Bk.
xk1 = xk + alpha*dk; % step 5
gx1 = gradfcn(n, xk1, nprob);
if (norm(gx1)<=epsilon)
break;
end
% 利用秩2修正公式,即上面的BFGS修正公式
sk = xk1 - xk; %alpha*dk; % s_k= x_{k+1}-x_k = alpha*dk % (n, 1) xk1 - xk
yk = gx1 - gx; % (n, 1)
B = B - (B*(sk*sk')*B)/(sk'*B*sk) + (yk*yk')/(yk'*sk);
xk = xk1;
end
% 获取输出值
iter_num = k;
value = norm(gx1);
t2=clock;
time = etime(t2,t1);
%fprintf('问题编号\t\t运行次数\t\t运行时间\t\t\t最后的梯度\n');
fprintf('\t%2d\t\tBFGS算法\t\t%5d\t\t%2f\t\t%5d\n', nprob, iter_num, time, value);
return;
end
参考文献:
相关文章:
常见的约束问题求解算法——乘子法和Frank-Wolfe算法
完整代码与函数问题测试见github(点我)
如有不对之处,还望指出,我会进行修改的,谢谢!