Lesson 6.动态计算图与梯度下降入门
? ? ? ?在《Lesson 5.基本優(yōu)化思想與最小二乘法》的結(jié)尾,我們提到PyTorch中的AutoGrad(自動微分)模塊,并簡單嘗試使用該模塊中的autograd.grad進(jìn)行函數(shù)的微分運算,我們發(fā)現(xiàn),autograd.grad函數(shù)可以靈活進(jìn)行函數(shù)某一點的導(dǎo)數(shù)或偏導(dǎo)數(shù)的運算,但微分計算其實也只是AutoGrad模塊中的一小部分功能。本節(jié)課,我們將繼續(xù)講解AutoGrad模塊中的其他常用功能,并在此基礎(chǔ)上介紹另一個常用優(yōu)化算法:梯度下降算法。
import numpy as np import torch一、AutoGrad的回溯機(jī)制與動態(tài)計算圖
1.可微分性相關(guān)屬性
??在上一節(jié)中我們提到,新版PyTorch中的張量已經(jīng)不僅僅是一個純計算的載體,張量本身也可支持微分運算。這種可微分性其實不僅體現(xiàn)在我們可以使用grad函數(shù)對其進(jìn)行求導(dǎo),更重要的是這種可微分性會體現(xiàn)在可微分張量參與的所有運算中。
- requires_grad屬性:可微分性
不難發(fā)現(xiàn),z也同時存儲了張量計算數(shù)值、z是可微的,并且z還存儲了和y的計算關(guān)系(add)。據(jù)此我們可以知道,在PyTorch的張量計算過程中,如果我們設(shè)置初始張量是可微的,則在計算過程中,每一個由原張量計算得出的新張量都是可微的,并且還會保存此前一步的函數(shù)關(guān)系,這也就是所謂的回溯機(jī)制。而根據(jù)這個回溯機(jī)制,我們就能非常清楚掌握張量的每一步計算,并據(jù)此繪制張量計算圖。
2.張量計算圖
??借助回溯機(jī)制,我們就能將張量的復(fù)雜計算過程抽象為一張圖(Graph),例如此前我們定義的x、y、z三個張量,三者的計算關(guān)系就可以由下圖進(jìn)行表示。
- 計算圖的定義
??上圖就是用于記錄可微分張量計算關(guān)系的張量計算圖,圖由節(jié)點和有向邊構(gòu)成,其中節(jié)點表示張量,邊表示函數(shù)計算關(guān)系,方向則表示實際運算方向,張量計算圖本質(zhì)是有向無環(huán)圖。
- 節(jié)點類型
??在張量計算圖中,雖然每個節(jié)點都表示可微分張量,但節(jié)點和節(jié)點之間卻略有不同。就像在前例中,y和z保存了函數(shù)計算關(guān)系,但x沒有,而在實際計算關(guān)系中,我們不難發(fā)現(xiàn)z是所有計算的終點,因此,雖然x、y、z都是節(jié)點,但每個節(jié)點卻并不一樣。此處我們可以將節(jié)點分為三類,分別是:
a):葉節(jié)點,也就是初始輸入的可微分張量,前例中x就是葉節(jié)點;
b):輸出節(jié)點,也就是最后計算得出的張量,前例中z就是輸出節(jié)點;
c):中間節(jié)點,在一張計算圖中,除了葉節(jié)點和輸出節(jié)點,其他都是中間節(jié)點,前例中y就是中間節(jié)點。
當(dāng)然,在一張計算圖中,可以有多個葉節(jié)點和中間節(jié)點,但大多數(shù)情況下,只有一個輸出節(jié)點,若存在多個輸出結(jié)果,我們也往往會將其保存在一個張量中。
3.計算圖的動態(tài)性
??值得一提的是,PyTorch的計算圖是動態(tài)計算圖,會根據(jù)可微分張量的計算過程自動生成,并且伴隨著新張量或運算的加入不斷更新,這使得PyTorch的計算圖更加靈活高效,并且更加易于構(gòu)建,相比于先構(gòu)件圖后執(zhí)行計算的部分框架(如老版本的TensorFlow),動態(tài)圖也更加適用于面向?qū)ο缶幊獭?/p>
二、反向傳播與梯度計算
1.反向傳播的基本過程
??在《Lesson 5.》中,我們曾使用autograd.grad進(jìn)行函數(shù)某一點的導(dǎo)數(shù)值得計算,其實,除了使用函數(shù)以外,我們還有另一種方法,也能進(jìn)行導(dǎo)數(shù)運算:反向傳播。當(dāng)然,此時導(dǎo)數(shù)運算結(jié)果我們也可以有另一種解讀:計算梯度結(jié)果。
注:此處我們暫時不區(qū)分微分運算結(jié)果、導(dǎo)數(shù)值、梯度值三者區(qū)別,目前位置三個概念相同,后續(xù)講解梯度下降時再進(jìn)行區(qū)分。
首先,對于某一個可微分張量的導(dǎo)數(shù)值(梯度值),存儲在grad屬性中。
x.grad在最初,x.grad屬性是空值,不會返回任何結(jié)果,我們雖然已經(jīng)構(gòu)建了x、y、z三者之間的函數(shù)關(guān)系,x也有具體取值,但要計算x點導(dǎo)數(shù),還需要進(jìn)行具體的求導(dǎo)運算,也就是執(zhí)行所謂的反向傳播。所謂反向傳播,我們可以簡單理解為,在此前記錄的函數(shù)關(guān)系基礎(chǔ)上,反向傳播函數(shù)關(guān)系,進(jìn)而求得葉節(jié)點的導(dǎo)數(shù)值。在必要時求導(dǎo),這也是節(jié)省計算資源和存儲空間的必要規(guī)定。
z #tensor(2., grad_fn=<AddBackward0>)z.grad_fn #<AddBackward0 at 0x7fad381971c0># 執(zhí)行反向傳播 z.backward() '''反向傳播結(jié)束后,即可查看葉節(jié)點的導(dǎo)數(shù)值'''x #tensor(1., requires_grad=True)# 在z=y+1=x**2+1函數(shù)關(guān)系基礎(chǔ)上,x取值為1時的導(dǎo)數(shù)值 x.grad #tensor(2.)'''注意,在默認(rèn)情況下,在一張計算圖上執(zhí)行反向傳播,只能計算一次,再次調(diào)用backward方法將報錯''' z.backward() #--------------------------------------------------------------------------- #RuntimeError Traceback (most recent call last) #<ipython-input-52-40c0c9b0bbab> in <module> #----> 1 z.backward()當(dāng)然,在y上也能執(zhí)行反向傳播
x = torch.tensor(1.,requires_grad = True) y = x ** 2 z = y + 1y.backward()x.grad #tensor(2.)'''第二次執(zhí)行時也會報錯''' y.backward() #--------------------------------------------------------------------------- #RuntimeError Traceback (most recent call last) #<ipython-input-60-ab75bb780f4c> in <module> #----> 1 y.backward() z.backward() #--------------------------------------------------------------------------- #RuntimeError Traceback (most recent call last) #<ipython-input-61-40c0c9b0bbab> in <module> #----> 1 z.backward()'''無論何時,我們只能計算葉節(jié)點的導(dǎo)數(shù)值''' y.grad #D:\Users\ASUS\anaconda3\lib\site-packages\ipykernel_launcher.py:1: UserWarning: #The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its #.grad attribute won't be populated during autograd.backward(). If you indeed want #the gradient for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If #you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor #instead. See github.com/pytorch/pytorch/pull/30531 for more informations. # """Entry point for launching an IPython kernel.至此,我們就了解了反向傳播的基本概念和使用方法:
- 反向傳播的本質(zhì):函數(shù)關(guān)系的反向傳播(不是反函數(shù));
- 反向傳播的執(zhí)行條件:擁有函數(shù)關(guān)系的可微分張量(計算圖中除了葉節(jié)點的其他節(jié)點);
- 反向傳播的函數(shù)作用:計算葉節(jié)點的導(dǎo)數(shù)/微分/梯度運算結(jié)果;
2.反向傳播運算注意事項
- 中間節(jié)點反向傳播和輸出節(jié)點反向傳播區(qū)別
??盡管中間節(jié)點也可進(jìn)行反向傳播,但很多時候由于存在復(fù)合函數(shù)關(guān)系,中間節(jié)點反向傳播的計算結(jié)果和輸出節(jié)點反向傳播輸出結(jié)果并不相同。
x = torch.tensor(1.,requires_grad = True) y = x ** 2 z = y ** 2 z.backward() x.grad #tensor(4.)x = torch.tensor(1.,requires_grad = True) y = x ** 2 z = y ** 2 y.backward() x.grad #tensor(2.)- 中間節(jié)點的梯度保存
??默認(rèn)情況下,在反向傳播過程中,中間節(jié)點并不會保存梯度
x = torch.tensor(1.,requires_grad = True) y = x ** 2 z = y ** 2 z.backward() y.grad #D:\Users\ASUS\anaconda3\lib\site-packages\ipykernel_launcher.py:2: UserWarning: #The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its #.grad attribute won't be populated during autograd.backward(). If you indeed want #the gradient for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If #you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor #instead. See github.com/pytorch/pytorch/pull/30531 for more informations. x.grad #tensor(4.)'''若想保存中間節(jié)點的梯度,我們可以使用retain_grad()方法''' x = torch.tensor(1.,requires_grad = True) y = x ** 2 y.retain_grad() z = y ** 2 z.backward() y #tensor(1., grad_fn=<PowBackward0>) y.grad #tensor(2.) x.grad #tensor(4.)3.阻止計算圖追蹤
??在默認(rèn)情況下,只要初始張量是可微分張量,系統(tǒng)就會自動追蹤其相關(guān)運算,并保存在計算圖關(guān)系中,我們也可通過grad_fn來查看記錄的函數(shù)關(guān)系,但在特殊的情況下,我們并不希望可微張量從創(chuàng)建到運算結(jié)果輸出都被記錄,此時就可以使用一些方法來阻止部分運算被記錄。
- with torch.no_grad():阻止計算圖記錄
??例如,我們希望x、y的函數(shù)關(guān)系被記錄,而y的后續(xù)其他運算不被記錄,可以使用with torch.no_grad()來組織部分y的運算不被記錄。
x = torch.tensor(1.,requires_grad = True) y = x ** 2with torch.no_grad():z = y ** 2'''with相當(dāng)于是一個上下文管理器,with torch.no_grad()內(nèi)部代碼都“屏蔽”了計算圖的追蹤記錄''' z #tensor(1.)z.requires_grad #Falsey #tensor(1., grad_fn=<PowBackward0>)- .detach()方法:創(chuàng)建一個不可導(dǎo)的相同張量
在某些情況下,我們也可以創(chuàng)建一個不可導(dǎo)的相同張量參與后續(xù)運算,從而阻斷計算圖的追蹤
x = torch.tensor(1.,requires_grad = True) y = x ** 2 y1 = y.detach() z = y1 ** 2y #tensor(1., grad_fn=<PowBackward0>)y1 #tensor(1.)z #tensor(1.)4.識別葉節(jié)點
??由于葉節(jié)點較為特殊,如果需要識別在一個計算圖中某張量是否是葉節(jié)點,可以使用is_leaf屬性查看對應(yīng)張量是否是葉節(jié)點。
x.is_leaf #Truey.is_leaf #False'''但is_leaf方法也有容易混淆的地方,對于任何一個新創(chuàng)建的張量,無論是否可導(dǎo)、是否加入計算圖,都是可以是葉節(jié)點,這些節(jié)點距離真正的葉節(jié)點,只差一個requires_grad屬性調(diào)整。''' torch.tensor([1]).is_leaf #True# 經(jīng)過detach的張量,也可以是葉節(jié)點 y1 #tensor(1.)y1.is_leaf #True三、梯度下降基本思想
??有了AutoGrad模塊中各函數(shù)方法的支持,接下來,我們就能嘗試手動構(gòu)建另一個優(yōu)化算法:梯度下降算法。
1.最小二乘法的局限與優(yōu)化
??在《Lesson 5.》中,我們嘗試使用最小二乘法求解簡單線性回歸的目標(biāo)函數(shù),并順利的求得了全域最優(yōu)解。但正如上節(jié)所說,在所有的優(yōu)化算法中最小二乘法雖然高效并且結(jié)果精確,但也有不完美的地方,核心就在于最小二乘法的使用條件較為苛刻,要求特征張量的交叉乘積結(jié)果必須是滿秩矩陣,才能進(jìn)行求解。而在實際情況中,很多數(shù)據(jù)的特征張量并不能滿足條件,此時就無法使用最小二乘法進(jìn)行求解。
最小二乘法結(jié)果:
??當(dāng)最小二乘法失效的情況時,其實往往也就代表原目標(biāo)函數(shù)沒有最優(yōu)解或最優(yōu)解不唯一。針對這樣的情況,有很多中解決方案,例如,我們可以在原矩陣方程中加入一個擾動項𝜆𝐼,修改后表達(dá)式如下:
其中,𝜆是擾動項系數(shù),𝐼是單元矩陣。由矩陣性質(zhì)可知,加入單位矩陣后,(𝑋^𝑇𝑋+𝜆𝐼)部分一定可逆,而后即可直接求解𝑤?^𝑇?,這也就是嶺回歸的一般做法。
??當(dāng)然,上式修改后求得的結(jié)果就不再是全域最小值,而是一個接近最小值的點。鑒于許多目標(biāo)函數(shù)本身也并不存在最小值或者唯一最小值,在優(yōu)化的過程中略有偏差也是可以接受的。當(dāng)然,伴隨著深度學(xué)習(xí)的逐漸深入,我們會發(fā)現(xiàn),最小值并不唯一存在才是目標(biāo)函數(shù)的常態(tài)。基于此情況,很多根據(jù)等式形變得到的精確的求解析解的優(yōu)化方法(如最小二乘)就無法適用,此時我們需要尋找一種更加通用的,能夠高效、快速逼近目標(biāo)函數(shù)優(yōu)化目標(biāo)的最優(yōu)化方法。在機(jī)器學(xué)習(xí)領(lǐng)域,最通用的求解目標(biāo)函數(shù)的最優(yōu)化方法就是著名的梯度下降算法。
??值得一提的是,我們通常指的梯度下降算法,并不是某一個算法,而是某一類依照梯度下降基本理論基礎(chǔ)展開的算法簇,包括梯度下降算法、隨機(jī)梯度下降算法、小批量梯度下降算法等等。接下來,我們就從最簡單的梯度下降入手,講解梯度下降的核心思想和一般使用方法。
2.梯度下降核心思想
??梯度下降的基本思想其實并不復(fù)雜,其核心就是希望能夠通過數(shù)學(xué)意義上的迭代運算,從一個隨機(jī)點出發(fā),一步步逼近最優(yōu)解。
例如,在此前求解簡單線性回歸方程的過程中,我們曾查看SSE的三維函數(shù)圖像如下:
from matplotlib import pyplot as plt from mpl_toolkits.mplot3d import Axes3Dx = np.arange(-1,3,0.05) y = np.arange(-1,3,0.05) a, b = np.meshgrid(x, y) SSE = (2 - a - b) ** 2 + (4 - 3 * a - b) ** 2fig = plt.figure() ax = plt.axes(projection='3d')ax.plot_surface(a, b, SSE, cmap='rainbow') ax.contour(a, b, SSE, zdir='z', offset=0, cmap="rainbow") #生成z方向投影,投到x-y平面 plt.show()而梯度下降,作為最優(yōu)化算法,核心目標(biāo)也是找到或者逼近最小值點,而其基本過程則:
- 在目標(biāo)函數(shù)上隨機(jī)找到一個初始點;
- 通過迭代運算,一步步逼近最小值點;
數(shù)學(xué)意義上的迭代運算,指的是上一次計算的結(jié)果作為下一次運算的初始條件帶入運算
3.梯度下降的方向與步長
??當(dāng)然,梯度下降的基本思想好理解,但實現(xiàn)起來卻并不容易(這也是大多數(shù)機(jī)器學(xué)習(xí)算法的常態(tài))。在實際沿著目標(biāo)函數(shù)下降的過程中,我們核心需要解決兩個問題,其一是往哪個方向走,其二是每一步走多遠(yuǎn)。以上述簡單線性回歸的目標(biāo)函數(shù)為例,在三維空間中,目標(biāo)函數(shù)上的每個點理論上都有無數(shù)個移動的方向,每次移動多遠(yuǎn)的物理距離也沒有明顯的約束,而這些就是梯度下降算法核心需要解決的問題,也就是所謂的方向和步長。
首先,是關(guān)于方向的討論。
關(guān)于方向的討論,其實梯度下降是采用了一種局部最優(yōu)推導(dǎo)全域最優(yōu)的思路,我們首先是希望能夠找到讓目標(biāo)函數(shù)變化最快的方向作為移動的方向,而這個方向,就是梯度。
3.1 導(dǎo)數(shù)與梯度
??我們都知道,函數(shù)上某一點的導(dǎo)數(shù)值的幾何含義就是函數(shù)在該點上切線的斜率。例如y=x**2中,x在1點的導(dǎo)數(shù)就是函數(shù)在1點的切線的斜率。
x = np.arange(-10,10,0.1) y = x ** 2 # y = 2x z = 2 * x - 1 # 在(1,1)點的切線方程 plt.plot(x, y, '-') plt.plot(x, z, 'r-') plt.plot(1, 1, 'bo') plt.show()?
而更進(jìn)一步來講,對于上述函數(shù),x取值為1的時候,導(dǎo)數(shù)和切線的斜率為2,代表含義是給1這個點一個無窮小的增量,1只能沿著切向方向移動(但仍然在曲線上)。當(dāng)然,該點導(dǎo)數(shù)值的另外一個解釋就是該點的梯度,梯度的值(grad)和導(dǎo)數(shù)相同,而梯度的概念可以視為導(dǎo)數(shù)概念的延申,只不過梯度更側(cè)重方向的概念,也就是從梯度的角度解讀導(dǎo)數(shù)值,就代表著當(dāng)前這個點的可以使得y值增加最快的移動方向。
梯度:梯度本身是一個代表方向的矢量,代表某一函數(shù)在該點處沿著梯度方向變化時,變化率最大。當(dāng)然,梯度的正方向代表函數(shù)值增長最快的方向,梯度的負(fù)方向表示函數(shù)減少最快的方向。
x = torch.tensor(1., requires_grad = True) y = x ** 2 y.backward() x.grad #tensor(2.)不過此時由于自變量存在一維空間,只能沿著x軸變化(左右移動,只有兩個方向),梯度給出的方向只能解讀為朝著2,也就是正方向變化時,y的增加最快(確實如此,同時也顯而易見)。
3.2 梯度與方向
??為了更好的解讀梯度與方向之間的關(guān)系,我們以《Lesson 5.》中簡單線性回歸損失函數(shù)為例來進(jìn)行查看。我們有目標(biāo)函數(shù)及其圖像如下:
fig = plt.figure() ax = plt.axes(projection='3d')ax.plot_surface(a, b, SSE, cmap='rainbow') ax.contour(a, b, SSE, zdir='z', offset=0, cmap="rainbow") #生成z方向投影,投到x-y平面 plt.show()此時a、b是在實數(shù)域上取值。假設(shè)二者初始值為0,也就是初始隨機(jī)點為原點。對于(0,0)點,有梯度計算如下
a = torch.tensor(0., requires_grad = True) a #tensor(0., requires_grad=True)b = torch.tensor(0., requires_grad = True) b #tensor(0., requires_grad=True)s0 = torch.pow((2 - a - b), 2) + torch.pow((4 - 3 * a - b), 2) s0 #tensor(20., grad_fn=<AddBackward0>) s0.backward() a.grad, b.grad #(tensor(-28.), tensor(-12.)) '''也就是原點和(-28,-12)這個點之間連成直線的方向,就是能夠使得sse變化最快的方向,并且朝向(-28,-12)方向就是使得sse增加最快的方向,反方向則是令sse減少最快的方向。''' # 通過繪制直線,確定原點的移動方向 x = np.arange(-30,30,0.1) y = (12/28) * x plt.plot(x, y, '-') plt.plot(0, 0, 'ro') plt.plot(-28, -12, 'ro')Point:這里有關(guān)于方向的兩點討論
- 方向沒有大小,雖然這是個顯而易見的觀點,但我們當(dāng)我們說朝著(-28,-12)方向移動,只是說沿著直線移動,并非一步移動到(-28,-12)上;
- 方向跟隨梯度,隨時在發(fā)生變化。值得注意的是,一旦點發(fā)生移動,梯度就會隨之發(fā)生變化,也就是說,哪怕是沿著讓sse變化最快的方向移動,一旦“沿著方向”移動了一小步,這個方向就不再是最優(yōu)方向了。
當(dāng)然,逆梯度值的方向變化是使得sse變小的最快方向,我們嘗試移動“一小步”。一步移動到(28,12)是沒有意義的,梯度各分量數(shù)值的絕對值本身也沒有距離這個層面的數(shù)學(xué)含義。由于a和b的取值要按照(28,12)等比例變化,因此我們不妨采用如下方法進(jìn)行移動:
s0 #tensor(20., grad_fn=<AddBackward0>)a = torch.tensor(0.28, requires_grad = True) a #tensor(0.2800, requires_grad=True) b = torch.tensor(0.12, requires_grad = True) b #tensor(0.1200, requires_grad=True) s1 = (2 - a - b) ** 2 + (4 - 3 * a - b) ** 2 s1 #tensor(11.8016, grad_fn=<AddBackward0>)'''確實有所下降,繼續(xù)求解新的點的梯度''' s1.backward() a.grad, b.grad #(tensor(-21.4400), tensor(-9.2800))不難看出,方向已經(jīng)發(fā)生變化。其實無論移動“多小”一步,只要移動,方向就需要重新計算。如果每個點的梯度提供了移動方向的最優(yōu)解,那移動多長,其實并沒有統(tǒng)一的規(guī)定。這里,我們將上述0.01稱作學(xué)習(xí)率,而學(xué)習(xí)率乘以梯度,則是原點移動的“長度”。
當(dāng)然,在移動到(0.28,0.12)之后,還沒有取到全域最優(yōu)解,因此還需要繼續(xù)移動,當(dāng)然我們還可以繼續(xù)按照0.01這個學(xué)習(xí)率繼續(xù)移動,此時,新的梯度為(-21.44,-9.28),則有
接下來,我們可以繼續(xù)計算新的(0.94,0.148)這個點的梯度,然后繼續(xù)按照學(xué)習(xí)率0.01繼續(xù)移動,在移動若干次之后,就將得到非常接近于(1,1)的結(jié)果。
四、梯度下降的數(shù)學(xué)表示
1.梯度下降的代數(shù)表示
??根據(jù)上述描述過程,我們可以通過代數(shù)運算方式總結(jié)梯度下降運算的一般過程
令多元線性回歸方程為
令
出于加快迭代收斂速度的目標(biāo),我們在定義梯度下降的損失函數(shù)L時,在原SSE基礎(chǔ)上進(jìn)行比例修正,新的損失函數(shù)𝐿(𝑤1,𝑤2,...,𝑤𝑑,𝑏)=1/(2m)*SSE,其中,m為樣本個數(shù)。?
損失函數(shù)有:
并且,根據(jù)此前描述過程,在開始梯度下降求解參數(shù)之前,我們首先需要設(shè)置一組參數(shù)的初始取值(𝑤1,𝑤2...,𝑤𝑑,𝑏),以及學(xué)習(xí)率𝛼,然后即可執(zhí)行迭代運算,其中每一輪迭代過程需要執(zhí)行以下三步
Step 1.計算梯度表達(dá)式
?Step 2.用學(xué)習(xí)率乘以損失函數(shù)梯度,得到迭代移動距離
?Step 3.用原參數(shù)減Step 2中計算得到的距離,更新所有的參數(shù)w
更新完所有參數(shù),即完成了一輪的迭代,接下來就能以新的一組𝑤𝑖參與下一輪迭代。
上一輪計算結(jié)果作為下一輪計算的初始值,就是所謂的迭代。
而何時停止迭代,一般來說有兩種情況,其一是設(shè)置迭代次數(shù),到達(dá)迭代次數(shù)即停止迭代;其二則是設(shè)置收斂區(qū)間,即當(dāng)某兩次迭代過程中,每個𝑤𝑖更新的數(shù)值都小于某個預(yù)設(shè)的值,則停止迭代。
2.再次理解步長
根據(jù)梯度下降的線性代數(shù)表示方法,我們可以通過某個實例來強(qiáng)化理解步長這一概念。
有數(shù)據(jù)集表示如下:
?假設(shè),我們使用𝑦=𝑤𝑥進(jìn)行擬合,則SSE為:
此時,SSE就是一個關(guān)于w的一元函數(shù)。當(dāng)使用最小二乘法進(jìn)行求解時,SSE就是損失函數(shù),并且SSE對于w求導(dǎo)為0的點就是最小值點,因此有:
?但我們使用梯度下降求解時:
由于梯度表示方向,在某些情況下我們可以對其絕對數(shù)值進(jìn)行一定程度上的“縮放”,此時我們規(guī)定有效梯度是原梯度的1/28,則有
設(shè)步長α=0.5,初始值點取為𝑤0=0,則迭代過程如下:
第一輪迭代:
第二輪迭代:
第三輪迭代:
第四輪迭代:
依次類推:
我們不難發(fā)現(xiàn),如果損失函數(shù)是凸函數(shù),并且全域最小值存在,則步長可以表示當(dāng)前點和最小值點之間距離的比例關(guān)系。但總的來說,對于步長的設(shè)置,我們有如下初步結(jié)論:
- 步長太短:會極大的影響迭代收斂的時間,整體計算效率會非常低;
- 步長太長:容易跳過最優(yōu)解,導(dǎo)致結(jié)果震蕩。
關(guān)于步長的設(shè)置,其實更多的會和實際使用情況相關(guān),和實際損失函數(shù)特性相關(guān),因此我們會在后續(xù)使用梯度下降求解目標(biāo)函數(shù)時根據(jù)實際情況,講解步長的實際調(diào)整策略。
3.梯度下降的矩陣表示
??和最小二乘法一樣,代數(shù)表示形式易于理解但不易與代碼操作,在實際編程實現(xiàn)梯度下降的過程中,我們還是更傾向于使用矩陣來表示梯度下降計算過程。
令?
- 𝑤? :方程系數(shù)所組成的向量,并且我們將自變量系數(shù)和截距放到了一個向量中,此處𝑤? 就相當(dāng)于前例中的a、b組成的向量(a,b);
- 𝑥? :方程自變量和1共同組成的向量;
因此,方程可表示為
另外,我們將所有自變量的值放在一個矩陣中,并且和此前A矩陣類似,為了捕捉截距,添加一列全為1的列在矩陣的末尾,設(shè)總共有m組取值,則
對應(yīng)到前例中的A矩陣,A矩陣就是擁有一個自變量、兩個取值的X矩陣。令y為自變量的取值,則有
?此時,SSE可表示為:
梯度下降損失函數(shù)為:
同樣,我們需要設(shè)置初始化參數(shù)(𝑤1,𝑤2...,𝑤𝑑,𝑏),以及學(xué)習(xí)率𝛼,然后即可開始執(zhí)行迭代過程,同樣,每一輪迭代需要有三步計算:
Step 1.計算梯度表達(dá)式
對于參數(shù)向量𝑤? ,其梯度計算表達(dá)式如下:
Step 2.用學(xué)習(xí)率乘以損失函數(shù)梯度,得到迭代移動距離
Step 3.用原參數(shù)減Step 2中計算得到的距離,更新所有的參數(shù)w
更新完所有參數(shù),即完成了一輪的迭代,接下來就能以新的𝑤? 參與下一輪迭代。
五、手動實現(xiàn)梯度下降
??接下來,我們使用上述矩陣表示的梯度下降公式,圍繞此前的簡單線性回歸的目標(biāo)函數(shù),利用此前介紹的AutoGrad模塊中的梯度計算功能,來進(jìn)行手動求解梯度下降。
在轉(zhuǎn)化為矩陣表示的過程中,我們令?
- 手動嘗試實現(xiàn)一輪迭代
總結(jié)
以上是生活随笔為你收集整理的Lesson 6.动态计算图与梯度下降入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 5.基本优化思想与最小二乘
- 下一篇: Lesson 7(12)神经网络的诞生与