寒假学习日报(八)

  昨天提到过过拟合与欠拟合的概念,今天主要学习了如何抑制过拟合,首先看一下如何判断过拟合:

  代码测试:

#先看一下什么是过拟合
import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

(train_image, train_label), (test_image, test_label) = tf.keras.datasets.fashion_mnist.load_data()
train_image = train_image/255
test_image = test_image/255
#转化独热编码
train_label_onehot = tf.keras.utils.to_categorical(train_label)
test_label_onehot = tf.keras.utils.to_categorical(test_label)
#增加层
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
#训练时使用history记录训练情况,并使用validation_data来调用test_image和test_label_onehot来验证正确率情况
history = model.fit(train_image, train_label_onehot,
                    epochs=10, 
                    validation_data=(test_image, test_label_onehot))

这里直接剪一下最后一次训练的结果:

#看一下history都显示什么
history.history.keys()

#用折线图表示loss和val_loss损失率,判断是否发生过拟合
plt.plot(history.epoch, history.history.get('loss'), label='loss')
plt.plot(history.epoch, history.history.get('val_loss'), label='val_loss')
plt.legend()
#由于从8开始,loss一直下降,而val_loss却反而开始上升,证明发生了过拟合

#用折线图表示acc和val_acc正确率,判断是否发生过拟合
plt.plot(history.epoch, history.history.get('acc'), label='acc')
plt.plot(history.epoch, history.history.get('val_acc'), label='val_acc')
plt.legend()
#在test数据集上的正确率val_acc与acc差距很大,证明发生了过拟合

#总结:
##过拟合:训练数据上得分很高,测试数据上得分相对比较低
##欠拟合:训练数据上得分比较低,测试数据上得分相对比较低

 

   在判断出这个训练模型存在过拟合现象之后,我们要想办法去抑制它,下面介绍抑制过拟合的方法。

Dropout抑制过拟合

       实现原理:添加dropout层,在该层中会人为设置随机丢弃掉一些单元。图示如下:

 

 

由于是随机丢弃的一些隐藏单元,这次建立模型和下一次训练结果使用的是不同的树,而在预测的时候使用的是全部的隐藏单元。

Dropout抑制过拟合的原因:

(1)使用dropout层就像是取平均值一样,假如使用相同的训练数据训练5次,就像是训练了5次不同的神经网络,一般得到5个不同的结果,这时可以选择取均值或者多数取胜策略。

(2)减少神经元之间复杂的共适应关系:dropout每次是随机丢弃神经元的,导致两个神经元不一定每次都在一个dropout网络层中出现,这样权值的更新不再依赖于有固定关系的隐含节点(神经元)的共同作用,阻止某些特征仅仅在其他特征下才有的情况。

  只是了解概念还不够,咱们再拿代码来测试一下:

#抑制过拟合:插入dropout层
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))
model.add(tf.keras.layers.Dense(128, activation='relu'))
#Dropout参数rate:丢弃比率,取值范围是0-1,设置0.5证明每次随机丢弃50%的神经元
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
#再训练一下,还是使用history保存一下模型
history = model.fit(train_image, train_label_onehot,
                    epochs=10, 
                    validation_data=(test_image, test_label_onehot))

依然是剪最后一次的训练结果:

 

 再用图表示一下:

#添加dropout层后正确率情况
plt.plot(history.epoch, history.history.get('acc'), label='acc')
plt.plot(history.epoch, history.history.get('val_acc'), label='val_acc')
plt.legend()

#添加dropout层后损失率情况
plt.plot(history.epoch, history.history.get('loss'), label='loss')
plt.plot(history.epoch, history.history.get('val_loss'), label='val_loss')
plt.legend()

 

 可以看到效果已经得到了很大的改善。

除了Dropout以外,还有其他方法,如增加训练数据,但我们使用的fashion mnist数据已经是下载好的了,再添加也不太现实,因此才用减少网络容量的方法:

#抑制过拟合2:减少网络容量
model = tf.keras.Sequential()
model.add(tf.keras.layers.Flatten(input_shape=(28,28)))
#原隐藏单元数设置是128,这里改成32
model.add(tf.keras.layers.Dense(32, activation='relu'))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
###此外还有正则化的方法,Dense方法参数中kernel_regularizer可设置,默认为None。
###正则化本质是控制网络规模

#再训练
history = model.fit(train_image, train_label_onehot,
                    epochs=10, 
                    validation_data=(test_image, test_label_onehot))

 

 

#减小网络容量后正确率情况
plt.plot(history.epoch, history.history.get('acc'), label='acc')
plt.plot(history.epoch, history.history.get('val_acc'), label='val_acc')
plt.legend()
#训练次数不够,epoch次数再多一些效果更好

 

 

#减小网络容量后损失率情况
plt.plot(history.epoch, history.history.get('loss'), label='loss')
plt.plot(history.epoch, history.history.get('val_loss'), label='val_loss')
plt.legend()
#训练次数不够,epoch次数再多一些效果更好

 

 

在了解过拟合以及抑制方法之后,我们可以总结一下参数选择原则:

首先开发一个过拟合模型(添加更多层;让每层更大;训练更多轮次),然后去抑制过拟合(dropout;正则化;图像增强;而抑制过拟合的最好办法是增加训练数据),之后再次调节超参数(学习速率;隐藏层单元数;训练轮次),调参的时候要注意交叉验证。

 构建网络的总原则:保证神经网络容量足够拟合数据!(增大网络容量直到过拟合=>采取措施抑制过拟合=>继续增大网络容量直到过拟合)

  以上是今天学习的关于过拟合的全部内容,此外今天还了解了函数式api的使用概念,这里涉及到的一些操作会在明天的日报中做一个总结。

posted @ 2021-01-17 19:09  千幽行  阅读(120)  评论(0编辑  收藏  举报