昨天提到过过拟合与欠拟合的概念,今天主要学习了如何抑制过拟合,首先看一下如何判断过拟合:
代码测试:
#先看一下什么是过拟合 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的使用概念,这里涉及到的一些操作会在明天的日报中做一个总结。