5. 降低过拟合
在深度学习中,应对过拟合问题,大致有以下几种常用方法:
增大训练集、权重衰减、丢弃法。
其中,增大训练集可能会减轻过拟合,但是获取额外的训练数据往往代价高昂。
本小节依次介绍权重衰减和丢弃法。
5.1 权重衰减
5.1.1 定义
- 权重衰减等价于 L 2 L_2 L2 范数正则化(regularization)。
什么是 L 2 L_2 L2 范数正则化?
L 2 L_2 L2 范数正则化,是降低模型复杂度的方法之一,做法为:在模型原损失函数基础上,添加 L 2 L_2 L2范数惩罚项,从而得到训练所需要最小化的新损失函数。
其中, L 2 L_2 L2范数惩罚项,是模型权重参数每个元素的平方和与一个正的常数的乘积。
以 线性回归 小节中的线性回归损失函数为例:
ℓ ( w 1 , w 2 , b ) = 1 n ∑ i = 1 n 1 2 ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) 2 \ell(w_1, w_2, b) = \frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right)^2 ℓ(w1,w2,b)=n1i=1∑n21(x1(i)w1+x2(i)w2+b−y(i))2
其中, w 1 , w 2 w_1, w_2 w1,w2是权重参数, b b b是偏差参数,样本 i i i的输入为 x 1 ( i ) , x 2 ( i ) x_1^{(i)}, x_2^{(i)} x1(i),x2(i),标签为 y ( i ) y^{(i)} y(i),样本数为 n n n。
将权重参数用向量 w = [ w 1 , w 2 ] \boldsymbol{w} = [w_1, w_2] w=[w1,w2]表示,则带有 L 2 L_2 L2范数惩罚项的新损失函数为:
ℓ ( w 1 , w 2 , b ) + λ 2 n ∣ w ∣ 2 \ell(w_1, w_2, b) + \frac{\lambda}{2n} |\boldsymbol{w}|^2 ℓ(w1,w2,b)+2nλ∣w∣2
其中,超参数 λ > 0 \lambda > 0 λ>0。
当权重参数均为0时,惩罚项最小。
当
λ
\lambda
λ较大时,惩罚项在损失函数中的比重较大,通常会使学到的权重参数的元素较接近0;
当
λ
\lambda
λ设为0时,惩罚项完全不起作用。
上式中, L 2 L_2 L2范数平方 ∣ w ∣ 2 |\boldsymbol{w}|^2 ∣w∣2展开后得到 w 1 2 + w 2 2 w_1^2 + w_2^2 w12+w22。由此,在小批量随机梯度下降中,可将 线性回归 一节中,权重 w 1 w_1 w1和 w 2 w_2 w2的迭代方式更改为:
w 1 ← ( 1 − η λ ∣ B ∣ ) w 1 − η ∣ B ∣ ∑ i ∈ B x 1 ( i ) ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) w_1\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_1 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_1^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) w1←(1−∣B∣ηλ)w1−∣B∣ηi∈B∑x1(i)(x1(i)w1+x2(i)w2+b−y(i))
w 2 ← ( 1 − η λ ∣ B ∣ ) w 2 − η ∣ B ∣ ∑ i ∈ B x 2 ( i ) ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) w_2\leftarrow \left(1- \frac{\eta\lambda}{|\mathcal{B}|} \right)w_2 - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}}x_2^{(i)} \left(x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}\right) w2←(1−∣B∣ηλ)w2−∣B∣ηi∈B∑x2(i)(x1(i)w1+x2(i)w2+b−y(i))
可见,
L
2
L_2
L2范数正则化,令权重
w
1
w_1
w1和
w
2
w_2
w2先自乘小于1的数,再减去不含惩罚项的梯度。
因此,
L
2
L_2
L2范数正则化又叫权重衰减。
权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制,这可能对过拟合有效。
实际场景中,有时也在惩罚项中添加偏差元素的平方和。
5.1.2 代码示例(高维线性回归实验)
以高维线性回归为例,引入一个过拟合问题,并使用权重衰减来应对过拟合。
5.1.2.1 生成数据集
设数据样本特征的维度为 p p p。对于训练集和测试集中特征为 x 1 , x 2 , … , x p x_1, x_2, \ldots, x_p x1,x2,…,xp的任一样本,使用如下的线性函数来生成该样本的标签:
y = 0.05 + ∑ i = 1 p 0.01 x i + ϵ y = 0.05 + \sum_{i = 1}^p 0.01x_i + \epsilon y=0.05+i=1∑p0.01xi+ϵ
其中,噪声项
ϵ
\epsilon
ϵ服从均值为0、标准差为0.01的正态分布。
为了较容易地观察过拟合,考虑高维线性回归问题,如设维度
p
=
200
p=200
p=200;同时,把训练集的样本数设低,如20。
%matplotlib inline
import tensorflow as tf
from tensorflow.keras import backend, Sequential, layers, optimizers, losses
import matplotlib.pyplot as plt
n_train, n_test, num_inputs = 10, 100, 200
true_w, true_b = tf.ones(shape=(num_inputs, 1)) * 0.01, 0.05
features = tf.random.normal(shape=(n_train+n_test, num_inputs))
# tf.keras.backend.dot: 点乘
labels = backend.dot(features, true_w) + true_b
labels += tf.random.normal(shape=labels.shape, mean=0, stddev=0.01)
train_features, test_features = features[:n_train,:], features[n_train:,:]
train_labels, test_labels = labels[:n_train], labels[n_train:]
5.1.2.2 定义、训练和测试模型
def l2_penalty(w):
"""tf.reduce_sum: Computes the sum of elements across dimensions of a tensor."""
return tf.reduce_sum((w**2)) / 2
def use_svg_display():
"""矢量图显示"""
%config InlineBackend.figure_format='svg'
def semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None,
legend=None, figsize=(4, 3)):
"""Make a plot with log scaling on the y axis."""
use_svg_display()
fig,ax = plt.subplots(figsize=figsize)
plt.xlabel(x_label)
plt.ylabel(y_label)
plt.semilogy(x_vals, y_vals)
if x2_vals and y2_vals:
plt.semilogy(x2_vals, y2_vals,':')
plt.legend(legend)
plt.show()
def fit_and_plot_tf2(wd, batch_size, num_inputs=num_inputs, lr=1e-3, num_epochs=100):
net = Sequential()
net.add(layers.Dense(1))
net.build(input_shape=(1, num_inputs))
"""必须调用tf.keras.Sequential.build,否则tf.keras.Sequential.trainable_variables返回空列表"""
w, b = net.trainable_variables
optimizer = optimizers.SGD(learning_rate=lr)
loss = losses.MeanSquaredError()
train_iter = tf.data.Dataset.from_tensor_slices((train_features, train_labels))\
.batch(batch_size=batch_size).shuffle(buffer_size=batch_size)
train_ls, test_ls = [], []
for _ in range(num_epochs):
for X, y in train_iter:
with tf.GradientTape() as tape:
l = loss(y, net(X)) + wd * l2_penalty(w)
grads = tape.gradient(l, net.trainable_variables)
optimizer.apply_gradients(zip(grads, net.trainable_variables))
train_ls.append(tf.reduce_mean(loss(train_labels, net(train_features)).numpy()))
test_ls.append(tf.reduce_mean(loss(test_labels, net(test_features)).numpy()))
use_svg_display()
semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
range(1, num_epochs + 1), test_ls, ['train', 'test'])
print('L2 norm of w:', tf.norm(w).numpy())
上述代码中,因新的损失函数为 ℓ + λ 2 n ∣ w ∣ 2 \ell+ \frac{\lambda}{2n} |\boldsymbol{w}|^2 ℓ+2nλ∣w∣2,相当于 ℓ + λ n ∗ ∣ w ∣ 2 2 \ell+ \frac{\lambda}{n}*\frac{|\boldsymbol{w}|^2}{2} ℓ+nλ∗2∣w∣2,所以有:
l = loss(y, net(X)) + wd * l2_penalty(w)
其中,wd为weight decay的缩写,即 λ n \frac{\lambda}{n} nλ。
5.1.2.3 无权重衰减(过拟合)
fit_and_plot_tf2(wd=0, batch_size=1)
输出:
5.1.2.4 使用权重衰减
fit_and_plot_tf2(wd=10, batch_size=1)
输出:
结论:使用权重衰减,可以在一定程度上缓解过拟合问题。