原文地址:https://github.com/fchollet/deep-learning-with-python-notebooks/blob/master/4.4-overfitting-and-underfitting.ipynb
我们使用第3章第5节的代码准备数据:
from keras.datasets import imdb
import numpy as np
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
def vectorize_sequences(sequences, dimension=10000):
# Create an all-zero matrix of shape (len(sequences), dimension)
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1. # set specific indices of results[i] to 1s
return results
# Our vectorized training data
x_train = vectorize_sequences(train_data)
# Our vectorized test data
x_test = vectorize_sequences(test_data)
# Our vectorized labels
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
让我们在我们的电影评论分类网络上试试这个。我们原来的网络是这样的:
from keras import models
from keras import layers
original_model = models.Sequential()
original_model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
original_model.add(layers.Dense(16, activation='relu'))
original_model.add(layers.Dense(1, activation='sigmoid'))
original_model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
现在让我们尝试用这个更小的网络替换它:
smaller_model = models.Sequential()
smaller_model.add(layers.Dense(4, activation='relu', input_shape=(10000,)))
smaller_model.add(layers.Dense(4, activation='relu'))
smaller_model.add(layers.Dense(1, activation='sigmoid'))
smaller_model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
以下是原始网络和较小网络的验证损失比较。 点是较小网络的验证损失值,并且十字架是初始网络(记住:较低的验证损失表示更好的模型)。
original_hist = original_model.fit(x_train, y_train,
epochs=20,
batch_size=512,
validation_data=(x_test, y_test))
smaller_model_hist = smaller_model.fit(x_train, y_train,
epochs=20,
batch_size=512,
validation_data=(x_test, y_test))
epochs = range(1, 21)
original_val_loss = original_hist.history['val_loss']
smaller_model_val_loss = smaller_model_hist.history['val_loss']
import matplotlib.pyplot as plt
# b+ is for "blue cross"
plt.plot(epochs, original_val_loss, 'b+', label='Original model')
# "bo" is for "blue dot"
plt.plot(epochs, smaller_model_val_loss, 'bo', label='Smaller model')
plt.xlabel('Epochs')
plt.ylabel('Validation loss')
plt.legend()
plt.show()
正如您所看到的,较小的网络比参考时间晚开始过度拟合(经过6个历元而不是4个),并且一旦开始过拟合,其性能下降速度就会慢得多。
现在,对于踢球,让我们在这个基准测试中增加一个具有更多容量的网络,远远超出问题所需:
bigger_model = models.Sequential()
bigger_model.add(layers.Dense(512, activation='relu', input_shape=(10000,)))
bigger_model.add(layers.Dense(512, activation='relu'))
bigger_model.add(layers.Dense(1, activation='sigmoid'))
bigger_model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
bigger_model_hist = bigger_model.fit(x_train, y_train,
epochs=20,
batch_size=512,
validation_data=(x_test, y_test))
以下是与参考价格相比更大的网络票价的情况。 点是较大网络的验证损失值,而十字是初始网络。
bigger_model_val_loss = bigger_model_hist.history['val_loss']
plt.plot(epochs, original_val_loss, 'b+', label='Original model')
plt.plot(epochs, bigger_model_val_loss, 'bo', label='Bigger model')
plt.xlabel('Epochs')
plt.ylabel('Validation loss')
plt.legend()
plt.show()
更大的网络几乎马上就开始过度配备,仅仅过了一个时代,并且过度适应更严重。 其验证损失也更加嘈杂。
同时,这里是我们两个网络的训练损失:
original_train_loss = original_hist.history['loss']
bigger_model_train_loss = bigger_model_hist.history['loss']
plt.plot(epochs, original_train_loss, 'b+', label='Original model')
plt.plot(epochs, bigger_model_train_loss, 'bo', label='Bigger model')
plt.xlabel('Epochs')
plt.ylabel('Training loss')
plt.legend()
plt.show()
正如你所看到的,更大的网络很快就会使训练损失接近零。 网络容量越大,训练数据建模速度就越快(导致训练损失低),但过度拟合更容易(导致训练和验证损失之间存在很大差异)。
在Keras中,重量正则化是通过将重量调节器实例作为关键字参数传递给图层来添加的。让我们在我们的电影评论分类网络中添加L2权重正则化:
from keras import regularizers
l2_model = models.Sequential()
l2_model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu', input_shape=(10000,)))
l2_model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu'))
l2_model.add(layers.Dense(1, activation='sigmoid'))
l2_model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
l2(0.001)意味着该层的权重矩阵中的每个系数将增加0.001 * weight_coefficient_value到网络的总损失。 请注意,由于这种惩罚仅在训练时间添加,因此该训练网络的损失在训练时会比在测试时间高得多。
以下是我们二级正规化惩罚的影响:
l2_model_hist = l2_model.fit(x_train, y_train,
epochs=20,
batch_size=512,
validation_data=(x_test, y_test))
l2_model_val_loss = l2_model_hist.history['val_loss']
plt.plot(epochs, original_val_loss, 'b+', label='Original model')
plt.plot(epochs, l2_model_val_loss, 'bo', label='L2-regularized model')
plt.xlabel('Epochs')
plt.ylabel('Validation loss')
plt.legend()
plt.show()
正如您所看到的,即使两个模型具有相同数量的参数,具有L2正则化(点)的模型也比参考模型(十字架)更能抵抗过度拟合。
作为L2正规化的替代品,您可以使用以下Keras体重调节器之一:
from keras import regularizers
# L1 regularization
regularizers.l1(0.001)
# L1 and L2 regularization at the same time
regularizers.l1_l2(l1=0.001, l2=0.001)
# At training time: we drop out 50% of the units in the output
layer_output *= np.randint(0, high=2, size=layer_output.shape)
在测试时间,我们将通过退出率缩小输出。 在这里我们缩小0.5(因为我们之前下降了一半的单位):
# At test time:
layer_output *= 0.5
注意,这个过程可以通过在训练时进行两个操作并在测试时保持输出不变来实现,这通常是实践中实现的方式:
# At training time:
layer_output *= np.randint(0, high=2, size=layer_output.shape)
# Note that we are scaling *up* rather scaling *down* in this case
layer_output /= 0.5
这种技术可能看起来很奇怪和任意。 为什么这会帮助减少过度配合? Geoff Hinton表示,他受到了银行所使用的防欺诈机制的启发 - 用他自己的话说:“我去了银行,出纳员一直在变,我问他们其中一个为什么,他说他 我不知道但他们有很多东西,我想这一定是因为它需要员工之间的合作来成功地欺骗银行,这让我意识到,在每个例子中随机删除一个不同的神经元子集可以防止阴谋,从而避免阴谋 减少过度配合“。
在Keras中,您可以通过Dropout图层在网络中引入丢失,该图层将应用于其之前的图层输出,例如:
model.add(layers.Dropout(0.5))
让我们在我们的IMDB网络中添加两个Dropout图层,以查看它们在减少过度拟合方面的表现如何:
dpt_model = models.Sequential()
dpt_model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
dpt_model.add(layers.Dropout(0.5))
dpt_model.add(layers.Dense(16, activation='relu'))
dpt_model.add(layers.Dropout(0.5))
dpt_model.add(layers.Dense(1, activation='sigmoid'))
dpt_model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
dpt_model_hist = dpt_model.fit(x_train, y_train,
epochs=20,
batch_size=512,
validation_data=(x_test, y_test))