我们将探索两种常见的正则化技术(权重正则化和丢弃),并使用它们改进我们的 IMDB 影评分类笔记本。
我们不会像在上一次那样使用嵌入,而是对句子进行独热编码。该模型将很快过拟合训练集。它将用来演示何时发生过拟合,以及如何防止过拟合。
务必谨记:深度学习模型往往善于与训练数据拟合,但真正的挑战是泛化,而非拟合。
和之前采用嵌入方法不同,这里我们直接使用one hot 编码,该模型将很快过拟合训练集。
从上往下一共有5个模型,我们来对比它们在validation上的结果:
enumerate
操作,可同时获得索引和值def multi_hot_sequences(sequences, dimension):
# create an all_zero matrix of shape(len(sequences), dimension)
results = np.zeros( (len(sequences), dimension) ) # 参数应该提供一个元组
for i, word_indices in enumerate(sequences): # 可同时获得索引和值
results[i,word_indices] = 1.0
return results
要缓解过拟合,一种常见方法是限制网络的复杂性,具体方法是强制要求其权重仅采用较小的值,使权重值的分布更“规则”。这称为“权重正则化”,通过向网络的损失函数添加与权重较大相关的代价来实现。这个代价分为两种类型:
L2
正则化 表示层的权重矩阵中的每个系数都会将 0.001 * weight_coefficient_value**2
添加到网络的总损失中。请注意,由于此惩罚仅在训练时添加,此网络在训练时的损失将远高于测试时。 keras.layers.Dense(16,activation=tf.nn.relu,kernel_regularizer=keras.regularizers.l2(0.001),input_shape=(NUM_WORDS, )),
[0.2, 0.5, 1.3, 0.8, 1.1]
;在应用丢弃后,此向量将随机分布几个 0 条目,例如 [0, 0.5, 1.3, 0, 1.1]
。“丢弃率”指变为 0 的特征所占的比例,通常设置在 0.2 和 0.5 之间。在测试时,网络不会丢弃任何单元,而是将层的输出值按等同于丢弃率的比例进行缩减,以便平衡以下事实:测试时的活跃单元数大于训练时的活跃单元数。keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
keras.layers.Dropout(0.5),
...
...
def plot_history(histories, key='binary_crossentropy'):
plt.figure(figsize=(16,10))
for name,history in histories:
val = plt.plot(history.epoch, history.history['val_'+key],'--
',label=name.title()+' Val')
plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
label=name.title()+' Train')
plt.xlabel('Epochs')
plt.ylabel(key.replace('_',' ').title())
plt.legend()
plt.xlim([0,max(history.epoch)])
实线表示训练损失,虚线表示验证损失(谨记:验证损失越低,表示模型越好)。在此示例中,较小的网络开始过拟合的时间比基准模型晚(前者在 6 个周期之后,后者在 4 个周期之后),并且开始过拟合后,它的效果下降速度也慢得多。
可以看到,L2 正则化模型的过拟合抵抗能力比基准模型强得多,虽然这两个模型的参数数量相同。:
下面总结一下防止神经网络出现过拟合的最常见方法:
还有两个重要的方法在本指南中没有介绍:数据增强和批次标准化。
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt
'''
数据预处理
'''
NUM_WORDS = 10000
(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)
print(train_data.shape)
def multi_hot_sequences(sequences, dimension):
# create an all_zero matrix of shape(len(sequences), dimension)
results = np.zeros( (len(sequences), dimension) ) #提供一个元组
for i, word_indices in enumerate(sequences): # 可同时获得索引和值
results[i,word_indices] = 1.0
return results
train_data = multi_hot_sequences(train_data, NUM_WORDS)
test_data = multi_hot_sequences(test_data, NUM_WORDS)
# plt.plot(train_data[0])
# plt.show()
'''
建立模型
'''
baseline_model = keras.Sequential([
# 'input_shape' is only required here so that '.summary' works
keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS, )),
keras.layers.Dense(16, activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model.compile( optimizer = 'adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
baseline_model.summary()
smaller_model = keras.Sequential([
keras.layers.Dense(4,activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
keras.layers.Dense(4,activation=tf.nn.relu),
keras.layers.Dense(1,activation=tf.nn.sigmoid)
])
smaller_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
smaller_model.summary()
bigger_model = keras.Sequential([
keras.layers.Dense(512, activation=tf.nn.relu, input_shape=(NUM_WORDS,)),
keras.layers.Dense(512, activation=tf.nn.relu),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
bigger_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
bigger_model.summary()
baseline_model_l2 = keras.Sequential([
keras.layers.Dense(16, activation=tf.nn.relu, kernel_regularizer=keras.regularizers.l2(0.001),
input_shape=(NUM_WORDS, )),
keras.layers.Dense(16, activation=tf.nn.relu, kernel_regularizer=keras.regularizers.l2(0.001)),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model_l2.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
baseline_model_l2.summary()
baseline_model_dropout = keras.Sequential([
keras.layers.Dense(16, activation=tf.nn.relu, input_shape=(NUM_WORDS, )),
keras.layers.Dropout(0.5),
keras.layers.Dense(16, activation=tf.nn.relu),
keras.layers.Dropout(0.5),
keras.layers.Dense(1, activation=tf.nn.sigmoid)
])
baseline_model_dropout.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy','binary_crossentropy'])
baseline_model_dropout.summary()
'''
训练模型
'''
def train_model(model):
history = model.fit(train_data,
train_labels,
epochs=20,
batch_size=512,
validation_data=(test_data, test_labels),
verbose=2)
return history
a= train_model(baseline_model)
# b= train_model(smaller_model)
# c= train_model(bigger_model)
# d = train_model(baseline_model_l2)
e = train_model(baseline_model_dropout)
'''
作图 查看模型效果
'''
def plot_history(histories, key='binary_crossentropy'):
plt.figure(figsize=(16,10))
for name,history in histories:
val = plt.plot(history.epoch, history.history['val_'+key],'--',label=name.title()+' Val')
plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
label=name.title()+' Train')
plt.xlabel('Epochs')
plt.ylabel(key.replace('_',' ').title())
plt.legend()
plt.xlim([0,max(history.epoch)])
plot_history([('baseline',a),
#('smaller_model',b),
#('bigger_model',c),
#('baseline_l2',d),
('baseline_dropout',e)])
plt.show()