上個(gè)模型令人討厭的地方是光訓(xùn)練就花了一個(gè)小時(shí)的時(shí)間,等結(jié)果并不是一個(gè)令人心情愉快的事情。這一部分,我們將討論將兩個(gè)技巧結(jié)合讓網(wǎng)絡(luò)訓(xùn)練的更快!
直覺上的解決辦法是,開始訓(xùn)練時(shí)取一個(gè)較高的學(xué)習(xí)率,隨著迭代次數(shù)的增多不停的減小這個(gè)值。這是有道理的,因?yàn)殚_始的時(shí)候我們距離全局最優(yōu)點(diǎn)非常遠(yuǎn),我們想要朝著最優(yōu)點(diǎn)的方向大步前進(jìn);然而里最優(yōu)點(diǎn)越近,我們就前進(jìn)的越謹(jǐn)慎,以免一步跨過去。舉個(gè)例子說就是你乘火車回家,但你進(jìn)家門的時(shí)候肯定是走進(jìn)去,不能讓火車開進(jìn)去。
關(guān)于深度學(xué)習(xí)中的初始化和動(dòng)量的重要性是Ilya Sutskever等人的談話和論文的標(biāo)題。在那里,我們學(xué)習(xí)了另一個(gè)有用的技巧來促進(jìn)深度學(xué)習(xí):即在訓(xùn)練期間增加優(yōu)化方法的動(dòng)量參數(shù)。
在我們以前的模型中,我們將學(xué)習(xí)率和學(xué)習(xí)勢(shì)初始化為靜態(tài)的0.01和0.9。讓我們來改變這兩個(gè)參數(shù),使得學(xué)習(xí)率隨著迭代次數(shù)線性減小,同時(shí)讓學(xué)習(xí)動(dòng)量增大。
NeuralNet允許我們?cè)谟?xùn)練時(shí)通過on_epoch_finished函數(shù)來更新參數(shù)。于是我們傳一個(gè)函數(shù)給on_epoch_finished,使得這個(gè)函數(shù)在每次迭代之后被調(diào)用。然而,在我們改變學(xué)習(xí)率和學(xué)習(xí)勢(shì)這兩個(gè)參數(shù)之前,我們必須將這兩個(gè)參數(shù)改變?yōu)門heano shared variables。好在這非常簡(jiǎn)單。
import theano
def float32(k):
return np.cast['float32'](k)
net4 = NeuralNet(
# ...
update_learning_rate=theano.shared(float32(0.03)),
update_momentum=theano.shared(float32(0.9)),
# ...
)
我們傳遞的回調(diào)函數(shù)或者回調(diào)列表在調(diào)用時(shí)需要兩個(gè)參數(shù):nn,它是NeuralNet的實(shí)例;train_history,它和nn.history是同一個(gè)值。
不使用硬編碼值的毀掉函數(shù),我們將使用一個(gè)可參數(shù)化的類,在其中定義一個(gè)call函數(shù)來作為我們的回調(diào)函數(shù)。讓我們把這個(gè)類叫做AdjustVariable,實(shí)現(xiàn)是相當(dāng)簡(jiǎn)單的:
class AdjustVariable(object):
def __init__(self, name, start=0.03, stop=0.001):
self.name = name
self.start, self.stop = start, stop
self.ls = None
def __call__(self, nn, train_history):
if self.ls is None:
self.ls = np.linspace(self.start, self.stop, nn.max_epochs)
epoch = train_history[-1]['epoch']
new_value = float32(self.ls[epoch - 1])
getattr(nn, self.name).set_value(new_value)
現(xiàn)在讓我們把這些變化放到一起,并開始準(zhǔn)備訓(xùn)練網(wǎng)絡(luò):
net4 = NeuralNet(
# ...
update_learning_rate=theano.shared(float32(0.03)),
update_momentum=theano.shared(float32(0.9)),
# ...
regression=True,
# batch_iterator_train=FlipBatchIterator(batch_size=128),
on_epoch_finished=[
AdjustVariable('update_learning_rate', start=0.03, stop=0.0001),
AdjustVariable('update_momentum', start=0.9, stop=0.999),
],
max_epochs=3000,
verbose=1,
)
X, y = load2d()
net4.fit(X, y)
with open('net4.pickle', 'wb') as f:
pickle.dump(net4, f, -1)
我們將訓(xùn)練兩個(gè)網(wǎng)絡(luò):net4不使用我們的FlipBatchIterator,net5采用了。 除此之外,他們是相同的。
這是net4的學(xué)習(xí):
Epoch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|----------------
50 | 0.004216 | 0.003996 | 1.055011
100 | 0.003533 | 0.003382 | 1.044791
250 | 0.001557 | 0.001781 | 0.874249
500 | 0.000915 | 0.001433 | 0.638702
750 | 0.000653 | 0.001355 | 0.481806
1000 | 0.000496 | 0.001387 | 0.357917
酷,訓(xùn)練發(fā)生得更快了! 在我們調(diào)整學(xué)習(xí)速度和學(xué)習(xí)動(dòng)量之前,在500代和1000代的訓(xùn)練誤差是以前在net2中的一半。 這一次,泛化程度似乎已經(jīng)在750個(gè)左右的時(shí)期之后停止改善; 看起來沒有什么意義的培訓(xùn)更長(zhǎng)。
net5用了數(shù)據(jù)擴(kuò)充之后怎么樣?
poch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|----------------
50 | 0.004317 | 0.004081 | 1.057609
100 | 0.003756 | 0.003535 | 1.062619
250 | 0.001765 | 0.001845 | 0.956560
500 | 0.001135 | 0.001437 | 0.790225
750 | 0.000878 | 0.001313 | 0.668903
1000 | 0.000705 | 0.001260 | 0.559591
1500 | 0.000492 | 0.001199 | 0.410526
2000 | 0.000373 | 0.001184 | 0.315353
再次,我們有比net3更快的訓(xùn)練,更好的結(jié)果。在1000次迭代之后,結(jié)果比net3迭代了3000次的效果還要好。 此外,使用數(shù)據(jù)擴(kuò)充訓(xùn)練的模型現(xiàn)在比沒有數(shù)據(jù)擴(kuò)充的模型好約10%。
丟棄技巧(Dropout)
2012年,在通過防止特征探測(cè)器的共適應(yīng)來改進(jìn)神經(jīng)網(wǎng)絡(luò)論文中引入了dropout,它是一種流行的正則化技術(shù),工作非常好。我不會(huì)深入了解它為什么這么好的細(xì)節(jié),你可以在別的地方讀到。
像任何其他正則化技術(shù)一樣,如果我們有一個(gè)過度擬合的網(wǎng)絡(luò),dropout才有意義,這在上一節(jié)中我們訓(xùn)練的net5網(wǎng)絡(luò)顯然是這樣。 重要的是要記住,讓你的網(wǎng)絡(luò)訓(xùn)練得很好,首先過擬合,然后正則化。
要在Lasagne中使用dropout,我們將在現(xiàn)有圖層之間添加DropoutLayer圖層,并為每個(gè)圖層指定退出概率。 這里是我們新網(wǎng)的完整定義。我在這些行的末尾添加了一個(gè)#!,用于區(qū)分和net5的不同。
net6 = NeuralNet(
layers=[
('input', layers.InputLayer),
('conv1', layers.Conv2DLayer),
('pool1', layers.MaxPool2DLayer),
('dropout1', layers.DropoutLayer), # !
('conv2', layers.Conv2DLayer),
('pool2', layers.MaxPool2DLayer),
('dropout2', layers.DropoutLayer), # !
('conv3', layers.Conv2DLayer),
('pool3', layers.MaxPool2DLayer),
('dropout3', layers.DropoutLayer), # !
('hidden4', layers.DenseLayer),
('dropout4', layers.DropoutLayer), # !
('hidden5', layers.DenseLayer),
('output', layers.DenseLayer),
],
input_shape=(None, 1, 96, 96),
conv1_num_filters=32, conv1_filter_size=(3, 3), pool1_pool_size=(2, 2),
dropout1_p=0.1, # !
conv2_num_filters=64, conv2_filter_size=(2, 2), pool2_pool_size=(2, 2),
dropout2_p=0.2, # !
conv3_num_filters=128, conv3_filter_size=(2, 2), pool3_pool_size=(2, 2),
dropout3_p=0.3, # !
hidden4_num_units=500,
dropout4_p=0.5, # !
hidden5_num_units=500,
output_num_units=30, output_nonlinearity=None,
update_learning_rate=theano.shared(float32(0.03)),
update_momentum=theano.shared(float32(0.9)),
regression=True,
batch_iterator_train=FlipBatchIterator(batch_size=128),
on_epoch_finished=[
AdjustVariable('update_learning_rate', start=0.03, stop=0.0001),
AdjustVariable('update_momentum', start=0.9, stop=0.999),
],
max_epochs=3000,
verbose=1,
)
我們的網(wǎng)路現(xiàn)在已經(jīng)大到可以讓Python報(bào)一個(gè)“超過最大遞歸限制”錯(cuò)誤了,所以為了避免這一點(diǎn),我們最好增加python的遞歸限制。
import sys
sys.setrecursionlimit(10000)
X, y = load2d()
net6.fit(X, y)
import cPickle as pickle
with open('net6.pickle', 'wb') as f:
pickle.dump(net6, f, -1)
看一下我們現(xiàn)在的訓(xùn)練,我們注意到訓(xùn)練速度又變慢了,以為添加了dropout,這是不出意料的效果。然而,整個(gè)網(wǎng)絡(luò)的表現(xiàn)事實(shí)上超過了net5:
Epoch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
50 | 0.004619 | 0.005198 | 0.888566
100 | 0.004369 | 0.004182 | 1.044874
250 | 0.003821 | 0.003577 | 1.068229
500 | 0.002598 | 0.002236 | 1.161854
1000 | 0.001902 | 0.001607 | 1.183391
1500 | 0.001660 | 0.001383 | 1.200238
2000 | 0.001496 | 0.001262 | 1.185684
2500 | 0.001383 | 0.001181 | 1.171006
3000 | 0.001306 | 0.001121 | 1.164100
過擬合也似乎沒有那么糟糕。雖然我們必須小心這些數(shù)字:訓(xùn)練錯(cuò)誤和驗(yàn)證錯(cuò)誤之間的比率現(xiàn)在有一個(gè)稍微不同的意義,因?yàn)橛?xùn)練錯(cuò)誤評(píng)估與遺漏,而驗(yàn)證錯(cuò)誤評(píng)估沒有遺漏。訓(xùn)練錯(cuò)誤的更有價(jià)值的值是
from sklearn.metrics import mean_squared_error
print mean_squared_error(net6.predict(X), y)
# prints something like 0.0010073791
在我們以前的沒有dropout的模型中,訓(xùn)練上的誤差為0.000373。 所以不僅我們的dropout網(wǎng)表現(xiàn)略微好一點(diǎn),它的過擬合也比我們以前的模型少得多。 這是個(gè)好消息,因?yàn)檫@意味著當(dāng)我們使網(wǎng)絡(luò)更大(更具表現(xiàn)力)時(shí),我們可以期望更好的性能。 這就是我們將嘗試下一步:我們將最后兩個(gè)隱藏層中的單位數(shù)從500增加到1000。我們需要修改這些行:
net7 = NeuralNet(
# ...
hidden4_num_units=1000, # !
dropout4_p=0.5,
hidden5_num_units=1000, # !
# ...
)
相比于沒有dropout的網(wǎng)絡(luò),改進(jìn)效果更加明顯:
Epoch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
50 | 0.004756 | 0.007043 | 0.675330
100 | 0.004440 | 0.005321 | 0.834432
250 | 0.003974 | 0.003928 | 1.011598
500 | 0.002574 | 0.002347 | 1.096366
1000 | 0.001861 | 0.001613 | 1.153796
1500 | 0.001558 | 0.001372 | 1.135849
2000 | 0.001409 | 0.001230 | 1.144821
2500 | 0.001295 | 0.001146 | 1.130188
3000 | 0.001195 | 0.001087 | 1.099271
有一點(diǎn)過擬合,但效果著實(shí)不錯(cuò)。我的感覺是,如果繼續(xù)增加訓(xùn)練次數(shù),模型效果會(huì)變得更棒。試一下:
net12 = NeuralNet(
# ...
max_epochs=10000,
# ...
)
Epoch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
50 | 0.004756 | 0.007027 | 0.676810
100 | 0.004439 | 0.005321 | 0.834323
500 | 0.002576 | 0.002346 | 1.097795
1000 | 0.001863 | 0.001614 | 1.154038
2000 | 0.001406 | 0.001233 | 1.140188
3000 | 0.001184 | 0.001074 | 1.102168
4000 | 0.001068 | 0.000983 | 1.086193
5000 | 0.000981 | 0.000920 | 1.066288
6000 | 0.000904 | 0.000884 | 1.021837
7000 | 0.000851 | 0.000849 | 1.002314
8000 | 0.000810 | 0.000821 | 0.985769
9000 | 0.000769 | 0.000803 | 0.957842
10000 | 0.000760 | 0.000787 | 0.966583
現(xiàn)在你是dropout魔力的見證者了。:-)
讓我們比較一下到目前為止我們訓(xùn)練過的網(wǎng)絡(luò):
Name Description Epochs Train loss Valid loss
net1?single hidden?400?0.002244?0.003255?
net2?convolutions?1000?0.001079?0.001566?
net3?augmentation?3000?0.000678?0.001288?
net4?mom + lr adj?1000?0.000496?0.001387?
net5?net4 + augment?2000?0.000373?0.001184?
net6?net5 + dropout?3000?0.001306?0.001121?
net7?net6 + epochs?10000?0.000760?0.000787
評(píng)論