小伙伴们,“五一节”快乐。节前我们讲到了牛顿法的推导,以及拟牛顿法中DFP算法的推导与Python手写实现。今天我们继续来讲讲拟牛顿法中的BFGS算法的推导和实现。
1、BFGS算法(Broyden-Fletcher-Goldfarb-Shanno)推导在DFP算法中,我们用矩阵
逼近海塞矩阵的逆矩阵
,相应的拟牛顿条件是:DFP算法的拟牛顿条件在BFGS算法中,我们用矩阵
逼近海塞矩阵
,相应的拟牛顿条件是:BFGS算法的拟牛顿条件同样地,我们假设:两边乘以
,可得:为满足上式要求,我们不妨令:求解矩阵P过程如下:求解矩阵Q过程如下:最终,BFGS算法中的迭代公式如下:到这里,我们已经能用矩阵B来逼近海塞矩阵H了,不过这并不是我们的最终目的,我们最终还是希望得到矩阵B的逆矩阵的迭代公式,因此我们接着用Sherman-Morrison公式进行推导。Sherman-Morrison公式推导过程省略,最后得到矩阵B的逆矩阵迭代公式如下:BFGS算法中逼近海塞矩阵逆矩阵的迭代公式
Python实现BFGS算法:
我们接着上次DFP算法来添加方法。
def train(self, X, y, n_iters=1000, learning_rate=0.01):
"""fit model"""
if X.ndim < 2:
raise ValueError("X must be 2D array-like!")
self.trainSet = X
self.label = y
if self.method.lower() == "gradient":
self._LogisticRegressionSelf__train_gradient(n_iters, learning_rate)
elif self.method.lower() == "newton":
self._LogisticRegressionSelf__train_newton(n_iters)
elif self.method.lower() == "dfp":
self.__train_dfp(n_iters, learning_rate)
elif self.method.lower() == "bfgs":
self.__train_bfgs(n_iters, learning_rate)
else:
raise ValueError("method value not found!")
return
在写算法主体前,我们同样先看下BFGS的简易优化过程:初始化参数W0,初始化替代矩阵Bk0,计算初始梯度Gk0,迭代次数k;
While ( k < n_iters ) and ( ||Gk0|| > e ):
1. 计算出W0需要优化的方向Pk = Bk*Gk0
2. 更新参数W1如下:
---- for n in N:
-------- 尝试用一维搜索方法改变Pk的学习率,
-------- 求得最小似然值,
-------- 求出W1和deltaW(W1 - W0)
3. 更新梯度Gk1,并求deltaG(Gk1 - Gk0)
4. 更新BK1,利用上述推导的BFGS的迭代公式
5. 重新赋值W0,Bk0,Gk0
6. k += 1end.
由此我们可以看到,我们可以直接复用DFP算法的内循环,而外循环也仅需改动下
的迭代方法。
#新增拟牛顿法-BFGS优化算法
def __train_bfgs(self, n_iters, learning_rate):
n_samples, n_features = self.trainSet.shape
X = self.trainSet
y = self.label
#合并w和b,在X尾部添加一列全是1的特征
X2 = np.hstack((X, np.ones((n_samples, 1))))
#将y转置变为(n_samples,1)的矩阵
Y = np.expand_dims(y, axis=1)
#初始化特征系数W,初始化替代对称矩阵
W = np.zeros((1, n_features+1))
Bk0 = np.eye(n_features+1)
#计算初始的预测值、似然值,并记录似然值
Ypreprob, LL0 = self.PVandLLV(X2, Y, W)
self.llList.append(LL0)
#根据初始的预测值计算初始梯度,并记录梯度的模长
Gk0 = self._LogisticRegressionSelf__calGradient(X2, Y, Ypreprob)
graLength = np.linalg.norm(Gk0)
self.graList.append(graLength)
#初始化迭代次数
k = 0
while (kself.tol):
#计算优化方向的值Pk=Gk0.Bk0
Pk = np.dot(Gk0, Bk0)
#一维搜索更新参数,并保存求得的最小似然值
W, deltaW, min_LLvalue, Ypreprob = self.__updateW(X2, Y, learning_rate, W, Pk)
self.llList.append(min_LLvalue)
#更新梯度Gk和deltaG,同时求得梯度的模长和更新前后的模长差值
Gk1 = self._LogisticRegressionSelf__calGradient(X2, Y, Ypreprob)
graLength = np.linalg.norm(Gk1)
self.graList.append(graLength)
deltaG = Gk1 - Gk0
Gk0 = Gk1
#更新替代矩阵Bk
temp = (np.eye(n_features+1)-np.dot(deltaW.T,deltaG)/np.dot(deltaW,deltaG.T))
Bk1 = np.dot(np.dot(temp, Bk0), temp.T) + np.dot(deltaW.T, deltaW)/np.dot(deltaW, deltaG.T)
Bk0 = Bk1
k += 1
self.n_iters = k
self.w = W.flatten()[:-1]
self.b = W.flatten()[-1]
Ypre = np.argmax(np.column_stack((1-Ypreprob,Ypreprob)), axis=1)
self.accurancy = sum(Ypre==y)/n_samples
print("第{}次停止迭代,梯度模长为{},似然值为{},准确率为{}".format(self.n_iters, self.graList[-1], self.llList[-1], self.accurancy))
print("w:{};\nb:{}".format(self.w, self.b))
return我们和DFP算法做个对比,先看DFP算法的结果:再看BFGS算法的结果:其实经过多次实验,我们可以发现,DFP和BFGS算法在似然值和梯度的下降曲线比较近似,但是在同样的停止条件下,BFGS的迭代次数更少一些。
以上就是拟牛顿法BFGS算法的全部内容,谢谢阅读。
全部代码可前往以下地址下载:https://github.com/shoucangjia1qu/ML_gzh/tree/master/LogisticRegressiongithub.com
往期回顾:
学无止境,欢迎关注笔者公众号,互相学习!嗷大豆的数据工厂