python實現(xiàn)梯度下降法

梯度下降法

梯度定義

梯度的本意是一個向量(矢量)龄坪,表示某一函數(shù)在該點處的方向導數(shù)沿著該方向取得最大值,即函數(shù)在該點處沿著該方向(此梯度的方向)變化最快泪姨,變化率最大(為該梯度的模)肠槽。
<p align="right">--------百度百科</p>

對于f(x)=2x來說,其梯度\nabla f(x)為:
\nabla f(x)=\dfrac{df(x)}{dx}=2
對于f(x,y)=x^2+2y來說阔加,其梯度\nabla f(x,y)為:
\nabla f(x,y)=\left(\dfrac{\partial f}{\partial x},\dfrac{\partial f}{\partial y}\right )=(2x,2)

梯度下降法思路

因為梯度是函數(shù)上升最快的方向饵史,所以如果我們要尋找函數(shù)的最小值,只需沿著梯度的反方向尋找即可胜榔。這里以f(x)=2x為例胳喷,簡述梯度下降法實現(xiàn)的大體步驟:

  1. 確定變量的初始點x_0,從初始點開始一步步向函數(shù)最小值逼近苗分。
  2. 求函數(shù)梯度厌蔽,然后求梯度的反向牵辣,將變量的初始點代入摔癣,確定變量變化的方向:-\nabla f(x_0);用求得的梯度向量(變量變化的方向)乘以學習率\alpha (變量變化的步長)得到一個新的向量纬向;變量的初始點加上求得的新向量择浊,到達下一個點。
    x = x_0 - \alpha \nabla f(x_0)
  3. 判斷此時函數(shù)值的變化量是否滿足精度要求逾条。定義一個我們認為滿足要求的精度p_0琢岩;用上一個點的函數(shù)值減去當前點的函數(shù)值,得到此時函數(shù)值變化量的精度值p(可以近似認為p為損失函數(shù))师脂;判斷p<p_0是否成立担孔。不成立則反復執(zhí)行步驟2、3吃警。 \begin{cases}p = f(x)-f(x_0)\\p < p_0\end{cases}

但是梯度下降法對初始點的選取要求比較高糕篇,選取不當容易陷入極小值(局部最優(yōu)解)。

梯度下降法的簡單應用

梯度下降法求二維曲線的最小值

下圖為梯度下降法求曲線y=x^2+2x+5最小值的結果圖酌心,左圖紅色的點為求解過程中的過程點拌消,右圖為求解過程中精度的變化(損失函數(shù)值的變化),代碼見附錄安券。

梯度下降法求二維曲線的最小值

梯度下降法求三維曲面的最小值

下圖為梯度下降法求曲面z=\sqrt{x^2+y^2}最小值的結果圖墩崩,圖中紅色的點為求解過程中的過程點,代碼見附錄侯勉。

梯度下降法求三維曲面的最小值

代碼附錄

# -*- encoding=utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as aplt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import sympy 

class gradientDescent(object):
    def init2D(self,vector:float,precision:float,startPoint:float):
    """
    vector:學習率
    precision:精度
    startPoint:起始點
    """
        self.vector = vector
        self.precision = precision
        self.startPoint = startPoint
        self.startPrecision = precision + 1

    def init3D(self,vector:float,precision:float,startVar1Point:float,startVar2Point:float):
    """
    vector:學習率
    precision:精度
    startVar1Point:變量1的起始位置
    startVar2Point:變量2的起始位置
    """
        self.vector = vector
        self.precision = precision
        self.startVar1Point = startVar1Point
        self.startVar2Point = startVar2Point
        self.startPrecision = precision + 1

    def singleVar2D(self, func:str, var:str):
        grad = sympy.diff(func, var)
        grad = str(grad)
        xpoint = []
        ypoint = []
        errors = []
        x = self.startPoint
        while self.startPrecision > self.precision:
            y = eval(func)
            xpoint.append(x)
            ypoint.append(y)
            x1 = x - self.vector*eval(grad)
            x = x1
            y1 = eval(func)
            self.startPrecision = y - y1
            errors.append(self.startPrecision)
        xpoint.append(x)
        ypoint.append(y)
        xlen = len(xpoint)
        return [xpoint,ypoint,errors,xlen]
        
    def doubleVar3D(self, func:str, var1:str, var2:str):
        var1Grad = sympy.diff(func, var1)
        var1Grad = str(var1Grad)
        var1Grad = var1Grad.replace("sqrt","np.sqrt")
        var2Grad = sympy.diff(func, var2)
        var2Grad = str(var2Grad)
        var2Grad = var2Grad.replace("sqrt","np.sqrt")
        func = func.replace("sqrt","np.sqrt")
        xpoint = []
        ypoint = []
        zpoint = []
        errors = []
        x = self.startVar1Point
        y = self.startVar2Point
        while self.startPrecision > self.precision:
            z = eval(func)
            xpoint.append(x)
            ypoint.append(y)
            zpoint.append(z)
            x1 = x - self.vector*eval(var1Grad)
            y1 = y - self.vector*eval(var2Grad)
            x = x1
            y = y1
            z1 = eval(func)
            self.startPrecision = z - z1
            errors.append(self.startPrecision)
        xpoint.append(x)
        ypoint.append(y)
        zpoint.append(z)
        xlen = len(xpoint)
        return [xpoint,ypoint,zpoint,errors,xlen]


if __name__ == '__main__':
            xData = np.arange(-100,100,0.1)
            yData = xData**2 + 2*xData + 5
            vector=0.2
            precision=10e-6
            startPoint=-100
            x = sympy.symbols("x")
            func = "x**2+2*x+5"
            gradient_descent = gradientDescent()
            gradient_descent.init2D(vector,precision,startPoint)
            [xpoint,ypoint,errors,xlen] = gradient_descent.singleVar2D(func,x)        
            fig,ax = plt.subplots(figsize=(12,8),ncols=2,nrows=1)
            for i in range(xlen):
                ax[0].cla()
                ax[0].plot(xData,yData,color="green",label="$y=x^2+2x+5$")
                ax[0].scatter(xpoint[i],ypoint[i],color="red",label="process point")
                plt.pause(0.1)
            ax[0].legend(loc = "best")
            ax[1].plot(errors,label="Loss curve")
            ax[1].legend(loc = "best")
            plt.pause(0.1)
            plt.show()
            # =======================================================================
            xData = np.arange(-100,100,0.1)
            yData = np.arange(-100,100,0.1)
            X,Y = np.meshgrid(xData,yData)
            # z = sqrt(x^2+y^2)
            Z = np.sqrt(X**2+Y**2)
            x = sympy.symbols("x")
            y = sympy.symbols("y")
            func = "sqrt(x**2+y**2)"
            vector=0.2
            precision=10e-6
            startVar1Point=100
            startVar2Point=-100           
            gradient_descent = gradientDescent()
            gradient_descent.init3D(vector, precision, startVar1Point, startVar2Point)
            [xpoint,ypoint,zpoint,errors,xlen] = gradient_descent.doubleVar3D(func,x,y)        
            fig = plt.figure()
            ax = Axes3D(fig)
            surf = ax.plot_surface(X,Y,Z,label="$z=\sqrt{x^2+y^2}$")
            ax.scatter(xpoint,ypoint,zpoint,color="red",label="process point")
            # 解決標簽報錯鹦筹,不顯示問題
            surf._facecolors2d=surf._facecolors3d
            surf._edgecolors2d=surf._edgecolors3d
            ax.legend()
            plt.show()
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市址貌,隨后出現(xiàn)的幾起案子铐拐,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件余舶,死亡現(xiàn)場離奇詭異啊鸭,居然都是意外死亡,警方通過查閱死者的電腦和手機匿值,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門赠制,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挟憔,你說我怎么就攤上這事钟些。” “怎么了绊谭?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵政恍,是天一觀的道長。 經常有香客問我达传,道長篙耗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任宪赶,我火速辦了婚禮宗弯,結果婚禮上,老公的妹妹穿的比我還像新娘搂妻。我一直安慰自己蒙保,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布欲主。 她就那樣靜靜地躺著邓厕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扁瓢。 梳的紋絲不亂的頭發(fā)上详恼,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音涤妒,去河邊找鬼单雾。 笑死,一個胖子當著我的面吹牛她紫,可吹牛的內容都是我干的硅堆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贿讹,長吁一口氣:“原來是場噩夢啊……” “哼渐逃!你這毒婦竟也來了?” 一聲冷哼從身側響起民褂,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤茄菊,失蹤者是張志新(化名)和其女友劉穎疯潭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體面殖,經...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡竖哩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年脊僚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辽幌。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖乌企,靈堂內的尸體忽然破棺而出虑润,到底是詐尸還是另有隱情,我是刑警寧澤加酵,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站舞蔽,受9級特大地震影響,放射性物質發(fā)生泄漏码撰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一个盆、第九天 我趴在偏房一處隱蔽的房頂上張望脖岛。 院中可真熱鬧,春花似錦柴梆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽偿渡。三九已至霸奕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間质帅,已是汗流浹背留攒。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工嫉嘀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人剪侮。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像票彪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子降铸,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內容