介绍
权重正则化可以减轻深度神经网络模型的过拟合问题,可以提升对新数据的泛化能力。有多种正则方法可供选择,如: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
- 避免过长时间训练模型,设置提前终止。