TensorFlow之DNN(三):神经⽹络的正则化⽅法(Dropout、
L2正则化、早停。。。
这⼀篇博客整理⽤TensorFlow实现神经⽹络正则化的内容。
深层神经⽹络往往具有数⼗万乃⾄数百万的参数,可以进⾏⾮常复杂的特征变换,具有强⼤的学习能⼒,因此容易在训练集上过拟合。缓解神经⽹络的过拟合问题,⼀般有两种思路,⼀种是⽤正则化⽅法,也就是限制模型的复杂度,⽐如Dropout、L1和L2正则化、早停和权重衰减(Weight Decay),⼀种是增⼤训练样本量,⽐如数据增强(Data Augmentation)。这些⽅法的原理阐述可以看我之前整理的⽂章《》。
下⾯⽤TensorFlow来实现这些正则化⽅法。
⼀、Dropout正则化
⾸先来实现Dropout正则化。这种⽅法实在是太优秀太流⾏了,是⼀个⽐红包更天才的想法,对于提⾼模型的准确性有⽴竿见影的效果。
1、Dropout怎么做的呢?
其实很简单,在每个训练步骤中,输⼊层、隐藏层(不包括输出层)的神经元以p的概率被随机丢弃(谐⾳记忆法:神经元被抓爆了),然后在这次训练中,被丢弃的神经元不再起作⽤,但是在接下来的训练中,之前被“抓爆”的神经元可能继续被丢弃,也可能重新加⼊训练。
这个p叫做dropout rate(丢弃率),⼀般设置为0.5,也可以相机抉择。如果发现模型可能发⽣严重的过拟合问题,可以增⼤dropout rate,⽐如⼤型神经⽹络模型,⽽如果模型发⽣过拟合的风险较⼩,那么可以减⼩dropout rate,⽐如⼩型神经⽹络模型。
此外,与Batch Normalization有点类似,Dropout在训练阶段和测试阶段的做法不⼀样。训练阶段就是按照上⾯所说的去做,⽽测试阶段不需要做神经元的丢弃,这就是导致⼀个问题:假设p=0.5,那么测试阶段每个神经元的输⼊信号⼤约是训练阶段的两倍。为此,我们需要把神经元的输⼊连接权重乘以保持概率0.5(keep prop,也就是1-dropout rate),让输⼊信号减⼩⼀倍,与训练阶段⼤概保持⼀致。
2、为什么同是正则化⽅法,Dropout却如此优秀呢?
第⼀个原因是如果某个神经元相邻的伙伴被丢弃了,那么这个神经元就需要学会和更远的神经元进⾏配合,同时它⾃⼰也要⼀个顶俩,让⾃⼰更有⽤。模型不会依赖于某些神经元,每个神经元都会受到特殊关注,从⽽使⽹络变得更强⼤。最终模型对输⼊的微⼩变化不再敏感,这等价于测试阶段输⼊前所未见的样本时,模型也能很好地进⾏预测。
第⼆个原因是Dropout可以看成是⼀种集成学习⽅法。每次对神经元进⾏随机丢弃,都会产⽣⼀个不同的神经⽹络结构,那么训练完毕后所得到的最终的神经⽹络,就可以看作是所有这些⼩型神经⽹络的
集成。
于是Dropout正则化就像国发邓紫棋⼀样优秀了。
3、⽤TensorFlow实现Dropout
使⽤TensorFlow实现Dropout,可以将tf.layers.dropout()这个函数应⽤于输⼊层和每个隐藏层。在训练期间,此函数随机丢弃⼀些神经元(将它们的输出设置为0)。训练结束后,这个函数就不再发挥作⽤了。
好,下⾯还是⽤MINIST数据集来构建⼀个神经⽹络模型,⽤Dropout来正则化,除了把优化器设为Momentum外没有使⽤上⼀篇博客中整理的其他加速⽅法。
第⼀步还是先准备⼩批量样本⽤于训练,以及准备验证集和测试集。
import tensorflow as tf
import numpy as np
from functools import partial
import time
from datetime import timedelta
# 记录训练花费的时间
def get_time_dif(start_time):
end_time = time.time()
time_dif = end_time - start_time
#timedelta是⽤于对间隔进⾏规范化输出,间隔10秒的输出为:00:00:10
return timedelta(seconds=int(round(time_dif)))
# 定义输⼊层、输出层和中间隐藏层的神经元数量
n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10
# 准备训练数据集、验证集和测试集,并⽣成⼩批量样本
(X_train, y_train), (X_test, y_test) = tf.ist.load_data()
X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0
X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0
y_train = y_train.astype(np.int32)
y_test = y_test.astype(np.int32)
X_valid, X_train = X_train[:5000], X_train[5000:]
y_valid, y_train = y_train[:5000], y_train[5000:]
def shuffle_batch(X, y, batch_size):
rnd_idx = np.random.permutation(len(X))
n_batches = len(X) // batch_size
for batch_idx in np.array_split(rnd_idx, n_batches):
X_batch, y_batch = X[batch_idx], y[batch_idx]
yield X_batch, y_batch
第⼆步是在构建⽹络层时,运⽤Dropout正则化。
下⾯的代码已经进⾏了注释,这⾥再说明⼀点,在隐藏层的神经元中,对输⼊值是先激活再去做Dropout,也就是对f(WX+b)进⾏丢弃,⽽不是对WX+b。
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
# 和Batch Norm的操作有点类似,先定义⼀个trianing
training = tf.placeholder_with_default(False, shape=(), name='training')
# 设置丢弃率率
dropout_rate = 0.5  # == 1 - keep_prob
with tf.name_scope("dnn"):
# 在输⼊层对输⼊数据先进⾏Dropout,便于应⽤到隐藏层
X_drop = tf.layers.dropout(X, dropout_rate, training=training)
# 在隐藏层先进⾏激活,得到激活之后的值
hidden1 = tf.layers.dense(X_drop, n_hidden1, lu,
name="hidden1")
# 把Dropout应⽤到隐藏层,对激活值进⾏随机丢弃,所以这⾥没有激活函数了
hidden1_drop = tf.layers.dropout(hidden1, dropout_rate, training=training)
hidden2 = tf.layers.dense(hidden1_drop, n_hidden2, lu,
name="hidden2")
hidden2_drop = tf.layers.dropout(hidden2, dropout_rate, training=training)
logits = tf.layers.dense(hidden2_drop, n_outputs, name="outputs")
第三步定义模型其他部分,然后进⾏训练和测试。
这⾥再提醒⼀点,那就是在sess.run()中,别忘了传⼊feed_dict={training: True}。
取batch size = 50,训练耗时1分08秒,测试精度为97.41%。
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
learning_rate = 0.01
with tf.name_scope("train"):
optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)
training_op = optimizer.minimize(loss)
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 40
batch_size = 50
with tf.Session() as sess:
init.run()
start_time = time.time()
for epoch in range(n_epochs):
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run(training_op,
feed_dict={training: True, X: X_batch, y: y_batch})
if epoch % 5 ==0 or epoch == 39:
accuracy_batch = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "Batch accuracy:", accuracy_batch,"Validation accuracy:", accuracy_val)
time_dif = get_time_dif(start_time)
print("\nTime usage:", time_dif)
save_path = saver.save(sess, "./my_model_final_dropout.ckpt")
with tf.Session() as sess:
X_test_20 = X_test[:20]
# 得到softmax之前的输出
Z = logits.eval(feed_dict={X: X_test_20})
# 得到每⼀⾏最⼤值的索引
y_pred = np.argmax(Z, axis=1)
print("Predicted classes:", y_pred)
print("Actual calsses:  ", y_test[:20])
# 评估在测试集上的正确率
acc_test = accuracy.eval(feed_dict={X: X_test, y: y_test})
print("\nTest_accuracy:", acc_test)
0 Batch accuracy: 0.96 Validation accuracy: 0.9316
5 Batch accuracy: 0.94 Validation accuracy: 0.965
10 Batch accuracy: 1.0 Validation accuracy: 0.9728
15 Batch accuracy: 0.96 Validation accuracy: 0.9728
20 Batch accuracy: 1.0 Validation accuracy: 0.9764
25 Batch accuracy: 0.96 Validation accuracy: 0.9768
30 Batch accuracy: 0.96 Validation accuracy: 0.9768
35 Batch accuracy: 0.98 Validation accuracy: 0.979
39 Batch accuracy: 0.96 Validation accuracy: 0.977
Time usage: 0:01:08
INFO:tensorflow:Restoring parameters from ./my_model_final_dropout.ckpt
Predicted classes: [7 2 1 0 4 1 4 9 6 9 0 6 9 0 1 5 9 7 3 4]
Actual calsses:    [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5 9 7 3 4]
Test_accuracy: 0.9741
⼆、L1和L2正则化
实现了优秀的Dropout正则化,我们再把传统机器学习领域中⽐较通⽤的L1和L2正则化⽅法运⽤到神经⽹络的训练中。
1、如何做L1和L2正则化?
L1和L2正则化⽅法就是把权重的L1范数或L2范数加⼊到经验风险最⼩化的损失函数中(或者把⼆者同时加进去),⽤来约束神经⽹络的权重,让部分权重为0(L1范数的效果)或让权重的值⾮常⼩(L2范数的效果),从⽽让模型变得简单,减少过拟合。得到的损失函数为结构风险最⼩化的损失函数。
公式如下,p∈{1,2},λ是正则化系数,如果模型可能发⽣严重的过拟合问题,那就选择⽐较⼤的λ,⽐如训练⼀个⾮常庞⼤的⽹络,否则可以设置得⼩⼀些。
2、L1正则化和L2正则化的区别?
L1正则化会使得最终的权重参数是稀疏的,也就是说权重矩阵中很多值为0;⽽L2正则化会使得最终的权重值⾮常⼩,但不会等于0。这么说来L1正则化有点像Dropout的另⼀种不常见的⽅式——丢弃连接边。在实际操作中⼀般来说选择L2正则化,因为L1范数在取得最⼩值处是不可导的,这会给后续求梯度带来⿇烦。
⽤TensorFlow来做L1正则化或L2正则化,就要在构建⽹络时传⼊相应的函数:
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
# 这是正则化系数,由于这是⼩型⽹络模型,过拟合的可能⽐较⼩,所以这个系数设置得⼩⼀些。
scale = 0.001
# 构造这个全连接模块,传⼊各层都相同的参数,便于复⽤。
my_dense_layer = partial(
tf.layers.dense, lu,
# 在这⾥传⼊了L2正则化函数,并在函数中传⼊正则化系数。
kernel_ib.layers.l2_regularizer(scale))
with tf.name_scope("dnn"):
hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")
hidden2 = my_dense_layer(hidden1, n_hidden2, name="hidden2")
eval是做什么的
logits = my_dense_layer(hidden2, n_outputs, activation=None,
name="outputs")
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels=y, logits=logits)
# 经验风险损失
base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")
# L2正则化损失
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
# 必须将正规化损失添加到基本损失中,构成结构风险损失。不过没有明⽩为啥把base_loss放在列表⾥。
loss = tf.add_n([base_loss] + reg_losses, name="loss")
完整的代码如下,下⾯的代码和Dropout正则化的代码还是有不少差异,需要注意。
前⾯说了选择L2正则化可能效果更好,为了验证这个说法,我选择batch size = 50,对神经⽹络分别做L1正则化和L2正则化,得到的测试精度分别为94.95%和98.17%,可见L2正则化的确效果更好。
import tensorflow as tf
import numpy as np
from functools import partial
import time
from datetime import timedelta
# 记录训练花费的时间
def get_time_dif(start_time):
end_time = time.time()
time_dif = end_time - start_time
#timedelta是⽤于对间隔进⾏规范化输出,间隔10秒的输出为:00:00:10
return timedelta(seconds=int(round(time_dif)))
# 定义输⼊层、输出层和中间隐藏层的神经元数量
n_inputs = 28 * 28  # MNIST
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10
# 准备训练数据集、验证集和测试集,并⽣成⼩批量样本
(X_train, y_train), (X_test, y_test) = tf.ist.load_data()
X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0
X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0
y_train = y_train.astype(np.int32)
y_test = y_test.astype(np.int32)
X_valid, X_train = X_train[:5000], X_train[5000:]
y_valid, y_train = y_train[:5000], y_train[5000:]
def shuffle_batch(X, y, batch_size):
rnd_idx = np.random.permutation(len(X))
n_batches = len(X) // batch_size
for batch_idx in np.array_split(rnd_idx, n_batches):
X_batch, y_batch = X[batch_idx], y[batch_idx]
yield X_batch, y_batch
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
# 这是正则化系数,由于这是⼩型⽹络模型,过拟合的可能⽐较⼩,所以这个系数设置得⼩⼀些。
scale = 0.001
# 构造这个全连接模块,传⼊各层都相同的参数,便于复⽤。
my_dense_layer = partial(
tf.layers.dense, lu,
# 在这⾥传⼊了L2正则化函数,并在函数中传⼊正则化系数。
kernel_ib.layers.l2_regularizer(scale))
with tf.name_scope("dnn"):
hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")
hidden2 = my_dense_layer(hidden1, n_hidden2, name="hidden2")
logits = my_dense_layer(hidden2, n_outputs, activation=None,
name="outputs")
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
labels=y, logits=logits)
# 经验风险损失
base_loss = tf.reduce_mean(xentropy, name="avg_xentropy")
# L2正则化损失
reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
# 必须将正规化损失添加到基本损失中,构成结构风险损失。不过没有明⽩为啥把base_loss放在列表⾥。    loss = tf.add_n([base_loss] + reg_losses, name="loss")
learning_rate = 0.01
with tf.name_scope("train"):
optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=0.9)
training_op = optimizer.minimize(loss)
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 40
batch_size = 50
with tf.Session() as sess:
init.run()
start_time = time.time()
for epoch in range(n_epochs):
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run(training_op,feed_dict={X: X_batch, y: y_batch})
if epoch % 5 ==0 or epoch == 39:
accuracy_batch = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "Batch accuracy:", accuracy_batch,"Validation accuracy:", accuracy_val)
time_dif = get_time_dif(start_time)
print("\nTime usage:", time_dif)
save_path = saver.save(sess, "./my_model_final_L2.ckpt")

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。