目录
S7.1更好的优化Fancier optimization
训练神经网络的核心问题是对损失函数的优化问题。优化方法有:随机梯度下降法(Stochastic Gradient Descent,SGD),带动量的SGD,AdaGrad,RMSProp,Adam等等。下面是对这些方法的简单描述。
- SGD:这是一个简单的优化方法。该方法先计算损失函数的值,再计算权重参数的梯度,再更新权重参数的值。不停地重复上述过程,直到损失函数值最小。但存在一些问题。首先:在某些函数上(某一维变化很小,某一维变化很大)运行SGD,会得到特有的之字形图形。因为梯度并不是与最小值成一条线。这样会造成优化速度慢,特别在高维空间尤其普遍。有的损失函数有局部最小点或者鞍点(不是最大值,也不是极小值的临界点),如下图所示,直线代表损失函数。当遇到这个值,SGD得到的梯度为0,训练就会卡在这里。在高维空间,鞍点更多。当接近鞍点时,优化都会非常缓慢,这是个大问题。除此之外,SGD会耗费很大的计算量。在遇到噪声时,SGD会耗费更大的计算量才能得到极小值。
- 带动量的SGD:保持一个不随时间变化的速度,并将梯度估计添加到这个速度上,然后在这个速度的方向上前进。v_w作为梯度的运行均值。rho是摩擦参数,该参数会对速度进行衰减,通常设置为0.9或0.99。该方法解决了SGD存在的问题。即使经过局部最小点或者鞍点,即使梯度为0,也会有速度,使之跨过这些点。
- AdaGrad:在优化时保持一个在训练过程中的每一步的梯度的平方和的持续估计。如果有两个坐标轴,其中一个轴有很高的梯度,另一个轴有很小的梯度。随着累加小梯度的平方,会在最后更新参数向量时除以一个很小的数字,从而加速了在小梯度维度上的学习速度。在另一个维度方向上,由于梯度变得特别大,会除以一个非常大的数,所以会降低在这个维度方向上的训练进度。但存在一个问题,当训练时间越来越长时,由于grad_squared值一直在随时间单调递增,步长会变得越来越小。当学习目标是一个凸函数的情况下,该特性很好,因为到达极小点会逐渐慢下来最后到达收敛。但当不是凸函数时,训练过程会被困在局部极小点。
- RMSProp:AdaGrad的改进方法。仍然计算梯度的平方,但在训练时,会让平方梯度按照一定比率下降。decay_rate通常是0.9或者0.99。但该方法可能会造成训练总是一直在变慢,这并不是我们想要的。
- Adam:首先构造第一动量和第二动量。第一动量等于梯度的加权和,第二动量是梯度平方的动态近似值。然后构造第一动量和第二动量的无偏估计。最后使用无偏估计做每一步更新。Adam是一个很好的算法。并且对于不同的问题使用该算法都能得到非常不错的结果。因此,基本上先考虑使用该方法。beta1设置为0.9,beta2设置为0.999,学习率为1e-3或者5e-4。
上述部分优化算法的代码如下:
''' SGD method '''
while True:
weights_grad = compute_gradient(loss_fun, data, weights)
weights -= learning_rate * weigthts_grad # perform parameter update
''' SGD with Momentum method '''
v_w = 0
while True:
weights_grad = compute_gradient(loss_fun, data, weights)
v_w = rho * v_w + weights_grads
weights -= learning_rate * v_w
''' AdaGrad method '''
grad_squared = 0
while True:
weights_grad = compute_gradient(loss_fun, data, weights)
grad_squared += weights_grad * weights_grad
weights -= learning_rate * weights_grad / (np.sqrt(grad_squared) + 1e-7)
''' RMSProp method '''
grad_squared = 0
while True:
weights_grad = compute_gradient(loss_fun, data, weights)
grad_squared = decay_rate * grad_squared + (1 - decay_rate) * weights_grad * weights_grad
weights -= learning_rate * weights_grad / (np.sqrt(grad_squared) + 1e-7)
''' Adam method '''
first_moment = 0
second_moment = 0
for t in range(num_iterations):
weights_grad = compute_gradient(loss_fun, data, weights)
first_moment = beta1 * first_moment + (1 - beta1) * weights_grad # moment
second_moment = beta2 * second_moment + (1 - beta2) * weights_grad * weights_grad # moment
first_unbias = first_moment / (1 - beta1 ** t) # bias correction
second_unbias = second_moment / (1 - beta2 ** t) # bias correction
weights -= learning_rate * first_unbias / (np.sqrt(second_unbias) + 1e-7) # AdaGrad/RMSProp
上述优化方法都需要学习learning_rate这个超参数。不必在训练中一直固定使用同一个值。有时候人们会把学习率沿着时间衰减,在训练开始使用较大的一些学习率,然后在训练过程中逐渐衰减地越来越小。一个衰减的策略是步长衰减。需要注意的是,在开始之前,还是要挑选一个不带衰减的合适的学习率。在交叉验证中调整学习率衰减。学习率衰减在带动量SGD很常见,但在Adam的优化算法就很少用。在实际中,选择Adam方法作为优化方法。
优化方法力图降低损失函数的值,使得模型在训练集上的效果好,但更应该关注的是验证集/测试集上的效果。往往会出现训练集效果好而验证集效果一般甚至不好,或者是训练集的loss/acc与验证集的loss/acc之间有很大的差距。这是过拟合现象,如下图所示。出现这种情况是因为模型在训练时过度拟合了训练集的特征,从而造成模型的泛化性能差。过拟合常常是因为数据较小。在小的数据集上很容易过拟合。为了解决过拟合问题,提高在未训练数据上的性能,可以使用这三种方法:集成模型,正则化和迁移学习。需要注意的是,我们不是为了缩小训练集和测试集结果之间的差距,而是关注在测试集上的性能,提高测试集上的性能。
S7.2集成模型
该方法首先训练多个独立的模型,然后在测试时对模型结果求平均。一般能够提高2%的准确率。这个提升不是很大,但却是固定的提升。在使用集成方法时,有如下几个技巧:
- 选择从不同的随机初始值上训练多个不同的模型。这种集成技术是很常见的。
- 有时候可以不用独立地训练不同的模型,可以在训练时保留多个模型的快照,然后用这些快照做集成。该方式可以在一次训练中集成多个模型。
- 不使用实际的参数向量,而是保持参数向量的移动平均值,并在测试时使用它(polyak averaging)
S7.3正则化(Regularization)
常见正则化模式如下表所示,y为模型,W为模型,x是模型输入,z是随机数据。给模型添加随机性是防止模型过度拟合训练数据。在一定程度上扰乱它,防止它拟合数据。在测试时,要抵消所有随机性,提高模型泛化能力。
阶段 | 操作 | 函数 |
---|---|---|
训练 | 给网络添加一些随机性 | ![]() |
测试 | 将随机性进行平均(有时近似) | ![]() |
符合该模式的方法有:
- 随机失活(Dropout):每次在网络正向传播时,以p的概率,随机地将每层的部分神经元的输出置为0。每次正向传递随机置0的神经元都不完全相同。该方法在网络中常用,一般在全连接层中使用,有时候也在卷积层使用。但在卷积层,是将某些通道的所有激活映射图置0。被置零的项即为z。在训练时,梯度只在未被丢弃的节点上传递。在测试阶段需要消除这种随机性。有人认为dropout避免了特征之间的相互适应;也有人认为dropout是在进行集成学习。下面是使用Dropout的3层网络的实现代码:
-
''' Dropout implementation ''' p=0.5 # probability of keeping a unit active.higher = less dropout ''' Vanilla Dropout: Not recommended implementation ''' def train_step(X): ''' X constains the data ''' # forward pass for example 3-layer neural network H1 = np.maximum(0, np.dot([W1, X) + b1) U1 = np.random.rand(*H1.shape) < p # first dropout mask H1 *= U1 # drop! H2 = np.maximum(0, np.dot([W2, H1) + b2) U2 = np.random.rand(*H2.shape) < p # second dropout mask H2 *= U2 # drop! out = np.dot(W3, H2)+b3 # backward pass: compute gradients...(not shown) # perform parameter update...(not shown) def predict(X): #ensembled forward pass H1 = np.maximum(0, np.dot([W1, X) + b1) * p # NOTE: scale the activations H2 = np.maximum(0, np.dot([W2, H1) + b2) * p # NOTE: scale the activations out = np.dot(W3, H2)+b3 ''' Inverted dropout: more common ''' def train_step(X): ''' X constains the data ''' # forward pass for example 3-layer neural network H1 = np.maximum(0, np.dot([W1, X) + b1) U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p! H1 *= U1 # drop! H2 = np.maximum(0, np.dot([W2, H1) + b2) U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p! H2 *= U2 # drop! out = np.dot(W3, H2)+b3 # backward pass: compute gradients...(not shown) # perform parameter update...(not shown) def predict(X): #ensembled forward pass H1 = np.maximum(0, np.dot([W1, X) + b1) # no scaling necessary H2 = np.maximum(0, np.dot([W2, H1) + b2) out = np.dot(W3, H2)+b3
- 批归一化(Batch Normalization,BN):BN在S6已经讲过。它在训练阶段从随机批数据中获得统计值进行归一化,测试阶段使用固定的统计值来进行归一化。BN在网络中基本上是单独使用。相比之下,Dropout能够使用p调整正则化的力度,但在BN中并没有这种控制机制。
- 数据增强(Data Augmentation,DA):在训练中以某种方式对输入图像进行随机地转换。使用转换的图像进行训练。增强方式有:水平翻转Horizontal Flips,随机裁剪Random crops,scales,色彩抖动Color Jitter(不太常见)。下图给出在训练和测试时数据增强的具体操作。
-
- DropConnect:在训练时添加随机噪声,在测试时边缘化噪声。该方法在正向传播时,随机将权重矩阵的一些值置零。Dropout和DropConnect方法效果如下图所示:
-
- 部分最大池化Fractional Max Pooling:对区域进行随机池化。该方法并不常用,但视频老师会使用该方法,认为该想法很好。
- 随机深度Stochastic Depth:有一个很深的网络,在训练时,随机丢弃部分层。测试时,使用全部的网络。该方法并不常用,但想法不错。
在进行正则化时,一般选择单独使用BN方法。当仅使用BN不够用时,就再添加了Dropout等方法,但不要盲目地去使用这些方法。当网络过拟合时,再把它们加进去。
S7.4迁移学习(Transer Learning)
集成模型和正则化能够帮助减小训练误差和测试误差的间隙。然而有时候过拟合是由于数据不够。使用小数据集时,大的和功能强的网络更容易过拟合。此时,可以使用迁移学习方法来进行模型的训练。迁移学习不需要大的数据集就能够训练卷积网络。迁移学习方法首先找到预训练好的网络模型,然后冻结模型的前几个层的权重,最后微调模型最后一层或者最后几层的权重(一般这些层是全连接层)。该方法是为了获得预训练模型的学习特征能力,因此才冻结前几个层的权重。如下图所示。
对于所拥有数据集的大小,以及任务的相似度,迁移学习的训练方式可分为以下四种:
深度学习软件包有提供一个模型库,可以从中下载不同模型的预训练版本。以下是相关网址: