text.html 14.1 KB
Newer Older
ToTensor's avatar
ToTensor 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
 
<p class="content_105">大家休息了一会儿,回到课堂上继续讨论如何优化这个卷积网络的性能。</p> 
<p class="content_105">一个同学率先发言:“是不是像上一课讲的归一化一样,即特征缩放方面的问题?”</p> 
<p class="content_105">“非常好!”咖哥回答,“你倒是记住了上一课的要点。放入神经网络中的数据的确需要归一化,然而这件事我们已经做了—图像数据张量的值已经压缩至[0,1]区间。”</p> 
<p class="content_105">“是激活函数的关系?”另一个同学发言。咖哥说:“使用ReLU函数进行卷积网络的激活,基本上是没有大问题的。当然,你们也可以试一试其他的激活函数,如eLU等。而后面的Softmax函数对于多分类问题的激活是标配。因此,也没有问题。”</p> 
<p class="content_105">小冰突然喊道:“那个Drop……解决过拟合的……”</p> 
<p class="content_105">“嗯,Dropout,”咖哥终于点头,“这倒是值得一试。实践是检验真理的唯一标准,我们来动手试几招。”</p> 
<h3 class="thirdTitle" id="bw183"><a >6.6.1 第一招:更新优化器并设置学习速率</a></h3> 
<p class="content">从最简单的修改开始,暂时不改变网络结构,先考虑一下优化器的调整,并尝试使用不同的学习速率进行梯度下降。因为很多时候神经网络完全没有训练起来,是学习速率设定得 不好。</p> 
<p class="content">示例代码如下:</p> 
<div class="content_106"> 
 <p class="content_105">from keras import optimizers # 导入优化器</p> 
 <p class="content_105">cnn = models.Sequential() # 贯序模型</p> 
 <p class="content_105">cnn.add(layers.Conv2D(32, (3, 3), activation='relu', # 卷积层</p> 
 <p class="content_124">input_shape=(150, 150, 3)))</p> 
 <p class="content_105">cnn.add(layers.MaxPooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(64, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.MaxPooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(128, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.MaxPooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(256, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.MaxPooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Flatten()) # 展平层</p> 
 <p class="content_105">cnn.add(layers.Dense(512, activation='relu')) # 全连接层</p> 
 <p class="content_105">cnn.add(layers.Dense(10, activation='sigmoid')) # 分类输出</p> 
 <p class="content_105">cnn.compile(loss='categorical_crossentropy', # 损失函数</p> 
 <p class="content_111">optimizer=optimizers.Adam(lr=1e-4), # 更新优化器并设定学习速率</p> 
 <p class="content_111">metrics=['acc']) # 评估指标</p> 
 <p class="content_105">history = cnn.fit(X_train, y_train, # 指定训练集</p> 
 <p class="content_119">epochs=50,   # 指定轮次</p> 
 <p class="content_119">batch_size=256, # 指定批量大小</p> 
 <p class="content_119">validation_data=(X_test, y_test)) # 指定验证集</p> 
</div> 
<p class="content">输出结果如下:</p> 
<div class="content_113"> 
 <p class="content_109">Train on 1537 samples, validate on 385 samples</p> 
 <p class="content_109">Epoch 1/50</p> 
 <p class="content_109">1537/1537 [==============================] - 2s 1ms/step - loss: 2.2900 - acc: 0.1386</p> 
 <p class="content_109">- val_loss: 2.2611 - val_acc: 0.1714</p> 
 <p class="content_109">Epoch 2/50 1537/1537 [==============================] - 1s 876us/step - loss: 2.2068 - acc:</p> 
 <p class="content_109">0.2088 - val_loss: 2.1376 - val_acc: 0.2753</p> 
 <p class="content_109">… …</p> 
 <p class="content_109">Epoch 50/50</p> 
 <p class="content_109">1537/1537 [==============================] - 2s 1ms/step - loss: 0.0190 - acc: 0.9967</p> 
 <p class="content_109">- val_loss: 4.4028 - val_acc: 0.3896</p> 
</div> 
<p class="content">更换了优化器,并设定了学习速率之后,再次训练网络,发现准确率有了很大的提升,最后达到了40%左右,提高了近一倍。不过,从损失曲线上看(如下图所示),20轮之后,验证集的损失突然飙升了。这是比较典型的过拟合现象。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0233-0324.jpg"> 
 <p class="imgtitle">训练集和验证集上的损失曲线和准确率曲线(第一次调优)</p> 
</div> 
<h3 class="thirdTitle" id="bw184"><a >6.6.2 第二招:添加Dropout层</a></h3> 
<p class="content_105">“那么下一步怎么办呢?”咖哥说,“这时可以考虑一下小冰刚才说的Dropout层,降低过拟合风险。”</p> 
<p class="content">示例代码如下:</p> 
<div class="content_106"> 
 <p class="content_105">cnn = models.Sequential() # 序贯模型</p> 
 <p class="content_105">cnn.add(layers.Conv2D(32, (3, 3), activation='relu', # 卷积层</p> 
 <p class="content_124">input_shape=(150, 150, 3)))</p> 
 <p class="content_105">cnn.add(layers.Max Pooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(64, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.Dropout(0.5)) # Dropout层</p> 
 <p class="content_105">cnn.add(layers.Max Pooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(128, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.Dropout(0.5)) # Dropout层</p> 
 <p class="content_105">cnn.add(layers.Max Pooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Conv2D(256, (3, 3), activation='relu')) # 卷积层</p> 
 <p class="content_105">cnn.add(layers.Max Pooling2D((2, 2))) # 最大池化层</p> 
 <p class="content_105">cnn.add(layers.Flatten()) # 展平层</p> 
 <p class="content_105">cnn.add(layers.Dropout(0.5)) # Dropout</p> 
 <p class="content_105">cnn.add(layers.Dense(512, activation='relu')) # 全连接层</p> 
 <p class="content_105">cnn.add(layers.Dense(10, activation='sigmoid')) # 分类输出</p> 
 <p class="content_105">cnn.compile(loss='categorical_crossentropy', # 损失函数</p> 
 <p class="content_111">optimizer=optimizers.Adam(lr=1e-4), # 更新优化器并设定学习速率</p> 
 <p class="content_111">metrics=['acc']) # 评估指标</p> 
 <p class="content_105">history = cnn.fit(X_train, y_train, # 指定训练集</p> 
 <p class="content_119">epochs=50,   # 指定轮次</p> 
 <p class="content_119">batch_size=256, # 指定批量大小</p> 
 <p class="content_119">validation_data=(X_test, y_test)) # 指定验证集</p> 
</div> 
<p class="content">输出结果如下:</p> 
<div class="content_113"> 
 <p class="content_109">Train on 1537 samples, validate on 385 samples</p> 
 <p class="content_109">Epoch 1/50</p> 
 <p class="content_109">1537/1537 [==============================] - 2s 1ms/step - loss: 2.2998 - acc: 0.1496</p> 
 <p class="content_109">- val_loss: 2.2810 - val_acc: 0.2416</p> 
 <p class="content_109">Epoch 2/50</p> 
 <p class="content_109">1537/1537 [==============================] - 2s 987us/step - loss: 2.1917 - acc:</p> 
 <p class="content_109">0.2219 - val_loss: 2.2217 - val_acc: 0.2416</p> 
 <p class="content_109">… …</p> 
 <p class="content_109">Epoch 50/50</p> 
 <p class="content_109">1537/1537 [==============================] - 2s 1ms/step - loss: 0.0190 - acc: 0.9967</p> 
 <p class="content_109">- val_loss: 4.4028 - val_acc: 0.3896</p> 
</div> 
<p class="content">添加了Dropout层防止过拟合之后,损失曲线显得更平滑了(如下图所示),不再出现在验证集上飙升的现象。但是准确率的提升不大,还是40%左右。而且训练集和验证集之间的准确率,仍然是天壤之别。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0234-0325.jpg"> 
 <p class="imgtitle">训练集和验证集上的损失曲线和准确率曲线(第二次调优)</p> 
</div> 
<h3 class="thirdTitle" id="bw185"><a >6.6.3 “大杀器”:进行数据增强</a></h3> 
<p class="content_105">大家看到各种调试还是取得了一些效果,于是继续献计。有的同学提议像搭积木一样再多加几层,看看增加网络的深度是否会进一步提高性能。</p> 
<p class="content_105">咖哥说:“先别试了,等会儿你们可以自己慢慢调试。现在我要给大家介绍一个提高卷积网络图像处理问题的性能的‘大杀器’,名字叫作<span class="bold">数据增强</span>(data augmentation)。这种方法肯定能够进一步提高计算机视觉问题的准确率,同时降低过拟合。”</p> 
<p class="content_105">同学们听到还有这么神奇的方法后,纷纷集中注意力。咖哥说:“机器学习,数据量是多多益善的。数据增强,能把一张图像当成7张、8张甚至10张、100张来用,也就是从现有的样本中生成更多的训练数据。”</p> 
<p class="content">怎么做到的?是通过对图像的平移、颠倒、倾斜、虚化、增加噪声等多种手段。这是利用能够生成可信图像的随机变换来增加样本数,如下图所示。这样,训练集就被大幅地增强了,无论是图像的数目,还是多样性。因此,模型在训练后能够观察到数据的更多内容,从而具有更好的准确率和泛化能力。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0235-0326.jpg"> 
 <p class="imgtitle">针对同一张狗狗图像的数据增强:一张变多张</p> 
</div> 
<p class="content">在Keras中,可以用Image Data- Generator工具来定义一个数据增强器:</p> 
<div class="content_106"> 
 <p class="content_105"># 定义一个数据增强器, 并设定各种增强选项</p> 
 <p class="content_105">from keras.preprocessing.image import ImageDataGenerator</p> 
 <p class="content_105">augs_gen = ImageDataGenerator(</p> 
 <p class="content_111">featurewise_center=False,</p> 
 <p class="content_111">samplewise_center=False,</p> 
 <p class="content_111">featurewise_std_normalization=False,</p> 
 <p class="content_111">samplewise_std_normalization=False,</p> 
 <p class="content_111">zca_whitening=False,</p> 
 <p class="content_111">rotation_range=10,</p> 
 <p class="content_111">zoom_range = 0.1,</p> 
 <p class="content_111">width_shift_range=0.2,</p> 
 <p class="content_111">height_shift_range=0.2,</p> 
 <p class="content_111">horizontal_flip=True,</p> 
 <p class="content_111">vertical_flip=False)</p> 
 <p class="content_105">augs_gen.fit(X_train) # 针对训练集拟合数据增强器</p> 
</div> 
<p class="content">网络还是用回相同的网络,唯一的区别是在训练时,需要通过fit_generator方法动态生成被增强后的训练集:</p> 
<div class="content_106"> 
 <p class="content_105">history = cnn.fit_generator( # 使用fit_generator</p> 
 <p class="content_105">augs_gen.flow(X_train, y_train, batch_size=16), # 增强后的训练集</p> 
 <p class="content_105">validation_data = (X_test, y_test), # 指定验证集</p> 
 <p class="content_105">validation_steps = 100, # 指定验证步长</p> 
 <p class="content_105">steps_per_epoch = 100, # 指定每轮步长</p> 
 <p class="content_105">epochs = 50, # 指定轮次</p> 
 <p class="content_105">verbose = 1) # 指定是否显示训练过程中的信息</p> 
</div> 
<p class="content">输出结果如下:</p> 
<div class="content_113"> 
 <p class="content_109">Epoch 1/50</p> 
 <p class="content_109">100/100 [==============================] - 8s 76ms/step - loss: 2.3003 - acc: 0.1293</p> 
 <p class="content_109">- val_loss: 2.2951 - val_acc: 0.1532</p> 
 <p class="content_109">Epoch 2/50</p> 
 <p class="content_109">100/100 [==============================] - 7s 71ms/step - loss: 2.2571 - acc: 0.1735</p> 
 <p class="content_109">- val_loss: 2.2648 - val_acc: 0.1662</p> 
 <p class="content_109">… …</p> 
 <p class="content_109">Epoch 50/50</p> 
 <p class="content_109">100/100 [==============================] - 7s 73ms/step - loss: 1.3982 - acc: 0.5091</p> 
 <p class="content_109">- val_loss: 1.7499 - val_acc: 0.5065</p> 
</div> 
<p class="content">训练集和验证集上的损失曲线和准确率曲线如下图所示。</p> 
<div class="pic"> 
 <img src="http://csdn-ebook-resources.oss-cn-beijing.aliyuncs.com/images/b88b00f6ad14402ea66695d6809614da/figure-0236-0327.jpg"> 
 <p class="imgtitle">训练集和验证集上的损失曲线和准确率曲线(数据增强后)</p> 
</div> 
<p class="content">这次训练的速度似乎变慢了很多(因为数据增强需要时间),但是训练结果更令人满意。而且,训练集和验证集的准确率最终呈现出在相同区间内同步上升的状态,这是很好的现象。从损失曲线上看,过拟合的问题基本解决了。而验证集准确率也上升至50%左右。对于这个多种狗狗的分类问题来说,这已经是一个相当不错的成绩了。</p> 
<p class="content">下面的代码可以将神经网络模型(包括训练好的权重等所有参数)保存到一个文件中,并随时可以读取。</p> 
<div class="content_106"> 
 <p class="content_105">from keras.models import load_model # 导入模型保存工具</p> 
 <p class="content_105">cnn.save('../my_dog_cnn.h5') # 创建一个HDF5格式的文件'my_dog_cnn.h5'</p> 
 <p class="content_105">del cnn # 删除当前模型</p> 
 <p class="content_105">cnn = load_model('../my_dog_cnn.h5') # 重新载入已经保存的模型</p> 
</div> 
<p class="content">总结一下,深度神经网络的性能优化是一个很大的课题。希望上一课和本课两次的尝试能带给大家一个基本的思路。此外,其他可以考虑的方向还包括以下几种。</p> 
<p class="content">■增加或减少网络层数。</p> 
<p class="content">■尝试不同的优化器和正则化方法。</p> 
<p class="content">■尝试不同的激活函数和损失函数。</p>