0. 前言
正则化 (Regularization) 是一种防止机器学习模型过拟合 (overfitting) 的技术,它通过在模型训练过程中引入额外的束缚或惩罚项来控制模型的复杂度,使得模型在训练数据之外的未见数据上有更好的泛化本领。在本节中,我们将先容改善过拟合问题的最佳实践,探讨正则化技术。
1. 过拟合问题先容
过拟合是指模型在训练数据上表现很好,但在测试数据或新的数据上表现较差,通常是因为模型过于复杂,捕捉到了数据中的噪声和无关特性。为了相识导致过拟合的征象,对比两种环境,在这些环境下比较训练和测试的准确性以及权重的直方图:
- 模型运行 5 个 epoch
- 模型运行 100 个 epoch
两个模型的架构完全相同,不同之处仅在于利用不同的 epoch:
- # 模型 1 运行 5 个 epoch
- model_1 = Sequential()
- model_1.add(Dense(1000, input_dim=num_pixels, activation='relu'))
- model_1.add(Dense(num_classes, activation='softmax'))
- model_1.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
- history = model_1.fit(x_train, y_train,
- validation_data=(x_test, y_test),
- epochs=100,
- batch_size=64,
- verbose=1)
- # 模型 2 运行 5 个 epoch
- model_2 = Sequential()
- model_2.add(Dense(1000, input_dim=num_pixels, activation='relu'))
- model_2.add(Dense(num_classes, activation='softmax'))
- model_2.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
- history = model_2.fit(x_train, y_train,
- validation_data=(x_test, y_test),
- epochs=100,
- batch_size=64,
- verbose=1)
复制代码 两种环境之间的训练数据集和测试数据集之间的准确率比较结果如下:
Epoch数训练集准确率测试集准确率597.65%97.23%100100%98.32% 绘制权重直方图如下,与 5 个 epoch 相比,100 个 epoch 的权重分布更广:
- plt.subplot(211)
- plt.hist(model.get_weights()[0].flatten()
- )
- plt.title('Histogram of weight values in 5 epochs')
- plt.xlabel('Weight values')
- plt.ylabel('Frequency')
- plt.subplot(212)
- plt.hist(model.get_weights()[0].flatten()
- )
- plt.title('Histogram of weight values in 100 epochs')
- plt.xlabel('Weight values')
- plt.ylabel('Frequency')
- plt.show()
复制代码
从前面的图片中,可以看到,与训练 5 个 epoch 的模型相比,训练 100 个 epoch 的模型权重值分散度更高。这是因为与运行 5 个 epoch 时相比,当模型运行 100 个 epoch 时,该模型在训练数据集上过拟合的时机更大,其会针对离群值举行调解。
过拟合的模型更有可能具有更大/更小的权重值(权重值的分布范围更广)去拟合那些异常数据,因此偶然机通过调解权重来解决过拟合。
另一个必要考虑的征象是欠拟合 (underfitting),欠拟合是指模型在训练数据和测试数据上都表现不佳的环境,模型无法准确捕捉输入和输出变量之间的关系,欠拟合表明模型对数据的学习不敷充实,必要通过进步模型复杂性、增长特性或调解训练过程来改善模型的表现。
2. 利用正则化解决过拟合问题
接下来,我们将研究解决过拟合问题的方法。我们已经确定了权重过大是过拟合的缘故原由之一,因此我们起首想到的就是对高权重值举行惩罚。正则化会对模型中的较高的权重较高举行惩罚, L1 和 L2 正则化是最常用的正则化技术之一,其工作方式如下:
- L2 正则化除了最小化损失函数(以下公式中的第一项,这里假设为平方损失的总和)之外,还使神经网络指定层的权重平方的加权总和(以下公式的第二项)最小化:
L = ∑ ( y − y ˉ ) 2 + λ ∑ w i 2 \mathcal L = \sum (y-\bar y)^2 + \lambda \sum w_i^2 L=∑(y−yˉ)2+λ∑wi2
其中, λ \lambda λ 是与正则化项相关联的权重,是一个权衡两项损失重要性的超参数, y y y 是真实值, y ˉ \bar y yˉ 是预测值, w i w_i wi 是模型中的权重值。
- L1 正则化除了最小化损失函数(以下公式中的第一项,这里假设为平方损失的总和)之外,还使神经网络指定层的权重绝对值的加权总和(以下公式的第二项)最小化:
L = ∑ ( y − y ˉ ) 2 + λ ∑ ∣ w i ∣ \mathcal L = \sum (y-\bar y)^2 + \lambda \sum |w_i| L=∑(y−yˉ)2+λ∑∣wi∣
如许,我们就确保模型不会仅针对训练数据集中的非常/异常环境微调权重。模型中利用 L1/L2 正则化在 Tensorflow 中实现如下所示:
- from tensorflow.keras.regularizers import l2
- model = Sequential()
- model.add(Dense(1000, input_dim=num_pixels, activation='relu', kernel_regularizer=l2(0.1)
- ))
- model.add(Dense(num_classes, activation='softmax', kernel_regularizer=l2(0.1)
- ))
- model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
- history = model.fit(x_train, y_train,
- validation_data=(x_test, y_test),
- epochs=100,
- batch_size=64,
- verbose=1)
复制代码 以上代码调用了 Dense 层附加参数 kernel_regularizer,用于利用指定正则化方法,本例中利用 L2 正则化,并指定正则化权重的 λ \lambda λ 值为 0.1:
- kernel_regularizer=l2(0.1)
复制代码 在举行正则化后,训练数据集的准确率并未达到 100%,而测试数据的准确性同样可以达到 98%。将输入层链接到隐藏层的权重提取如下:
- model.get_weights()[0].flatten()
复制代码 提取权重后,将按以下方式绘制权重:
- plt.hist(model.get_weights()[0].flatten()
- )
复制代码 L2 正则化后的权重直方图如下所示:
与之前的环境相比,现在大多数权重都接近于 0,因此避免过拟合问题的出现。在 L1 正则化的环境下,我们也可以看到类似的环境。与未举行正则化时的权重值相比,存在正则化时的权重值要低得多。因此,L1 和 L2 正则化有助于我们避免训练数据集上方的过拟合问题。
除了,L1 正则化和 L2 正则化外,还可以利用 ElasticNet 正则化,ElasticNet 正则化组合了 L1 和 L2 正则化:
L = ∑ ( y − y ˉ ) 2 + λ 1 ∑ i ∣ w i ∣ + λ 2 ∑ i w i 2 \mathcal L = \sum (y-\bar y)^2 + \lambda_1 \sum_{i} |w_i| + \lambda_2 \sum_{i} w_i^2 L=∑(y−yˉ)2+λ1i∑∣wi∣+λ2i∑wi2
如果我们有两个模型 M1 和 M2,损失函数几乎相同,那么我们应该选择最简单的模型,即具有最少非零权重的模型。我们可以利用一个超参数 λ > = 0 λ >= 0 λ>=0 来控制简单模型的重要性:
m i n : l o s s ( T r a i n i n g D a t a ∣ M o d e l ) + λ c o m p l e x i t y ( M o d e l ) min:{loss(Training Data|Model)}+\lambda complexity(Model) min:loss(TrainingData∣Model)+λcomplexity(Model)
3. 批归一化
批归一化是另一种正则化形式,批归一化能够显著加速训练,并且提供了一些正则化结果。在训练过程中,前面层的权重天然会发生变革,因此后面层的输入可能会发生显著变革。换句话说,每一层必须不停调解其权重以顺应每批数据的不同分布,这可能会大大减慢模型的训练速度。关键思想是使每批数据和每个 epoch 的层输入分布更加相似。
另一个问题是,Sigmoid 激活函数在接近零时表现很好,但在阔别零的值时容易“卡住”。如果神经元的输出偶然阔别 Sigmoid 零点,那么该神经元将无法更新自己的权重。
因此,另一个关键思想是将层的输出转换为接近零的高斯分布。如许,不同批数据在层之间的变革会显著减少。批归一化的算法流程如下:
- 对于每个批次的输入数据,计算其均值和方差。可以通过求取批次内样本的均值和方差的无偏估计来计算
- 对于每个特性,将其均值归一化为 0,方差归一化为 1。这可以通过减去均值并除以尺度差来完成
- 引入可学习的参数 γ \gamma γ 和 β \beta β,用于缩放和平移归一化后的数据。这两个参数允许模型自动学习恰当的尺度和偏移,以便更好地拟合数据
批归一化利用以下公式缩放每个批次的输入数据值:
B a t c h m e a n μ B = 1 m ∑ i = 1 m x i B a t c h V a r i a n c e σ 2 B = 1 m ∑ i = 1 m ( x i − μ B ) 2 N o r m a l i z e d i n p u t x ‾ i = ( x i − μ B ) σ B 2 + ϵ B a t c h n o r m a l i z e d i n p u t = γ x ‾ i + β Batch\ mean\ \mu_B=\frac 1 m\sum_{i=1}^mx_i \\ Batch\ Variance\ \sigma_2^B=\frac 1m\sum_{i=1}^m(x_i-\mu_B)^2 \\ Normalized\ input\ \overline x_i=\frac {(x_i-\mu_B)}{\sqrt {\sigma_B^2+\epsilon}}\\ Batch\ normalized\ input=\gamma \overline x_i+\beta Batch mean μB=m1i=1∑mxiBatch Variance σ2B=m1i=1∑m(xi−μB)2Normalized input xi=σB2+ϵ (xi−μB)Batch normalized input=γxi+β
通过每个输入数据减去批数据输入的平均值,然后将其除以批数据方差,可以将一个节点处全部批数据点归一化到一个固定范围,通过引入 γ γ γ 和 β β β 参数,可以让网络识别最佳归一化参数。
批归一化在训练和预测阶段的计算方式是不同的,在训练阶段,批归一化利用当前批次的均值和方差举行归一化;而在预测阶段,利用训练过程中计算得到的整体均值和方差来归一化输入数据。
批归一化是进步训练速度和精确度的非常有效的方法,因为它有助于防止激活值变得过小而消失或过大而爆炸。
在神经网络训练过程中利用批归一化起首必要导入 BatchNormalization 方法,如下所示:
- from tensorflow.keras.layers.normalization import BatchNormalization
复制代码 实例化模型并构建神经网络模型,在隐藏层中执行批归一化:
- model = Sequential()
- model.add(Dense(1000, input_dim=num_pixels, activation='relu'))
- model.add(BatchNormalization())
- model.add(Dense(num_classes, activation='softmax'))
复制代码 可以看到,利用批归一化和利用网络层的方法是一样的,可以将批归一化当作一个网络层添加 (add),必要注意的是,实际上它并非网络层。接下来,编译并训练模型:
- model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
- history = model.fit(x_train, y_train,
- validation_data=(x_test, y_test),
- epochs=50,
- batch_size=64,
- verbose=1)
复制代码 利用批归一化训练比没有批归一化时的训练速度要快得多,下图表现了利用批归一化的训练和测试损失以及准确性:
3. 利用 Dropout 解决过拟合问题
Dropout 是一种在神经网络训练时利用的正则化技术,通过随机“丢弃”神经网络中的一部门神经元(即暂时将其权重设为零)来避免过拟合。Dropout 可以认为是一种模型训练方法,其中每批数据训练时只有一定比例的权重被更新,而其余的权重不被更新。也就是说,在权重更新过程中并非全部权重都得到更新,避免了某些权重达到非常高的值:
- from tensorflow.keras.layers import Dropoutmodel = Sequential()model.add(Dense(1000, input_dim=num_pixels, activation='relu'))model.add(Dropout(0.75))model.add(Dense(num_classes, activation='softmax'))model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
- history = model.fit(x_train, y_train,
- validation_data=(x_test, y_test),
- epochs=50,
- batch_size=64,
- verbose=1)
复制代码 在前面的代码中,我们给出了 0.75 的 Dropout 率;也就是说,在每次权重更新迭代中随机选择 75% 的权重不会更新,或者可以理解为每次训练时网络间的每个连接有 75% 概率断开,从而减少了网络间的连接:
这可以令训练和测试准确率之间的差距低于没有 Dropout 时模型训练和测试准确率的差距。绘制隐藏层的权重直方图:
- plt.hist(model.get_weights()[0].flatten()
- )
复制代码
小结
正则化是防止过拟合的有效手段,它通过对模型的复杂度举行束缚来进步模型的泛化本领。选择合适的正则化方法可以帮助模型在面对未见过的数据时有更好的表现,而不但是仅仅记取训练数据的细节。
系列链接
TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——利用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |