Lesson 14.3 Batch Normalization综合调参实战
Lesson 14.3 Batch Normalization綜合調參實戰
??根據Lesson 14.2最后一部分實驗結果不難看出,帶BN層的模型并不一定比不帶BN層模型效果好,要充分發揮BN層的效果,就必須掌握一些圍繞帶BN層模型的調參理論和調參技巧。
一、Batch Normalization與Batch_size綜合調參
??我們知道,BN是一種在長期實踐中被證明行之有效的優化方法,但在使用過程中首先需要知道,BN的理論基礎(盡管不完全正確)是以BN層能夠有效預估輸入數據整體均值和方差為前提的,如果不能盡可能的從每次輸入的小批數據中更準確的估計整體統計量,則后續的平移和放縮也將是有偏的。而由小批數據估計整體統計量的可信度其實是和小批數據本身數量相關的,如果小批數據數量太少,則進行整體統計量估計時就將有較大偏差,此時會影響模型準確率。
??因此,一般來說,我們在使用BN時,至少需要保證小批數據量(batch_size)在15-30以上,才能進行相對準確的預估。此處我們適當調整小批數據量參數,再進行模型計算。
我們發現,當提升batch_size之后,帶BN層的模型效果有明顯提升,相比原始模型,帶BN層的模型擁有更快的收斂速度。
??當然,為了確保BN層對整體統計量估計的可信度,除了提高batch_size之外,還能夠通過調低momentum參數來實現,當然,伴隨著momentum值得降低,我們也必須進一步提升遍歷數據集得次數,同學們可以根據上述代碼自行進行實驗。
二、復雜模型上的Batch_normalization表現
??一般來說,BN方法對于復雜模型和復雜數據會更加有效,換而言之,很多簡單模型是沒必要使用BN層(徒增計算量)。對于上述net_class1來說,由于只存在一個隱藏層,因此也不會存在梯度不平穩的現象,而BN層的優化效果也并不明顯。接下來,我們嘗試構建更加復雜的模型,來測試BN層的優化效果。
從另一個角度來說,其實我們是建議更頻繁的使用更加復雜的模型并帶上BN層的,核心原因在于,復雜模型帶上BN層之后會有更大的優化空間。
接下來,我們嘗試設置更加復雜的數據集,同時增加模型復雜度,測試在更加復雜的環境下BN層表現情況。
此處我們創建滿足y=2x12?x22+3x32+x42+2x52y=2x_1^2-x_2^2+3x_3^2+x_4^2+2x_5^2y=2x12??x22?+3x32?+x42?+2x52?的回歸類數據集。
# 設置隨機數種子 torch.manual_seed(420) # 創建最高項為2的多項式回歸數據集 features, labels = tensorGenReg(w=[2, -1, 3, 1, 2], bias=False, deg=2)# 進行數據集切分與加載 train_loader, test_loader = split_loader(features, labels, batch_size=50)接下來,我們同時創建Sigmoid1-4,并且通過對比帶BN層的模型和不帶BN層的模型來進行測試。
# class1對比模型 # 設置隨機數種子 torch.manual_seed(24) # 實例化模型 sigmoid_model1 = net_class1(act_fun= torch.sigmoid, in_features=5) sigmoid_model1_norm = net_class1(act_fun= torch.sigmoid, in_features=5, BN_model='pre')# 創建模型容器 model_ls1 = [sigmoid_model1, sigmoid_model1_norm] name_ls1 = ['sigmoid_model1', 'sigmoid_model1_norm']# 核心參數 lr = 0.03 num_epochs = 40# 模型訓練 train_ls1, test_ls1 = model_comparison(model_l = model_ls1, name_l = name_ls1, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# class2對比模型 # 設置隨機數種子 torch.manual_seed(24) # 實例化模型 sigmoid_model2 = net_class2(act_fun= torch.sigmoid, in_features=5) sigmoid_model2_norm = net_class2(act_fun= torch.sigmoid, in_features=5, BN_model='pre')# 創建模型容器 model_ls2 = [sigmoid_model2, sigmoid_model2_norm] name_ls2 = ['sigmoid_model2', 'sigmoid_model2_norm']# 核心參數 lr = 0.03 num_epochs = 40# 模型訓練 train_ls2, test_ls2 = model_comparison(model_l = model_ls2, name_l = name_ls2, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# class3對比模型 # 設置隨機數種子 torch.manual_seed(24) # 實例化模型 sigmoid_model3 = net_class3(act_fun= torch.sigmoid, in_features=5) sigmoid_model3_norm = net_class3(act_fun= torch.sigmoid, in_features=5, BN_model='pre')# 創建模型容器 model_ls3 = [sigmoid_model3, sigmoid_model3_norm] name_ls3 = ['sigmoid_model3', 'sigmoid_model3_norm']# 核心參數 lr = 0.03 num_epochs = 40# 模型訓練 train_ls3, test_ls3 = model_comparison(model_l = model_ls3, name_l = name_ls3, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# class4對比模型 # 設置隨機數種子 torch.manual_seed(24) # 實例化模型 sigmoid_model4 = net_class4(act_fun= torch.sigmoid, in_features=5) sigmoid_model4_norm = net_class4(act_fun= torch.sigmoid, in_features=5, BN_model='pre')# 創建模型容器 model_ls4 = [sigmoid_model4, sigmoid_model4_norm] name_ls4 = ['sigmoid_model4', 'sigmoid_model4_norm']# 核心參數 lr = 0.03 num_epochs = 40# 模型訓練 train_ls4, test_ls4 = model_comparison(model_l = model_ls4, name_l = name_ls4, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal) # 訓練誤差 plt.subplot(221) for i, name in enumerate(name_ls1):plt.plot(list(range(num_epochs)), train_ls1[i], label=name) plt.legend(loc = 1) plt.title('mse_train_ls1')plt.subplot(222) for i, name in enumerate(name_ls2):plt.plot(list(range(num_epochs)), train_ls2[i], label=name) plt.legend(loc = 1) plt.title('mse_train_ls2')plt.subplot(223) for i, name in enumerate(name_ls3):plt.plot(list(range(num_epochs)), train_ls3[i], label=name) plt.legend(loc = 1) plt.title('mse_train_ls3')plt.subplot(224) for i, name in enumerate(name_ls4):plt.plot(list(range(num_epochs)), train_ls4[i], label=name) plt.legend(loc = 1) plt.title('mse_train_ls4') # 訓練誤差 plt.subplot(221) for i, name in enumerate(name_ls1):plt.plot(list(range(num_epochs)), test_ls1[i], label=name) plt.legend(loc = 1) plt.title('mse_test_ls1')plt.subplot(222) for i, name in enumerate(name_ls2):plt.plot(list(range(num_epochs)), test_ls2[i], label=name) plt.legend(loc = 1) plt.title('mse_test_ls2')plt.subplot(223) for i, name in enumerate(name_ls3):plt.plot(list(range(num_epochs)), test_ls3[i], label=name) plt.legend(loc = 1) plt.title('mse_test_ls3')plt.subplot(224) for i, name in enumerate(name_ls4):plt.plot(list(range(num_epochs)), test_ls4[i], label=name) plt.legend(loc = 1) plt.title('mse_test_ls4')
??由此,我們可以清楚的看到,BN層對更加復雜模型的優化效果更好。換而言之,越復雜的模型對于梯度不平穩的問題就越明顯,因此BN層在解決該問題后模型效果提升就越明顯。
??并且,針對復雜數據集,在一定范圍內,伴隨模型復雜度提升,模型效果會有顯著提升。
這也算是對Lesson 13.2節實驗的一個補充。
不過呢,和Lesson 13.2中我們看到的一樣,模型復雜度提升也是過猶不及的,當模型太過于復雜時,仍然會出現模型效果下降的問題。
for i, name in enumerate(name_ls2):plt.plot(list(range(num_epochs)), test_ls2[i], label=name) for i, name in enumerate(name_ls4):plt.plot(list(range(num_epochs)), test_ls4[i], label=name) plt.legend(loc = 1) plt.title('mse_test')關于該問題的解決,我們會在下一節課詳細討論。
??對于Sigmoid來說,BN層能很大程度上緩解梯度消失問題,從而提升模型收斂速度,并且小幅提升模型效果。而對于激活函數本身就能輸出Zero-Centered結果的tanh函數,BN層的優化效果會更好。
# 設置隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model2 = net_class2(act_fun= torch.tanh, in_features=5) tanh_model2_norm = net_class2(act_fun= torch.tanh, in_features=5, BN_model='pre')# 創建模型容器 model_l = [tanh_model2, tanh_model2_norm] name_l = ['tanh_model2', 'tanh_model2_norm']# 核心參數 lr = 0.03 num_epochs = 40# 模型訓練 train_lh, test_lh = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 訓練誤差 for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_lh[i], label=name) plt.legend(loc = 1) plt.title('mse_train') # 測試誤差 for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), test_lh[i], label=name) plt.legend(loc = 1) plt.title('mse_test')
??相比Sigmoid,使用tanh激活函數本身就是更加復雜的一種選擇,因此,BN層在tanh上所表現出的更好的優化效果,也能看成是BN在復雜模型上效果有所提升。此處對上述模型最終輸出結果進行記錄,方便后續進行對比實驗。
三、包含BN層的神經網絡的學習率優化
??根據此前的實驗結果,我們不難發現,BN層對模型迭代的平穩性提升幫助不大,相反,加入BN層的模型收斂過程“不平穩”的特點好像有增無減,這點從Sigmoid激活函數的收斂過程看的尤其明顯。
??而收斂不平穩的模型,一般都對學習率非常敏感(相關內容我們會在后續學習率優化章節詳細討論),也就是學習率的調整將有效緩解迭代不平穩的問題,而一旦迭代不平穩被修正,模型就有可能最后收斂到一個更優的結果,當然,這只是有可能,最終結果還需要看到底是什么原因導致模型收斂過程不平穩。而BN層所帶來的不平穩性,我們可以簡單理解成模型可以在更大范圍搜索最優解,相比不帶BN層的神經網絡模型,帶BN層的神經網絡的不平穩會更大程度受到學習率的影響。換而言之,帶BN層的神經網絡模型對學習率是高度敏感的,并且帶BN層的神經網絡模型,在進行學習率調整時能夠有更大的優化空間。也就是說,相比不帶BN層的模型,帶BN層的模型在同樣進行某種學習率調整時,會有更好的效果。
一般有很多材料簡單認為添加BN層的神經網絡模型可以通過提高學習率來加快收斂速度,但對于學習率敏感的BN層來說,一味增加學習率可能并不是最優方法。
??為了更好的說明所謂優化空間,我們需要鋪墊兩個基礎認知,其一是學習率敏感度,其二是學習率學習曲線(伴隨學習率調整模型效果變化曲線)。
1.學習率敏感度
??首先,我們通過簡單實驗來觀測帶BN層的模型對于學習率的敏感程度。我們挑選相對復雜、迭代不平穩的tanh3和tanh4模型進行實驗。
# 0.1學習率 # 創建隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model3 = net_class3(act_fun=torch.tanh, in_features=5) tanh_model3_norm = net_class3(act_fun=torch.tanh, in_features=5, BN_model='pre') tanh_model4 = net_class4(act_fun=torch.tanh, in_features=5) tanh_model4_norm = net_class4(act_fun=torch.tanh, in_features=5, BN_model='pre') # 創建模型容器 model_l = [tanh_model3, tanh_model3_norm, tanh_model4, tanh_model4_norm] name_l = ['tanh_model3', 'tanh_model3_norm', 'tanh_model4', 'tanh_model4_norm']# 核心參數 num_epochs = 40 lr = 0.1# 模型訓練 train_l1, test_l1 = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 0.03學習率 # 創建隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model3 = net_class3(act_fun=torch.tanh, in_features=5) tanh_model3_norm = net_class3(act_fun=torch.tanh, in_features=5, BN_model='pre') tanh_model4 = net_class4(act_fun=torch.tanh, in_features=5) tanh_model4_norm = net_class4(act_fun=torch.tanh, in_features=5, BN_model='pre') # 創建模型容器 model_l = [tanh_model3, tanh_model3_norm, tanh_model4, tanh_model4_norm] name_l = ['tanh_model3', 'tanh_model3_norm', 'tanh_model4', 'tanh_model4_norm']# 核心參數 num_epochs = 40 lr = 0.03# 模型訓練 train_l03, test_l03 = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 0.01學習率 # 創建隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model3 = net_class3(act_fun=torch.tanh, in_features=5) tanh_model3_norm = net_class3(act_fun=torch.tanh, in_features=5, BN_model='pre') tanh_model4 = net_class4(act_fun=torch.tanh, in_features=5) tanh_model4_norm = net_class4(act_fun=torch.tanh, in_features=5, BN_model='pre') # 創建模型容器 model_l = [tanh_model3, tanh_model3_norm, tanh_model4, tanh_model4_norm] name_l = ['tanh_model3', 'tanh_model3_norm', 'tanh_model4', 'tanh_model4_norm']# 核心參數 num_epochs = 40 lr = 0.01# 模型訓練 train_l01, test_l01 = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 0.005學習率 # 創建隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model3 = net_class3(act_fun=torch.tanh, in_features=5) tanh_model3_norm = net_class3(act_fun=torch.tanh, in_features=5, BN_model='pre') tanh_model4 = net_class4(act_fun=torch.tanh, in_features=5) tanh_model4_norm = net_class4(act_fun=torch.tanh, in_features=5, BN_model='pre') # 創建模型容器 model_l = [tanh_model3, tanh_model3_norm, tanh_model4, tanh_model4_norm] name_l = ['tanh_model3', 'tanh_model3_norm', 'tanh_model4', 'tanh_model4_norm']# 核心參數 num_epochs = 40 lr = 0.005# 模型訓練 train_l005, test_l005 = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 訓練誤差 plt.subplot(221) for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_l1[i])plt.subplot(222) for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_l03[i])plt.subplot(223) for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_l01[i])plt.subplot(224) for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_l005[i], label=name) plt.legend(loc = 1)
能夠看出,隨著學習率逐漸變化,擁有BN層的模型表現出更加劇烈的波動,這也說明擁有BN層的模型對學習率變化更加敏感。
??BN層對學習率敏感的背后,其實代表的是BN層可以在更大范圍內進行最小值搜索(可以想象成下山的時候山會同步移動),此時調整學習率,也就擁有了更大的優化空間。
2.學習率學習曲線
??另外,我們需要知道,學習率作為模型重要參數,學習率的調整也會影響實際模型效果。接下來我們將tanh2模型的學習率調整為0.01,測試模型表現。
# 設置隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model2 = net_class2(act_fun= torch.tanh, in_features=5) tanh_model2_norm = net_class2(act_fun= torch.tanh, in_features=5, BN_model='pre')# 創建模型容器 model_l = [tanh_model2, tanh_model2_norm] name_l = ['tanh_model2', 'tanh_model2_norm']# 核心參數 lr = 0.01 num_epochs = 40# 模型訓練 train_ls, test_ls = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal) # 訓練誤差 for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), train_ls[i], label=name) plt.legend(loc = 1) plt.title('mse_train') # 測試誤差 for i, name in enumerate(name_l):plt.plot(list(range(num_epochs)), test_ls[i], label=name) plt.legend(loc = 1) plt.title('mse_test')
同樣,我們統計最后5輪訓練誤差和測試誤差
對比此前tanh2模型訓練誤差和測試誤差
# 學習率為0.03時模型誤差 train_lh[1:,-5:].mean() test_lh[1:,-5:].mean() #tensor(7.6008) #tensor(10.7237)我們發現,學習率調小之后模型出現這種情況,很大概率是因為學習率較大時,迭代到后期會出現模型迭代解在最小值點附近反復震蕩,出于各種原因,無法抵達最小值點。而當我們調小學習率之后,迭代解就能夠通過更小的孔。但這也不是絕對的,如果學習率調的過小,不僅會降低迭代速度,同時也極有可能導致迭代解在最小值附近停止不動(每次移動步幅過小)。也就是對于學習率學習曲線來說,實際上也是個U型曲線。我們嘗試將學習率調整為0.005和0.001進行建模。
# 設置隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model2 = net_class2(act_fun= torch.tanh, in_features=5) tanh_model2_norm = net_class2(act_fun= torch.tanh, in_features=5, BN_model='pre')# 創建模型容器 model_l = [tanh_model2, tanh_model2_norm] name_l = ['tanh_model2', 'tanh_model2_norm']# 核心參數 lr = 0.001 num_epochs = 40# 模型訓練 train_lss, test_lss = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 學習率為0.01時模型誤差 train_lss[1:,-5:].mean() test_lss[1:,-5:].mean() #tensor(9.4691) #tensor(16.9594)# 設置隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model2 = net_class2(act_fun= torch.tanh, in_features=5) tanh_model2_norm = net_class2(act_fun= torch.tanh, in_features=5, BN_model='pre')# 創建模型容器 model_l = [tanh_model2, tanh_model2_norm] name_l = ['tanh_model2', 'tanh_model2_norm']# 核心參數 lr = 0.005 num_epochs = 40# 模型訓練 train_lms, test_lms = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal)# 學習率為0.05時模型誤差 train_lms[1:,-5:].mean() test_lms[1:,-5:].mean() #tensor(5.0444) #tensor(7.4759)同樣,取最后四個結果取均值,繪制折線圖進行觀察。
lr_l = [0.03, 0.01, 0.005, 0.001] train_ln = [train_lh[1:,-5:].mean(), train_ls[1:,-5:].mean(), train_lms[1:,-5:].mean(), train_lss[1:,-5:].mean()] test_ln = [test_lh[1:,-5:].mean(), test_ls[1:,-5:].mean(), test_lms[1:,-5:].mean(), test_lss[1:,-5:].mean()]plt.plot(lr_l, train_ln, label='train_mse') plt.plot(lr_l, test_ln, label='test_mse') plt.legend(loc = 1) #plt.ylim(0, 15)
對于學習率的調整,一般都會出現倒U型曲線。我們能夠發現,在當前模型條件下,學習率為0.005左右時模型效果較好。當然,我們這里也只取了四個值進行測試,也有可能最佳學習率在0.006或者0.0051,關于學習率參數的調整策略(LR-scheduler),我們將在下一節進行詳細介紹,本節我們將利用此處實驗得到的0.005作為學習率進行后續實驗。
3.不同學習率下不同模型優化效果
??既然學習率學習曲線是U型曲線,那么U型的幅度其實就代表著學習率對于該模型的優化空間,這里我們可以通過簡單實驗,來觀測不同模型的U型曲線的曲線幅度。首先,對于tanh2來說,帶BN層的模型學習率優化效果比不帶BN層學習率優化效果更好。
lr_l = [0.03, 0.01, 0.005, 0.001] train_ln = [train_lh[1:,-5:].mean(), train_ls[1:,-5:].mean(), train_lms[1:,-5:].mean(), train_lss[1:,-5:].mean()] test_ln = [test_lh[1:,-5:].mean(), test_ls[1:,-5:].mean(), test_lms[1:,-5:].mean(), test_lss[1:,-5:].mean()] train_l = [train_lh[0:,-5:].mean(), train_ls[0:,-5:].mean(), train_lms[0:,-5:].mean(), train_lss[0:,-5:].mean()] test_l = [test_lh[0:,-5:].mean(), test_ls[0:,-5:].mean(), test_lms[0:,-5:].mean(), test_lss[0:,-5:].mean()]plt.subplot(121) plt.plot(lr_l, train_ln, label='train_mse') plt.plot(lr_l, test_ln, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('With BN(tanh2)') plt.subplot(122) plt.plot(lr_l, train_l, label='train_mse') plt.plot(lr_l, test_l, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('Without BN(tanh2)') train_lms[1:,-5:].mean() #tensor(5.0444)類似的,我們可以補充tanh3、4在學習率為0.001時的表現,并進行類似實驗。
# 設置隨機數種子 torch.manual_seed(24) # 實例化模型 tanh_model3 = net_class3(act_fun= torch.tanh, in_features=5) tanh_model3_norm = net_class3(act_fun= torch.tanh, in_features=5, BN_model='pre') tanh_model4 = net_class4(act_fun= torch.tanh, in_features=5) tanh_model4_norm = net_class4(act_fun= torch.tanh, in_features=5, BN_model='pre')# 創建模型容器 model_l = [tanh_model3, tanh_model3_norm, tanh_model4, tanh_model4_norm] name_l = ['tanh_model3', 'tanh_model3_norm', 'tanh_model4', 'tanh_model4_norm']# 核心參數 lr = 0.001 num_epochs = 40# 模型訓練 train_l001, test_l001 = model_comparison(model_l = model_l, name_l = name_l, train_data = train_loader,test_data = test_loader,num_epochs = num_epochs, criterion = nn.MSELoss(), optimizer = optim.SGD, lr = lr, cla = False, eva = mse_cal) train_l001 #tensor([[78.6160, 73.0764, 62.7336, 49.7248, 39.8199, 35.4495, 34.1540, 33.8683, # 33.8073, 33.7815, 33.7592, 33.7343, 33.7035, 33.6606, 33.5913, 33.4548, # 33.0735, 31.2249, 26.4116, 22.3701, 16.3730, 14.1721, 13.5942, 12.5150, # 11.3104, 10.8606, 10.5622, 11.5685, 10.1088, 9.9441, 10.6014, 10.3630, # 9.8647, 10.0165, 9.6261, 9.9919, 9.6189, 9.9426, 9.6061, 9.7663], # [87.5913, 83.8961, 78.8945, 71.5308, 62.9129, 53.6322, 43.6664, 36.2891, # 31.4903, 27.8138, 25.3230, 22.6066, 19.1205, 16.6132, 15.5674, 13.9563, # 13.0120, 12.7016, 11.8167, 11.5438, 11.1577, 10.9892, 10.6801, 10.6650, # 10.3403, 10.0786, 10.3988, 9.7349, 9.6953, 9.4739, 9.4324, 9.4121, # 9.3468, 9.1834, 9.1460, 9.0151, 9.0186, 8.8875, 8.9087, 9.0112], # [88.9965, 81.7952, 68.5453, 52.4968, 40.7098, 35.6605, 34.2223, 33.9285, # 33.8783, 33.8672, 33.8606, 33.8549, 33.8497, 33.8450, 33.8408, 33.8370, # 33.8336, 33.8305, 33.8277, 33.8251, 33.8228, 33.8206, 33.8186, 33.8166, # 33.8148, 33.8130, 33.8111, 33.8092, 33.8072, 33.8048, 33.8018, 33.7979, # 33.7921, 33.7822, 33.7618, 33.7020, 33.3427, 27.4417, 16.7350, 14.2512], # [84.4171, 81.9105, 76.7044, 69.2630, 60.1697, 49.6640, 39.1671, 32.3561, # 27.0085, 24.4072, 22.3425, 20.2697, 17.3455, 15.5881, 14.0471, 13.4442, # 12.4427, 12.8695, 13.1858, 11.5790, 11.5644, 11.2683, 10.6948, 10.7707, # 10.1701, 10.7595, 10.2070, 9.9687, 10.0931, 9.8065, 9.4242, 9.9034, # 9.4053, 9.6317, 9.1807, 8.9898, 8.9243, 8.8501, 9.0580, 9.1494]]) lr_l = [0.03, 0.01, 0.005, 0.001] train_ln = [train_l03[1:,-5:].mean(), train_l01[1:,-5:].mean(), train_l005[1:,-5:].mean(), train_l001[1:,-5:].mean()] test_ln = [test_l03[1:,-5:].mean(), test_l01[1:,-5:].mean(), test_l005[1:,-5:].mean(), test_l001[1:,-5:].mean()] train_l = [train_l03[0:,-5:].mean(), train_l01[0:,-5:].mean(), train_l005[0:,-5:].mean(), train_l001[0:,-5:].mean()] test_l = [test_l03[0:,-5:].mean(), test_l01[0:,-5:].mean(), test_l005[0:,-5:].mean(), test_l1[0:,-5:].mean()]plt.subplot(121) plt.plot(lr_l, train_ln, label='train_mse') plt.plot(lr_l, test_ln, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('With BN(tanh3)') plt.subplot(122) plt.plot(lr_l, train_l, label='train_mse') plt.plot(lr_l, test_l, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('Without BN(tanh3)') lr_l = [0.03, 0.01, 0.005, 0.001] train_ln = [train_l03[3:,-5:].mean(), train_l01[3:,-5:].mean(), train_l005[3:,-5:].mean(), train_l001[3:,-5:].mean()] test_ln = [test_l03[3:,-5:].mean(), test_l01[3:,-5:].mean(), test_l005[3:,-5:].mean(), test_l001[3:,-5:].mean()] train_l = [train_l03[2:,-5:].mean(), train_l01[2:,-5:].mean(), train_l005[2:,-5:].mean(), train_l001[2:,-5:].mean()] test_l = [test_l03[2:,-5:].mean(), test_l01[2:,-5:].mean(), test_l005[2:,-5:].mean(), test_l1[2:,-5:].mean()]plt.subplot(121) plt.plot(lr_l, train_ln, label='train_mse') plt.plot(lr_l, test_ln, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('With BN(tanh4)') plt.subplot(122) plt.plot(lr_l, train_l, label='train_mse') plt.plot(lr_l, test_l, label='test_mse') plt.legend(loc = 1) plt.ylim(4, 25) plt.title('Without BN(tanh4)')
整體來看,帶BN層的模型對學習率調整更加敏感,優化空間更大。
??BN層這種伴隨模型更加復雜、對學習率也更加敏感的屬性,最終會使得更加復雜的模型在相同的學習率下表現更好。這是一個隱藏很深的結論,但同時也是一個非常有用的結論。
??當然,截至目前,帶BN的tanh4在當前數據集上表現最好,并且遠超不帶BN的其他所有神經網絡。據此我們可以得出結論,學習率調整在復雜且帶BN層模型上表現效果更好。但值得一提的是,對于加入BN層的tanh模型的調參,目前還只是冰山一角,以目前所掌握的調參手段,尚未發揮BN層的全部效能,因此效果仍然只能說是中規中矩。
??另外,本節重點強調了關于學習率調整的模型優化方法,也花費了較大篇幅去探索學習率學習曲線的U型特性,該內容也將為后續課程的學習率優化部分做鋪墊。
加入BN層對模型重大的改變,不是說加了BN層之后模型效果立馬就好了,而是說它為我們模型未來的優化提供了非常多的可能性,有很多一些方法可能對于不帶BN層的模型效果不是很顯著。但是一旦加入BN層后,這些方法就會變得很好,比如說學習率優化。
四、帶BN層的神經網絡模型綜合調整策略總結
??最后,我們總結下截至目前,針對BN層的神經網絡模型調參策略。
- 簡單數據、簡單模型下不用BN層,加入BN層效果并不顯著;
- BN層的使用需要保持running_mean和running_var的無偏性,因此需要謹慎調整batch_size;(不能調大batch_size,momentum調小,增加迭代次數)
- 學習率是重要的模型優化的超參數,一般來說學習率學習曲線都是U型曲線;
- 從學習率調整角度出發,對于加入BN層的模型,學習率調整更加有效;對于帶BN層模型角度來說,BN層能夠幫助模型拓展優化空間,使得很多優化方法都能在原先無效的模型上生效;
- 對于復雜問題,在計算能力能夠承擔的范圍內,應當首先構建帶BN層的復雜模型,然后再試圖進行優化,就像上文所述,很多優化方法只對帶BN層的模型有效;
其他拓展方面結論:
- 關于BN和Xavier/Kaiming方法,一般來說,使用BN層的模型不再會用參數初始化方法,從理論上來看添加BN層能夠起到參數初始化的相等效果(各個不同線性層梯度平穩性);(另外,帶BN層模型一般也不需要使用Dropout方法)
- 本節尚未討論ReLU激活函數的優化,相關優化方法將放在后續進行詳細討論,但需要知道的是,對于ReLU疊加的模型來說,加入BN層之后能夠有效緩解Dead ReLU Problem,此時無須刻意調小學習率,能夠在收斂速度和運算結果間保持較好的平衡。
- BN層是目前大部分深度學習模型的標配,但前提是你有能力去對其進行優化;
總結
以上是生活随笔為你收集整理的Lesson 14.3 Batch Normalization综合调参实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 14.1 数据归一化与Ba
- 下一篇: Lesson 15.1 学习率调度基本概