在机器学习中,许多策略被显式地设计为减少测试误差,这些策略统称为正则化。 有些策略向模型添加限制参数的额外约束,有些策略向目标函数增加参数值软约束的额外项。有时候,这些约束和惩罚被设计为编码特定类型的先验知识;其他时候,这些约束和惩罚被设计为偏好简单模型,以便提高泛化能力。在实践中,复杂的模型族不一定包括目标函数或者真实数据的生成过程,深度学习算法通常应用于极为复杂的邻域,如图像和文本,本质上这些邻域的真实生成过程涉及模拟整个宇宙。这意味着控制模型的复杂度不是找到合适规模的模型(参数个数)这样简单,在实际的深度学习场景中我们几乎总是会发现,最好的拟合模型是一个适当正则化的大型模型。
许多正则化方法通过对目标函数 J J J 添加一个参数范数惩罚 Ω ( θ ) \Omega(\boldsymbol{\theta}) Ω(θ) 来限制模型的学习能力,正则化后的目标函数为
J ^ ( θ ; X , y ) = J ( θ ; X , y ) + α Ω ( θ ) \hat{J}(\boldsymbol{\theta};\boldsymbol{X},\boldsymbol{y})=J(\boldsymbol{\theta};\boldsymbol{X},\boldsymbol{y}) + \alpha\Omega(\boldsymbol{\theta}) J^(θ;X,y)=J(θ;X,y)+αΩ(θ)
α \alpha α 越大,对应正则化惩罚越大。
当训练算法最小化正则化后的目标函数 J ^ \hat{J} J^ 时,它会降低原始目标 J J J 关于训练数据的误差并同时减小参数 θ \boldsymbol{\theta} θ 的规模。不同的参数范数 Ω \Omega Ω 会偏好不同的解法。
在介绍不同范数的正则化之前,先需要说明一下,在神经网络中通常只对每一层仿射变换的权重做惩罚而不对偏置做惩罚。每个权重会指定两个变量如何相互作用,而每个偏置仅控制一个单变量,精确拟合偏置所需的数据通常比拟合权重少得多,这意味着我们不对其进行正则化也不会导致太大的方差。另外,正则化偏置参数可能会导致明显的欠拟合。
在《机器学习基础(二)容量、过拟合、欠拟合》中我们已经见过最简单和最常见的参数范数惩罚,即通常被称为权重衰减的 L 2 L^2 L2 参数范数惩罚。这个正则化策略通过向目标函数添加一个正则项 Ω ( θ ) = 1 2 ∥ w ∥ 2 2 \Omega(\boldsymbol{\theta})=\frac{1}{2}\Vert \boldsymbol{w}\Vert^2_2 Ω(θ)=21∥w∥22,使权重更加接近原点,权重值都很小,接近于 0,但不会等于 0。
在单步骤执行的梯度更新操作中,在每步执行通常的梯度更新之前权重向量会先被收缩(乘以一个常量因子)。在训练的整个过程,只有在显著减小目标函数方向上的参数会保留得相对完好,无助于目标函数减小的方向上改变参数不会显著增加梯度。这种不重要的方向对应的分量会在训练过程中因正则化而衰减掉。详细的数学推导可以参考 《Deep Learning》。[^1]
对模型参数 w \boldsymbol{w} w 的 L 1 L^1 L1 正则化被定义为 (假设无偏置):
Ω ( θ ) = ∥ w ∥ 1 = ∑ i ∣ w i ∣ \Omega(\boldsymbol{\theta})=\Vert \boldsymbol{w}\Vert_1=\sum_i|w_i| Ω(θ)=∥w∥1=i∑∣wi∣
即各个参数的绝对值之和。相比 L 2 L^2 L2 正则化, L 1 L^1 L1 正则化会产生更稀疏的解。此处稀疏性指的是最优值中的一些参数为 0。
由 L 1 L^1 L1 正则化导出的稀疏性质已经被广泛地用于特征选择机制。著名的 LASSO (Least Absolute Shrinkage and Selection Operator) 模型将 L 1 L^1 L1 惩罚和线性模型结合,并使用最小二乘代价函数。 L 1 L^1 L1 惩罚使部分子集地权重为零,表明相应的特征可以被安全地忽略。
让机器学习模型泛化得更好的最好方法是使用更多的数据进行训练,但在实践中,我们拥有的数据量总是有限的。解决这个问题的一种方法是创建假数据并添加到训练集中。
数据增强对对象识别这个分类问题特别有效。图像是高维的并包括各种巨大的变化因素,其中有许多可以轻易地模拟。即使模型已经使用卷积和池化技术对部分平移保持不变,沿训练图像每个方向平移几个像素的操作通常可以大大改善泛化。许多其他操作如旋转图像或缩放图像也已被证明非常有效。
在某些情况下,我们要注意不能使用可能会改变类别的转换。例如对手写数字的识别,数字 6 和 9 的旋转可能会造成混淆。
三种在 tensorflow 和 pytorch 实现数据增强的方法:
import cv2
augmented_img = []
augmented_label = []
for i in range(len(img_path)):
img = cv2.imread(str(img_path[i]))
img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))
rows, cols, channels = img.shape
for _ in range(10):
angle = random.randint(0, 180)
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), angle*2, 1)
img_rotated = cv2.warpAffine(img, M, (IMG_SIZE, IMG_SIZE))
augmented_img.append(img_rotated)
augmented_label.append(label[i])
"""
By adjusting number x in for _ in range(x), we can control the final size of the dataset
x * original length
"""
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(rescale = 1./255,
rotation_range=30,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
from torchvision import transforms
train_transforms = transforms.Compose([
transforms.Resize((IMG_SIZE, IMG_SIZE)),
transforms.RandomCrop((IMG_SIZE, IMG_SIZE),padding=~,padding_mode='reflect'),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(degrees=(0, 180)),
transforms.ToTensor(),
transforms.Normalize(mean,std)
])
val_transforms = transforms.Compose([
transforms.Resize((IMG_SIZE, IMG_SIZE)),
transforms.ToTensor(),
transforms.Normalize(mean,std)
])
神经网络的输入层注入噪声也可以被看作是数据增强的一种方式。但神经网络被证明对噪声的鲁棒性不强,改善鲁棒性的方法之一是简单地将随机噪声添加到输入再进行训练。Dropout 就可以看作是通过与噪声相乘构建新输入的过程。
[^1] I. J. Goodfellow, Y. Bengio, and A. Courville, Deep Learning. Cambridge, MA, USA: MIT Press, 2016, http://www.deeplearningbook.org.