2

clipboard.png

介绍

权重正则化可以减轻深度神经网络模型的过拟合问题,可以提升对新数据的泛化能力。有多种正则方法可供选择,如:L1L2正则化,每种方法在使用前需要超参数配置。在这篇文章中,你将学习在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_regularizerbias_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))
)

权重正则化使用经验

  1. 最常见的权重正则化是L2正则化,数值通常是0-0.1之间,如:0.1,0.001,0.0001。
  2. 找到最优的系数并不容易,需要尝试不同的权重系数,找到模型表现最平稳优秀的系数
  3. L2正则化在CNN网络中,建议系数设置小一些,如:0.0005
  4. 少量的权重正则对模型很重要,可以减少模型训练误差
  5. 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常用数据集:

clipboard.png

数据集格式:

clipboard.png

matplot结果显示:

clipboard.png

如图所示,该问题是非线性问题,可以使用神经网络来解决。我们只生成了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))

模型输出结果:

clipboard.png

我们看到训练表现远大于测试表现,这是过拟合问题的典型标志。我们将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()

clipboard.png

如图所示,在某一点,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))

clipboard.png

乍一看,好像除了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()

clipboard.png

如图所示,现在就很清楚了,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()

clipboard.png

总结

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

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

醇岩
286 声望1.1k 粉丝

在你说干不成某事之前,先试试


« 上一篇
模型微调
下一篇 »
从Word2Vec到Bert