优化算法推导&手写Python实现——牛顿法和拟牛顿法1

  • 在上一篇文章《机器学习算法推导&实现——逻辑斯蒂回归》中,我们分别使用了梯度下降法牛顿法来求解对数似然函数的最优化问题,从实例中我们也能发现牛顿法的收敛速度远快于梯度下降法,但是在上文中,直接使用了牛顿法的结果,并没有进行相应推导,故本文一方面是补上牛顿法的推导,另一方面是展开讨论下拟牛顿法。

牛顿法推导

  • 牛顿法的推导得先从泰勒公式说起:


    0.PNG
  • 假设现在函数f(x)迭代了k次的值为X(k),则在X(k)上进行二阶泰勒展开可近似得到以下公式:


    1.PNG
  • 我们要求得f(x)的极小值,则必要条件是f(x)在极值点处的一阶导数为0,即:


    2.PNG
  • 因为我们把每轮迭代求得的满足目标函数极小值的x作为下一轮迭代的值,因此我们可以假设第k+1轮的值就是最优解:


    3.PNG
  • 代入二阶泰勒展开并求导可得:
    4.PNG

    令:
    5.PNG
    6.PNG

    可得最终的优化公式为:
    7.PNG
  • 虽然根据推导,我们已经知道了牛顿法的迭代方法,但是在实际应用过程中,我们会发现海塞矩阵的逆矩阵往往计算比较复杂,于是又有了拟牛顿法来简化这一过程。

拟牛顿法的原理

  • 在拟牛顿法中,我们考虑优化出一个n阶矩阵D来代替海塞矩阵的逆矩阵,首先我们得先来看看替代矩阵D要满足什么条件:
  • 首先根据二阶泰勒展开,我们令:


    8.PNG
  • 代入f(x),并求导,可得:


    9.PNG
  • 再令:


    5.PNG
    6.PNG
  • 最终可得矩阵D需满足的条件:


    10.PNG
  • 另外在每次迭代中可以更新矩阵D,故可以假设以下更新条件:


    11.PNG

    由此我们可以发现海塞矩阵逆矩阵的近似矩阵D(x)的选择条件比较灵活,可以有多种具体的实现方法。

1、DFP算法(Davidon-Fletcher-Powell)推导

  • 在DFP算法中,我们假设:
    12.PNG

    *在这里我们知道凸函数的二阶导数海塞矩阵(Hessian)是对称矩阵,因此我们假设想要替代的D, P, Q矩阵也是对称矩阵,即矩阵的转置等于矩阵本身。
  • 两边乘以\Delta{gk}可得:
    13.PNG
  • 为了满足上式要求,我们不妨令:


    14.PNG
  • 我们先来求P矩阵,最简单的就是令:
    16.PNG
  • 接着两边乘以\Delta{gk}可得:
    18.PNG

    求得\alpha,并代入P矩阵,可求得:
    19.PNG
  • 同样的方法我们求解下Q矩阵
    20.PNG
  • 最终,DFP算法的迭代公式为:
    21.PNG

python实现DFP算法

  • 我们继承上一篇文章逻辑回归算法的类,并且增加拟牛顿法的优化算法。
  • 我们先看下算法的主函数部分,在继承逻辑回归类的基础上主要有2点改动:一是把"method"参数提前,在实例化的时候就要求定位好优化算法;二是重写了train方法,衍生出其他拟牛顿算法。
from ML_LogisticRegression import LogisticRegressionSelf as logit
import numpy as np
​
class allLogitMethod(logit):
    def __init__(self, method):
        """init class"""
        super().__init__()
        self.method = method
        self.graList = []               #记录梯度模长的列表
​
    #重写下train方法
    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)
        else:
            raise ValueError("method value not found!")
        return  
  • 在写算法主体前,我们先看下DFP的简易优化过程
  1. 初始化参数W0,初始化替代矩阵Dk0,计算初始梯度Gk0,迭代次数k;
  2. While ( k < n_iters ) and ( ||Gk0|| > e ):
    1. 计算出W0需要优化的方向Pk = Dk*Gk0
    2. 更新参数W1如下:
      for n in N:
      • 尝试用一维搜索方法改变Pk的学习率,
      • 求得最小似然值,
      • 求出W1和deltaW(W1 - W0)
    3. 更新梯度Gk1,并求deltaG(Gk1 - Gk0)
    4. 更新DK1,利用上述推导的DFP的迭代公式
    5. 重新赋值W0,Dk0,Gk0
    6. k += 1
  3. end.
  • 我们可以大致看到DFP算法的主体部分有外循环内循环两个部分组成,所以我们也是将内外循环分开来写。
  • 我们先看内循环部分。内循环是已经知道了参数W的优化方向的基础上,想要找到最合适的学习率,使得更新后的W求得的似然值最小。实际有很多方法,在这里为了简单,笔者只是对学习率进行了i次方的操作。具体函数如下:
    #一维搜索法求出最优lambdak,更新W后,使得似然值最小
    def __updateW(self, X, Y, lambdak, W0, Pk):
        """此处对lambdak的处理仅简单用1~i次方来逐步变小,以选取到最小似然值的lambdak"""
        min_LLvalue = np.inf
        W1 = np.zeros(W0.shape)
        for i in range(10):
            Wi = W0 - (lambdak**i)*Pk
            Ypreprob, LLvalue = self.PVandLLV(X, Y, Wi)
            if LLvalue < min_LLvalue:
                min_LLvalue = LLvalue
                W1 = np.copy(Wi)
                deltaW = - (lambdak**i)*Pk
                bestYpreprob = Ypreprob
        return W1, deltaW, min_LLvalue, bestYpreprob
  • 再看外循环部分
    #新增拟牛顿法-DFP优化算法
    def __train_dfp(self, n_iters, learning_rate):
        """Quasi-Newton Method DFP(Davidon-Fletcher-Powell)"""
        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))
        Dk0 = 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 (k<n_iters) and (graLength>self.tol):
            #计算优化方向的值Pk=Gk0.Dk0
            Pk = np.dot(Gk0, Dk0)
            #一维搜索更新参数,并保存求得的最小似然值
            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
            #更新替代矩阵Dk
            Dk1 = Dk0 + np.dot(deltaW.T, deltaW)/np.dot(deltaW, deltaG.T) - \
            np.dot(np.dot(np.dot(Dk0, deltaG.T), deltaG), Dk0)/np.dot(np.dot(deltaG, Dk0), deltaG.T)
            Dk0 = Dk1
            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算法经常会导致求解似然值和P(y=1|X)的时候报值溢出的错误。主要是python的numpy.exp(wx)中的wx过大导致的,比如看下图的报错:
    22.PNG
  • 所以在这里我们对求解似然值和P(y=1|X)的公式进行了一些调整。当wx为负数时,使用原公式;当wx为正数时,使用转换公式替换。其实质是一致的。
    23.PNG
  • 最后我们用拟牛顿法-DFP算法牛顿法实测比较下。
    1. 牛顿法:迭代了7次,迭代时长0.18s,似然值176.81,准确率0.94,模型参数如下图所示:
if __name__ == "__main__":
    import matplotlib.pyplot as plt
    from sklearn.datasets import make_classification
    import time
    X, y = make_classification(n_samples=1000, n_features=4)
    #1、自编的牛顿法进行拟合
    time_nt = time.time()
    logit_nt = allLogitMethod("newton")
    logit_nt.train(X, y, n_iters=100)
    print("迭代时长:", time.time()-time_nt)
    plt.plot(range(logit_nt.n_iters+1), logit_nt.llList)
    plt.show()

24.PNG

2. 拟牛顿法-DFP算法:迭代了18次,迭代时长0.038s,似然值176.81,准确率0.94,模型参数如下图所示:

    #3、自编的拟牛顿法-DFP算法进行拟合
    time_dfp = time.time()
    logit_dfp = allLogitMethod("DFP")
    logit_dfp.train(X, y, n_iters=20, learning_rate=0.5)
    print("迭代时长:", time.time()-time_dfp)
    fig = plt.figure(figsize=(10,6)) 
    ax1 = fig.add_subplot(1,2,1)
    ax1.plot(range(logit_dfp.n_iters+1), logit_dfp.llList)
    ax2 = fig.add_subplot(1,2,2)
    ax2.plot(range(logit_dfp.n_iters+1), logit_dfp.graList)
    plt.show()
25.PNG
  • 经过两种方法的对比,我们发现两者最终训练出来的模型参数基本一致,似然值也下降到同一水平DFP虽然迭代18次比牛顿法多,但是训练总时间只有0.038s,远小于牛顿法的训练总时间。
  • 可见,DFP算法中的Dk矩阵完全可以逼近于牛顿法中的海塞矩阵的逆矩阵,而且DFP算法中的Dk矩阵的训练也比二阶导数矩阵的训练来得方便与快速。

以上就是本次的全部内容,谢谢阅读。(今天先写到这,写的好累,拟牛顿法的其他算法我们下次再接着推导与实现。)

全部代码可前往以下地址下载:
https://github.com/shoucangjia1qu/ML_gzh/tree/master/LogisticRegression

往期回顾:
机器学习算法推导&实现——逻辑斯蒂回归
机器学习算法推导&实现——线性回归

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • @[toc] 1. 牛顿法 1.1 求解f' = 0的点,牛顿法推导 Newton method NR法是寻找函数...
    fly_dragon阅读 2,024评论 0 0
  • 概率论与数理统计 无穷小阶数 无穷小量表述:线性逼近 相当于利用切线和斜率来理解误差和逼近。 泰勒级数:线性逼近 ...
    Babus阅读 809评论 0 1
  • 机器学习就是需要找到模型的鞍点,也就是最优点。因为模型很多时候并不是完全的凸函数,所以如果没有好的优化方法可能会跑...
    冒绿光的盒子阅读 1,065评论 0 3
  • 之前,我发过一篇文章,通俗地解释了梯度下降算法的数学原理和推导过程,推荐一看。链接如下: 简单的梯度下降算法,你真...
    红色石头Will阅读 1,703评论 1 10
  • 你还是每天都在刷抖音吗? 你是否还在被抖音统治折? 一滑一上午, 一滑滑到半夜? 抖音占据了我们太多时间, 很多可...
    梓梓梓寒阅读 163评论 0 1