乐趣区

使用权重正则化较少模型过拟合

介绍
权重正则化可以减轻深度神经网络模型的过拟合问题,可以提升对新数据的泛化能力。有多种正则方法可供选择,如:L1,L2 正则化,每种方法在使用前需要超参数配置。在这篇文章中,你将学习在 keras 如何使用权重正则化的方法来减轻模型过拟合问题。读完本篇文章,你将学习到:

如何在 keras 中使用权重正则化应用到 MLP,CNN, 或者 LSTM 神经网络任务中
一些常见论文在模型中使用权重正则化的方法和经验
通过一个案例学习如何使用权重正则化解决过拟合问题

## keras 中权重正则化方法 ##keras 提供了权重正则化方法,可以在损失函数中通过添加惩罚系数来使用。keras 提供了三种正则化方法:

L1:绝对值权重之和
L2:平方权重之和
L1L2: 两者累加之和

tf.keras.regularizers.l1(l=0.01)
tf.keras.regularizers.l2(l=0.01)
tf.keras.regularizers.l1_l2(l1=0.01,l2=0.01)
keras 中,权重正则化可以应用到任意一层,不过,模型默认不使用任何权重正则化。
全连接层使用权重正则化
全连接层使用 L2 权重正则化:
import tensorflow as tf

model=tf.keras.models.Sequential(

# 权重正则化,bias 正则化(应用较少)
tf.keras.layers.Dense(512,activation=tf.nn.relu,kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001))
)
卷积层使用权重正则化
同全连接层一样,卷积层也使用 kernel_regularizer 和 bias_regularizer 参数添加正则化。代码展示在卷积层中使用 L2 正则化。
import tensorflow as tf

model=tf.keras.models.Sequential(
tf.keras.layers.Conv2D(32,3,activation=tf.nn.relu,kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001))
)
RNN 网络中使用权重正则化
代码展示在 LSTM 网络中使用 L2 权重正则化
import tensorflow as tf

model=tf.keras.models.Sequential(
tf.keras.layers.LSTM(32,activation=tf.nn.tanh,recurrent_regularizer=tf.keras.regularizers.l2(l=0.001),kernel_regularizer=tf.keras.regularizers.l2(l=0.001),bias_regularizer=tf.keras.regularizers.l2(l=0.001))
)
权重正则化使用经验

最常见的权重正则化是 L2 正则化,数值通常是 0 -0.1 之间,如:0.1,0.001,0.0001。
找到最优的系数并不容易,需要尝试不同的权重系数,找到模型表现最平稳优秀的系数
L2 正则化在 CNN 网络中,建议系数设置小一些,如:0.0005
少量的权重正则对模型很重要,可以减少模型训练误差
LSTM 网络中 L2 权重系数通常更小,如:10^-6

权重正则化案例学习
我们将使用标准二元分类问题来定义两个半圆观察:每个类一个半圆。每个观测值都有两个输入变量,它们具有相同的比例,类输出值为 0 或 1. 该数据集称为“月亮”数据集,因为绘制时每个类中的观测值的形状。
# 导入 sklearn 中的数据集
from sklearn.datasets import make_moons
from matplotlib import pyplot
from pandas import DataFrame
# 生成 2 分类数据集
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)

print(X.shape)
print(X[:6])
print(y.shape)
print(y[:6])

df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
colors = {0:’red’, 1:’blue’}
fig, ax = pyplot.subplots()
grouped = df.groupby(‘label’)
for key, group in grouped:
group.plot(ax=ax, kind=’scatter’, x=’x’, y=’y’, label=key, color=colors[key])
pyplot.show()
sklearn 常用数据集:

数据集格式:

matplot 结果显示:

如图所示,该问题是非线性问题,可以使用神经网络来解决。我们只生成了 100 个样本,对神经网络来说数据量很少,这很容易造成过拟合问题。我们使用正则化,添加噪声数据来处理问题。
虽然卷积神经网络(CNN)功能强大,并广泛应用于各种计算机视觉任务中,但由于参数过多而导致过度拟合 [22]。神经网络的最初发展受到人脑机制的启发[18],它不像计算机那样精确。受到差异的启发,我们推断在训练过程中添加噪音可能会指示 CNN 学习更强大的特征表示以抵消噪音的影响,从而降低过度拟合的风险。许多正则化方法通过向训练数据添加噪声来防止过拟合。数据增强的输入图像,如随机裁剪,翻转和阻塞[9,21,30] 已广泛用于提高 CNNs 的泛化能力。Adversarial Training [1]被提出来通过在图像中添加基于梯度的扰动来调整网络。DisturbLabel [26]随机地将样本的一小部分子集的标签改变为不正确的值,从而在损失层上规则化 CNN。
过拟合模型
我们创建一个只有一层隐藏层的 MLP 模型,并让神经元数量大于样本数量,然后过长时间训练模型,以此来人为造成过拟合问题。训练模型之前,我们拆分下数据集,训练数据 30%,验证数据 70%,来训练模型表现。
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
# 拆分数据集
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
模型隐藏层有 500 个神经元,激活函数使用 relu,输出层使用 sigmoid 激活函数,输出一项类别。模型使用 bind_crossentropy 损失函数,adam 优化器。
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])

模型迭代数据集 400 次,batch_size=32。
model.fit(trainX, trainy, epochs=4000, verbose=0)
在测试集上评估模型表现:
# model.evaluate 返回:loss value;metrics value
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))
模型输出结果:

我们看到训练表现远大于测试表现,这是过拟合问题的典型标志。我们将 train 和 test 训练精度过程图形化展示出来。
from sklearn.datasets import make_moons
from keras.layers import Dense
from keras.models import Sequential
from matplotlib import pyplot

X, y = make_moons(n_samples=100, noise=0.2, random_state=1)

n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# 创建模型
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# 返回训练,验证集的损失和准确率
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0)
pyplot.plot(history.history[‘acc’], label=’train’)
pyplot.plot(history.history[‘val_acc’], label=’test’)
pyplot.legend()
pyplot.show()

如图所示,在某一点,train 和 test 的准确率出现分叉口。
使用正则化的模型
我们将在隐藏层中使用权重正则化,并设置系数为 0.001,以此来减轻过拟合问题。
model.add(Dense(500, input_dim=2, activation=’relu’, kernel_regularizer=l2(0.001)))

完整代码如下:
from sklearn.datasets import make_moons
from keras.layers import Dense
from keras.models import Sequential
from keras.regularizers import l2
# 创建数据集
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
# 拆分数据集
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# 创建模型
model = Sequential()
# 设置权重正则化
model.add(Dense(500, input_dim=2, activation=’relu’, kernel_regularizer=l2(0.001)))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# 训练模型
model.fit(trainX, trainy, epochs=4000, verbose=0)
# 评估模型
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Train: %.3f, Test: %.3f’ % (train_acc, test_acc))

乍一看,好像除了 test 准确率降低些,其它也没什么了。让我们画图看下 train 和 test 的训练过程。
from sklearn.datasets import make_moons
from keras.layers import Dense
from keras.models import Sequential
from keras.regularizers import l2
from matplotlib import pyplot

X, y = make_moons(n_samples=100, noise=0.2, random_state=1)

n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’, kernel_regularizer=l2(0.001)))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])

history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0
pyplot.plot(history.history[‘acc’], label=’train’)
pyplot.plot(history.history[‘val_acc’], label=’test’)
pyplot.legend()
pyplot.show()

如图所示,现在就很清楚了,test 与 train 一致。
网格搜索正则化超参数
当确定权重正则化可以改善模型的时候,这时你可以尝试不同的权重系数值。首先对 0.0 到 0.1 之间的一些数量级进行网格搜索,然后再找到一个级别后进行网格搜索,这是一个很好的做法。
# 待测权重正则化值
values = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6]
all_train, all_test = list(), list()
for param in values:

model.add(Dense(500, input_dim=2, activation=’relu’, kernel_regularizer=l2(param)))

all_train.append(train_acc)
all_test.append(test_acc)
我们依然可以图形化展示训练过程:
pyplot.semilogx(values, all_train, label=’train’, marker=’o’)
pyplot.semilogx(values, all_test, label=’test’, marker=’o’)
完整代码如下:
from sklearn.datasets import make_moons
from keras.layers import Dense
from keras.models import Sequential
from keras.regularizers import l2
from matplotlib import pyplot
# 创建数据集
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
# 拆分数据集
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# 待测权重系数值
values = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6]
all_train, all_test = list(), list()
for param in values:
# 创建模型
model = Sequential()
model.add(Dense(500, input_dim=2, activation=’relu’, kernel_regularizer=l2(param)))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# 训练模型
model.fit(trainX, trainy, epochs=4000, verbose=0)
# 评估模型
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print(‘Param: %f, Train: %.3f, Test: %.3f’ % (param, train_acc, test_acc))
all_train.append(train_acc)
all_test.append(test_acc)
# plot train and test means
pyplot.semilogx(values, all_train, label=’train’, marker=’o’)
pyplot.semilogx(values, all_test, label=’test’, marker=’o’)
pyplot.legend()
pyplot.show()

总结
降低过拟合问题,我们一般需要从“数据”,“模型结构”,“模型参数”,“模型训练方法”等角度,采用的方法如下:

数据增强:图像的平移,旋转,裁剪等;利用 GAN 生成新数据;利用机器翻译生成新数据。
降低模型复杂度:减少网络层数,神经元个数;
添加正则化项,如:L1,L2
集成学习:神经网络,dropout
避免过长时间训练模型,设置提前终止。

退出移动版