??博客主頁(yè):Code_文曉
??歡迎關(guān)注:感謝大家的點(diǎn)贊評(píng)論+關(guān)注,祝您學(xué)有所成!?
一.深度學(xué)習(xí)簡(jiǎn)介??
?在介紹深度學(xué)習(xí)之前,我們先看下這幅圖:人工智能>機(jī)器學(xué)習(xí)>深度學(xué)習(xí)。
深度學(xué)習(xí)是機(jī)器學(xué)習(xí)的?個(gè)子集,也就是說(shuō)深度學(xué)習(xí)是實(shí)現(xiàn)機(jī)器學(xué)習(xí)的一種方法。與機(jī)器學(xué)習(xí)算法的主要區(qū)別如下圖所示:
傳統(tǒng)機(jī)器學(xué)習(xí)算術(shù)依賴人工設(shè)計(jì)特征,并進(jìn)行特征提取,而深度學(xué)習(xí)方法不需要人工,而是依賴算法自動(dòng)提取特征,這也是深度學(xué)習(xí)被看做黑盒子,可解釋性差的原因。
隨著計(jì)算機(jī)軟硬件的飛速發(fā)展,現(xiàn)階段通過(guò)擁有眾多層數(shù)神經(jīng)網(wǎng)絡(luò)(Neural Network)來(lái)模擬人腦來(lái)解釋數(shù)據(jù),包括圖像,文本,音頻等內(nèi)容。目前來(lái)看常用的神經(jīng)網(wǎng)絡(luò)包括:
- 卷積神經(jīng)網(wǎng)絡(luò)(Convolutional Neural Network)
- 循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network)
- 生成對(duì)抗網(wǎng)絡(luò)(Generative Adversarial Networks)
- 深度強(qiáng)化學(xué)習(xí)(Deep Reinforcement Learning)等。
二.什么是神經(jīng)網(wǎng)絡(luò)?
1.什么是神經(jīng)網(wǎng)絡(luò)
人工神經(jīng)網(wǎng)絡(luò)( Artificial NeuralNetwork, 簡(jiǎn)寫(xiě)為ANN) 也簡(jiǎn)稱為神經(jīng)網(wǎng)絡(luò)(NN),是一種模仿生物神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)和功能的計(jì)算模型。人腦可以看做是一個(gè)生物神經(jīng)網(wǎng)絡(luò),由眾多的神經(jīng)元連接而成。各個(gè)神經(jīng)元傳遞復(fù)雜的電信號(hào),樹(shù)突接收到輸入信號(hào),然后對(duì)信號(hào)進(jìn)行處理,通過(guò)軸突輸出信號(hào)。下圖是生物神經(jīng)元示意圖:
當(dāng)電信號(hào)通過(guò)樹(shù)突進(jìn)入到細(xì)胞核時(shí),會(huì)逐漸聚集電荷。達(dá)到一定的電位后,細(xì)胞就會(huì)被激活,通過(guò)軸突發(fā)出電信號(hào)。
2.人工神經(jīng)網(wǎng)絡(luò)
怎么構(gòu)建人工神經(jīng)網(wǎng)絡(luò)中的神經(jīng)元呢?
受生物神經(jīng)元的啟發(fā),人工神經(jīng)元接收來(lái)自其他神經(jīng)元或外部源的輸入每個(gè)輸入都有一個(gè)相關(guān)的權(quán)值(w),它是根據(jù)該輸入對(duì)當(dāng)前神經(jīng)元的重要性來(lái)確定的,對(duì)該輸入加權(quán)并與其他輸入求和后,經(jīng)過(guò)一個(gè)激活函數(shù)f計(jì)算得到該神經(jīng)元的輸出。這個(gè)流程就像,來(lái)源不同樹(shù)突(樹(shù)突都會(huì)有不同的權(quán)重)的信息,進(jìn)行的加權(quán)計(jì)算,輸入到細(xì)胞中做加和,再通過(guò)激活函數(shù)輸出細(xì)胞值。
那接下來(lái)我們就利用神經(jīng)元來(lái)構(gòu)建神經(jīng)網(wǎng)絡(luò),相鄰層之間的神經(jīng)元相互連接,并給每一個(gè)連接分配一個(gè)強(qiáng)度,如下圖所示:
神經(jīng)網(wǎng)絡(luò)中信息只向一個(gè)方向移動(dòng),即從輸入節(jié)點(diǎn)向前移動(dòng),通過(guò)隱藏節(jié)點(diǎn),再向輸出節(jié)點(diǎn)移動(dòng),網(wǎng)絡(luò)中沒(méi)有循環(huán)或者環(huán)。其中的基本構(gòu)件是:
- 輸入層: 即輸入x的那一層
- 輸出層:即輸出y的那一層
- 隱藏層: 輸入層和輸出層之間都是隱藏層
特點(diǎn)是:
- 同?層的神經(jīng)元之間沒(méi)有連接。
- 第N層的每個(gè)神經(jīng)元和第N-1層的所有神經(jīng)元相連(這就是full connected的含義),第N-1層神經(jīng)元的輸出就是第N層神經(jīng)元的輸入。
- 每個(gè)連接都有?個(gè)權(quán)值。
?3.神經(jīng)元是如何工作的
?人工神經(jīng)元接收到一個(gè)或多個(gè)輸入,對(duì)他們進(jìn)行加權(quán)并相加,總和通過(guò)個(gè)非線性函數(shù)產(chǎn)生輸出。
所有的輸入xi,與相應(yīng)的權(quán)重wi相乘并求和:
- 將求和結(jié)果送入到激活函數(shù)中,得到最終的輸出結(jié)果:
三.激活函數(shù)
1.網(wǎng)絡(luò)非線性因素的理解?
激活函數(shù)用于對(duì)每層的輸出數(shù)據(jù)進(jìn)行變換,進(jìn)而為整個(gè)網(wǎng)絡(luò)結(jié)構(gòu)結(jié)構(gòu)注入了非線性因素。此時(shí),神經(jīng)網(wǎng)絡(luò)就可以擬合各種曲線。如果不使用激活函數(shù),整個(gè)網(wǎng)絡(luò)雖然看起來(lái)復(fù)雜,其本質(zhì)還相當(dāng)于一種線性模型,如下公式所示:
- 沒(méi)有引入非線性因素的網(wǎng)絡(luò)等價(jià)于使用一個(gè)線性模型來(lái)擬合.
- 通過(guò)給網(wǎng)絡(luò)輸出增加激活函數(shù)實(shí)現(xiàn)引入非線性因素,使得網(wǎng)絡(luò)模型可以逼近任意函數(shù),提升網(wǎng)絡(luò)對(duì)復(fù)雜問(wèn)題的擬合能力.?
2.激活函數(shù)?
在神經(jīng)元中引入了激活函數(shù),它的本質(zhì)是向神經(jīng)網(wǎng)絡(luò)中引入非線性因素的,通過(guò)激活函數(shù),神經(jīng)網(wǎng)絡(luò)就可以擬合各種曲線。如果不用激活函數(shù)每一層輸出都是上層輸入的線性函數(shù),無(wú)論神經(jīng)網(wǎng)絡(luò)有多少層,輸出都是輸入的線性組合,引入非線性函數(shù)作為激活函數(shù),那輸出不再是輸入的線性組合,可以逼近任意函數(shù)。
常用的激活函數(shù)有:
2.1?Sigmoid/logistics激活函數(shù)
數(shù)學(xué)表達(dá)式為:
函數(shù)和其導(dǎo)數(shù)圖像如下所示:
- sigmoid 在定義域內(nèi)處處可導(dǎo),且兩側(cè)導(dǎo)數(shù)逐漸趨近于0。如果X的值很大或者很小的時(shí)候,那么函數(shù)的梯度 (函數(shù)的斜率) 會(huì)非常小,在反向傳播的過(guò)程中,導(dǎo)致了向低層傳遞的梯度也變得非常小。此時(shí),網(wǎng)絡(luò)參數(shù)很難得到有效訓(xùn)練。這種現(xiàn)象被稱為梯度消失。?
- 從 sigmoid 函數(shù)圖像可以得到,sigmoid 函數(shù)可以將任意的輸入映射到(0,1)之間,當(dāng)輸入的值大致在<-6 或者>6 時(shí),意味著輸入任何值得到的激活值都是差不多的,這樣會(huì)丟失部分的信息。比如:輸入100 和輸出10000 經(jīng)過(guò) sigmid 的激活值幾乎都是等于1的,但是輸入的數(shù)據(jù)之間相差100倍的信息就丟失了。
- 對(duì)于 sigmoid 函數(shù)而言,輸入值在[6,6] 之間輸出值才會(huì)有明顯差異,輸入值在[3,3]之間才會(huì)有比較好的效果。
- 通過(guò)上述導(dǎo)數(shù)圖像,我們發(fā)現(xiàn)導(dǎo)數(shù)數(shù)值范圍是(0,0.25),當(dāng)輸入<-6或者>6時(shí),sigmoid 激活函數(shù)圖像的導(dǎo)數(shù)接近為0,此時(shí)網(wǎng)絡(luò)參數(shù)將更新極其緩慢,或者無(wú)法更新。
- 一般來(lái)說(shuō),sigmoid 網(wǎng)絡(luò)在 5 層之內(nèi)就會(huì)產(chǎn)生梯度消失現(xiàn)象。而且,該激活函數(shù)并不是以0為中心的,所以在實(shí)踐中這種激活函數(shù)使用的很少。sigmoid函數(shù)一般只用于二分類的輸出層。
實(shí)現(xiàn)方法:
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
_,axes = plt.subplots(1,2)
# 繪制sigmoid函數(shù)圖像
x = torch.linspace(-20,20,1000)
y = torch.sigmoid(x)
# 防止標(biāo)題中文亂碼以及刻度不顯示負(fù)號(hào)問(wèn)題
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
axes[0].plot(x,y)
axes[0].grid
axes[0].set_title('sigmoid 函數(shù)圖像')
# 繪制sigmoid導(dǎo)數(shù)圖像
x = torch.linspace(-20,20,1000,requires_grad=True)
y = torch.sigmoid(x).sum().backward() # 轉(zhuǎn)化為標(biāo)量再進(jìn)行反向傳播
axes[1].plot(x.detach(),x.grad)
axes[1].grid
axes[1].set_title('sigmoid 導(dǎo)數(shù)圖像')
plt.show()
運(yùn)行結(jié)果就是上圖函數(shù)和導(dǎo)數(shù)圖像,可以自己測(cè)試。?
2.2?tanh激活函數(shù)(雙曲正切曲線)
數(shù)學(xué)表達(dá)式如下:
tanh的函數(shù)和導(dǎo)數(shù)曲線圖如下所示:
- 由上面的函數(shù)圖像可以看到,Tanh 函數(shù)將輸入映射到(-1,1)之間,圖像以0為中心,在0點(diǎn)對(duì)稱,當(dāng)輸入 大概-3 或者3 時(shí)將被映射為-1 或者1其導(dǎo)數(shù)值范圍(0,1),當(dāng)輸入的值大概<-3 或者>3時(shí),其導(dǎo)數(shù)近似0。
- 與Sigmoid 相比,它是以0為中心的,使得其收斂速度要比 Sigmoid 快,減少送代次數(shù)。然而從圖中可以看出,Tanh 兩側(cè)的導(dǎo)數(shù)也為 0,同樣會(huì)造成梯度消失。
- 若使用時(shí)可在隱藏層使用tanh函數(shù),在輸出層使用sigmoid函數(shù)。
實(shí)現(xiàn)方法:
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
_,axes = plt.subplots(1,2)
# 繪制sigmoid函數(shù)圖像
x = torch.linspace(-20,20,1000)
y = torch.tanh(x)
# 防止標(biāo)題中文亂碼以及刻度不顯示負(fù)號(hào)問(wèn)題
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
axes[0].plot(x,y)
axes[0].grid
axes[0].set_title('tanh 函數(shù)圖像')
# 繪制sigmoid導(dǎo)數(shù)圖像
x = torch.linspace(-20,20,1000,requires_grad=True)
y = torch.tanh(x).sum().backward() # 轉(zhuǎn)化為標(biāo)量再進(jìn)行反向傳播
axes[1].plot(x.detach(),x.grad)
axes[1].grid
axes[1].set_title('tanh 導(dǎo)數(shù)圖像')
plt.show()
?運(yùn)行結(jié)果就是上圖函數(shù)和導(dǎo)數(shù)圖像,可以自己測(cè)試一下。?
2.3?ReLu激活函數(shù)
數(shù)學(xué)表達(dá)式為:
ReLu激活函數(shù)和其導(dǎo)數(shù)曲線如圖所示 :
- ReLU是目前最常用的激活函數(shù)。
- 從上述函數(shù)圖像可知,ReLU 激活函數(shù)將小于0的值映射為 0,而大于0的值則保持不變,它更加重視正信號(hào),而忽略負(fù)信號(hào),這種激活函數(shù)運(yùn)算更為簡(jiǎn)單、能夠提高模型的訓(xùn)練效率。但是,如果我們網(wǎng)絡(luò)的參數(shù)采用隨機(jī)初始化時(shí),很多參數(shù)可能為負(fù)數(shù),這就使得輸入的正值會(huì)被舍去,而輸入的負(fù)值則會(huì)保留,這可能在大部分的情況下并不是我們想要的結(jié)果。
- 從圖中還能看出,當(dāng)x<0時(shí),relu導(dǎo)數(shù)為0,而當(dāng)x>0時(shí),則不存在飽和問(wèn)題。所以,ReLU 能夠在x>0時(shí)保持梯度不衰減,從而緩解梯度消失問(wèn)題。然而,隨著訓(xùn)練的推進(jìn),部分輸入會(huì)落入小于0區(qū)域,導(dǎo)致對(duì)應(yīng)權(quán)重?zé)o法更新。這種現(xiàn)象被稱為“神經(jīng)元死亡”
?與sigmoid相比,RELU的優(yōu)勢(shì)是:
- 采用sigmoid函數(shù),計(jì)算量大(指數(shù)運(yùn)算),反向傳播求誤差梯度時(shí),求導(dǎo)涉及除法,計(jì)算量相對(duì)大,而采用Relu激活函數(shù),整個(gè)過(guò)程的計(jì)算量節(jié)省很多。
- sigmoid函數(shù)反向傳播時(shí),很容易就會(huì)出現(xiàn)梯度消失的情況,從而無(wú)法完成深層網(wǎng)絡(luò)的訓(xùn)練。
- Relu會(huì)使一部分神經(jīng)元的輸出為0,這樣就造成了網(wǎng)絡(luò)的稀疏性,并且減少了參數(shù)的相互依存關(guān)系,緩解了過(guò)擬合問(wèn)題的發(fā)生。
實(shí)現(xiàn)方法:?
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
_,axes = plt.subplots(1,2)
# 繪制sigmoid函數(shù)圖像
x = torch.linspace(-20,20,1000)
y = torch.relu(x)
# 防止標(biāo)題中文亂碼以及刻度不顯示負(fù)號(hào)問(wèn)題
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
axes[0].plot(x,y)
axes[0].grid
axes[0].set_title('relu 函數(shù)圖像')
# 繪制sigmoid導(dǎo)數(shù)圖像
x = torch.linspace(-20,20,1000,requires_grad=True)
y = torch.relu(x).sum().backward() # 轉(zhuǎn)化為標(biāo)量再進(jìn)行反向傳播
axes[1].plot(x.detach(),x.grad)
axes[1].grid
axes[1].set_title('relu 導(dǎo)數(shù)圖像')
plt.show()
?運(yùn)行結(jié)果就是上圖函數(shù)和導(dǎo)數(shù)圖像,可以自己測(cè)試一下。?
2.4?Leaky ReLu激活函數(shù)
該激活函數(shù)是對(duì)RELU的改進(jìn),數(shù)學(xué)表達(dá)式為:
Leaky ReLu激活函數(shù)和其導(dǎo)數(shù)曲線如圖所示 :
Leaky Relu激活函數(shù)相比relu激活函數(shù)的優(yōu)點(diǎn)是什么?
- 避免“神經(jīng)元死亡”問(wèn)題:當(dāng)ReLU中的輸入為負(fù)數(shù)時(shí),輸出為0,此時(shí)該神經(jīng)元就不再參與后續(xù)計(jì)算,稱為“神經(jīng)元死亡”。而Leaky ReLU在輸入為負(fù)數(shù)時(shí)會(huì)有一個(gè)小的斜率,可以使神經(jīng)元不至于完全失活,從而避免“神經(jīng)元死亡”問(wèn)題。
- 更好的收斂性: ReLU 在輸入為負(fù)數(shù)時(shí)梯度為0,這可能會(huì)導(dǎo)致梯度消失或梯度爆炸等問(wèn)題。而Leaky ReLU由于有一個(gè)小的斜率,因此可以解決這個(gè)問(wèn)題,使得模型訓(xùn)練更加穩(wěn)定且收斂更快。
2.5 SoftMax?
softmax用于多分類過(guò)程中,它是二分類函數(shù)sigmoid在多分類上的推廣目的是將多分類的結(jié)果以概率的形式展現(xiàn)出來(lái)。
計(jì)算方法如下圖所示:
Softmax直白來(lái)說(shuō)就是將網(wǎng)絡(luò)輸出的 logits 通過(guò) softmax 函數(shù),就映射成為(0,1)的值,而這些值的累和為1(滿足概率的性質(zhì)),那么我們將它理解成概率,選取概率最大(也就是值對(duì)應(yīng)最大的) 節(jié)點(diǎn)作為我們的預(yù)測(cè)目標(biāo)類別。
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
# 數(shù)字中的score
x = torch.tensor([0.2,0.02,0.15,1.3,0.5,0.06,1.1,0.05,3.75])
# 將其送?到softmax中計(jì)算分類結(jié)果
y = torch.softmax(x,dim=0)
# 將結(jié)果進(jìn)?打印
print(y)
2.6 其它激活函數(shù)
2.7?激活函數(shù)的思考
這么多激活函數(shù),我們應(yīng)該如何選擇呢?
對(duì)于隱藏層
- 優(yōu)先選擇RELU激活函數(shù)。
- 如果ReLu效果不好,那么嘗試其他激活,如Leaky ReLu等。
- 如果你使用了Relu,需要注意一下Dead Relu問(wèn)題,避免出現(xiàn)大的梯度從而導(dǎo)致過(guò)多的神經(jīng)元死亡。
- 不要使用sigmoid激活函數(shù),可以嘗試使用tanh激活函數(shù)。
?對(duì)于輸出層
- 二分類問(wèn)題選擇sigmoid激活函數(shù)。
- 多分類問(wèn)題選擇softmax激活函數(shù)。
- 回歸問(wèn)題選擇identity激活函數(shù)。
四.網(wǎng)絡(luò)參數(shù)初始化
對(duì)于某一個(gè)神經(jīng)元來(lái)說(shuō),需要初始化的參數(shù)有兩類:一類是權(quán)重W,還有一類是偏置b,偏置b初始化為0即可。而權(quán)重W的初始化比較重要,我們著重來(lái)介紹常見(jiàn)的初始化方式。
以下初始化方式代碼開(kāi)頭都需要導(dǎo)入以下庫(kù),后面不重復(fù)導(dǎo)入。?
import torch
import torch.nn.functional as F
import torch.nn as nn
1.隨機(jī)初始化(標(biāo)準(zhǔn)正態(tài)分布初始化)
隨機(jī)初始化從均值為0,標(biāo)準(zhǔn)差是1的高斯分布中取樣,使用一些很小的值對(duì)參數(shù)W進(jìn)行初始化。
#隨機(jī)初始化(標(biāo)準(zhǔn)正態(tài)分布初始化)
#nn.init.normal_(linear.weight,均值=0,方差=1)
#可以省略“均值”“方差”二字
linear = nn.Linear(5,3)
nn.init.normal_(linear.weight,mean=0, std=1)
print(linear.weight)
?
2.均勻分布初始化(標(biāo)準(zhǔn)初始化)
權(quán)重參數(shù)初始化從區(qū)間均勻隨機(jī)取值。即在(-1/√d,1/√d)均勻分布中生成當(dāng)前神經(jīng)元的權(quán)重,其中d為每個(gè)神經(jīng)元的輸入數(shù)量。
# 均勻分布初始化 nn.init.constant_(linear.weight)
# 創(chuàng)建一個(gè)線性層,輸入數(shù)據(jù)特征維度是5,輸出維度是3
linear = nn.Linear(5,3)
# 對(duì)linear線性層 均勻分布初始化,init:初始化 uniform_:均勻分布 參數(shù):linear.weight
nn.init.uniform_(linear.weight)
print(linear.weight)
3.Xavier初始化
該方法的基本思想是各層的激活值和梯度的方差在傳播過(guò)程中保持一致,也叫做Glorot初始化。初始化方法有兩種:
- 標(biāo)準(zhǔn)正態(tài)分布的Xavier初始化:Glorot 正態(tài)分布初始化器,也稱為 Xavier 正態(tài)分布初始化器。它從以 0為中?,標(biāo)準(zhǔn)差為 stddev = sqrt(2 / (fan_in + fan_out)) 的正態(tài)分布中抽取樣本, 其中 fan_in 是輸?神經(jīng)元的個(gè)數(shù), fan_out是輸出的神經(jīng)元個(gè)數(shù)。
- 均勻分布的Xavier初始化:Glorot 均勻分布初始化器,也稱為 Xavier 均勻分布初始化器。它從 [-limit,limit] 中的均勻分布中抽取樣本, 其中 limit 是 sqrt(6 /(fan_in + fan_out)) , 其中 fan_in 是輸?神經(jīng)元的個(gè)數(shù),fan_out 是輸出的神經(jīng)元個(gè)數(shù)。
# xavier初始化
# 1.正態(tài)分布的xavier初始化 nn.init.xavier_normal_(linear.weight)
linear = nn.Linear(5,3)
nn.init.xavier_normal_(linear.weight)
print(linear.weight)
# 2.均勻分布的xavier初始化 nn.init.xavier_uniform_(linear.weight)
linear = nn.Linear(5,3)
nn.init.xavier_uniform_(linear.weight)
print(linear.weight)
4.He初始化
He初始化,也稱為Kaiming初始化,出自大神何愷明之手,它的基本思想是正向傳播時(shí),激活值的方差保持不變;反向傳播時(shí),關(guān)于狀態(tài)值的梯度的方差保持不變。初始化方法也有兩種:
- 標(biāo)準(zhǔn)正態(tài)分布的he初始化:He 正態(tài)分布初始化是以 0 為中心,標(biāo)準(zhǔn)差為stddev=sqrt(2/fan_in) 的截?cái)嗾龖B(tài)分布中抽取樣本, 其中 fan_in 是輸?神經(jīng)元的個(gè)數(shù)
- 均勻分布的he初始化:He 均勻?差縮放初始化器。它從 [-limit,limit] 中的均勻分布中抽取樣本, 其中 limit 是 sqrt(6 / fan_in) , 其中 fan_in 輸?神經(jīng)元的個(gè)數(shù)。
# Kaiming初始化
#1.正態(tài)分布的Kaiming初始化 nn.init.kaiming_normal_(linear.weight)
linear = nn.Linear(5,3)
nn.init.kaiming_normal_(linear.weight)
print(linear.weight)
#2.均勻分布的Kaiming初始化 nn.init.kaiming_uniform_(linear.weight)
linear = nn.Linear(5,3)
nn.init.kaiming_uniform_(linear.weight)
print(linear.weight)
5.全0、全1和固定初始化?
# 1.固定初始化 nn.init.constant_(linear.weight, 固定值)
linear = nn.Linear(5,3)
nn.init.constant_(linear.weight,5)
print(linear.weight)
# 2.全0初始化 nn.init.zeros_(linear.weight)
linear = nn.Linear(5,3)
nn.init.zeros_(linear.weight)
print(linear.weight)
# 3.全1初始化 nn.init.ones_(linear.weight)
linear = nn.Linear(5,3)
nn.init.ones_(linear.weight)
print(linear.weight)
總結(jié):
神經(jīng)網(wǎng)絡(luò)中的偏置可以初始化為0,但是權(quán)重參數(shù)不能初始化為0。
一般不會(huì)手動(dòng)初始化參數(shù),pytorch和tensorfloe默認(rèn)都會(huì)有自己的合適的初始化方式,除非效果不好的時(shí)候可以初始化一下參數(shù)。
五.神經(jīng)網(wǎng)絡(luò)的搭建
接下來(lái)我們來(lái)構(gòu)建如下圖所示的神經(jīng)網(wǎng)絡(luò)模型:
首先我們介紹下用來(lái)構(gòu)建網(wǎng)絡(luò)的全連接層:
PyTorch中的全連接層(Fully Connected Layer),也被稱為線性層(Linear Layer),是深度學(xué)習(xí)中最基本、常用的一種神經(jīng)網(wǎng)絡(luò)層。其作用是將輸入數(shù)據(jù)進(jìn)行矩陣變換,實(shí)現(xiàn)特征提取和分類等功能。一般情況下,全連接層會(huì)在特征提取部分后緊接著使用.
函數(shù)接口為:
torch.nn.Linear(in_features: int, out_features: int, bias: bool = True, device=None, dtype=None)
一般常用的參數(shù)就兩個(gè):
torch.nn.Linear(in_features=?, out_features=?,bias= );
全連接層的參數(shù)意義如下:
- in_features:輸入數(shù)據(jù)張量的大小,也就是輸入數(shù)據(jù)的特征數(shù)。比如,如果輸入的是一張28 * 28像素的灰度圖像,那么輸入數(shù)據(jù)的特征數(shù)就是28 * 28 = 784。
- out_features: 輸出數(shù)據(jù)張量的大小,也就是全連接層神經(jīng)元的數(shù)量。比如,如果想訓(xùn)練一個(gè)10類別的分類器,那么輸出數(shù)據(jù)的張量大小應(yīng)該是10。
- bias:是否添加偏置項(xiàng)。偏置項(xiàng)是一個(gè)常數(shù)向量,用于增加模型的表達(dá)能力。默認(rèn)情況下,PyTorch的全連接層會(huì)自動(dòng)添加偏置項(xiàng)。
torch.nn.Linear()構(gòu)建全連接神經(jīng)網(wǎng)絡(luò)有兩種方式:
- 自定義神經(jīng)網(wǎng)絡(luò)類:這是一種更靈活的方式,它允許你自定義每個(gè)層的行為,并實(shí)現(xiàn)更高級(jí)的功能,如跳躍連接、條件計(jì)算等等。你可以通過(guò)繼承nn.Module類來(lái)創(chuàng)建自定義神經(jīng)網(wǎng)絡(luò)。
- 使用nn.Sequential():這是一種簡(jiǎn)單的方式,其中每個(gè)層都按照順序依次鏈接。nn.Linear()可以使用函數(shù)定義線性層,然后將其添加到Sequential中以創(chuàng)建一個(gè)完整的模型。
1.自定義神經(jīng)網(wǎng)絡(luò)類:
import torch
import torch.nn as nn
# 定義神經(jīng)網(wǎng)絡(luò)類
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
# 定義層1(輸入層到隱藏層)
self.fc1 = nn.Linear(784, 512)
# 定義激活函數(shù)1
self.relu1 = nn.ReLU()
# 定義層2(隱藏層到隱藏層)
self.fc2 = nn.Linear(512, 256)
# 定義激活函數(shù)2
self.relu2 = nn.ReLU()
# 定義層3(隱藏層到輸出層)
self.fc3 = nn.Linear(256, 10)
# 定義前向傳播方法
def forward(self,x):
# 展平圖片數(shù)據(jù),變成(batch_size, size)的二維張量
x = x.view(-1, 784)
# 輸入層到隱藏層:(batch_size, 784) -> (batch_size, 512)
x = self.fc1(x)
# 激活函數(shù)1:(batch_size, 512) -> (batch_size, 512)
x = self.relu1(x)
# 隱藏層到隱藏層:(batch_size, 512) -> (batch_size, 256)
x = self.fc2(x)
# 激活函數(shù)2:(batch_size, 256) -> (batch_size, 256)
x = self.relu2(x)
# 隱藏層到輸出層: (batch_size, 256) -> (batch_size, 10)
x = self.fc3(x)
return x
# 實(shí)例化神經(jīng)網(wǎng)絡(luò)
model = Net()
print(model)
2.使用nn.Sequential():
import torch
import torch.nn as nn
# 定義神經(jīng)網(wǎng)絡(luò)類
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
# 使用Sequential快捷構(gòu)造神經(jīng)網(wǎng)絡(luò)
self.fc = nn.Sequential(
nn.Linear(784, 512), # 輸入層到隱藏層
nn.ReLU(), # 非線性激活函數(shù)
nn.Linear(512, 256), # 隱藏層到隱藏層
nn.ReLU(),
nn.Linear(256, 10) # 隱藏層到輸出層
)
# 定義前向傳播方法
def forward(self,x):
# 展平圖片數(shù)據(jù),變成(batch_size, size)的二維張量
x = x.view(-1, 784)
# 完整前向傳播
x = self.fc(x)
return x
# 實(shí)例化神經(jīng)網(wǎng)絡(luò)
model = Net()
print(model)
3.神經(jīng)網(wǎng)絡(luò)的優(yōu)缺點(diǎn)?
1.優(yōu)點(diǎn)
- 精度高,性能優(yōu)于其他的機(jī)器學(xué)習(xí)方法,甚至在某些領(lǐng)域超過(guò)了人類。
- 可以近似任意的非線性函數(shù)
- 隨之計(jì)算機(jī)硬件的發(fā)展,近年來(lái)在學(xué)界和業(yè)界受到了熱捧,有大量的框架和庫(kù)可供調(diào)用
2.缺點(diǎn)?
- 黑箱,很難解釋模型是怎么工作的。
- 訓(xùn)練時(shí)間長(zhǎng),需要大量的計(jì)算力。
- 網(wǎng)絡(luò)結(jié)構(gòu)復(fù)雜,需要調(diào)整超參數(shù)。
- 小數(shù)據(jù)集上表現(xiàn)不佳,容易發(fā)生過(guò)擬合。?
六.損失函數(shù)??
在深度學(xué)習(xí)中,損失函數(shù)是用來(lái)衡量模型參數(shù)的質(zhì)量的函數(shù),衡量的方式是比較網(wǎng)絡(luò)輸出和真實(shí)輸出的差異。損失函數(shù)在不同的文獻(xiàn)中名稱是不一樣的,主要有以下幾種命名方式:
1.分類任務(wù)
在深度學(xué)習(xí)的分類任務(wù)中使用最多的是交叉熵?fù)p失函數(shù),所以在這里我們著重介紹這種損失函數(shù)。
1.1 多分類任務(wù)
在多分類任務(wù)通常使用softmax將logits轉(zhuǎn)換為概率的形式,所以多分類的交叉熵?fù)p失也叫做softmax損失,它的計(jì)算方法是:
其中,y是樣本x屬于某一個(gè)類別的真實(shí)概率,而f(x)是樣本屬于某一類別的預(yù)測(cè)分?jǐn)?shù),S是softmax函數(shù),L用來(lái)衡量p,q之間差異性的損失結(jié)果。
例子:
上圖中的交叉熵?fù)p失為:
?從概率角度理解,我們的目的是,最小化 正確類別所對(duì)應(yīng)的預(yù)測(cè)概率的對(duì)數(shù) 的負(fù)值,如下圖所示:
?在torch.nn中使用CrossEntropyLoss()實(shí)現(xiàn),如下所示:
import torch
import torch.nn as nn
# 設(shè)置真實(shí)值和預(yù)測(cè)值
y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32)
y_pred = torch.tensor([[0.05, 0.9, 0.05], [0.05, 0.05, 0.9]], dtype=torch.float32)
# 創(chuàng)建損失函數(shù)對(duì)象
ce_loss_fn = nn.CrossEntropyLoss()
# 計(jì)算損失
ce_loss = loss_fn(y_pred, y_true)
# 打印損失值
print(loss.item()) #0.6177929639816284
1.2 二分類任務(wù)?
在處理二分類任務(wù)時(shí),我們不在使用softmax激活函數(shù),而是使用sigmoid激活函數(shù),那損失函數(shù)也相應(yīng)的進(jìn)行調(diào)整,使用二分類的交叉熵?fù)p失函數(shù):
其中,y是樣本x屬于某一個(gè)類別的真實(shí)概率,而y^是樣本屬于某一類別的預(yù)測(cè)概率,L用來(lái)衡量真實(shí)值與預(yù)測(cè)值之間差異性的損失結(jié)果。?
在torch.nn中使用BCELoss()實(shí)現(xiàn),如下所示:
import torch
import torch.nn as nn
# 設(shè)置真實(shí)值和預(yù)測(cè)值
y_true = torch.tensor([[0], [1]], dtype=torch.float32)
y_pred = torch.tensor([[0.4], [0.6]], dtype=torch.float32)
# 創(chuàng)建損失函數(shù)對(duì)象
loss_fn = nn.BCELoss()
# 計(jì)算損失
bce_loss = loss_fn(y_pred, y_true)
# 打印損失值
print(bec_loss.item()) #0.5108255743980408
2.回歸任務(wù)
回歸任務(wù)中常用的損失函數(shù)有以下幾種:
2.1 MAE損失
Mean absolute loss(MAE)也被稱為L(zhǎng)1Loss,是以絕對(duì)誤差作為距離。
曲線如下圖所示:
特點(diǎn)是: 由于L1Loss具有稀疏性,為了懲罰較大的值,因此常常將其作為正則項(xiàng)添加到其他loss中作為約束。L1Loss的最大問(wèn)題是梯度在零點(diǎn)不平滑,在0點(diǎn)處不可導(dǎo),導(dǎo)致會(huì)跳過(guò)極小值。?
在torch.nn中使用L1Loss()實(shí)現(xiàn),如下所示:?
import torch
import torch.nn as nn
# 設(shè)置真實(shí)值和預(yù)測(cè)值的張量
y_true = torch.tensor([[0.], [0.]]) # 真實(shí)值
y_pred = torch.tensor([[1.], [1.]]) # 預(yù)測(cè)值
# 創(chuàng)建損失函數(shù)對(duì)象
loss_fn = nn.L1Loss() # 使用L1Loss作為MAE損失函數(shù)
# 計(jì)算損失
L1_loss = loss_fn(y_pred, y_true)
# 打印損失值
print(L1_loss.item()) #1.0
2.2 MSE損失?
Mean Squared Loss/ Quadratic Loss(MSE loss)也被稱為L(zhǎng)2 loss,或歐氏距離,它以誤差的平方和作為距離:
曲線如下圖所示:
特點(diǎn)是: L2loss也常常作為正則項(xiàng)。當(dāng)預(yù)測(cè)值與目標(biāo)值相差很大時(shí)梯度容易爆炸。?
在torch.nn中使用L2Loss()實(shí)現(xiàn),如下所示:?
import torch
import torch.nn as nn
# 設(shè)置真實(shí)值和預(yù)測(cè)值的張量
y_true = torch.tensor([[0.], [0.]]) # 真實(shí)值
y_pred = torch.tensor([[1.], [1.]]) # 預(yù)測(cè)值
# 創(chuàng)建損失函數(shù)對(duì)象
loss_fn = nn.MSELoss() # 使用L1Loss作為MAE損失函數(shù)
# 計(jì)算損失
mse_loss = loss_fn(y_pred, y_true)
# 打印損失值
print(mse_loss.item()) #1.0
2.3?smooth L1 損失
Smooth L1損失函數(shù)如下式所示:
其中:x=f(x)-y 為真實(shí)值和預(yù)測(cè)值的差值。
從上圖中可以看出,該函數(shù)實(shí)際上就是一個(gè)分段函數(shù),在[-1,1]之間實(shí)際上就是L2損失,這樣解決了L1的不光滑問(wèn)題,在[-1,11]區(qū)間外,實(shí)際上就是L1損失,這樣就解決了離群點(diǎn)梯度爆炸的問(wèn)題。通常在目標(biāo)檢測(cè)中使用該損失函數(shù)。?
在torch.nn中使用SmoothL1Loss()實(shí)現(xiàn),如下所示:?
import torch
import torch.nn as nn
# 設(shè)置真實(shí)值和預(yù)測(cè)值的張量
y_true = torch.tensor([[0.], [1.]]) # 真實(shí)值
y_pred = torch.tensor([[0.2], [0.6]]) # 預(yù)測(cè)值
# 創(chuàng)建損失函數(shù)對(duì)象
loss_fn = nn.SmoothL1Loss() # 使用L1Loss作為MAE損失函數(shù)
# 計(jì)算損失
SL1_loss = loss_fn(y_pred, y_true)
# 打印損失值
print(SL1_loss.item()) #0.04999999701976776
總結(jié):
- 知道分類任務(wù)的損失函數(shù)
多分類的交叉熵?fù)p失函數(shù)和?分類的交叉熵?fù)p失函數(shù)
- 知道回歸任務(wù)的損失函數(shù)
MAE,MSE,smooth L1損失函數(shù)?
七.梯度下降和反向傳播算法
1.梯度下降算法?
梯度下降法簡(jiǎn)單來(lái)說(shuō)就是一種尋找使損失函數(shù)最小化的方法。大家在機(jī)器學(xué)習(xí)階段已經(jīng)學(xué)過(guò)該算法,所以我們?cè)谶@里就簡(jiǎn)單的回顧下,從數(shù)學(xué)上的角度來(lái)看,梯度的方向是函數(shù)增長(zhǎng)速度最快的方向,那么梯度的反方向就是函數(shù)減少最快的方向,所以有:
其中,n是學(xué)習(xí)率,如果學(xué)習(xí)率太小,那么每次訓(xùn)練之后得到的效果都太小,增大訓(xùn)練的時(shí)間成本。如果,學(xué)習(xí)率太大,那就有可能直接跳過(guò)最優(yōu)解,進(jìn)入無(wú)限的訓(xùn)練中。解決的方法就是,學(xué)習(xí)率也需要隨著訓(xùn)練的進(jìn)行而變化。?
在上圖中我們展示了一維和多維的損失函數(shù),損失函數(shù)呈碗狀。在訓(xùn)練過(guò)程中損失函數(shù)對(duì)權(quán)重的偏導(dǎo)數(shù)就是損失函數(shù)在該位置點(diǎn)的梯度。我們可以看到,沿著負(fù)梯度方向移動(dòng),就可以到達(dá)損失函數(shù)底部,從而使損失函數(shù)最小化。這種利用損失函數(shù)的梯度迭代地尋找局部最小值的過(guò)程就是梯度下降的過(guò)程。
在進(jìn)行模型訓(xùn)練時(shí),有三個(gè)基礎(chǔ)的概念:
- Eoch: 使用全部數(shù)據(jù)對(duì)模型進(jìn)行以此完整訓(xùn)練。
- Batch:使用訓(xùn)練集中的小部分樣本對(duì)模型權(quán)重進(jìn)行以此反向傳播的參數(shù)更新
- iteration: 使用一個(gè)Batch 數(shù)據(jù)對(duì)模型進(jìn)行一次參數(shù)更新的過(guò)程。
實(shí)際上,梯度下降的幾種方式的根本區(qū)別就在于 Batch Size不同,,如下表所示:?
注:上表中Mini-Batch 的 Batch 個(gè)數(shù)為 N/B+1是針對(duì)未整除的情況。整除則是N/B。
假設(shè)數(shù)據(jù)集有50000個(gè)訓(xùn)練樣本,現(xiàn)在選擇 Batch Size = 26對(duì)模型進(jìn)行訓(xùn)練。
- 每個(gè) Epoch 要訓(xùn)練的圖?數(shù)量:50000
- 訓(xùn)練集具有的 Batch 個(gè)數(shù):50000/256+1=196
- 每個(gè) Epoch 具有的 Iteration 個(gè)數(shù):196
- 10個(gè) Epoch 具有的 Iteration 個(gè)數(shù):1960
實(shí)際中使用較多的是小批量的梯度下降算法。?在torch.optim通過(guò)以下?法實(shí)現(xiàn):
# 定義優(yōu)化器,設(shè)置學(xué)習(xí)率和動(dòng)量參數(shù)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
2.反向傳播算法(BP算法)
- 多層神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)能力比單層網(wǎng)絡(luò)強(qiáng)得多。想要訓(xùn)練多層網(wǎng)絡(luò),需要更強(qiáng)大的學(xué)習(xí)算法。誤差反向傳播算法(Back Propagation)是其中最杰出的代表,它是目前最成功的神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)算法?,F(xiàn)實(shí)任務(wù)使用神經(jīng)網(wǎng)絡(luò)時(shí),大多是在使用 BP 算法進(jìn)行訓(xùn)練,值得指出的是BP算法不僅可用于多層前饋神經(jīng)網(wǎng)絡(luò),還可以用于其他類型的神經(jīng)網(wǎng)絡(luò)。通常說(shuō)BP網(wǎng)絡(luò)時(shí),一般是指用BP算法訓(xùn)練的多層前饋神經(jīng)網(wǎng)絡(luò)。
- 利用反向傳播算法對(duì)神經(jīng)網(wǎng)絡(luò)進(jìn)行訓(xùn)練。該方法與梯度下降算法相結(jié)合對(duì)網(wǎng)絡(luò)中所有權(quán)重計(jì)算損失函數(shù)的梯度,并利用梯度值來(lái)更新權(quán)值以最小化損失函數(shù)。在介紹BP算法前,我們先看下前向傳播與鏈?zhǔn)椒▌t的內(nèi)容。
2.1? 前向傳播與反向傳播的概念
前向傳播指的是數(shù)據(jù)輸入的神經(jīng)網(wǎng)絡(luò)中,逐層向前傳輸,一直到運(yùn)算到輸出層為止。?
在網(wǎng)絡(luò)的訓(xùn)練過(guò)程中經(jīng)過(guò)前向傳播后得到的最終結(jié)果跟訓(xùn)練樣本的真實(shí)值總是存在一定誤差,這個(gè)誤差便是損失函數(shù)。想要減小這個(gè)誤差,就用損失函數(shù)ERROR,從后往前,依次求各個(gè)參數(shù)的偏導(dǎo),這就是反向傳播(Back Propagation)。
BP (Back Propagation)算法也叫做誤差反傳播算法,它用于求解模型的參數(shù)梯度,從而使用梯度下降法來(lái)更新網(wǎng)絡(luò)參數(shù)。它的基本工作流程如下:
- 通過(guò)正向傳播得到誤差,所謂正向傳播指的是數(shù)據(jù)從輸入到輸出層,經(jīng)過(guò)層層計(jì)算得到預(yù)測(cè)值,并利用損失函數(shù)得到預(yù)測(cè)值和真實(shí)值之前的誤差
- 通過(guò)反向傳播把誤差傳遞給模型的參數(shù),從而對(duì)網(wǎng)絡(luò)參數(shù)進(jìn)行適當(dāng)?shù)恼{(diào)整,縮小預(yù)測(cè)值和真實(shí)值之間的誤差。
- 反向傳播算法是利用鏈?zhǔn)椒▌t進(jìn)行梯度求解,然后進(jìn)行參數(shù)更新。對(duì)于復(fù)雜的復(fù)合函數(shù),我們將其拆分為一系列的加減乘除或指數(shù),對(duì)數(shù),三角函數(shù)等初等函數(shù),通過(guò)鏈?zhǔn)椒▌t完成復(fù)合函數(shù)的求導(dǎo)。
2.2 鏈?zhǔn)椒▌t?
反向傳播算法是利用鏈?zhǔn)椒▌t進(jìn)行梯度求解及權(quán)重更新的。對(duì)于復(fù)雜的復(fù)合函數(shù),我們將其拆分為一系列的加減乘除或指數(shù),對(duì)數(shù),三角函數(shù)等初等函數(shù),通過(guò)鏈?zhǔn)椒▌t完成復(fù)合函數(shù)的求導(dǎo)。
為簡(jiǎn)單起見(jiàn),這里以一個(gè)神經(jīng)網(wǎng)絡(luò)中常見(jiàn)的復(fù)合函數(shù)的例子來(lái)說(shuō)明這個(gè)過(guò)程。復(fù)合函數(shù)f(x;w,b)為:
其中x是輸入數(shù)據(jù),w是權(quán)重,b是偏置。我們需要求關(guān)于w和b的偏導(dǎo),然后應(yīng)用梯度下降公式就可以更新參數(shù)。我們將復(fù)合函數(shù)分解為一系列的初等函數(shù)導(dǎo)數(shù)相乘的形式:
并進(jìn)行圖形化表示,如下所示:
整個(gè)復(fù)合函數(shù)f(x;wb)關(guān)于參數(shù) w 和b的導(dǎo)數(shù)可以通過(guò) (x;w,b) 與參數(shù)w和b之間路徑上所有的導(dǎo)數(shù)連乘來(lái)得到,即:?
以w為例,當(dāng)x = 1,w = 0,b= 0 時(shí),可以得到
注意:常用函數(shù)的導(dǎo)數(shù):
2.3 反向傳播算法舉例(重點(diǎn))
在網(wǎng)絡(luò)的訓(xùn)練過(guò)程中經(jīng)過(guò)前向傳播后得到的最終結(jié)果跟訓(xùn)練樣本的真實(shí)值總是存在一定誤差,這個(gè)誤差便是損失函數(shù)。想要減小這個(gè)誤差,就用損失函數(shù)ERROR,從后往前,依次求各個(gè)參數(shù)的偏導(dǎo),這就是反向傳播(Back Propagation)。
BP (Back Propagation)算法也叫做誤差反傳播算法,它用于求解模型的參數(shù)梯度,從而使用梯度下降法來(lái)更新網(wǎng)絡(luò)參數(shù)。它的基本工作流程如下:
- 通過(guò)正向傳播得到誤差,所謂正向傳播指的是數(shù)據(jù)從輸入到輸出層,經(jīng)過(guò)層層計(jì)算得到預(yù)測(cè)值,并利用損失函數(shù)得到預(yù)測(cè)值和真實(shí)值之前的誤差
- 通過(guò)反向傳播把誤差傳遞給模型的參數(shù),從而對(duì)網(wǎng)絡(luò)參數(shù)進(jìn)行適當(dāng)?shù)恼{(diào)整,縮小預(yù)測(cè)值和真實(shí)值之間的誤差。
- 反向傳播算法是利用鏈?zhǔn)椒▌t進(jìn)行梯度求解,然后進(jìn)行參數(shù)更新。對(duì)于復(fù)雜的復(fù)合函數(shù),我們將其拆分為一系列的加減乘除或指數(shù),對(duì)數(shù),三角函數(shù)等初等函數(shù),通過(guò)鏈?zhǔn)椒▌t完成復(fù)合函數(shù)的求導(dǎo)。
這里舉兩個(gè)例子,兩種表現(xiàn)形式,來(lái)講解神經(jīng)網(wǎng)絡(luò)如何進(jìn)行前向傳播,反向傳播,以及BP算法進(jìn)行更新網(wǎng)絡(luò)參數(shù)的過(guò)程:
例1?:
為了能夠把計(jì)算過(guò)程描述的更詳細(xì)一些,上圖中一個(gè)矩形代表一個(gè)神經(jīng)元,每個(gè)神經(jīng)元中分別是值和激活值的計(jì)算結(jié)果和其對(duì)應(yīng)的公式,最終計(jì)算出真實(shí)值和預(yù)測(cè)值之間的誤差 0.2984.其中:?
- 由下向上看,最下層綠色的兩個(gè)圓代表兩個(gè)輸入值
- 右側(cè)的8個(gè)數(shù)字,最下面4個(gè)表示 w1、w2、w3、w4 的參數(shù)初始值,最上面的4個(gè)數(shù)字表示w5、w6、w7、w8 的參數(shù)初始值
- b1 值為 0.35,b2值為 0.60
- 預(yù)測(cè)結(jié)果分別為:0.7514、0.7729?
我們首先計(jì)算w5和w7兩個(gè)權(quán)重的梯度,然后使用梯度下降更新這兩個(gè)參數(shù)。
計(jì)算出了梯度值,接下來(lái)使用使用梯度下降公式來(lái)更新模型參數(shù),假設(shè):學(xué)習(xí)率為 0.5,則:?
??
接下來(lái),我們計(jì)算 w1的梯度,以及更新該參數(shù):?
接下來(lái)更新該參數(shù):
??
其他的網(wǎng)絡(luò)參數(shù)更新過(guò)程和上面的過(guò)程是一樣的。下面我們使用代碼構(gòu)建上面的網(wǎng)絡(luò),并進(jìn)行一次正向傳播和反向傳播。
import torch
import torch.nn as nn
import torch.optim as optim
# 1.搭建神經(jīng)網(wǎng)絡(luò)模型
class Net(nn.Module):
def __init__(self):
# 注意:必須手動(dòng)調(diào)用父類的初始化函數(shù)
super(Net,self).__init__()
# 構(gòu)建全連接層神經(jīng)網(wǎng)絡(luò)
self.linear1 = nn.Linear(in_features=2,out_features=2)
self.linear2 = nn.Linear(in_features=2,out_features=2)
# 本案例手動(dòng)對(duì)網(wǎng)絡(luò)參數(shù)進(jìn)行初始化
self.linear1.weight.data = torch.tensor([[0.15,0.20],[0.25,0.30]])
self.linear2.weight.data = torch.tensor([[0.40,0.45],[0.50,0.55]])
self.linear1.bias.data = torch.tensor([0.35,0.35])
self.linear2.bias.data = torch.tensor([0.60,0.60])
# forward函數(shù)是進(jìn)行正向傳播的函數(shù),在該函數(shù)中會(huì)定義定向傳播的計(jì)算過(guò)程
def forward(self,x):
x = self.linear1(x)
x = torch.sigmoid(x)
x = self.linear2(x)
x = torch.sigmoid(x)
# 正向傳播結(jié)束之后,需要返回輸出結(jié)果
return x
# 2.反向傳播
# 輸入數(shù)據(jù),注意:二維列表表示批次樣本的輸入
inputs = torch.tensor([[0.05,0.10]])
# 真實(shí)值
target = torch.tensor([[0.01,0.99]])
# 初始化網(wǎng)絡(luò)對(duì)象
net =Net()
# 輸出值
output = net(inputs) # 這么寫(xiě)相當(dāng)于直接調(diào)用了對(duì)象內(nèi)部的 forward函數(shù)
# print(output) #tensor([[0.7514, 0.7729]], grad_fn=<SigmoidBackward0>)
# 計(jì)算誤差
loss = torch.sum((output - target)**2) / 2
# print(loss) #tensor(0.2984, grad_fn=<DivBackward0>)
# 構(gòu)建優(yōu)化器(梯度下降)
optimizer = optim.SGD(net.parameters(),lr=0.5) #net.parameters()獲得所有的參數(shù)
# 梯度清零
optimizer.zero_grad()
# 反向傳播
loss.backward()
# 參數(shù)更新
optimizer.step()
# 打印參數(shù)的梯度
print(net.linear1.weight.grad)
print(net.linear2.weight.grad)
# 打印更新之后的參數(shù)值
print(net.state_dict())
?例2:?
如下圖是一個(gè)簡(jiǎn)單的神經(jīng)網(wǎng)絡(luò)用來(lái)舉例:激活函數(shù)為sigmoid
前向傳播運(yùn)算:
接下來(lái)是反向傳播 (求網(wǎng)絡(luò)誤差對(duì)各個(gè)權(quán)重參數(shù)的梯度)?
我們先來(lái)求最簡(jiǎn)單的,求誤差E對(duì)w5的導(dǎo)數(shù)。首先明確這是一個(gè)“鏈?zhǔn)椒▌t”的求導(dǎo)過(guò)程,要求誤差E對(duì)w5的導(dǎo)數(shù),需要先求誤差E對(duì)out o1的導(dǎo)數(shù),再求out o1對(duì)net o1的導(dǎo)數(shù),最后再求net o1對(duì)w5的導(dǎo)數(shù),經(jīng)過(guò)這個(gè)鏈?zhǔn)椒▌t,我們就可以求出誤差E對(duì)w5的導(dǎo)數(shù)(偏導(dǎo)),如下圖所示:
導(dǎo)數(shù)(梯度) 已經(jīng)計(jì)算出來(lái)了,下面就是反向傳播與參數(shù)更新過(guò)程:?
如果要想求誤差E對(duì)w1的導(dǎo)數(shù),誤差E對(duì)w1的求導(dǎo)路徑不止一條,這會(huì)稍微復(fù)雜一點(diǎn),但換湯不換藥,計(jì)算過(guò)程如下所示:?
至此,“反向傳播算法”的過(guò)程就講完了啦!?
八.梯度下降優(yōu)化算法
優(yōu)化方法是對(duì)傳統(tǒng)的梯度下降算法進(jìn)行優(yōu)化,傳統(tǒng)的梯度下降優(yōu)化算法在進(jìn)行網(wǎng)絡(luò)訓(xùn)練時(shí),可能會(huì)碰到以下情況:
- 碰到平緩區(qū)域,梯度值較小,參數(shù)優(yōu)化變慢;
- 碰到“鞍點(diǎn)”,梯度為0,參數(shù)無(wú)法優(yōu)化;
- 碰到局部最小值;
對(duì)于這些問(wèn)題,我們?cè)趺锤倪M(jìn)SGD呢?這里就有一些對(duì)梯度下降算法的優(yōu)化方法,例如: Momentum、AdaGrad、RMSprop、Adam等。
前言:指數(shù)加權(quán)平均數(shù)計(jì)算方法
在介紹優(yōu)化算法之前先了解知道一下指數(shù)加權(quán)平均算法,因?yàn)楹罄m(xù)的梯度下降優(yōu)化算法都是基于這個(gè)思想的。我們最常見(jiàn)的:
- 算數(shù)平均指的是將所有數(shù)加起來(lái)除以數(shù)的個(gè)數(shù),每個(gè)數(shù)的權(quán)重是相同的;
- 加權(quán)平均指的是給每個(gè)數(shù)賦予不同的權(quán)重求得平均數(shù);
- 移動(dòng)平均數(shù)指的是計(jì)算最近鄰的 N 個(gè)數(shù)來(lái)獲得平均數(shù)。
- 指數(shù)移動(dòng)加權(quán)平均則是參考各數(shù)值,并且各數(shù)值的權(quán)重都不同,距離越遠(yuǎn)的數(shù)字對(duì)平均數(shù)計(jì)算的貢獻(xiàn)就越小(權(quán)重較小),距離越近則對(duì)平均數(shù)的計(jì)算貢獻(xiàn)就越大(權(quán)重越大)。
假設(shè)給定一個(gè)序列,例如北京一年每天的氣溫值,圖中藍(lán)色的點(diǎn)代表真實(shí)數(shù)據(jù)。?
這時(shí)溫度值波動(dòng)比較大,那我們就使用加權(quán)平均值來(lái)進(jìn)行平滑,如下圖紅線就是平滑后的結(jié)果:?
再舉一個(gè)例子:
比如:明天氣溫怎么樣,和昨天氣溫有很大關(guān)系,而和一個(gè)月前的氣溫關(guān)系就小一些。?
計(jì)算公式可以用下面的式子來(lái)表示:
- St為 t 指數(shù)加權(quán)平均后的值。
- Yt為 t 時(shí)刻時(shí)的真實(shí)值。
- β為權(quán)重值,該值越大平均數(shù)越平緩。?
如上圖所示,加權(quán)平均值進(jìn)行平滑后的圖中β設(shè)為0.9,那么指數(shù)加權(quán)平均的計(jì)算結(jié)果為:
- S1=Y1
- S2=0.9S1 +0.1Y2?
- ......
- S99?= 0.9S98 +0.1Y99?
- S100 = 0.9S99?+0.1Y100?
- ?......
那么第100天的結(jié)果就可以表示為:?
代碼:?
1.沒(méi)有指數(shù)加權(quán)平均
import torch
import matplotlib.pyplot as plt
# 隨機(jī)產(chǎn)生近30天的氣溫?cái)?shù)據(jù)
#1.沒(méi)有指數(shù)加權(quán)平均
#固定隨機(jī)數(shù)種子
torch.manual_seed(0)
# 隨機(jī)產(chǎn)生30天的溫度
temperature = torch.randn(size=[30, ]) * 10
# 繪制平均溫度值
days = torch.arange(1, 31,1)
plt.plot(days,temperature,'o-r')
plt.show()
2.有指數(shù)加權(quán)平均?
#2.有指數(shù)加權(quán)平均
#固定隨機(jī)數(shù)種子
torch.manual_seed(0)
#隨機(jī)產(chǎn)生30天的溫度
temperature = torch.randn(size=[30, ]) * 10
#繪制平均溫度值
days = torch.arange(1, 31,1)
# 存儲(chǔ)歷史指數(shù)加權(quán)平均值
exp_weight_avg = []
# 設(shè)置權(quán)重系數(shù)beta
beta = 0.9
for idx, temp in enumerate(temperature,1):
if idx ==1:
exp_weight_avg.append(temp)
continue
new_temp = exp_weight_avg[idx-2] * beta + (1 - beta) *temp
exp_weight_avg.append(new_temp)
# 繪制指數(shù)加權(quán)平均溫度值
days = torch.arange(1,31,1)
plt.plot(days,exp_weight_avg,'o-r')
plt.show()
1.Momentum(動(dòng)量梯度下降算法)
”平緩”、“鞍點(diǎn)”區(qū)域時(shí),參數(shù)更新速度變慢,Momentum通過(guò)指數(shù)加權(quán)平當(dāng)梯度下降碰到“峽谷”、均法,累計(jì)歷史梯度值,進(jìn)行參數(shù)更新,越近的梯度值對(duì)當(dāng)前參數(shù)更新的重要性越大。?
梯度計(jì)算公式為:?
- St-1 表示歷史梯度移動(dòng)加權(quán)平均值
- wt 表示當(dāng)前時(shí)刻的梯度值
- B為權(quán)重系數(shù)?
咱們舉個(gè)例子,假設(shè):權(quán)重β為0.9,例如:?
- w 表示初始梯度
- d 表示當(dāng)前輪數(shù)計(jì)算出的梯度值
- s 表示歷史梯度值?
梯度下降公式中梯度的計(jì)算,就不再是當(dāng)前時(shí)刻t的梯度值,而是歷史梯度值的指數(shù)移動(dòng)加權(quán)平均值。公式修改為:?
與原始的梯度下降算法相比,它的下降趨勢(shì)更平滑。那么,Monmentum 優(yōu)化方法是如何一定程度上克服“平緩”、”鞍點(diǎn)”、”峽谷”的問(wèn)題呢??
- 當(dāng)處于鞍點(diǎn)位置時(shí),由于當(dāng)前的梯度為 0,參數(shù)無(wú)法更新。但是 Momentum 動(dòng)量梯度下降算法已經(jīng)在先前積累了一些梯度值,很有可能使得跨過(guò)鞍點(diǎn)。?
- 由于 mini-batch 普通的梯度下降算法,每次選取少數(shù)的樣本梯度確定前進(jìn)方向,可能會(huì)出現(xiàn)震蕩,使得訓(xùn)練時(shí)間變長(zhǎng)。Momentum 使用移動(dòng)加權(quán)平均,平滑了梯度的變化,使得前進(jìn)方向更加平緩,有利于加快訓(xùn)練過(guò)程。一定程度上有利于降低“峽谷”問(wèn)題的影響。?
- 峽谷問(wèn)題:就是會(huì)使得參數(shù)更新出現(xiàn)劇烈震蕩。?
- Momentum算法可以理解為是對(duì)梯度值的一種調(diào)整,我們知道梯度下降算法中還有一個(gè)很重要的學(xué)習(xí)率,Momentum 并沒(méi)有學(xué)習(xí)率進(jìn)行優(yōu)化。?
2.AdaGrad
AdaGrad 通過(guò)對(duì)不同的參數(shù)分量使用不同的學(xué)習(xí)率,AdaGrad 的學(xué)習(xí)率總體會(huì)逐漸減小,這是因?yàn)锳daGrad 認(rèn)為: 在起初,我們距離最優(yōu)目標(biāo)仍較遠(yuǎn),可以使用較大的學(xué)習(xí)率,加快訓(xùn)練速度,隨著迭代次數(shù)的增加,距離最優(yōu)目標(biāo)越來(lái)越近,學(xué)習(xí)率也隨之逐漸下降。?
其計(jì)算步驟如下:
- 初始化學(xué)習(xí)率 α、初始化參數(shù) θ、小常數(shù) σ?= 1e-6
- 初始化梯度累積變量 s = 0
- 從訓(xùn)練集中采樣 m 個(gè)樣本的小批量,計(jì)算梯度g
- 累積平方梯度 s = s + g ⊙ g,⊙表示各個(gè)分量相乘?
5.學(xué)習(xí)率 α 的計(jì)算公式如下:
6.參數(shù)更新公式如下:
7.重復(fù)2-6步驟。
AdaGrad 缺點(diǎn)是:可能會(huì)使得學(xué)習(xí)率過(guò)早、過(guò)量的降低,導(dǎo)致模型訓(xùn)練后期學(xué)習(xí)率太小,較難找到最優(yōu)解。?
3.RMSprop
AdaGrad算法在迭代后期由于學(xué)習(xí)率過(guò)小,能較難找到最優(yōu)解。為了解決這一問(wèn)題,RMSProp算法對(duì)AdaGrad算法做了一點(diǎn)小小的優(yōu)化。最主要的不同是,其使用指數(shù)移動(dòng)加權(quán)平均梯度替換歷史梯度的平方和。其計(jì)算過(guò)程如下:
- 初始化學(xué)習(xí)率 α、初始化參數(shù) θ、小常數(shù) σ?= 1e-6
- 初始化參數(shù) θ
- 初始化梯度累計(jì)變量 s
- 從訓(xùn)練集中采樣 m 個(gè)樣本的小批量,計(jì)算梯度g
- 使用指數(shù)移動(dòng)平均累積歷史梯度,公式如下:
6.學(xué)習(xí)率 a 的計(jì)算公式如下:?
7.參數(shù)更新公式如下:?
RMSProp與AdaGrad 最大的區(qū)別是對(duì)梯度的累積方式不同,對(duì)于每個(gè)梯度分量仍然使用不同的學(xué)習(xí)率。
RMSProp 通過(guò)引入衰減系數(shù) β,控制歷史梯度對(duì)歷史梯度信息獲取的多少.被證明在神經(jīng)網(wǎng)絡(luò)非凸條件下的優(yōu)化更好,學(xué)習(xí)率衰減更加合理一些。?
需要注意的是:AdaGrad和 RMSProp 都是對(duì)于不同的參數(shù)分量使用不同的學(xué)習(xí)率,如果某個(gè)參數(shù)分量的梯度值較大,則對(duì)應(yīng)的學(xué)習(xí)率就會(huì)較小,如果某個(gè)參數(shù)分量的梯度較小,則對(duì)應(yīng)的學(xué)習(xí)率就會(huì)較大一些。
4.Adam?
Momentum 使用指數(shù)加權(quán)平均計(jì)算當(dāng)前的梯度值、AdaGrad、RMSProp 使用自適應(yīng)的學(xué)習(xí)率,Adam 結(jié)合了 Momentum、RMSProp 的優(yōu)點(diǎn),使用: 移動(dòng)加權(quán)平均的梯度和移動(dòng)加權(quán)平均的學(xué)習(xí)率。使得能夠自適應(yīng)學(xué)習(xí)率的同時(shí),也能夠使用Momentum 的優(yōu)點(diǎn)。
Adam 優(yōu)化算法 (Adaptive Moment Estimation,自適應(yīng)矩估計(jì)) 將Momentum 和 RMSProp 算法結(jié)合在一起。Adam算法在RMSProp算法基礎(chǔ)上對(duì)小批量隨機(jī)梯度也做了指數(shù)加權(quán)移動(dòng)平均。?
5.小結(jié)?
本小節(jié)主要學(xué)習(xí)了常見(jiàn)的一些對(duì)普通梯度下降算法的優(yōu)化方法,主要有 Momentum、AdaGrad、RMSProp、Adam等優(yōu)化方法,其中 Momentum 使用指數(shù)加權(quán)平均參考了歷史梯度,使得梯度值的變化更加平緩。AdaGrad 則是針對(duì)學(xué)習(xí)率進(jìn)行了自適應(yīng)優(yōu)化,由于其實(shí)現(xiàn)可能會(huì)導(dǎo)致學(xué)習(xí)率下降過(guò)快,RMSProp對(duì)AdaGrad 的學(xué)習(xí)率自適應(yīng)計(jì)算方法進(jìn)行了優(yōu)化,Adam 則是綜合Momentum和RMSProp 的優(yōu)點(diǎn),在很多場(chǎng)景下,Adam 的表示都很不錯(cuò)。?
九.學(xué)習(xí)率退火
在訓(xùn)練神經(jīng)網(wǎng)絡(luò)時(shí),一般情況下學(xué)習(xí)率都會(huì)隨著訓(xùn)練而變化,這主要是由于,在神經(jīng)網(wǎng)絡(luò)訓(xùn)練的后期,如果學(xué)習(xí)率過(guò)高,會(huì)造成loss的振蕩,但是如果學(xué)習(xí)率減小的過(guò)快,又會(huì)造成收斂變慢的情況。?
1.分段常數(shù)衰減?
分段常數(shù)衰減是在事先定義好的訓(xùn)練次數(shù)區(qū)間上,設(shè)置不同的學(xué)習(xí)率常數(shù)。剛開(kāi)始學(xué)習(xí)率大一些,之后越來(lái)越小,區(qū)間的設(shè)置需要根據(jù)樣本量調(diào)整,一般樣本量越大區(qū)間間隔應(yīng)該越小。?
2.指數(shù)衰減?
指數(shù)衰減可以?如下的數(shù)學(xué)公式表示:
其中,t表示迭代次數(shù),α0,k是超參數(shù)。
3.1/t 衰減?
1/t衰減可以?如下的數(shù)學(xué)公式表示:
其中,t表示迭代次數(shù),α0,k是超參數(shù)
十.深度學(xué)習(xí)正則化??
在設(shè)計(jì)機(jī)器學(xué)習(xí)算法時(shí)不僅要求在訓(xùn)練集上誤差小,而且希望在新樣本上的泛化能力強(qiáng)。許多機(jī)器學(xué)習(xí)算法都采用相關(guān)的策略來(lái)減小測(cè)試誤差,這些策略被統(tǒng)稱為正則化。因?yàn)樯窠?jīng)網(wǎng)絡(luò)的強(qiáng)大的表示能力經(jīng)常遇到過(guò)擬合,所以需要使用不同形式的正則化策略。
正則化通過(guò)對(duì)算法的修改來(lái)減少泛化誤差,目前在深度學(xué)習(xí)中使用較多的策略有參數(shù)范數(shù)懲罰,提前終止,DropOut等,接下來(lái)我們對(duì)其進(jìn)行詳細(xì)的介紹。?
1.Dropout正則化
1.在訓(xùn)深層練神經(jīng)網(wǎng)絡(luò)時(shí),由于模型參數(shù)較多,在數(shù)據(jù)量不的情況下,很容易過(guò)擬合。Dropout 就是在神經(jīng)網(wǎng)絡(luò)中一種緩解過(guò)擬合的方法。?
2.我們知道,緩解過(guò)擬合的方式就是降低模型的復(fù)雜度,而 Dropout 就是通過(guò)減少神經(jīng)元之間的連接,把稠密的神經(jīng)網(wǎng)絡(luò)神經(jīng)元連接,變成稀疏的神經(jīng)元連接,從而達(dá)到降低網(wǎng)絡(luò)復(fù)雜度的目的。?
3.Dropout是在深度學(xué)習(xí)領(lǐng)域最常用的正則化技術(shù)。Dropout的原理很簡(jiǎn)單:假設(shè)我們的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)如下所示,在每個(gè)迭代過(guò)程中,隨機(jī)選擇某些節(jié)點(diǎn),并且刪除前向和后向連接。
因此,每個(gè)迭代過(guò)程都會(huì)有不同的節(jié)點(diǎn)組合,從而導(dǎo)致不同的輸出,這可以看成機(jī)器學(xué)習(xí)中的集成方法 (ensemble technique) 。集成模型一般優(yōu)于單一模型,因?yàn)樗鼈兛梢圆东@更多的隨機(jī)性。相似地,dropout使得神經(jīng)網(wǎng)絡(luò)模型優(yōu)于正常的模型。
在torch.nn中實(shí)現(xiàn)dropout的方法是:nn.Dropout(p = rate);rate:每?個(gè)神經(jīng)元被丟棄的概率。
1.創(chuàng)建以及使用dropout:
import torch
import torch.nn as nn
# 初始化Dropout對(duì)象
dropout = nn.Dropout(p=0.8)
# 初始化數(shù)據(jù)
inputs = torch.randint(0,10,size=[5,8]).float()
print(inputs)
print(50*'-')
# 將inputs輸入經(jīng)過(guò)dropout,每個(gè)輸入的數(shù)據(jù)會(huì)有 p 的公概率設(shè)置為0
inputs = dropout(inputs)
print(inputs)
結(jié)果為:
說(shuō)明:?我們將 Dropout 層的概率 p 設(shè)置為 0.8,此時(shí)經(jīng)過(guò) Dropout 層計(jì)算的張量中就出現(xiàn)了很多0,概率p 設(shè)置值越大,則張量中出現(xiàn)的0就越多。上面結(jié)果的計(jì)算過(guò)程如下:
- 先按照p設(shè)置的概率,隨機(jī)將部分的張量元素設(shè)置為 0
- 為了校正張量元素被設(shè)置為0帶來(lái)的影響,需要對(duì)非0的元素進(jìn)行縮放,其縮放因子為:1 / (1 -p)。上面代碼中p的值為 0.8根據(jù)公式縮放因子為:1/(1-0.8)=5
- 比如:第3個(gè)元素,原來(lái)是 5,乘以縮放因子之后變成 25
我們也發(fā)現(xiàn)了,丟棄概率 p 的值越大,則縮放因子的值就越大,相對(duì)其他未被設(shè)置的元素就要更多的變大。丟棄概率 P 的值越小,則縮放因子的值就越小,相對(duì)應(yīng)其他未被置為 0的元素就要有較小的變大。
當(dāng)張量某些元素被設(shè)置為0時(shí),對(duì)網(wǎng)絡(luò)會(huì)帶來(lái)什么影響?
比如上面這種情況,如果輸入該樣本,會(huì)使得某些參數(shù)無(wú)法更新,請(qǐng)看下面的代碼:?
2.dropout 隨機(jī)丟棄對(duì)網(wǎng)絡(luò)參數(shù)的影響?
import torch
import torch.nn as nn
# 2.dropout 隨機(jī)丟棄對(duì)網(wǎng)絡(luò)參數(shù)的影響
## 2.1 未經(jīng)過(guò)dropout的梯度值用作對(duì)照
# 固定隨機(jī)種子
torch.manual_seed(0)
# 初始化權(quán)重參數(shù)
w = torch.randn(15,1,requires_grad=True)
# 隨機(jī)初始化數(shù)據(jù)
x = torch.randint(0,10,size=[5,15]).float()
y = x @ w
y = y.sum() # 轉(zhuǎn)換為張量
# 計(jì)算梯度
y.backward()
print('未經(jīng)過(guò)dropout的梯度值:',w.grad.reshape(1,-1).squeeze().numpy())
print(50*'-')
# 2.1 經(jīng)過(guò)dropout的梯度值
# 固定隨機(jī)種子
torch.manual_seed(0)
# 初始化權(quán)重參數(shù)
w = torch.randn(15,1,requires_grad=True)
# 隨機(jī)初始化數(shù)據(jù)
x = torch.randint(0,10,size=[5,15]).float()
# 初始化丟棄層
dropout = nn.Dropout(p=0.8)
x = dropout(x)
y = x @ w
y = y.sum() # 轉(zhuǎn)換為張量
# 計(jì)算梯度
y.backward()
print('經(jīng)過(guò)dropout后的梯度值:',w.grad.reshape(1,-1).squeeze().numpy())
由此可知 dropout之后的數(shù)據(jù)會(huì)有一定概率為0.從而降低網(wǎng)絡(luò)復(fù)雜度,具有正則化作用。
2.L1與L2正則化
L1和L2是最常見(jiàn)的正則化方法。它們?cè)趽p失函數(shù) (cost function) 中增加一個(gè)正則項(xiàng),由于添加了這個(gè)正則化項(xiàng),權(quán)重矩陣的值減小,因?yàn)樗俣ň哂懈?quán)重矩陣的神經(jīng)網(wǎng)絡(luò)導(dǎo)致更簡(jiǎn)單的模型。因此,它也會(huì)在一定程度上減少過(guò)擬合。然而,這個(gè)正則化項(xiàng)在L1和L2中是不同的。
- L2正則化??
這里的入是正則化參數(shù),它是一個(gè)需要優(yōu)化的超參數(shù)。L2正則化又稱為權(quán)重衰減,因?yàn)槠鋵?dǎo)致權(quán)重趨向于0(但不全是0。?
- L1正則化?
這里,我們懲罰權(quán)重矩陣的絕對(duì)值。其中,λ?為正則化參數(shù),是超參數(shù)不同于L2,權(quán)重值可能被減少到0.因此,L1對(duì)于壓縮模型很有用。其它情況下,一般選擇優(yōu)先選擇L2正則化。?
3.提前停止?
提前停止 (early stopping) 是將一部分訓(xùn)練集作為驗(yàn)證集 (validationset)。當(dāng)驗(yàn)證集的性能越來(lái)越差時(shí)或者性能不再提升,則立即停止對(duì)該模型的訓(xùn)練。這被稱為提前停止。
在上圖中,在虛線處停止模型的訓(xùn)練,此時(shí)模型開(kāi)始在訓(xùn)練數(shù)據(jù)上過(guò)擬合。?
4.批量歸一化?
在神經(jīng)網(wǎng)絡(luò)的搭建過(guò)程中,Batch Normalization (批量歸一化)是經(jīng)常使用一個(gè)網(wǎng)絡(luò)層,其主要的作用是控制數(shù)據(jù)的分布,加快網(wǎng)絡(luò)的收斂。
我們知道,神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)其實(shí)在學(xué)習(xí)數(shù)據(jù)的分布,隨著網(wǎng)絡(luò)的深度增加、網(wǎng)絡(luò)復(fù)雜度增加,一般流經(jīng)網(wǎng)絡(luò)的數(shù)據(jù)都是一個(gè) mini batch,每個(gè) mini batch 之間的數(shù)據(jù)分布變化非常劇烈,這就使得網(wǎng)絡(luò)參數(shù)頻繁的進(jìn)行大的調(diào)整以適應(yīng)流經(jīng)網(wǎng)絡(luò)的不同分布的數(shù)據(jù),給模型訓(xùn)練帶來(lái)非常大的不穩(wěn)定性使得模型難以收斂。?
如果我們對(duì)每一個(gè) mini batch 的數(shù)據(jù)進(jìn)行標(biāo)準(zhǔn)化之后,數(shù)據(jù)分布就變得穩(wěn)定,參數(shù)的梯度變化也變得穩(wěn)定,有助于加快模型的收斂。?
批量歸一化公式:?
- λ和β是可學(xué)習(xí)的參數(shù),它相當(dāng)于對(duì)標(biāo)準(zhǔn)化后的值做了一個(gè)線性變換,λ為系數(shù),β為偏置;
- ε (eps) 通常指為 1e-5,是一個(gè)非常小的數(shù),是為了避免分母為 0;
- E(x)表示變量的均值;
- Var(x)表示變量的方差;
數(shù)據(jù)在經(jīng)過(guò) BN 層之后,無(wú)論數(shù)據(jù)以前的分布是什么,都會(huì)被歸一化成均值為 β,標(biāo)準(zhǔn)差為γ的分布。?
注意:BN 層不會(huì)改變輸入數(shù)據(jù)的維度,只改變輸入數(shù)據(jù)的的分布.在實(shí)際使用過(guò)程中,BN 常常和卷積神經(jīng)網(wǎng)絡(luò)結(jié)合使用,卷積層的輸出結(jié)果后接 BN 層。?
PyTorch中BN層的接口:
nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
- momentum:由于每次使用的 minibatch 的數(shù)據(jù)集,所以BN 使用移動(dòng)加權(quán)平均來(lái)近似計(jì)算均值和方差,而momentum 參數(shù)則調(diào)節(jié)移動(dòng)加權(quán)平均值的計(jì)算;
- num_features:表示輸入的特征圖中包含的特征數(shù)量(即通道數(shù))。
- affine:一個(gè)布爾值,控制是否對(duì)縮放和偏移系數(shù)進(jìn)行學(xué)習(xí)。affine = False 表示 γ=1,β=0,反之,則表示 γ 和 β 要進(jìn)行學(xué)習(xí),默認(rèn)為True;
- BatchNorm2d 適用于輸入的數(shù)據(jù)為 4D,輸入數(shù)據(jù)的形狀[N,C,H,W]
- track_running_stats:一個(gè)布爾值,控制是否在訓(xùn)練時(shí)跟蹤輸入數(shù)據(jù)的均值和方差,默認(rèn)為 True,在訓(xùn)練過(guò)程中會(huì)統(tǒng)計(jì)每個(gè) batch 的均值和方差,然后計(jì)算整體的均值和方差。
其中:N 表示批次,C代表通道數(shù),H 代表高度,W 代表寬度。
由于每次輸入到網(wǎng)絡(luò)中的時(shí)小批量的樣本,我們使用指數(shù)加權(quán)平均來(lái)近似表示整體的樣本的均值和方法,其更新公式如下:?
running_mean = momentum * running_mean + (1.0 - momentum) * batch_mean
running_var = momentum * running_var + (1.0 - momentum) * batch_var
上面的式子中,batch_mean 和 batch_var 表示當(dāng)前批次的均值和方差。而running_mean 和running_var 是近似的整體的均值和方差的表示。當(dāng)我們進(jìn)行評(píng)估時(shí),可以使用該均值和方差對(duì)輸入數(shù)據(jù)進(jìn)行歸一化。?
代碼示例:
# 輸入的形狀:[batch size,channel,height,width]
inputs = torch.randint(0,10,[2,2,3,3]).float()
print(inputs)
print('-'*50)
#num features 表示每個(gè)樣本特征圖的數(shù)量,通道數(shù)量
#affine為 False 表示不帶 gama 和 beta 兩個(gè)學(xué)習(xí)參數(shù)
#eps 小常數(shù),避免分母為 0
bn = nn.BatchNorm2d(num_features=2, affine=False, eps=1e-5)
result = bn(inputs)
print(result)
#均值是每個(gè)樣本對(duì)應(yīng)通道的均值
#方差對(duì)應(yīng)通道的方差
?小結(jié):本小節(jié)學(xué)習(xí)了批量歸一化層,該層的作用主要是用來(lái)控制每層數(shù)據(jù)的流動(dòng)時(shí)的均值和方差,防止訓(xùn)練過(guò)程出現(xiàn)劇烈的波動(dòng),模型難以收斂,或者收斂較慢。批量歸一化層在計(jì)算機(jī)視覺(jué)領(lǐng)域使用較多。
十一.卷積神經(jīng)網(wǎng)絡(luò)
利用全連接神經(jīng)網(wǎng)絡(luò)對(duì)圖像進(jìn)行處理存在以下兩個(gè)問(wèn)題:?
1.需要處理的數(shù)據(jù)量大,效率低假如我們處理一張 1000x 1000 像素的圖片,參數(shù)量如下1000x1000x3=3,000,000這么大量的數(shù)據(jù)處理起來(lái)是非常消耗資源的。
2.圖像在維度調(diào)整的過(guò)程中很難保留原有的特征,導(dǎo)致圖像處理的準(zhǔn)確率不高。?
假如有圓形是1,沒(méi)有圓形是0,那么圓形的位置不同就會(huì)產(chǎn)生完全不同的數(shù)據(jù)表達(dá)。但是從圖像的角度來(lái)看,圖像的內(nèi)容 (本質(zhì)) 并沒(méi)有發(fā)生變化,只是位置發(fā)生了變化。所以當(dāng)我們移動(dòng)圖像中的物體,用全連接升降得到的結(jié)果會(huì)差異很大,這是不符合圖像處理的要求的。
1.CNN網(wǎng)絡(luò)概述
卷積神經(jīng)網(wǎng)絡(luò)是深度學(xué)習(xí)在計(jì)算機(jī)視覺(jué)領(lǐng)域的突破性成果.在計(jì)算機(jī)視覺(jué)領(lǐng)域,往往我們輸入的圖像都很大,使用全連接網(wǎng)絡(luò)的話,計(jì)算的代價(jià)較高.另外圖像也很難保留原有的特征,導(dǎo)致圖像處理的準(zhǔn)確率不高
卷積神經(jīng)網(wǎng)絡(luò) (Convolutional Neural Network) 是含有卷積層的神經(jīng)網(wǎng)絡(luò)卷積層的作用就是用來(lái)自動(dòng)學(xué)習(xí)、提取圖像的特征
CNN網(wǎng)絡(luò)主要有三部分構(gòu)成: 卷積層、池化層和全連接層構(gòu)成,其中卷積層負(fù)責(zé)提取圖像中的局部特征,池化層用來(lái)大幅降低參數(shù)量級(jí)(降維);全連接層類似人工神經(jīng)網(wǎng)絡(luò)的部分,用來(lái)輸出想要的結(jié)果。?
講解卷積神經(jīng)網(wǎng)絡(luò)知識(shí)之前很有必要學(xué)習(xí)一下圖像的基礎(chǔ)知識(shí),畢竟卷積神經(jīng)網(wǎng)絡(luò)主要用于圖像上。?
2.圖像基礎(chǔ)知識(shí)
- 我們?cè)谶M(jìn)行圖像任務(wù)時(shí),需要了解圖像的基礎(chǔ)知識(shí)。圖像是由像素點(diǎn)組成的,每個(gè)像素點(diǎn)的值范圍為:[0,255],像素值越大意味著較亮。比如一張 200x200 的圖像,則是由 40000 個(gè)像素點(diǎn)組成,如果每個(gè)像素點(diǎn)都是0的話意味著這是一張全黑的圖像。
- 我們看到的彩色圖一般都是多通道的圖像,所謂多通道可以理解為圖像由多個(gè)不同的圖像層疊加而成例如我們看到的彩色圖像一般都是由 RGB三個(gè)通道組成的,還有一些圖像具有RGBA四個(gè)通道,最后一個(gè)通道為透明通道,該值越小,則圖像越透明。?
2.1像素和通道的理解?
import torch
import numpy as np
import matplotlib.pyplot as plt
# 1.像素點(diǎn)的理解
# 構(gòu)建200 x 200,像素值全為0的圖像
img = np.zeros([200,200])
print(img)
plt.imshow(img, cmap='gray',vmin=0,vmax=255)
plt.show()
# 構(gòu)建200 x 200,像素值全為255的圖像
img = np.full([200,200],255)
print(img)
plt.imshow(img, cmap='gray',vmin=0,vmax=255)
plt.show()
# 2.對(duì)圖像通道的理解
# 從磁盤(pán)中讀取彩色圖片
img = plt.imread('彩色圖片.jpg')
print(img.shape) # (500,500,3)表示圖片有3個(gè)通道RGB
img = np.transpose(img,[2,0,1])
print(img.shape)
for channel in img:
print(channel)
plt.imshow(channel)
plt.show()
3.卷積層
CNN網(wǎng)絡(luò)受人類視覺(jué)神經(jīng)系統(tǒng)的啟發(fā),人類的視覺(jué)原理: 從原始信號(hào)攝入開(kāi)始(瞳孔攝入像素 Pixels),接著做初步處理 (大腦皮層某些細(xì)胞發(fā)現(xiàn)邊緣和方向),然后抽象 (大腦判定,眼前的物體的形狀,是圓形的)然后進(jìn)一步抽象 (大腦進(jìn)一步判定該物體是只人臉)。下面是人腦進(jìn)行人臉識(shí)別的一個(gè)示例:
CNN網(wǎng)絡(luò)主要有三部分構(gòu)成: 卷積層、池化層和全連接層構(gòu)成,其中卷積層負(fù)責(zé)提取圖像中的局部特征;池化層用來(lái)大幅降低參數(shù)量級(jí)(降維);全連接層類似人工神經(jīng)網(wǎng)絡(luò)的部分,用來(lái)輸出想要的結(jié)果。
整個(gè)CNN網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:
3.1 卷積運(yùn)算
在計(jì)算機(jī)視覺(jué)領(lǐng)域,往往我們輸入的圖像都很大,使用全連接網(wǎng)絡(luò)的話,計(jì)算的代價(jià)較高.另外圖像也很難保留原有的特征,導(dǎo)致圖像處理的準(zhǔn)確率不高。
卷積神經(jīng)網(wǎng)絡(luò) (Convolutional Neural Network) 是含有卷積層的神經(jīng)網(wǎng)絡(luò)卷積層的作用就是用來(lái)自動(dòng)學(xué)習(xí)、提取圖像的特征。
接下來(lái),我們開(kāi)始學(xué)習(xí)卷積核的計(jì)算過(guò)程,即: 卷積核是如何提取特征的。
卷積層是卷積神經(jīng)網(wǎng)絡(luò)中的核心模塊,卷積層的目的是提取輸入特征圖的特征,如下圖所示,卷積核可以提取圖像中的邊緣信息。
那么,它是如何進(jìn)行計(jì)算的呢?
- input 表示輸入的圖像
- filter 表示卷積核也叫做濾波器
- input 經(jīng)過(guò) filter 的得到輸出為最右側(cè)的圖像,該圖叫做特征圖?
卷積運(yùn)算本質(zhì)上就是在濾波器和輸入數(shù)據(jù)的局部區(qū)域間做點(diǎn)積。?
3.2 padding
通過(guò)上面的卷積計(jì)算過(guò)程,我們發(fā)現(xiàn)最終的特征圖比原始圖像小很多,如果想要保持經(jīng)過(guò)卷積后的圖像大小不變,可以在原圖周圍添加 padding 來(lái)實(shí)現(xiàn)。
3.3?stride
按照步長(zhǎng)為1來(lái)移動(dòng)卷積核,計(jì)算特征圖如下所示:
如果我們把stride增大,比如設(shè)為2,也是可以提取特征圖的,如下圖所示:
3.4?多通道卷積
實(shí)際中的圖像都是多個(gè)通道組成的,我們?cè)趺从?jì)算卷積呢?
計(jì)算方法如下:
當(dāng)輸入有多個(gè)通道 (channel) 時(shí)(例如圖片可以有 RGB三個(gè)通道),卷積核需要擁有相同的channel數(shù),每個(gè)卷積核 channel 與輸入層的對(duì)應(yīng) channel 進(jìn)行卷積,將每個(gè) channel 的卷積結(jié)果按位相加得到最終的 Feature Map。
3.5?多卷積核卷積
如果有多個(gè)卷積核時(shí)怎么計(jì)算呢? 當(dāng)有多個(gè)卷積核時(shí),每個(gè)卷積核學(xué)習(xí)到不同的特征,對(duì)應(yīng)產(chǎn)生包含多個(gè) channel 的 Feature Map,例如下圖有兩個(gè) filter,所以 output 有兩個(gè) channel。
3.6 特征圖大小?
輸出特征圖的大小與以下參數(shù)息息相關(guān):
- size:卷積核/過(guò)濾器大小,一般會(huì)選擇為奇數(shù),比如有:1*1,3*3,5*5
- padding:零填充的方式
- stride:步長(zhǎng)
計(jì)算方法如下所示:?
1.輸入體積大小H1 *W1* D1
2.四個(gè)超參數(shù):?
- Filter數(shù)量K;
- Filter大小F;
- 步長(zhǎng)S;
- 零填充大小P
3.輸出體積大小H2* W2* D2:
- H2 =(H1 -F+2P)/S +1
- W2 =(W1 - F+2P)/S +1
- D2=K
輸入特征圖為5x5,卷積核為3x3,外加padding 為1,則其輸出尺寸為:
(5-3+2*1) / 1 + 1 = 5
如下圖所示:
在torch.nn中卷積核的實(shí)現(xiàn)使用:
import torch.nn as nn
nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride, padding=padding)
pyTorch卷積的API使用例子:?jiǎn)蝹€(gè)卷積核
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
def show(img):
# 要求輸入圖像的形狀:(H,W,C)
img = plt.imshow(img)
plt.axis('off') # 去掉坐標(biāo)系刻度標(biāo)簽
plt.show()
#讀取圖像:(500,500,3)--> (H,W,C)
img = plt.imread('彩色圖片.jpg') # 這里放入相應(yīng)的圖片
print(img.shape)
show(img)
# 1.單個(gè)卷積核
#構(gòu)建卷積核
# in channels 輸入圖像的通道數(shù)
# out channels 指的是當(dāng)輸入一個(gè)圖像之后,產(chǎn)生幾個(gè)特征圖。也可以理解為卷積核的數(shù)量
# kernel size 表示卷積核大小
# stride 步長(zhǎng)
# padding 填充
conv = nn.Conv2d(in_channels=3, out_channels=1, kernel_size=3, stride=1, padding=1)
# 額外注意:卷積層對(duì)輸入的數(shù)據(jù)有形狀要求,(Batchsize,channel,Height,Width)
#將 (H,W,C)-> (C,H,W)
img = torch.tensor(img).permute(2,0,1)
print(img.shape)
# 將(C,H,W)-> (B,C,H,W)
new_img = img.unsqueeze(0)
print(new_img.shape)
# 將數(shù)據(jù)送到卷積層計(jì)算
new_img = conv(new_img.to(torch.float32))
print(new_img.shape)
# 將((B,C,H,W)-> (H,W,C)
new_img = new_img.squeeze(0).permute(1,2,0)
show(new_img.detach().numpy())
運(yùn)行結(jié)果:
pyTorch卷積的API使用例子:多個(gè)卷積核
## 2.多個(gè)卷積核
#讀取圖像:(500,500,3)--> (H,W,C)
img = plt.imread('彩色圖片.jpg')
# out channels = 3指的是當(dāng)輸入一個(gè)圖像之后,產(chǎn)生3個(gè)特征圖,卷積核的數(shù)量為3
conv = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3, stride=1, padding=1)
# 注意:卷積層對(duì)輸入的數(shù)據(jù)有形狀要求:(Batchsize,channel,Height,Width)
#將 (H,W,C)-> (C,H,W)
img = torch.tensor(img).permute(2,0,1)
print(img.shape)
# 將(C,H,W)-> (B,C,H,W)
new_img = img.unsqueeze(0)
print(new_img.shape)
# 將數(shù)據(jù)送到卷積層計(jì)算
new_img = conv(new_img.to(torch.float32))
print(new_img.shape)
# 將((B,C,H,W)-> (H,W,C)
new_img = new_img.squeeze(0).permute(1,2,0)
print(new_img.shape)
show(new_img[:,:,0].detach().numpy())
show(new_img[:,:,1].detach().numpy())
show(new_img[:,:,2].detach().numpy())
運(yùn)行結(jié)果:
4.池化層
池化層(Pooling)降低維度,縮減模型大小,提高計(jì)算速度并提高了Feature Map 的魯棒性,防止過(guò)擬合。
即主要對(duì)卷積層學(xué)習(xí)到的特征圖進(jìn)行下采樣(SubSampling)處理,池化層主要有兩種:
1.單通道最大池化和平均池化
Max Pooling,取窗口內(nèi)的最大值作為輸出,這種方式使用較廣泛。
最大池化:
- max(4, 9, 5, 6)
- max(2, 5, 2, 4)
- max(2, 4, 5, 6)
- max(5, 4,?8, 4)?
Avg Pooling,取窗口內(nèi)的所有值的均值作為輸出
平均池化:
- mean(4, 9, 5, 6)
- mean(2, 5, 2, 4)
- mean(2, 4, 5, 6)
- mean(5, 4,?8, 4)?
2.多通道最大池化和平均池化?
在處理多通道輸入數(shù)據(jù)時(shí),池化層對(duì)每個(gè)輸入通道分別池化,而不是像卷積層那樣將各個(gè)通道的輸入相加。這意味著池化層的輸出和輸入的通道數(shù)是相等。
PyTorch池化API使用:?
# 1.最大池化
maxpool = nn.MaxPool2d(kernel_size=2,stride=1,padding=0)
# 2.平均池化
avgpool = nn.AvgPool2d(kernel_size=2,stride=2,padding=1)
5.全連接層
全連接層位于CNN網(wǎng)絡(luò)的末端,經(jīng)過(guò)卷積層的特征提取與池化層的降維后,將特征圖轉(zhuǎn)換成一維向量送入到全連接層中進(jìn)行分類或回歸的操作。其實(shí)就是前十章的內(nèi)容。
十二.循環(huán)神經(jīng)網(wǎng)絡(luò)基礎(chǔ)?
1.RNN概述
自然語(yǔ)言處理 (Naturelanguage Processing,NLP)研究的主要是通過(guò)計(jì)算機(jī)算法來(lái)理解自然語(yǔ)言。對(duì)于自然語(yǔ)言來(lái)說(shuō),處理的數(shù)據(jù)主要就是人類的語(yǔ)言,例如: 漢語(yǔ)、英語(yǔ)、法語(yǔ)等,由于該類型的數(shù)據(jù)不像我們前面接觸的過(guò)的結(jié)構(gòu)化數(shù)據(jù)、或者圖像數(shù)據(jù)可以很方便的進(jìn)行數(shù)值化。所以,在本章節(jié),我們主要學(xué)習(xí)如何將文本數(shù)據(jù)進(jìn)行數(shù)值化的詞嵌入技術(shù)、以及如何對(duì)文本數(shù)據(jù)建模的循環(huán)網(wǎng)絡(luò)模型。
最后,我們通過(guò)使用學(xué)習(xí)到的技術(shù)完成文本生成任務(wù),即: 輸入一個(gè)詞,由模型預(yù)測(cè)出指定長(zhǎng)度的
歌詞。
1.1 詞嵌入層的使用
詞嵌入層首先會(huì)根據(jù)輸入的詞的數(shù)量構(gòu)建一個(gè)詞向量矩陣,例如:我們有100個(gè)詞,每個(gè)詞希望轉(zhuǎn)換成128 維度的向量,那么構(gòu)建的矩陣形狀即為:100*128,輸入的每個(gè)詞都對(duì)應(yīng)了一個(gè)該矩陣中的一個(gè)向量。
在 PyTorch 中,我們可以使用 nn.Embedding 詞嵌入層來(lái)實(shí)現(xiàn)輸入詞的向量化。接下來(lái),我們將會(huì)學(xué)習(xí)如何將詞轉(zhuǎn)換為詞向量,其步驟如下:
- 先將語(yǔ)料進(jìn)行分詞,構(gòu)建詞與索引的映射,我們可以把這個(gè)映射叫做詞表,詞表中每個(gè)詞都對(duì)應(yīng)了一個(gè)唯一的索引。
- 然后使用 nn.Embedding 構(gòu)建詞嵌入矩陣,詞索引對(duì)應(yīng)的向量即為該詞對(duì)應(yīng)的數(shù)值化后的向量表示。
例如,我們的文本數(shù)據(jù)為:"北京冬奧的進(jìn)度條已經(jīng)過(guò)半,不少外國(guó)運(yùn)動(dòng)員在完成自己的比賽后踏上歸途。",接下來(lái),我們看下如何使用詞嵌入層將其進(jìn)行轉(zhuǎn)換為向量表示,步驟如下:
- 首先,將文本進(jìn)行分詞;
- 然后,根據(jù)詞構(gòu)建詞表;
- 最后,使用嵌入層將文本轉(zhuǎn)換為向量表示?
nn.Embedding 對(duì)象構(gòu)建時(shí),最主要有兩個(gè)參數(shù):
- num_embeddings 表示詞的數(shù)量
- embedding_dim 表示用多少維的向量來(lái)表示每個(gè)詞?
nn.Embedding(num_embeddings=10,embedding_dim=4)
接下來(lái),我們就實(shí)現(xiàn)下剛才的需求:
詞嵌入層-Embedding使用:
import torch
import torch.nn as nn
import jieba
if __name__ == '__main__':
text = '北京東奧的進(jìn)度條已經(jīng)過(guò)半,不少外國(guó)運(yùn)動(dòng)員在完成自己的比賽后踏上歸途。'
# 1.分詞
words = jieba.lcut(text)
print(words)
# 2.構(gòu)建詞表
index_to_word = {} #給定一個(gè)索引,能夠查詢到該索引對(duì)應(yīng)的詞
word_to_index = {} #給定一個(gè)詞,能夠查詢到該詞對(duì)應(yīng)的索引
#去重
unique_words = list(set(words))
for idx, word in enumerate(unique_words):
index_to_word[idx] = word
word_to_index[word] = idx
# 3.構(gòu)建詞嵌入層
embed = nn.Embedding(num_embeddings=len(index_to_word), embedding_dim=4) # 維度一般為128,256等。這里用4僅作為演示
# 4.將文本轉(zhuǎn)換為詞向量表示(將句子數(shù)值化)
#print(index_to_word[0])
#print(embed(torch.tensor(0)))
for word in words:
# 獲得詞對(duì)應(yīng)的唯一的索引
idx = word_to_index[word]
# 根據(jù)唯一的索引找到找到該詞的向量表示
word_vec = embed((torch.tensor(idx)))
print('%3s\t' % word, word_vec)
1.2?關(guān)于詞嵌入層的思考
我們的詞嵌入層默認(rèn)使用的是均值為 0,標(biāo)準(zhǔn)差為1的正態(tài)分布進(jìn)行初始化,也可以理解為是隨機(jī)初始化。有些同學(xué)可能就想,這個(gè)用來(lái)表示詞的文本真的能夠表達(dá)出詞的含義嗎?
- nn.Embedding 中對(duì)每個(gè)詞的向量表示都是隨機(jī)生成的
- 當(dāng)一個(gè)詞輸入進(jìn)來(lái)之后,會(huì)使用隨機(jī)產(chǎn)生的向量來(lái)表示該詞
- 該詞向量參與到下游任務(wù)的計(jì)算
- 下游任務(wù)計(jì)算之后,會(huì)和目標(biāo)結(jié)果進(jìn)行對(duì)比產(chǎn)生損失
- 接下來(lái),通過(guò)反向傳播更新所有的網(wǎng)絡(luò)參數(shù),這里的參數(shù)就包括了nn.Embedding中的詞向量表示
這樣通過(guò)反復(fù)的前向計(jì)算、反向傳播、參數(shù)更新,最終我們每個(gè)詞的向量表示就會(huì)變得更合理?
1.3 小結(jié)
本小節(jié)主要講解了在自然語(yǔ)言處理任務(wù)中,經(jīng)常使用的詞嵌入層的使用。它的主要作用就是將輸入的詞映射為詞向量,便于在網(wǎng)絡(luò)模型中進(jìn)行計(jì)算。
這里需要注意的是,詞嵌入層中的向量表示是可學(xué)習(xí)的,并不是固定不變的。
2.RNN理解
我們前面學(xué)習(xí)了詞嵌入層,可以將文本數(shù)據(jù)映射為數(shù)值向量,進(jìn)而能夠送入到網(wǎng)絡(luò)進(jìn)行計(jì)算。但是,還存在一個(gè)問(wèn)題,文本數(shù)據(jù)是具有序列特性的,例如:"我愛(ài)你”這串文本就是具有序列關(guān)系的,"愛(ài)”需要在“我”之后,"你”需要在“愛(ài)”之后,如果顛倒了順序,那么可能就會(huì)表達(dá)不同的意思。
為了能夠表示出數(shù)據(jù)的序列關(guān)系我們需要使用循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Nearal Networks,RNN)來(lái)對(duì)數(shù)據(jù)進(jìn)行建模,RNN 是一個(gè)具有記憶功能的網(wǎng)絡(luò),它作用于處理帶有序列特點(diǎn)的樣本數(shù)據(jù)。?
本小節(jié),我們將會(huì)帶著大家深入學(xué)習(xí) RNN 循環(huán)網(wǎng)絡(luò)層的原理、計(jì)算過(guò)程,以及在 PyTorch 中如何使用RNN 層。?
2.1 RNN網(wǎng)絡(luò)原理?
當(dāng)我們希望使用循環(huán)網(wǎng)絡(luò)來(lái)對(duì)“我愛(ài)你"進(jìn)行語(yǔ)義提取時(shí),RNN 是如何計(jì)算過(guò)程是什么樣的呢?
?上圖中h 表示隱藏狀態(tài),每一次的輸入都會(huì)有包含兩個(gè)值:上一個(gè)時(shí)間步的隱藏狀態(tài)、當(dāng)前狀態(tài)的輸入值,輸出當(dāng)前時(shí)間步的隱藏狀態(tài)。
上圖中,為了更加容易理解,雖然我畫(huà)了3個(gè)神經(jīng)元,但是實(shí)際上只有一個(gè)神經(jīng)元,"我愛(ài)你"三個(gè)字是重復(fù)輸入到同一個(gè)神經(jīng)元中。
接下來(lái),我們舉個(gè)例子來(lái)理解上圖的工作過(guò)程,假設(shè)我們要實(shí)現(xiàn)文本生成,也就是輸入“我愛(ài)”這兩個(gè)字,來(lái)預(yù)測(cè)出“你",其如下圖所示:
我們將上圖展開(kāi)成不同時(shí)間步的形式,如下圖所示:
我們首先初始化出第一個(gè)隱藏狀態(tài),一般都是全0的一個(gè)向量,然后將“我”進(jìn)行詞嵌入,轉(zhuǎn)換為向量的表示形式,送入到第一個(gè)時(shí)間步,然后輸出隱藏狀態(tài) h1,然后將 h1 和“愛(ài)”輸入到第二個(gè)時(shí)間步,得到隱藏狀態(tài) h2,將 h2 送入到全連接網(wǎng)絡(luò),得到“你”的預(yù)測(cè)概率。
那么,你可能會(huì)想,循環(huán)網(wǎng)絡(luò)只能有一個(gè)神經(jīng)元嗎?我們的循環(huán)網(wǎng)絡(luò)網(wǎng)絡(luò)可以有多個(gè)神經(jīng)元,如下圖所示:
我們依次將“我愛(ài)你”三個(gè)字分別送入到每個(gè)神經(jīng)元進(jìn)行計(jì)算,假設(shè)詞嵌入時(shí),"我愛(ài)你”的維度為128,經(jīng)過(guò)循環(huán)網(wǎng)絡(luò)之,"我愛(ài)你”三個(gè)字的詞向量維度就會(huì)變成 4.所以,我們理解了循環(huán)神經(jīng)網(wǎng)絡(luò)的的神經(jīng)元個(gè)數(shù)會(huì)影響到輸出的數(shù)據(jù)維度.
每個(gè)神經(jīng)元內(nèi)部是如何計(jì)算的呢??
上述公式中:?
- Wi 表示輸入數(shù)據(jù)的權(quán)重
- bi 表示輸入數(shù)據(jù)的偏置
- Whh表示輸入隱藏狀態(tài)的權(quán)重
- bhh 表示輸入隱藏狀態(tài)的偏置
最后對(duì)輸出的結(jié)果使用 tanh 激活函數(shù)進(jìn)行計(jì)算,得到該神經(jīng)元你的輸出。
PyTorch RNN 層的使用:?
接下來(lái),我們學(xué)習(xí) PyTorch 的 RNN 層的用法
注意: RNN 層輸入的數(shù)據(jù)為三個(gè)維度:(sec_len,batch_size,input_size)
1.RNN輸入單個(gè)詞:?
# 1.RNN輸入單個(gè)詞
#初始化RNN網(wǎng)絡(luò)層
# input_size 輸入句子的每個(gè)詞的向量的維度,比如:'我' 經(jīng)過(guò)了詞嵌入,得到了一個(gè) 128 維的向量表示
# hidden_size 隱藏層的大小,隱藏層的神經(jīng)元的個(gè)數(shù),影響到最終輸出數(shù)據(jù)的維度
rnn = nn.RNN(input_size=128, hidden_size=256)
# 初始化輸入數(shù)據(jù)
#注意輸入的數(shù)據(jù)有兩個(gè):上一個(gè)時(shí)間步的隱藏狀態(tài)、當(dāng)前時(shí)間步的輸入
#inputs 的形狀 (seq_len,batch_size,input_size)
inputs = torch.randn(1,1,128)
# 隱藏層的形狀 (num_layers,batch_size,hidden_size)
# 初始的隱藏狀態(tài)全部為 0
hn = torch.zeros(1, 1,256)
# 將數(shù)據(jù)送入到循環(huán)網(wǎng)絡(luò)層
# 輸出:output,hn
output, hn = rnn(inputs,hn)
print(output.shape) #torch.Size([1, 1, 256])
print(hn.shape) # torch.Size([1, 1, 256])
2.RNN輸入句子:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-843063.html
# 2.RNN輸入句子
#初始化RNN網(wǎng)絡(luò)層
rnn = nn.RNN(input_size=128, hidden_size=256)
# 構(gòu)造輸入數(shù)據(jù)
# inputs的形狀 (seq_len,batch_size,input_size)
# 輸入的句子長(zhǎng)度為8,一次輸入1個(gè)句子
inputs = torch.randn(8,1,128)
# 隱藏層的形狀 (num_layers,batch_size,hidden_size)
# 初始的隱藏狀態(tài)全部為 0
hn = torch.zeros(1,1,256)
# 將數(shù)據(jù)送入到循環(huán)網(wǎng)絡(luò)層
# 輸出:output,hn
output, hn = rnn(inputs,hn)
print(output.shape) # torch.Size([8, 1, 256])
print(hn.shape) # torch.Size([1, 1, 256])
3.RNN輸入批次的句子:?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-843063.html
# 3.RNN輸入批次的句子
#初始化RNN網(wǎng)絡(luò)層
rnn = nn.RNN(input_size=128, hidden_size=256)
# 構(gòu)造輸入數(shù)據(jù)
# inputs的形狀 (seq_len,batch_size,input_size)
# 輸入的句子長(zhǎng)度為8,一次輸入1個(gè)句子
inputs = torch.randn(8,16,128)
# 隱藏層的形狀 (num_layers,batch_size,hidden_size)
# 初始的隱藏狀態(tài)全部為 0
hn = torch.zeros(1,16,256)
# 將數(shù)據(jù)送入到循環(huán)網(wǎng)絡(luò)層
# 輸出:output,hn
output, hn = rnn(inputs,hn)
print(output.shape)# torch.Size([8, 16, 256])
print(hn.shape) #torch.Size([1, 16, 256])
到了這里,關(guān)于爆肝3W多字,100多張配圖!深度學(xué)習(xí)從小白到精通一篇博文幫你打開(kāi)人工智能的大門建議收藏不容錯(cuò)過(guò)?。?!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!