所用例子:
求解二次目标函数极小点。设
设 G 是 n 阶对称正定矩阵,若 n 维向量组 d1,d2,⋯,dm(m≤n) 满足:
step 1 : 给定迭代精度 0≤ϵ≪1 和初始点 x0 . 计算 g0=∇f(x0) . 选取初始方向 d0 ,使得 dT0g0<0 . 令 k←0 .
step 2 : 若 ||gk||≤ϵ ,停止迭代,输出 x∗≈xk
step 3 : 确定搜索步长 αk
step 4 : 令 xk+1←xk+αkdk ,并计算 gk+1=∇f(xk+1)
step 5 : 选取 dk+1 满足如下下降性和共轭性条件:dTk+1gk+1<0,dTk+1Gdi=0,i=0,1,⋯,k
step 6 : k←k+1 ,转 step 2
设目标函数为之前定义的 f(x) , {xk} 是有算法产生的迭代序列,则每一步迭代 xk+1 都是 f(x) 在 x0 和方向 d0,d1,⋯,dk 所形成的线性流形
设任意 x∈Sk ,存在 γi∈R(i=0,1,⋯,k) ,使得
故每一步迭代 xk+1 都是 f(x) 在 x0 和方向 d0,d1,⋯,dk 所形成的线性流形
设 x∗ 是目标函数的极小值点, x0 为不同于 x∗ 的任意一点,则它们的差向量可以表示为
将其改写成如下形式
用共轭方向法的思想可以解决前面给出的二次目标函数 f(x)=12xTGx+bTx+c 的极小值,这等同于求线性方程组 Gx=b 的解。
在寻优过程中利用当前点 xk 处的梯度向量 gk 和前一迭代点 xk 处的搜索方向 dk−1 对搜索方向进行如下修正:
其修正系数 βk−1 的取值有一个约束条件,即要确保 dk 与 dk−1,dk−2,⋯,d0 之间满足关于 G 的共轭关系。这就是共轭梯度法的基本思想。
修正系数 βk−1 的取值方法有多个,下面的例子采用的取值公式为
可以看出共轭梯度法的搜索方向 dk 的计算只需要梯度向量,不需要矩阵 G ,可以推广到非二次目标函数的极小值求解,但是这种推广也带来了构造的搜索向量序列 {dk} 不共轭的问题,后面有提到解决办法。
step 1 : 给定迭代精度 0≤ϵ≪1 和初始点 x0 . 计算 g0=∇f(x0) . . 令 k←0 .
step 2 : 若 ||gk||≤ϵ ,停止迭代,输出 x∗≈xk
step 3 : 计算搜索方向 dk
dk={−gk−gk+βk−1dk−1k=0k≥1
step 4 : 利用线搜索方法确定搜索步长 αk
step 5 : 令 令 xk+1←xk+αkdk ,并计算 gk+1=∇f(xk+1)
step 6 : k←k+1 ,转 step 2
说明:
通常来说,共轭梯度法的收敛速度比最速下降法快,而且不用像牛顿法那样计算海森矩阵及其逆矩阵。但是随着迭代次数的增加,新构造的共轭方向由于误差(如果目标函数不是二次函数则会造成这种误差)积累会逐渐不精确甚至不下降,可能出现收敛速度极慢的现象。为了避免这种现象,一种有效的改进办法是:
每迭代 n 次或者不下降时就再次插入负梯度方向作为搜索方向,从新开始共轭梯度算法。下面的代码就采用了这种思想。
def frcg(fun,gfun,x0):
#用FR共轭梯度法求解无约束问题
#x0是初始点,fun和gfun分别是目标函数和梯度
#x,val分别是近似最优点和最优值,k是迭代次数
maxk = 5000
rho = 0.6
sigma = 0.4
k = 0
epsilon = 1e-5
n = np.shape(x0)[0]
itern = 0
while k < maxk:
gk = gfun(x0)
itern += 1
itern %= n
if itern == 1:
dk = -gk
else:
beta = 1.0*np.dot(gk,gk)/np.dot(g0,g0)
dk = -gk + beta*d0
gd = np.dot(gk,dk)
if gd >= 0.0:
dk = -gk
if np.linalg.norm(gk) < epsilon:
break
m = 0
mk = 0
while m < 20:
if fun(x0+rho**m*dk) < fun(x0) + sigma*rho**m*np.dot(gk,dk):
mk = m
break
m += 1
x0 += rho**mk*dk
g0 = gk
d0 = dk
k += 1
return x0,fun(x0),k
性能展示
与拟牛顿法http://blog.csdn.net/u012176591/article/details/46225289 对比,发现共轭梯度法还是挺挫的,需要的迭代次数很多,超过一半的样本的迭代次数超过500(上图没有显示)。
作图代码:
n = 50
x = y = np.linspace(-10,10,n)
xx,yy = np.meshgrid(x,y)
data = [[xx[i][j],yy[i][j]] for i in range(n) for j in range(n)]
iters = []
for i in xrange(np.shape(data)[0]):
rt = frcg(fun,gfun,data[i])
if rt[2] <=200:
iters.append(rt[2])
if i%100 == 0:
print i,rt[2]
plt.hist(iters,bins=50)
plt.title(u'共轭梯度法迭代次数分布',{'fontname':'STFangsong','fontsize':18})
plt.xlabel(u'迭代次数',{'fontname':'STFangsong','fontsize':18})
plt.ylabel(u'频率分布',{'fontname':'STFangsong','fontsize':18})
参考文献: