dataastron 2019-07-01
权重正则化可以减轻深度神经网络模型的过拟合问题,可以提升对新数据的泛化能力。有多种正则方法可供选择,如:L1,L2正则化,每种方法在使用前需要超参数配置。在这篇文章中,你将学习在keras如何使用权重正则化的方法来减轻模型过拟合问题。
读完本篇文章,你将学习到:
## keras中权重正则化方法 ##
keras提供了权重正则化方法,可以在损失函数中通过添加惩罚系数来使用。keras提供了三种正则化方法:
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)) )
代码展示在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)) )
我们将使用标准二元分类问题来定义两个半圆观察:每个类一个半圆。每个观测值都有两个输入变量,它们具有相同的比例,类输出值为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()
降低过拟合问题,我们一般需要从“数据”,“模型结构”,“模型参数”,“模型训练方法”等角度,采用的方法如下: