上个模型令人讨厌的地方是光训练就花了一个小时的时间,等结果并不是一个令人心情愉快的事情。这一部分,我们将讨论将两个技巧结合让网络训练的更快!
直觉上的解决办法是,开始训练时取一个较高的学习率,随着迭代次数的增多不停的减小这个值。这是有道理的,因为开始的时候我们距离全局最优点非常远,我们想要朝着最优点的方向大步前进;然而里最优点越近,我们就前进的越谨慎,以免一步跨过去。举个例子说就是你乘火车回家,但你进家门的时候肯定是走进去,不能让火车开进去。
关于深度学习中的初始化和动量的重要性是Ilya Sutskever等人的谈话和论文的标题。在那里,我们学习了另一个有用的技巧来促进深度学习:即在训练期间增加优化方法的动量参数。
在我们以前的模型中,我们将学习率和学习势初始化为静态的0.01和0.9。让我们来改变这两个参数,使得学习率随着迭代次数线性减小,同时让学习动量增大。
NeuralNet允许我们在训练时通过on_epoch_finished函数来更新参数。于是我们传一个函数给on_epoch_finished,使得这个函数在每次迭代之后被调用。然而,在我们改变学习率和学习势这两个参数之前,我们必须将这两个参数改变为Theano shared variables。好在这非常简单。
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)),
# ...
)
我们传递的回调函数或者回调列表在调用时需要两个参数:nn,它是NeuralNet的实例;train_history,它和nn.history是同一个值。
不使用硬编码值的毁掉函数,我们将使用一个可参数化的类,在其中定义一个call函数来作为我们的回调函数。让我们把这个类叫做AdjustVariable,实现是相当简单的:
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)
现在让我们把这些变化放到一起,并开始准备训练网络:
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)
我们将训练两个网络:net4不使用我们的FlipBatchIterator,net5采用了。 除此之外,他们是相同的。
这是net4的学习:
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
酷,训练发生得更快了! 在我们调整学习速度和学习动量之前,在500代和1000代的训练误差是以前在net2中的一半。 这一次,泛化程度似乎已经在750个左右的时期之后停止改善; 看起来没有什么意义的培训更长。
net5用了数据扩充之后怎么样?
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更快的训练,更好的结果。在1000次迭代之后,结果比net3迭代了3000次的效果还要好。 此外,使用数据扩充训练的模型现在比没有数据扩充的模型好约10%。
丢弃技巧(Dropout)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)