Lesson 2 Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization

这篇文章其实是 Coursera 上吴恩达老师的深度学习专业课程的第二门课程的课程笔记。

参考了其他人的笔记继续归纳的。

训练,验证,测试集 (Train / Dev / Test sets)

在机器学习发展的小数据量时代,常见做法是将所有数据三七分,就是人们常说的 70% 训练集,30% 测试集。如果明确设置了验证集,也可以按照 60% 训练集,20% 验证集和 20% 测试集来划分。这是前几年机器学习领域普遍认可的最好的实践方法。

如果只有 100 条,1000 条或者 1 万条数据,那么上述比例划分是非常合理的。

但是在大数据时代,我们现在的数据量可能是百万级别,那么验证集和测试集占数据总量的比例会趋向于变得更小。因为验证集的目的就是验证不同的算法,检验哪种算法更有效,因此,验证集只要足够大到能评估不同的算法,比如 2 个甚至 10 个不同算法,并迅速判断出哪种算法更有效。我们可能不需要拿出 20% 的数据作为验证集。

比如我们有 100 万条数据,那么取 1 万条数据便足以进行评估,找出其中表现最好的 1-2 种算法。同样地,根据最终选择的分类器,测试集的主要目的是正确评估分类器的性能,所以,如果拥有百万数据,我们只需要 1000 条数据,便足以评估单个分类器,并且准确评估该分类器的性能。假设我们有 100 万条数据,其中 1 万条作为验证集,1 万条作为测试集,100 万里取 1 万,比例是 1%,即:训练集占 98%,验证集和测试集各占 1%。对于数据量过百万的应用,训练集可以占到 99.5%,验证和测试集各占 0.25%,或者验证集占 0.4%,测试集占 0.1%。

因为我们要用验证集来评估不同的模型,尽可能地优化性能,所以我们尽量要使验证集和测试集来自同一分布。

偏差,方差 (Bias/Variance)

假设这就是数据集,如果给这个数据集拟合一条直线,可能得到一个逻辑回归拟合,但它并不能很好地拟合该数据,这是高偏差(high bias)的情况,我们称为“欠拟合”(underfitting)。

相反的如果我们拟合一个非常复杂的分类器,比如深度神经网络或含有隐藏单元的神经网络,可能就非常适用于这个数据集,但是这看起来也不是一种很好的拟合方式分类器方差较高(high variance),数据过度拟合(overfitting)。

在两者之间,可能还有一些像图中这样的,复杂程度适中,数据拟合适度的分类器,这个数据拟合看起来更加合理,我们称之为“适度拟合”(just right)是介于过度拟合和欠拟合中间的一类。

在这样一个只有 \(x_1\) 和 \(x_2\) 两个特征的二维数据集中,我们可以绘制数据,将偏差和方差可视化。在多维空间数据中,绘制数据和可视化分割边界无法实现,但我们可以通过几个指标,来研究偏差和方差。

以下就是几种情况,知道模型有什么样的表现,我们就能对应的采取什么样的策略去调试。

吴恩达老师在训练神经网络时用到的基本方法如下。

初始模型训练完成后,首先得知道算法的偏差高不高,如果偏差较高,那么试着评估训练集或训练数据的性能。如果偏差的确很高,甚至无法拟合训练集,那么我们要做的就是选择一个新的网络,比如说含有更多隐藏层或者隐藏单元的网络,或者花费更多时间来训练网络,或者尝试更先进的优化算法。

在训练学习算法时,我们需要不断尝试这些方法,直到解决偏差问题,这是最低标准。直到可以拟合数据为止,至少能够拟合训练集。

旦偏差降低到可以接受的数值,检查一下方差有没有问题,为了评估方差,我们要查看验证集性能,我们能从一个性能理想的训练集推断出验证集的性能是否也理想,如果方差高,最好的解决办法就是采用更多数据,但有时候,我们无法获得更多数据,所以可以尝试通过正则化来减少过拟合。系统地说出做法很难,总之就是不断重复尝试,直到找到一个低偏差、低方差的框架。

正则化 (Regularization)

正则化是解决深度学习存在高方差问题的方法。

\(L2\) 正则化

L2 正则化的作用原理

以逻辑回归为例。,求代价函数 \(J\) 的最小值。在逻辑回归函数中加入正则化,只需添加参数 \(\lambda\),也就是正则化参数。

\(\frac{\lambda}{2m}\) 乘以参数 \(w\) 范数的平方,即 \(\left\| w \right\|_2^2\) 是 \(w\) 的欧几里得范数的平方,等于 \(w_j\)(\(j\) 值从 1 到 \(n_x\))平方的和,也可表示为 \(w^Tw\),也就是向量参数 \(w\) 的欧几里得范数(2 范数)的平方。这个方法称为 \(L2\) 正则化,因为这里用了欧几里得范数,被称为向量参数 \(w\) 的 \(L2\) 范数。

\[J(w,b)=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{2m}\left\|w\right\|^2_2
\]

至于为什么只正则化参数 \(w\),而不再加上参数 \(b\) 呢?可以,但没必要。因为 \(w\) 通常是一个高纬度参数矢量,已经可以表达高偏差问题,\(w\) 可能包含有很多参数,而我们不可能拟合所有参数,\(b\) 只是单个数字,所以 \(w\) 几乎涵盖所有参数。如果加了参数 \(b\),其实也没太大影响。

\(L2\) 正则化是最常见的正则化类型,其实也有 \(L1\) 正则化。它加的不是 \(L2\) 范数,而是正则项 \(\frac{\lambda}{m}\) 乘以 \(\sum^{n_x}_{j=1}\left|w\right|\)。其中,\(\sum^{n_x}_{j=1}\left|w\right|\) 也被称为参数 \(w\) 向量的 \(L1\) 范数,无论分母是 \(m\) 还是 \(2m\),它都是一个比例常量。

\[J(w,b)=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{m}\sum^{n_x}_{j=1}\left|w\right|
\]

如果用的是 \(L1\) 正则化,\(w\) 最终会是稀疏的,也就是说 \(w\) 向量中有很多 0。有人说这样有利于压缩模型,因为集合中的参数均为 0,存储模型所占用的内存更少。实际上,虽然 \(L1\) 正则化使模型变得稀疏,却没有降低太多存储内存,所以吴恩达老师认为这并不是 \(L1\) 正则化的目的,至少不是为了压缩模型。所以人们在训练网络时,越来越倾向于使用 \(L2\) 正则化。

在神经网络中实现 \(L2\) 正则化

神经网络含有一个代价函数,该函数包含 \(W^{[1]},b^{[1]}\) 到 \(W^{[L]},b^{[L]}\) 所有参数,其中 \(l\) 表示的是神经网络的层数,\(L\) 为神经网络的总层数。那么,代价函数等于 \(m\) 个训练样本损失函数的均值,正则项为 \(\frac{\lambda}{2m}\sum^L_{l=1}{\left\|W^{[l]}\right\|^2}\)。这个矩阵范数 \(\left\|W^{[l]}\right\|^2\)(即平方范数),被定义为矩阵中所有元素的平方求和,我们称之为“弗罗贝尼乌斯范数 (Frobenius norm)”。

\[J(W^{[1]},b^{[1]},\dots,W^{[L]},b^{[L]})=\frac{1}{m} \sum^m_{i=1}{L(\hat{y}^{(i)},y^{(i)})}+\frac{\lambda}{2m}\sum^L_{l=1}\left\|W^{[l]}\right\|^2_F\\
\left\|W^{[l]}\right\|^2_F=\sum^{n^{[l-1]}}_{i=1}\sum^{n^{[l]}}_{j=1}(W^{[l]}_{ij})^2
\]

使用弗罗贝尼乌斯范数实现梯度下降

在没有使用弗罗贝尼乌斯范数时,我们是用反向传播计算出 \(dW\) 的值,然后使用它来更新参数 \(W\)。

\[dW^{[l]}=(from \ backprop)\\
W^{[l]}:=W^{[l]}-\alpha dW^{[l]}
\]

既然已经增加了正则项,那么我们需要给 \(dW\) 加上一项 \(\frac{\lambda}{m}W^{[l]}\)。然后更新参数。

\[dW^{[l]}=(from \ backprop)+\frac{\lambda}{m}W^{[l]}\\
W^{[l]}:=W^{[l]}-\alpha dW^{[l]}
\]

我们对上面的公式进行一下数学变换。

\[W^{[l]}:=W^{[l]}-\alpha ((from \ backprop)+\frac{\lambda}{m}W^{[l]})\\
=W^{[l]}-\frac{\alpha \lambda}{m}W^{[l]}-\alpha(from \ backprop)\\
=(1-\frac{\alpha \lambda}{m})W^{[l]}-\alpha(from \ backprop)
\]

我们发现 \((1-\frac{\alpha \lambda}{m})\) 这个系数时小于 1 的,所以不论 \(W^{[l]}\) 是什么,我们都试图让它变得更小。所以,\(L2\) 范数正则化也被称为“权重衰减”。

为什么正则化有利于预防过拟合?

直观理解就是正则化参数 \(\lambda\) 增加到足够大,参数 \(W\) 会接近于 0(实际上是不会发生这种情况的)。我们尝试消除或至少减少许多隐藏单元的影响,最终这个网络会变得更简单,这个神经网络越来越接近逻辑回归,也就是从过拟合状态不断接近高偏差状态。但是,\(\lambda\) 会存在一个中间值,于是会有一个接近“Just Right”的中间状态。

而从图形上来看的话,假设我们用的是 tanh 双曲线激活函数。用 \(g(z)\) 表示 \(tanh(z)\)。如果正则化参数 \(\lambda\) 很大,参数 \(W\) 会相对较小,而由于 \(Z^{[l]}=W^{[l]}a^{[l-1]}+b^{[l]}\),所以 \(z\) 也会很小。特别地,如果 \(z\) 的值最终在红色区域这个范围内,都是相对较小的值,\(g(z)\) 大致呈线性。而我们之前说过,如果每层都是线性的,那么整个网络就是一个线性网络,即使是一个非常深的深层网络,因具有线性激活函数的特征,最终我们只能计算线性函数,因此,它不适用于非常复杂的决策,以及过度拟合数据集的非线性决策边界。

Dropout 正则化

除了 \(L2\) 正则化,还有一个非常实用的正则化方法——“Dropout(随机失活)”。

Dropout 正则化的作用原理

假设我们在训练下图这样的神经网络,它存在过拟合。

我们复制这个神经网络,dropout 会遍历网络的每一层,并设置消除神经网络中节点的概率(假设为 0.5)。于是一些节点会被消除,最后我们得到一个节点更少,规模更小的网络,然后用反向传播方法进行训练。

在神经网络中实现 Dropout 正则化

实现 dropout 的最常用的方法是 inverted dropout(反向随机失活)

我们用神经网络的第三层来举例说明。

首先要定义向量 \(d\),\(d^{[3]}\) 表示网络第三层 的 dropout 向量:

d3 = np.random.rand(a3.shape[0],a3.shape[1])

然后看它是否小于某数,我们称之为 keep-prob,它是一个具体的数字。在上面的图里面,它被设为 0.5,现在我们把它设为 0.8。它表示保留某个隐藏单元的概率,即消除任意一个隐藏单元的概率是 0.2。

接下来要做的就是从第三层中获取激活函数,这里我们叫它 \(a^{[3]}\),\(a^{[3]}\) 含有要计算的激活函数。这个 \(a^{[3]}\) 就等于没有 dropout 的 \(a^{[3]}\) 乘以 \(d^{[3]}\),a3 = np.multiply(a3,d3),这里是元素相乘。它的作用就是让 \(d^{[3]}\) 中 0 元素与 \(a^{[3]}\) 中相对元素归零。

顺便一提,如果用 python 实现这个算法的话,我们的 \(d^{[3]}\) 其实是一个布尔型数组,值为 true 和 false,而不是 1 和 0。

由于 \(z^{[4]}=w^{[4]}a^{[3]}+b^{[4]}\),而 \(a^{[3]}\) 被随机减少了 20%。为了不影响 \(z^{[4]}\) 的期望值,我们需要用 \(\frac{w^{[4]}a^{[3]}}{0.8}\) 来修正被随机减少的那 20%。我们在测试阶段是不使用 dropout 函数的,它会使我们的输出结果随机。

当然,我们可以在网络的不同层使用不同的 keep-prob 值进行不同程度的 dropout。如下图所示。注意 keep-prob 的值为 1 表示的是保留所有单元,不在这一层使用 dropout。

dropout 一大缺点就是代价函数 \(J\) 不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。定义明确的代价函数 \(J\) 每次迭代后都会下降,因为我们所优化的代价函数 \(J\) 实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制迭代曲线。

吴恩达老师的做法是,关闭 dropout 函数,将 keep-prob 设为 1,运行代码,确保代价函数单调递减。然后再打开 dropout 函数。

理解 Dropout

Dropout 随机删除网络中的神经单元,看起来很奇怪的操作却能实现正则化。

直观上理解,dropout 使得网络不依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout 将产生收缩权重的平方范数的效果。

"Can't rely on any one feature, so have to spread out weights."

其他正则化方法

数据扩增 (Data augmentation)

我们可以通过扩增训练数据来解决过拟合,但扩增数据代价高,而且有时候是无法扩增数据的。那么可以通过水平翻转图片、随意裁剪图片等方法来扩增数据,这虽然不如额外收集一组新图片那么好,但这样节省了很高的数据收集成本。

early stopping

运行梯度下降时,我们可以绘制训练误差,或只绘制代价函数 \(J\) 的优化过程,在训练集上用 0-1 记录分类误差次数。

因为在训练过程中,我们希望训练误差,代价函数 \(J\) 都在下降,通过 early stopping,我们不但可以绘制上面这些内容,还可以绘制验证集误差,它可以是验证集上的分类误差,或验证集上的代价函数,逻辑损失和对数损失等。我们发现,验证集误差通常会先呈下降趋势,然后在某个节点处开始上升。而 early stopping 的作用是,在神经网络已经在迭代过程中表现得很好的时候停止训练。

当我们还未在神经网络上运行太多迭代过程的时候,参数 \(w\) 接近 0,因为随机初始化 \(w\) 值时,它的值可能都是较小的随机值,所以在长期训练神经网络之前 \(w\) 依然很小,在迭代过程和训练过程中 \(w\) 的值会变得越来越大,所以 early stopping 要做就是在中间点停止迭代过程,我们得到一个 \(w\) 值中等大小的弗罗贝尼乌斯范数,与\(L2\)正则化相似,选择参数 \(w\) 范数较小的神经网络。

early stopping 的主要缺点就是我们不能独立地处理这两个问题,因为提早停止梯度下降,也就是停止了优化代价函数 \(J\),因为现在不再尝试降低代价函数 \(J\),所以代价函数 \(J\) 的值可能不够小,同时我们又希望不出现过拟合,没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我们要考虑的东西变得更复杂。

early stopping 的优点是,只运行一次梯度下降,就可以找出 \(w\) 的较小值,中间值和较大值,而无需尝试 \(L2\) 正则化超参数 \(\lambda\) 的很多值。

归一化输入 (Normalizing inputs)

训练神经网络,其中一个加速训练的方法就是归一化输入。

假设一个训练集有两个特征,输入特征为 2 维,归一化需要两个步骤:(1)零均值化;(2)归一化方差。

(1)零均值化

\(\mu = \frac{1}{m}\sum_{i =1}^{m}x^{(i)}\),它是一个向量,\(x\) 等于每个训练数据 \(x\) 减去 \(\mu\),意思是移动训练集,直到它完成零均值化。

(2)归一化方差

注意特征 \(x_{1}\) 的方差比特征 \(x_{2}\) 的方差要大得多,我们要做的是给 \(\sigma\) 赋值,\(\sigma^{2}= \frac{1}{m}\sum_{i =1}^{m}{({x^{(i)})}^{2}}\),这是节点 \(y\) 的平方,\(\sigma^{2}\) 是一个向量,它的每个特征都有方差,注意,我们已经完成零值均化,\(({x^{(i)})}^{2}\) 元素 \(y^{2}\) 就是方差,我们把所有数据除以向量 \(\sigma^{2}\),最后变成上图形式。

如果你用它来调整训练数据,那么用相同的 \(μ\) 和 \(\sigma^{2}\) 来归一化测试集。

如果我们使用非归一化的输入特征,代价函数会非常细长狭窄,这样我们必须使用一个非常小的学习率,进行多次迭代,来找到最小值。而归一化特征之后,代价函数会更对称,是一个更圆的球形轮廓。那么不论从哪个位置开始,可以使用较大步长,梯度下降法都能够更直接地找到最小值。

所以如果输入特征处于不同范围内,可能有些特征值从 0 到 1,有些从 1 到 1000,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。

梯度消失/梯度爆炸 (Vanishing / Exploding gradients)

训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。

对于下图这个很深的神经网络(为了简便,我们只画出两个隐藏单元,实际可以有很多)。

为了简单起见,假设我们使用激活函数 \(g(z)=z\),也就是线性激活函数,我们忽略 \(b\),假设 \(b^{[l]}\)=0,如果那样的话,输出 \(y=W^{[L]}W^{[L -1]}W^{[L - 2]}\ldots W^{[3]}W^{[2]}W^{[1]}x\)。其实我们得到的输出结果是 \(\hat{y}\) 而不是 \(y\)。

接着,假设每个权重矩阵 \(W^{[l]}=\begin{bmatrix} 1.5 & 0 \\0 & 1.5 \\\end{bmatrix}\),最后一项有不同的维度,也就是 \(y= W^{[L]}\begin{bmatrix} 1.5 & 0 \\ 0 & 1.5 \\\end{bmatrix}^{(L -1)}x\)。但是我们假设所有的矩阵都等于 1.5 倍的单位矩阵,那么最后的计算结果 \(\hat{y}=1.5^Lx\)。所以,对于一个深度神经网络来说 \(L\) 值较大,那么 \(\hat{y}\) 的值也会非常大,实际上它是呈指数级增长的。

而如果权重是 0.5 倍的单位矩阵,即 \(W^{[l]} = \begin{bmatrix} 0.5& 0 \\ 0 & 0.5 \\ \end{bmatrix}\),那么 \(\hat{y}=0.5^Lx\)。激活函数的值将以指数级下降。

权重初始化

先以单个神经元的情况举例。

单个神经元可能有 4 个输入特征,从 \(x_1\) 到 \(x_4\),经过 \(a=g(z)\) 处理,最终得到 \(\hat{y}\)。这些输入表示为 \(a^{[l]}\),暂时我们用 \(x\) 表示。

我们把 \(b\) 设为 0,那么 \(z=w_1x_1+w_2x_2+\dots+w_nx_n\)。可以发现,\(n\) 越大,即输入特征数越多,\(z\) 的值就越大。为了预防 \(z\) 值过大或过小,我们希望每项值更小,合理的方法是设置 \(w_i=\frac{1}{n}\)。

实际上,我们设置每层权重矩阵如下

\[w^{[l]}=np.random.randn(shape)*np.sqrt(\frac{1}{n^{[l-1]}})
\]

如果用的是 Relu 激活函数,方差设置为 \(\frac{2}{n}\),效果会更好。

如果激活函数的输入特征被零均值和标准方差化,方差是 1,\(z\) 也会调整到相似范围,这就没有解决梯度消失和梯度爆炸问题。但它却是降低了这些问题,因为它给权重矩阵 \(w\) 设置了合理值,它不会比 1 大很多,也不会比 1 小很多,所以梯度没有爆炸或消失过快。

一篇由 Herd 等人撰写的论文曾介绍过。对于几个其它变体函数,如 tanh 激活函数,有篇论文提到,常量 1 比常量 2 的效率更高,对于 tanh 函数来说,它是\(\sqrt{\frac{1}{n^{[l-1]}}}\),它适用于 tanh 激活函数,被称为Xavier初始化。Yoshua Bengio 和他的同事还提出另一种方法,他们使用的是公式\(\sqrt{\frac{2}{n^{[l-1]} + n^{\left[l\right]}}}\)。

优化算法 (Optimization algorithms)

机器学习的应用是一个高度依赖经验的过程,伴随着大量的迭代的过程,我们需要训练诸多模型。而优化算法能够帮助我们快速训练模型。

Mini-batch 梯度下降 (Mini-batch gradient descent)

我们之前使用向量化能够让我们有效地对所有 \(m\) 个样本进行计算,只需把所有的训练样本放大巨大的矩阵 \(X\) 中去,\(X=[x^{(1)} x^{(2)} \dots x^{(m)}]\)。同理,\(Y\) 也是这样。但是如果 \(m\) 很大的话,处理速度仍然会很慢。我们必须处理整个训练集,才能迭代一次梯度下降。

那么我们可以把训练集分割为小一点的子集训练,这些子集被取名为 mini-batch。假设每个子集中只有 1000 个样本,那么把其中的 \(x^{(1)}\) 到 \(x^{(1000)}\) 取出来,将其称为第一个子训练集。然后接着取 \(x^{(1001)}\) 到 \(x^{(2000)}\),以此类推。我们把它们记为 \(X^{\{1\}}, X^{\{2\}},\dots\)。如果训练样本一共有 500 万个,每个 mini-batch 都有 1000 个样本,那么我们就有 5000 个 mini-batch,最后得到的是 \(X^{\{5000\}}\)。同样,\(Y\) 也进行这样的处理。从 \(Y^{\{1\}}\) 到 \(Y^{\{5000\}}\)。

Mini-batch 梯度下降与我们之前用的梯度下降算法不一样的是,我们每次处理的只是单个 mini-batch \(X^{\{t\}}\) 和 \(Y^{\{t\}}\),而不是同时处理全部的 \(X\) 和 \(Y\)。伪代码如下图。

需要注意的是,上图的代码只是进行了“一代 (1 epoch)”的训练,也就是只是一次遍历了训练集。

理解 mini-batch 梯度下降法

使用 batch 梯度下降法,每次迭代都需要遍历整个训练集,可以预期每次迭代成本都会下降,所以如果代价函数 \(J\) 在某次迭代中增加了,那肯定出 bug 了,可能是由于学习率太大了。

而使用 mini-batch 梯度下降法,我们会发现代价函数 \(J\) 并不是每次迭代都是下降的。那是因为代价函数 \(J^{\{t\}}\) 只和 \(X^{\{t\}},Y^{\{t\}}\) 有关,也就是说每次迭代我们都在训练不同的样本集(不同的 mini-batch)。所以 \(J^{\{t\}}\) 的图像是总体趋势朝下,但是有很多噪声。

对于 mini-batch 梯度下降法来说,我们需要决定的变量之一就是 mini-batch 的大小。

极端情况下,它可以等于训练集的大小,其实就是 batch 梯度下降法。但是它的弊端在于,样本数量巨大的时候,单次迭代耗时太长。如果训练样本不大, batch 梯度下降法运行地很好。

另一种极端情况下,它可以等于 1,也叫作随机梯度下降法。每个样本都是独立的 mini-batch,每次迭代我们都只处理一个样本。随机梯度下降法是有很多噪声的,平均来看,它最终会靠近最小值,不过有时候也会方向错误,因为随机梯度下降法永远不会收敛,而是会一直在最小值附近波动,但它并不会在达到最小值并停留在此。随机梯度下降法的一大缺点是,我们会失去所有向量化带给我们的加速,效率会过于低下。

所以 mini-batch 的大小应该取 1 到 \(m\) 之间的值。如果训练集较小(< 2000),直接使用 batch 梯度下降法,否则使用 mini-batch。一般 mini-batch 大小为 64 到 512,考虑到电脑内存设置和使用的方式,如果 mini-batch 大小是 2 的 n 次方,代码会运行地快一些。

指数加权平均 (Exponentially weighted averages)

在统计中也叫指数加权移动平均。以伦敦的气温为例。

散点看起来有些杂乱,如果要计算趋势的话,也就是温度的局部平均值,或者说移动平均值。我们要做的是,首先使 \(v_0=0\),每天使用 0.9 倍的之前的加权数加上当日温度的 0.1 倍。即 \(v_1=0.9v_0+0.1\theta_1\),得到第一天的温度值。以此类推。

这样,我们得到图中红色的曲线。

把 0.9 这个常数泛化为变量 \(\beta\),那么之前的 0.1 则为 \((1-\beta)\),即 \(v_t=\beta v_{t-1}+(1-\beta)\theta_t\)。在计算时,\(v_t\) 可以看作是 \(\frac{1}{1-\beta}\) 的每日温度。如果 \(\beta\) 是 0.9,这是十天的平均值。而如果 \(\beta\) 为 0.98,\(\frac{1}{1-0.98}=50\),那么可以看作是过去 50 天的温度,这时,我们就可以得到下图中绿色的曲线。

指数加权平均公式在温度变化时,适应地更缓慢一些,所以会出现一定延迟,因为当 \(\beta=0.98\),相当于给前一天的值加了太多权重,只有 0.02 的权重给了当日的值,所以温度变化时,温度上下起伏,当 \(\beta\) 较大时,指数加权平均值适应地更缓慢一些。

我们可以再换一个值试一试,如果 \(\beta\) 是另一个极端值,比如说 0.5,\(\frac{1}{1-0.5}=2\),这是平均了两天的温度。作图运行后得到黄色的曲线。

由于仅平均了两天的温度,平均的数据太少,所以得到的曲线有更多的噪声,有可能出现异常值,但是这个曲线能够更快适应温度变化。

理解指数加权平均

其实核心公式就是

\[v_t=\beta v_{t-1}+(1-\beta)\theta_t
\]

现在我们使 \(\beta\) 等于 0.9,把每个公式具体写出来,倒着写。

\[v_{100}=0.9v_{99}+0.1\theta_{100}\\
v_{99}=0.9v_{98}+0.1\theta_{99}\\
v_{98}=0.9v_{97}+0.1\theta_{98}\\
\dots \dots
\]

那么 \(v_{100}\) 其实可以写作下面这个形式

\[v_{100} = 0.1\theta_{100} + 0.1 \times 0.9 \theta_{99} + 0.1 \times {(0.9)}^{2}\theta_{98} + 0.1 \times {(0.9)}^{3}\theta_{97} + 0.1 \times {(0.9)}^{4}\theta_{96} + \dots
\]

假设我们有一些日期的温度,所以这是数据,\(t\) 为 100,99,98 等等,这就是数日的温度数值。

然后我们构建一个指数衰减函数,从 0.1 开始,到 \(0.1 \times 0.9\),到 \(0.1 \times {(0.9)}^{2}\),以此类推,所以就有了这个指数衰减函数。

计算 \(v_{100}\) 是通过把每日温度与指数衰减函数相乘,然后求和。

所有的这些系数(\((0.1 \times 0.1) \times (0.9 \times 0.1) \times ({(0.9)}^{2} \times 0.1)) \times ({(0.9)}^{3}\times 0.1) \times \dots\)),相加起来为 1 或者逼近 1,我们称之为偏差修正

我们如果实际运行 \(\beta=0.98\),得到的其实不是下图中的绿色曲线而是紫色曲线。而紫色曲线起点较低。

计算移动平均数的时候,初始化 \(v_{0} = 0\),\(v_{1} = 0.98v_{0} +0.02\theta_{1}\),但是 \(v_{0} =0\),所以这部分没有了(\(0.98v_{0}\)),所以 \(v_{1} =0.02\theta_{1}\),所以如果一天温度是 40 华氏度,那么 \(v_{1} = 0.02\theta_{1} =0.02 \times 40 = 8\),因此得到的值会小很多,所以第一天温度的估测不准。同理,第二天也会出现估测不准的情况。

有个办法可以修改这一估测,让估测变得更好,更准确,特别是在估测初期,也就是不用 \(v_{t}\),而是用 \(\frac{v_{t}}{1- \beta^{t}}\)。

举个具体例子,当 \(t=2\) 时,\(1 - \beta^{t} = 1 - {0.98}^{2} = 0.0396\),因此对第二天温度的估测变成了 \(\frac{v_{2}}{0.0396} =\frac{0.0196\theta_{1} + 0.02\theta_{2}}{0.0396}\),也就是 \(\theta_{1}\) 和 \(\theta_{2}\) 的加权平均数,并去除了偏差。随着 \(t\) 增加,\(\beta^{t}\) 接近于 0,所以当 \(t\) 很大的时候,偏差修正几乎没有作用,因此当 \(t\) 较大的时候,紫线基本和绿线重合了。

动量梯度下降法 (Gradient descent with Momentum)

动量梯度下降法运行速度几乎总是快于标准的梯度下降算法,简而言之,基本的想法就是计算梯度的指数加权平均数,并利用该梯度更新权重。

假如我们要优化下图所示的代价函数,红点表示最小值的位置。而我们从蓝色点开始梯度下降,无论是 batch 还是 mini-batch,一步一步迭代才会慢慢摆动到最小值。这种上下波动减慢了梯度下降法的速度,而如果使用较大的学习率(紫色箭头),结果可能会偏离函数的范围,为了避免摆动过大,我们要用一个较小的学习率。

使用动量梯度下降法,我们可以在纵轴上,学习慢一点,因为我们不想要这些摆动;而在横轴上,加快学习,快速移向最小值。

我们需要做的是,在每次迭代即第 \(t\) 次迭代的过程中,计算微分 \(dW,db\) 的移动平均数。也就是

\[v_{dW}=\beta v_{dW}+(1-\beta)dW\\
v_{db}=\beta v_{db}+(1-\beta)db
\]

然后重新赋值权重

\[W:=W-\alpha v_{dW}\\
b:=b-\alpha v_{db}
\]

这样平均过程中,纵轴上正负数相互抵消,平均值接近于 0。而横轴上,所有的微分都指向横轴方向,因此横轴方向的平均值仍然较大。

我们一般 \(\beta\) 为 0.9 可以达到不错的效果,当然也可以尝试不同的值。实际中,在使用梯度下降法或动量梯度下降法时,人们不会受到偏差修正的困扰。

RMSprop

RMSprop 算法的全称为 root mean square prop 算法,它也可以加速梯度下降。

还是上面那个例子。我们假设纵轴代表参数 \(b\),横轴代表参数 \(W\),可能有 \(W_1,W_2\) 或者其他重要的参数,为了便于理解,被称为 \(b\) 和 \(W\)。

在第 \(t\) 次迭代中,该算法会照常计算当下 mini-batch 的微分 \(dW,db\),保留指数加权平均数,但是用新符号 \(S_{dW}\) 而不是 \(v_{dW}\),因此 \(S_{dW}=\beta S_{dW}+(1-\beta)dW^2\),其中平方是对于整个符号进行平方的。同样地,\(S_{db}=\beta S_{db}+(1-\beta)db^2\)。

接着,仍然是更新参数

\[W:=W-\alpha \frac{dW}{\sqrt{S_{dW}}}\\
b:=b-\alpha \frac{db}{\sqrt{S_{db}}}
\]

我们来理解一下其原理。记得在横轴方向或者在例子中的 \(W\) 方向,我们希望学习速度快,而在垂直方向,也就是例子中的 \(b\) 方向,我们希望减缓纵轴上的摆动,所以有了 \(S_{dW}\) 和 \(S_{db}\),我们希望 \(S_{dW}\) 会相对较小,所以我们要除以一个较小的数,而希望 \(S_{db}\) 又较大,所以这里我们要除以较大的数字,这样就可以减缓纵轴上的变化。你看这些微分,垂直方向的要比水平方向的大得多,所以斜率在 \(b\) 方向特别大,所以这些微分中,\(db\) 较大,\(dW\) 较小,因为函数的倾斜程度,在纵轴上,也就是 b 方向上要大于在横轴上,也就是 \(W\) 方向上。\(db\) 的平方较大,所以 \(S_{db}\) 也会较大,而相比之下,\(dW\) 会小一些,亦或 \(dW\) 平方会小一些,因此 \(S_{dW}\) 会小一些,结果就是纵轴上的更新要被一个较大的数相除,就能消除摆动,而水平方向的更新则被较小的数相除。

Adam 优化算法

Adam 优化算法基本上就是将 Momentum 和 RMSprop 结合在一起。Adam代表的是Adaptive Moment Estimation

使用 Adam 算法,首先我们初始化各项参数 ,\(v_{dW} = 0\),\(S_{dW} =0\),\(v_{db} = 0\),\(S_{db} =0\),在第 \(t\) 次迭代中,计算微分 \(dW,db\),一般我们会用 mini-batch 梯度下降法。

接下来计算 Momentum 指数加权平均数,这里我们为了不混淆两个算法中的参数,Momentum 中使用 \(\beta_1\),RMSprop 中使用 \(\beta_2\)。 即

\[v_{dW}= \beta_{1}v_{dW} + ( 1 - \beta_{1})dW\\
v_{db}= \beta_{1}v_{db} + ( 1 - \beta_{1})db
\]

然后,使用 RMSprop 进行更新,即

\[S_{dW}=\beta_2 S_{dW}+(1-\beta_2)(dW)^2\\
S_{db}=\beta_2 S_{db}+(1-\beta_2)(db)^2
\]

一般使用 Adam 算法的时候,要计算偏差修正,即

\[v_{dW}^{corrected}=\frac{v_{dW}}{1-\beta_1^t}\\
v_{db}^{corrected}=\frac{v_{db}}{1-\beta_1^t}\\
S_{dW}^{corrected}=\frac{S_{dW}}{1-\beta_1^t}\\
S_{db}^{corrected}=\frac{S_{db}}{1-\beta_1^t}
\]

最后,更新权重。由于我们要确保我们的算法不会除以 0,而如果 \(S_dW\) 的平方根趋近于 0 的话,就会导致我们的结果非常大。为了确保数值稳定,在实际操作中,我们要在分母上加上一个很小很小的 \(\epsilon\),一般我们设为 \(10^{-8}\)。即

\[W:=W-\frac{\alpha v_{dW}^{corrected}}{\sqrt{S_{dW}^{corrected}}+\epsilon}\\
b:=b-\frac{\alpha v_{db}^{corrected}}{\sqrt{S_{db}^{corrected}}+\epsilon}
\]

所以 Adam 算法结合了两个算法,是一种极其常用的学习算法,被证明能有效适用于不同神经网络,适用于广泛的结构。

关于这个算法,它其实有很多超参数。

学习率 \(\alpha\) 很重要,经常需要调试。\(\beta_1\) 常用的缺省值为 0.9。\(\beta_2\) 的值,Adam 的作者推荐使用 0.999;而 \(\epsilon\) 一般也建议使用 \(10^{-8}\)。

学习率衰减 (Learning rate decay)

加快学习算法的一个办法就是随时间慢慢减少学习率,我们将之称为学习率衰减。

假设我们使用 mini-batch 梯度下降法,mini-batch 的数量不大,在迭代过程中会有噪音(下图中蓝色的线),下降朝向最小值。但是不会精确地收敛,所以算法最后在附近摆动,因为我们用的 \(\alpha\) 是固定值,不同的 mini-batch 中有噪音。

但是如果我们慢慢减小学习率 \(\alpha\) 的话,在初期的时候,学习率还较大,学习还是相对较快;随着学习率变小,学习的步伐也会变慢变小,最后曲线会在最小值附近的一小块区域里摆动(上图中绿色的线),而不是在训练过程中,大幅度在最小值附近摆动。

具体做法可以这样实现。我们将学习率 \(\alpha\) 设为 \(\alpha=\frac{1}{1+decay\_rate*epoch\_num}\alpha_0\),其中 decay_rate 为衰减率,epoch_num 为迭代代数,\(\alpha_0\) 为初始学习率。衰减率 decay_rate 其实是一个我们需要调整的超参数。

当然,还有其他人们会用的公式。如下。

\[\alpha=0.95^{epoch\_num}\alpha_0\\
\alpha=\frac{k}{\sqrt{epoch\_num}}\alpha_0\\
\alpha=\frac{k}{\sqrt{t}}\alpha_0
\]

有时也会用一个离散下降的学习率,也就是某个步骤有某个学习率,一会之后,学习率减少了一半,一会儿减少一半,一会儿又一半,这就是离散下降(discrete stair cease)的意思。还有时候,人们也会手动控制 \(\alpha\),但这只有模型数量小的时候有用。

Batch 归一化 (Batch Norm)

Batch 归一化算法由 Sergey Loffe 和 Christian Szegedy 创造,简称为 BN,它会使参数搜索问题变得很容易,使神经网络对超参数的选择更加稳定,超参数的范围会更加庞大,工作效果也更好。

当训练一个模型时,我们曾经使用过归一化输入特征加快学习过程。而对于更深的模型,我们不禁有输入特征值 \(x\),还有各个层的激活值 \(a^{[1]},a^{[2]}\) 等等。那么归一化这些激活值也会使我们的训练更有效率。严格来说,Batch 归一化的不是激活值 \(a^{[l]}\) 而是 \(z^{[l]}\)。

现在我们以第 \(l\) 层的归一化为例,在符号中省略上标 \(^{[l]}\)。于是我们有这一层隐藏单元的值,从 \(z^{(1)}\) 到 \(z^{(m)}\),于是我们用下面的公式使每个 \(z^{(i)}\) 值规范化。为了使数值稳定,通常会在分母中加上 \(\epsilon\),以防 \(\sigma=0\)。

\[\mu=\frac{1}{m}\sum_{i=1}^mz^{(i)}\\
\sigma^2=\frac{1}{m}\sum_{i=1}^m(z^{(i)}-\mu)\\
z^{(i)}_{norm}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}}
\]

这样我们就把这些 \(z\) 值标准化,化为含平均值 0 和标准单位方差。但是我们不想让隐藏单元总是含有平均值 0 和方差 1,也许隐藏单元有了不同的分布会有意义,所以我们还要计算一个变量 \({\tilde{z}}^{(i)}= \gamma z_{\text{norm}}^{(i)} +\beta\)。其中 \(\gamma\) 和 \(\beta\) 是我们需要学习的参数(这里的 \(\beta\) 与 Adam 等优化算法中的参数 \(\beta\) 不是同一个),正如更新权重一样,使用梯度下降或者优化算法,我们也会更新这两个参数。

通过赋予 \(\gamma\) 和 \(\beta\) 其他值,我们可以构造含其他平均值和方差的隐藏单元值。如果 \(\gamma=\sqrt{\sigma^2+\epsilon}\),\(\beta=\mu\),那么\(\tilde{z}^{(i)}=z^{(i)}\)。

一般来说,如果使用深度学习编程框架,一般一行代码就能实现 BN。比如在 TensorFlow 框架中,我们可以用函数 tf.nn.batch_normalization 来实现。

Softmax 回归

如果我们要进行多类型的预测的话,我们就需要用到 softmax 回归。

以下面这个例子进行说明。

于是为了分出上图中的四类,我们将建立一个神经网络,其输出层有 4 个或者说 \(C\) 个输出单元。

我们想要的是,输出层单元的数字告诉我们这 4 种类型中每个的概率有多大,因此这里的 \(\hat{y}\) 将是一个 \(4\times1\) 维向量,而且输出的四个数字加起来应该等于 1。

在神经网络的最后一层,我们将会像往常一样计算各层的线性部分,也就是 \(z^{[L]}=W^{[L]}a^{[L-1]}+b^{[L]}\)。然后我们应用 softmax 激活函数。首先,计算一个临时变量 \(t\),它等于 \(e^{z^{[L]}}\),也就是对所有元素求幂,然后对这个变量 \(t\) 进行归一化来输出 \(a^{[L]}\),也就是 \(a^{[L]}=\frac{t}{\sum_{i=1}^Ct_i}\)。

以一个具体的例子来说,就是,假如我们算出的 \(z^{[L]}\) 的值如下,那么计算过程也就如下所示。

\[z^{[L]}= \begin{bmatrix} 5 \\ 2 \\ - 1 \\ 3 \\ \end{bmatrix}\\
t =e^{z^{[L]}}=\begin{bmatrix} e^{5} \\ e^{2} \\ e^{- 1} \\ e^{3} \\ \end{bmatrix}=\begin{bmatrix} 148.4 \\ 7.4 \\ 0.4 \\ 20.1 \\ \end{bmatrix}\\
a^{[L]}=\frac{t}{\sum_{i=1}^4t_i}=\begin{bmatrix} 0.842 \\ 0.042 \\ 0.002 \\ 0.114 \\ \end{bmatrix}
\]

softmax 这个名词的来源是与所谓 hardmax 对比而来的。hardmax 会把向量 \(z\) 变成 \(\begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \\ \end{bmatrix}\) 这种形式的向量。

假如说对于某个样本的目标输出,真实标签是 \(\begin{bmatrix} 0 \\ 1 \\ 0 \\ 0 \\ \end{bmatrix}\),但是输出的 \(\hat{y}=\begin{bmatrix} 0.3 \\ 0.2\\0.1\\0.4\end{bmatrix}\)。说明这个样本,神经网络的表现不佳。在 softmax 分类中,一般用到的损失函数是 \(L(\hat{y},y)=-\sum_{j=1}^Cy_j\log{\hat{y}_j}\)。用刚刚说的具体的数据代入的话,如下所示

\[L(\hat{y},y)=-\sum_{j=1}^4y_j\log{\hat{y}_j}=-y_2\log{\hat{y}_2}=-\log{\hat{y}_2}
\]

而我们试图不断使这个损失函数变小,也就需要使 \(\hat{y}_2\) 尽可能大。

对于整个训练集的代价函数 \(J\) 来说,仍然是每个样本的损失的均值。

\[J(W^{[1]},b^{[1]},\dots)=\frac{1}{m}\sum_{i=1}^mL(\hat{y}^{(i)},y^{(i)})
\]

References

[1] Coursera深度学习教程中文笔记

[2] 深度学习 500 问

《Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization》课堂笔记的更多相关文章

  1. Coursera Deep Learning 2 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week1, Assignment(Initialization)

    声明:所有内容来自coursera,作为个人学习笔记记录在这里. Initialization Welcome to the first assignment of "Improving D ...

  2. [C4] Andrew Ng - Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization

    About this Course This course will teach you the "magic" of getting deep learning to work ...

  3. Coursera Deep Learning 2 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week2, Assignment(Optimization Methods)

    声明:所有内容来自coursera,作为个人学习笔记记录在这里. 请不要ctrl+c/ctrl+v作业. Optimization Methods Until now, you've always u ...

  4. Coursera, Deep Learning 2, Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week1, Course

    Train/Dev/Test set Bias/Variance Regularization  有下面一些regularization的方法. L2 regularation drop out da ...

  5. 课程二(Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization),第三周(Hyperparameter tuning, Batch Normalization and Programming Frameworks) —— 2.Programming assignments

    Tensorflow Welcome to the Tensorflow Tutorial! In this notebook you will learn all the basics of Ten ...

  6. 课程二(Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization),第二周(Optimization algorithms) —— 2.Programming assignments:Optimization

    Optimization Welcome to the optimization's programming assignment of the hyper-parameters tuning spe ...

  7. Coursera Deep Learning 2 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week1, Assignment(Gradient Checking)

    声明:所有内容来自coursera,作为个人学习笔记记录在这里. Gradient Checking Welcome to the final assignment for this week! In ...

  8. 课程二(Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization),第一周(Practical aspects of Deep Learning) —— 4.Programming assignments:Gradient Checking

    Gradient Checking Welcome to this week's third programming assignment! You will be implementing grad ...

  9. Coursera Deep Learning 2 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week3, Hyperparameter tuning, Batch Normalization and Programming Frameworks

    Tuning process 下图中的需要tune的parameter的先后顺序, 红色>黄色>紫色,其他基本不会tune. 先讲到怎么选hyperparameter, 需要随机选取(sa ...

随机推荐

  1. 使用select异步IO实现socketserver服务器 源码剖析

    #_*_coding:utf-8_*_ #这是一个echo server,客户端消息,服务端回复相同的消息 import select, socket, sys, queue # Create a T ...

  2. 第十五届四川省省赛 SCU - 4443 Range Query

    先给你1~N的N个数 再给你每种最多50个的条件(ai,bi,ci) 或者[ai,bi,ci] (ai,bi,ci)表示下标ai到bi的最小值必为ci [ai,bi,ci]表示下标ai到bi的最大值必 ...

  3. JVM 字节码指令手册 - 查看 Java 字节码

    JVM 字节码指令手册 - 查看 Java 字节码 jdk 进行的编译生成的 .class 是 16 进制数据文件,不利于学习分析.通过下命令 javap -c Demo.class > Dem ...

  4. 为什么程序员一定要会用Google和Stack Overflow?

    为什么程序员一定要会用Google和Stack Overflow? https://blog.csdn.net/u012207345/article/details/81139665 StackOve ...

  5. PLS做soft particle

    这个pixel local storage frame fetch 可以一个pass做出soft particle/deferred lighting/soft edge water programb ...

  6. es实战之数据导出成csv文件

    从es将数据导出分两步: 查询大量数据 将数据生成文件并下载 本篇主要是将第二步,第一步在<es实战之查询大量数据>中已讲述. csv vs excel excel2003不能超过6553 ...

  7. jquery实现在光标位置(input、textarea)插入内容的方法

    通过扫码枪扫码.按钮点击事件在光标处插入文本,这是前台js常用的功能.但是在input输入框和textarea文本框定位光标,插入数据是有点不同的 首先最简单的,适用于input输入框的方法 HTML ...

  8. JAVA-IO流大文件拷贝

    package com.test.io; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import ...

  9. 【原】maven web项目eclipse搭建

    1.new->other->Maven Project,点击next 2.继续next 3.选择maven-archetype-webapp,如果找不到可以再Filter里面搜索web,就 ...

  10. poj 3050 Hopscotch DFS+暴力搜索+set容器

    Hopscotch Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2774 Accepted: 1940 Description ...