上个模型令人讨厌的地方是光训练就花了一个小时的时间,等结果并不是一个令人心情愉快的事情。这一部分,我们将讨论将两个技巧结合让网络训练的更快!
直觉上的解决办法是,开始训练时取一个较高的学习率,随着迭代次数的增多不停的减小这个值。这是有道理的,因为开始的时候我们距离全局最优点非常远,我们想要朝着最优点的方向大步前进;然而里最优点越近,我们就前进的越谨慎,以免一步跨过去。举个例子说就是你乘火车回家,但你进家门的时候肯定是走进去,不能让火车开进去。
关于深度学习中的初始化和动量的重要性是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)
2012年,在通过防止特征探测器的共适应来改进神经网络论文中引入了dropout,它是一种流行的正则化技术,工作非常好。我不会深入了解它为什么这么好的细节,你可以在别的地方读到。
像任何其他正则化技术一样,如果我们有一个过度拟合的网络,dropout才有意义,这在上一节中我们训练的net5网络显然是这样。 重要的是要记住,让你的网络训练得很好,首先过拟合,然后正则化。
要在lasagne中使用dropout,我们将在现有图层之间添加dropoutlayer图层,并为每个图层指定退出概率。 这里是我们新网的完整定义。我在这些行的末尾添加了一个#!,用于区分和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,
)
我们的网路现在已经大到可以让python报一个“超过最大递归限制”错误了,所以为了避免这一点,我们最好增加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)
看一下我们现在的训练,我们注意到训练速度又变慢了,以为添加了dropout,这是不出意料的效果。然而,整个网络的表现事实上超过了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
过拟合也似乎没有那么糟糕。虽然我们必须小心这些数字:训练错误和验证错误之间的比率现在有一个稍微不同的意义,因为训练错误评估与遗漏,而验证错误评估没有遗漏。训练错误的更有价值的值是
from sklearn.metrics import mean_squared_error
print mean_squared_error(net6.predict(x), y)
# prints something like 0.0010073791
在我们以前的没有dropout的模型中,训练上的误差为0.000373。 所以不仅我们的dropout网表现略微好一点,它的过拟合也比我们以前的模型少得多。 这是个好消息,因为这意味着当我们使网络更大(更具表现力)时,我们可以期望更好的性能。 这就是我们将尝试下一步:我们将最后两个隐藏层中的单位数从500增加到1000。我们需要修改这些行:
net7 = neuralnet(
# ...
hidden4_num_units=1000, # !
dropout4_p=0.5,
hidden5_num_units=1000, # !
# ...
)
相比于没有dropout的网络,改进效果更加明显:
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
有一点过拟合,但效果着实不错。我的感觉是,如果继续增加训练次数,模型效果会变得更棒。试一下:
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
现在你是dropout魔力的见证者了。:-)
让我们比较一下到目前为止我们训练过的网络:
name description epochs train loss valid loss
net1single hidden4000.0022440.003255
net2convolutions10000.0010790.001566
net3augmentation30000.0006780.001288
net4mom + lr adj10000.0004960.001387
net5net4 + augment20000.0003730.001184
net6net5 + dropout30000.0013060.001121
net7net6 + epochs100000.0007600.000787
全球云计算2025年收入预计将达到3420亿美元
Autel Robotics背后竟然是这家中国公司
Libra的可行性分析以及对比特币的影响
日本强震摧残全球三大产业
物联网设备商万创科技创业板IPO!9成收入来自境外客户,募资4.25亿
卷积神经网络检测脸部关键点的教程之两个重要参数:学习率,学习动量
小米5X怎么样?小米5X黑色版上手评测:小米5X首轮开售成绩傲人,网友热评:送货快,价格合理
100/1000BASE-T1千兆车载以太网转换器
感应电动势是什么_感应电动势公式_感应电动势的计算
云知声在AI语音领域荣膺2022数字经济优秀新锐掌舵案例
东芝推出20k至100kbps光电耦合器TLP2301/TLP2303
什么是库 安装拓展库的两种方式
北汽蓝谷前三季度营收同比增长63%,销量增91%
硅结构的深且窄的各向异性蚀刻研究
Epic Games融资12.5亿美元,总估值达150亿美元
基站信号是怎么发出的?
怎么在数据可视化报表上展现车辆行驶轨迹
奥迪首次引入安卓9.0系统
MF51单端玻封型热敏电阻
什么是智慧用电设备_电猫猫_西安闽泰科技