【Pytorch神经网络理论篇】 16 过拟合问题的优化技巧(三):批量归一化
1 批量歸一化理論
1.1 批量歸一化原理
1.2 批量歸一化定義
將每一層運(yùn)算出來的數(shù)據(jù)歸一化成均值為0、方差為1的標(biāo)準(zhǔn)高斯分布。這樣就會(huì)在保留樣本的分布特征,又消除了層與層間的分布差異。
在實(shí)際應(yīng)用中,批量歸一化的收斂非常快,并且具有很強(qiáng)的泛化能力,某種情況下可以完全代替前面講過的正則化、Dropout。
?在訓(xùn)練過程中,會(huì)通過優(yōu)化器的反向求導(dǎo)來優(yōu)化出合適的r,β值。BN層計(jì)算每次輸入的均值與方差,并進(jìn)行移動(dòng)平均。移動(dòng)平均默認(rèn)的動(dòng)量值為0.1。
在驗(yàn)證過程中,會(huì)使用訓(xùn)練求得的均值和方差對(duì)驗(yàn)證數(shù)據(jù)做歸一化處理。
1.3 批量歸一化實(shí)現(xiàn)
- BatchNorm1d:對(duì)二維或三維的線性數(shù)據(jù)進(jìn)行批量歸一化處理,輸入形然是[N,D]或者[D,N,L](N代表批次數(shù),D代表數(shù)據(jù)的個(gè)數(shù),L代表數(shù)據(jù)的長(zhǎng)度)。
- BatchNorm2d:對(duì)二維的平面數(shù)據(jù)進(jìn)行批量歸一化處理,輸入形然是以[N,C,H,W](N代表批次數(shù),C代表通道數(shù),H代表亮度,W代表寬度)。
- BatchNorm3d:對(duì)三維的立體數(shù)據(jù)進(jìn)行批量歸一化處理,輸入形狀是[N,C,D,H,W](N代表批次數(shù),C代表通道數(shù),D代表深度,H代表高度,W代表寬度)。
2 批量歸一化實(shí)操
torch.nn.BatchNormld(num_features,eps=1e-05,momentum=0.1,affine=True,trackrunning_stats=True)其中的參數(shù)含義如下。
- num features:待處理的輸入數(shù)據(jù)的特征數(shù),該值需要手動(dòng)計(jì)算,如果輸入數(shù)據(jù)的形狀是[N,D](N代表批次數(shù),D代表數(shù)據(jù)的個(gè)數(shù)),那么該值為D。如果是在BatchNorm2d中,那么該參數(shù)要填入圖片的通道數(shù)。
- s:默認(rèn)值為1e-5。為保證數(shù)值穩(wěn)定性(分母不取0),給分母加上值,即給式(7-11)中的σ加上eps。
- momentum:動(dòng)態(tài)均值和動(dòng)態(tài)方差使用的動(dòng)量,默認(rèn)值為0.1。
- affine:是否使用自適應(yīng)模式。如果參數(shù)值設(shè)置為True,那么使用自適應(yīng)模式,系統(tǒng)將自動(dòng)對(duì)式(7-11)中的r、β值進(jìn)行優(yōu)化學(xué)習(xí);如果參數(shù)值設(shè)置為Flase,那么不使用自適應(yīng)模型,相當(dāng)于將式(7-11)中的r、β去掉。
- track_running stats:是否跟蹤當(dāng)前批次數(shù)據(jù)的統(tǒng)計(jì)特性。在訓(xùn)練過程中,如果參數(shù)值設(shè)置為False,那么系統(tǒng)只使用當(dāng)前批次數(shù)據(jù)的均值和方差;如果參數(shù)值設(shè)置為True,那么系統(tǒng)將跟蹤每批次輸入的數(shù)據(jù)并實(shí)時(shí)更新整個(gè)數(shù)據(jù)集的均值和方差。(在使用過程中,該參數(shù)值一般設(shè)置為True,表示系統(tǒng)將使用訓(xùn)練時(shí)的均值和方差。)
3 批量歸一化函數(shù)的手動(dòng)代碼實(shí)現(xiàn)
3.0?使用批量歸一化方法的注意事項(xiàng)
- 1、批量歸一化方法不能緊跟在Dropout,層后面使用若有這種情況,Dropout層的結(jié)果會(huì)改變批量歸一化所計(jì)算的數(shù)據(jù)分布,導(dǎo)致批量歸一化后的偏差更大。
- 2、在批量歸一化方法與Switch激活函數(shù)一起使用時(shí),需要對(duì)Switch激活函數(shù)進(jìn)行權(quán)值縮放(可以使用縮放參數(shù)自學(xué)習(xí)的方法),否側(cè)則會(huì)引起更大的抖動(dòng)。
- 3、批量歸一化方法對(duì)擬次依賴嚴(yán)重,即對(duì)于較小批次,效果并不理想。因?yàn)榕繗w一化側(cè)重的是對(duì)批次樣本的歸一化,當(dāng)輸入批次較小時(shí),個(gè)體樣本將無法代替批次樣本的特征,導(dǎo)致模型抖動(dòng),難以收斂。
- 4、批量歸一化方法適用深層網(wǎng)絡(luò)。因?yàn)樵跍\層網(wǎng)絡(luò)中,內(nèi)部協(xié)變量轉(zhuǎn)移問題并不明顯,所以批量歸一化的效果也不明顯。
3.1 使用接口調(diào)用方式實(shí)現(xiàn)批量歸一化計(jì)算
實(shí)例描述:以二維的平面數(shù)據(jù)為例,調(diào)用BN接口對(duì)一組數(shù)據(jù)執(zhí)行批量歸一化算法,并將該計(jì)算結(jié)果與手動(dòng)方式計(jì)算的結(jié)果進(jìn)行比較。
import torch import torch.nn as nn# 1.1 使用接口調(diào)用的方式實(shí)現(xiàn)批量歸一化計(jì)算 data = torch.randn(2,2,2,1) # 定義一個(gè)形狀為[2,2,2,1]的模擬數(shù)據(jù),批次中的樣本數(shù)為2,每個(gè)樣本的通道數(shù)為2,高和寬分別為2和1。 print(data) # 輸出模擬數(shù)據(jù) # 實(shí)例化BatchNorm2d接口,并調(diào)用該實(shí)例化對(duì)象對(duì)模擬數(shù)據(jù)進(jìn)行批量歸一化計(jì)算。 obn = nn.BatchNorm2d(2,affine=True) # 實(shí)例化自適應(yīng)BN對(duì)象 print(obn.weight) # 輸出自適應(yīng)參數(shù) r print(obn.bias) # 輸出自適應(yīng)參數(shù)β print(obn.eps) # 輸出BN中的epsoutput = obn(data) # 計(jì)算BN print(output,output.size()) # 輸出BN結(jié)果及形狀# 兩個(gè)模擬樣本數(shù)據(jù),每個(gè)樣本都有兩個(gè)通道
tensor([[[[-1.3640],?[ 0.7662]],[[ 1.0771],?[ 0.4655]]],
???????????[[[-1.4258],?[ 1.0477]],[[-0.1646],[ 1.6063]]]])
# 自適應(yīng)參數(shù)
Parameter containing:tensor([1., 1.], requires_grad=True)
Parameter containing:tensor([0., 0.], requires_grad=True)
1e-05
# 批量歸一化結(jié)果及形狀===》僅改變輸入數(shù)據(jù)的值,沒有修改輸入數(shù)據(jù)的形狀
tensor([[[[-0.9694],[ 0.8743]],?[[ 0.4994],[-0.4232]]],
?[[[-1.0228],?[ 1.1179]],?[[-1.3738],?[ 1.2977]]]],
grad_fn=<NativeBatchNormBackward0>) torch.Size([2, 2, 2, 1])
3.2 使用手動(dòng)方式實(shí)現(xiàn)歸一化計(jì)算
3.2.1 方差計(jì)算與貝塞爾校正
- 方差的計(jì)算方法是先將每個(gè)值減去均值的結(jié)果后再求平方,并求它們的均值(求和后除以總數(shù)量)。
- 貝塞爾校正(Bessel's Correction)是一個(gè)與統(tǒng)計(jì)學(xué)的方差和標(biāo)準(zhǔn)差相關(guān)的修正方法,是指在計(jì)算提示樣本的方差和標(biāo)準(zhǔn)差時(shí),將分母中的n替換成n-1。這種修正方法得到的方差和標(biāo)準(zhǔn)差更近似于當(dāng)前樣本所在的總體集合中的方差和標(biāo)準(zhǔn)差。
3.2.2 歸一化實(shí)驗(yàn)
為了使手動(dòng)計(jì)算批量歸一化的步驟更加清晰,這里只對(duì)模擬數(shù)據(jù)中第一個(gè)樣本中的第一個(gè)具體數(shù)據(jù)進(jìn)行手動(dòng)批量歸一化計(jì)算,具體步驟如下:
- (1)取出模擬數(shù)據(jù)中兩個(gè)樣本的第一個(gè)通道數(shù)據(jù);
- (2)用手動(dòng)的方式計(jì)算該數(shù)據(jù)均值和方差;
- (3)將均值和方差代入式(7-11),對(duì)模擬數(shù)據(jù)中第一個(gè)樣本中的第一個(gè)具體數(shù)據(jù)進(jìn)行計(jì)算,具體代碼如下。
輸出:
第1通道的數(shù)據(jù) tensor([[[-1.3640],[ 0.7662]],?[[-1.4258],?[1.0477]]])
均值 tensor(-0.2440)
方差 tensor(1.3350)
BN結(jié)果 tensor(-0.9694, grad_fn=<AddBackward0>)
4 通過批量歸一化方法改善模型的過擬合狀況
修改第1篇文章中的# 2 搭建網(wǎng)絡(luò)模型部分?
# 2 搭建網(wǎng)絡(luò)模型 # model = LogicNet(inputdim=2,hiddendim=500,outputdim=2) # 實(shí)例化模型,增加擬合能力將hiddendim賦值為500 # 修改為 class Logic_Dropout_Net(LogicNet):def __init__(self,inputdim,hiddendim,outputdim):super(Logic_Dropout_Net, self).__init__(hiddendim=hiddendim)def forward(self,x):x = self.Linear1(x)x = torch.tanh(x)x = self.BN(x)x = self.Linear2(x)return x model = Logic_Dropout_Net(inputdim=2,hiddendim=500,outputdim=2) # 初始化模型 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定義優(yōu)化器:反向傳播過程中使用。# 3 訓(xùn)練模型+訓(xùn)練過程loss可視化 xt = torch.from_numpy(X).type(torch.FloatTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 yt = torch.from_numpy(Y).type(torch.LongTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 epochs = 200 # 定義迭代次數(shù) losses = [] # 損失值列表4.2 批量歸一化方法改善模型---代碼總覽
Over_fitting_Batch normalization.py # 2 搭建網(wǎng)絡(luò)模型 # model = LogicNet(inputdim=2,hiddendim=500,outputdim=2) # 實(shí)例化模型,增加擬合能力將hiddendim賦值為500 # 修改為 class Logic_Dropout_Net(LogicNet):def __init__(self,inputdim,hiddendim,outputdim): # 初始化網(wǎng)絡(luò)結(jié)構(gòu)super(Logic_Dropout_Net, self).__init__(inputdim,hiddendim,outputdim)self.BN = nn.BatchNorm1d(hiddendim) # 定義BN層def forward(self,x): # 搭建兩個(gè)全連接層組成的網(wǎng)絡(luò)模型x = self.Linear1(x) # 將輸入數(shù)據(jù)傳入第1層x = torch.tanh(x) # 對(duì)第1層的結(jié)果進(jìn)行非線性變換x = self.BN(x) # 對(duì)第1層的數(shù)據(jù)做BN處理x = self.Linear2(x) # 將數(shù)據(jù)傳入第2層return x model = Logic_Dropout_Net(inputdim=2,hiddendim=500,outputdim=2) # 初始化模型 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定義優(yōu)化器:反向傳播過程中使用。# 3 訓(xùn)練模型+訓(xùn)練過程loss可視化 xt = torch.from_numpy(X).type(torch.FloatTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 yt = torch.from_numpy(Y).type(torch.LongTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 epochs = 200 # 定義迭代次數(shù) # 帶有批量歸一化處理的模型比帶有Dropout處理的模型需要更少的訓(xùn)練次教。因?yàn)镈ropout每次都會(huì)使一部分節(jié)點(diǎn)不參與運(yùn)算,相當(dāng)于減少了單次的樣本處理量,所以帶有Dropout處理的模型需要更多的訓(xùn)練次數(shù)才可以使模型收斂。 losses = [] # 損失值列表 LogicNet_fun.py import sklearn.datasets import torch import numpy as np import matplotlib.pyplot as plt from torch import nnfrom LogicNet_fun import LogicNet,moving_average,predict,plot_decision_boundary# 1 構(gòu)建數(shù)據(jù)集 np.random.seed(0) # 設(shè)置隨機(jī)數(shù)種子 X , Y =sklearn.datasets.make_moons(40,noise=0.2) # 生成兩組半圓形數(shù)據(jù) arg = np.squeeze(np.argwhere(Y==0),axis=1) # 獲取第1組數(shù)據(jù)索引 arg2 = np.squeeze(np.argwhere(Y==1),axis=1) # 獲取第2組數(shù)據(jù)索引 # 顯示數(shù)據(jù) plt.title("train moons data") plt.scatter(X[arg,0],X[arg,1],s=100,c='b',marker='+',label = 'data1') plt.scatter(X[arg2,0],X[arg2,1],s=40,c='r',marker='o',label = 'data2') plt.legend() plt.show()# 2 搭建網(wǎng)絡(luò)模型 # model = LogicNet(inputdim=2,hiddendim=500,outputdim=2) # 實(shí)例化模型,增加擬合能力將hiddendim賦值為500 # 修改為 class Logic_Dropout_Net(LogicNet):def __init__(self,inputdim,hiddendim,outputdim): # 初始化網(wǎng)絡(luò)結(jié)構(gòu)super(Logic_Dropout_Net, self).__init__(inputdim,hiddendim,outputdim)self.BN = nn.BatchNorm1d(hiddendim) # 定義BN層def forward(self,x): # 搭建兩個(gè)全連接層組成的網(wǎng)絡(luò)模型x = self.Linear1(x) # 將輸入數(shù)據(jù)傳入第1層x = torch.tanh(x) # 對(duì)第1層的結(jié)果進(jìn)行非線性變換x = self.BN(x) # 對(duì)第1層的數(shù)據(jù)做BN處理x = self.Linear2(x) # 將數(shù)據(jù)傳入第2層return x model = Logic_Dropout_Net(inputdim=2,hiddendim=500,outputdim=2) # 初始化模型 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定義優(yōu)化器:反向傳播過程中使用。# 3 訓(xùn)練模型+訓(xùn)練過程loss可視化 xt = torch.from_numpy(X).type(torch.FloatTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 yt = torch.from_numpy(Y).type(torch.LongTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 epochs = 200 # 定義迭代次數(shù) # 帶有批量歸一化處理的模型比帶有Dropout處理的模型需要更少的訓(xùn)練次教。因?yàn)镈ropout每次都會(huì)使一部分節(jié)點(diǎn)不參與運(yùn)算,相當(dāng)于減少了單次的樣本處理量,所以帶有Dropout處理的模型需要更多的訓(xùn)練次數(shù)才可以使模型收斂。 losses = [] # 損失值列表for i in range(epochs):loss = model.getloss(xt,yt)losses.append(loss.item()) # 保存損失值中間狀態(tài)optimizer.zero_grad() # 清空梯度loss.backward() # 反向傳播損失值optimizer.step() # 更新參數(shù) avgloss = moving_average(losses) # 獲得損失值的移動(dòng)平均值 plt.figure(1) plt.subplot(211) plt.xlabel('step number') plt.ylabel('Training loss') plt.title('step number vs Training loss') plt.show()# 4 模型結(jié)果可視化,觀察過擬合現(xiàn)象 plot_decision_boundary(lambda x: predict(model,x),X,Y) from sklearn.metrics import accuracy_score print("訓(xùn)練時(shí)的準(zhǔn)確率",accuracy_score(model.predict(xt),yt)) # 重新生成兩組半圓數(shù)據(jù) Xtest,Ytest = sklearn.datasets.make_moons(80,noise=0.2) plot_decision_boundary(lambda x: predict(model,x),Xtest,Ytest) Xtest_t = torch.from_numpy(Xtest).type(torch.FloatTensor) # 將numpy數(shù)據(jù)轉(zhuǎn)化為張量 Ytest_t = torch.from_numpy(Ytest).type(torch.LongTensor) print("測(cè)試時(shí)準(zhǔn)確率",accuracy_score(model.predict(Xtest_t),Ytest_t))4.3 小結(jié)
批量歸一化更擅長(zhǎng)解決深層網(wǎng)絡(luò)的內(nèi)部協(xié)變量轉(zhuǎn)移問題,在深層網(wǎng)路中,才會(huì)體現(xiàn)出更好的性能。
5?擴(kuò)展:多種批量歸一化算法介紹
總結(jié)
以上是生活随笔為你收集整理的【Pytorch神经网络理论篇】 16 过拟合问题的优化技巧(三):批量归一化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3列表生成式中的for循环与
- 下一篇: OpenCV_08 边缘检测:Sobel