在前面的两个例子中(电影影评分类和预测燃油效率),我们看到,在训练许多周期之后,我们的模型对验证数据的准确性会到达峰值,然后开始下降。
换句话说,我们的模型会过度拟合训练数据,学习如果处理过拟合很重要,尽管通常可以在训练集上实现高精度,但我们真正想要的是开发能够很好泛化测试数据(或之前未见过的数据)的模型。
过拟合的反面是欠拟合,当测试数据仍有改进空间会发生欠拟合,出现这种情况的原因有很多:模型不够强大,过度正则化,或者根本没有经过足够长的时间训练,这意味着网络尚未学习训练数据中的相关模式。
如果训练时间过长,模型将开始过度拟合,并从训练数据中学习模式,而这些模式可能并不适用于测试数据,我们需要取得平衡,了解如何训练适当数量的周期,我们将在下面讨论,这是一项有用的技能。
为了防止过拟合,最好的解决方案是使用更多的训练数据,受过更多数据训练的模型自然会更好的泛化。当没有更多的训练数据时,另外一个最佳解决方案是使用正则化等技术,这些限制了模型可以存储的信息的数据量和类型,如果网络只能记住少量模式,那么优化过程将迫使它专注于最突出的模式,这些模式有更好的泛化性。
在本章节中,我们将探索两种常见的正则化技术:权重正则化和dropout丢弃正则化,并使用它们来改进我们的IMDB电影评论分类。
from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__)
1. 下载IMDB数据集
我们不会像以前一样使用嵌入,而是对句子进行多重编码。这个模型将很快适应训练集。它将用于证明何时发生过拟合,以及如何处理它。
对我们的列表进行多热编码意味着将它们转换为0和1的向量,具体地说,这将意味着例如将序列[3,5]转换为10000维向量,除了索引3和5的值是1之外,其他全零。
NUM_WORDS = 10000
(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)
def multi_hot_sequences(sequences, dimension):
# 创建一个全零的形状矩阵 (len(sequences), dimension)
results = np.zeros((len(sequences), dimension))
for i, word_indices in enumerate(sequences):
results[i, word_indices] = 1.0 # 将results[i]的特定值设为1
return results
train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)
test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)
让我们看一下生成的多热矢量,单词索引按频率排序,因此预计索引零附近有更多的1值,我们可以在下图中看到:
plt.plot(train_data[0])
2. 演示过度拟合
防止过度拟合的最简单方法是减小模型的大小,即模型中可学习参数的数量(由层数和每层单元数决定)。在深度学习中,模型中可学习参数的数量通常被称为模型的“容量”。直观地,具有更多参数的模型将具有更多的“记忆能力”,因此将能够容易地学习训练样本与其目标之间的完美的字典式映射,没有任何泛化能力的映射,但是在对未见过的数据做出预测时这将是无用的。
始终牢记这一点:深度学习模型往往善于适应训练数据,但真正的挑战是泛化,而不是适应。
另一方面,如果网络具有有限的记忆资源,则将不能容易地学习映射。为了最大限度地减少损失,它必须学习具有更强预测能力的压缩表示。同时,如果您使模型太小,则难以适应训练数据。“太多容量”和“容量不足”之间存在平衡。
不幸的是,没有神奇的公式来确定模型的正确大小或架构(就层数而言,或每层的正确大小),您将不得不尝试使用一系列不同的架构。
要找到合适的模型大小,最好从相对较少的层和参数开始,然后开始增加层的大小或添加新层,直到您看到验证损失的收益递减为止。让我们在电影评论分类网络上试试。
我们将仅适用Dense
层作为基线创建一个简单模型,然后创建更小和更大的版本,并进行比较。
2.1. 创建一个基线模型
baseline_model = keras.Sequential([
# `input_shape` is only required here so that `.summary` works.
keras.layers.Dense(16, activation&#