deepleanrningai深度学习笔记

中文课程教案

第一门课 神经网络和深度学习(Neural Networks and Deep Learning)

激活函数

如果不使用激活函数或者使用线性激活函数,那么NN的效果只是一个线性模型,可以和LR等价

tanh在各种场景下一般都优于sigmoid,因此除了在而分类问题的输出层上使用sigmoid外,隐藏层基本都应该选择tanh而非sigmoid

Relu是最常用的激活函数,在不确定使用何种类型激活函数的情况下,可以优先选择Relu或者leaky relu(一般效果更好但是很少用,可能的原因或许是Apart from efficiency, ReLUs have sparse activations, for whatever that’s worth.)。
$$
Relu: g(z) = \max(0, z) \\
Leaky Relu: g(z) = \max(\alpha z, z)
$$

sigmoid和tanh的导数:
$$
\frac{\mathrm{d}\sigma(z)}{\mathrm{d}z} = \sigma(z)(1-\sigma(z)) \\
\frac{\mathrm{d}\tanh z}{\mathrm{d}z} = 1-(\tanh z)^2
$$
可以看到,这两个函数的导数都只和函数自身的取值有关,因此非常方便计算!但是需要注意他们存在饱和区。

参数初始化

如果将参数全部初始化为0,那么就会存在神经元之间的对称问题(symmetry breaking problem),同层的神经元一致变化,从而优化无效,没层其实只有一个神经元的效果。

一般采用随机初始化,再乘以一个很小的值。因为如果权重过大,那么一旦使用sigmoid或者tanh激活函数,很可能激活值落在饱和区域,从而使得梯度下降学习缓慢。

神经网络搭建

多隐藏层,单层少隐藏单元的深层神经网络实现一个复杂函数拟合需要的隐藏单元数量指数级小于少隐藏层,单层多隐藏单元的情况。

第二门课 改善深层神经网络:超参数调试、正则化以及优化(Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)

L2正则化

L2正则化会导致参数绝对值变小,接近0。从而会导致激活函数的输入也会非常小。考虑一个非常强的正则化要求(正则化系数极大),激活函数的输入值将集中在0的小邻域内,从而激活函数在该邻域内可以视为线性的。故而整个神经网络退化为线性模型,复杂度大大降低,不再有能力拟合非线性函数。

dropout

dropout方法是在训练过程中针对神经元引入了随机失活的处理。从而使得权重不会集中到个别的神经元上(因为随机失活的缘故,不会有机会形成这样的强势神经元)。从而可以起到类似于l2正则化的权重衰减效果。

[TODO]why?L2正则化与dropout的区别:L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。dropout更适用于不同的输入范围。

正则化方法是为了应对过拟合。因此并不应该一上来进行正则化,对于dropout尤其如此。因为dropout每轮迭代的随机失活会导致代价函数不再被明确定义,从而难以检查梯度下降的性能。

其他正则化方法

  • 数据扩增
  • early stopping
  • 归一化
    • 可以使得损失函数在各个方向更均匀, 从而更加容易优化,对于初始点和步长都没有严格的要求。

梯度消失和梯度爆炸

由于网络的层次传导,会使得激活值越来越大或者越来越小。从而对应的梯度也会极大或极小。

在权重初始化时,针对每层采用修正的初始权重可以削弱这个问题。因为传导进来的激活值和做过修正的权重相乘后可以平衡影响。

一般的:
$$
对于relu:\omega^{[l]} = np.random.randn(shape)np.sqrt(\frac{2}{n^{[l-1]}}) \\
对于tanh: \omega^{[l]} = np.random.randn(shape)
np.sqrt(\frac{1}{n^{[l-1]}})
其中:\\
\omega^{[l]}是l层权重,n^{[l-1]}是l-1层神经元数量
$$

梯度检验

很有必要对代码的正确性(主要就是梯度的计算过程)进行核查。

给定一个小的$\epsilon$,一般是$10^{-7}$,利用双边误差方法(优于单边公差的结果)逼近梯度,然后和算法运算出的梯度值计算距离进行比较,一般小于$10^{-7}$比较好,大于$10^{-3}$很可能存在bug。
$$
梯度逼近效果对比: \frac{f(\theta + \epsilon) - f(\theta - \epsilon)}{2\epsilon} > \frac{f(\theta + \epsilon)}{\epsilon}\\
距离计算: \frac{||d(\theta)_{approx} - d(\theta)||_2}{||d((\theta)||_2 + ||d(\theta)_{approx}||_2}
$$
注意,进行梯度检验时不要打开dropout,不然由于dropout随机失活的原因,导致检验比较没有太大意义。

优化算法

  • mini-batch

  • 指数加权平均:

  • $$
    v_0 = 0 \\
    v_t = \beta v_{t-1} + (1-\beta)\theta_t = (1-\beta)\theta_t + \beta(1-\beta)\theta_{t-1} + …+\beta^{t}(1-\beta)\theta_0+\beta v_0
    $$

    一般$\beta=0.9$,虽然看起来$\theta_t$的权重较小,但其实依旧是单个最大权重,只是充分考虑了过去的影响并平滑而已

  • momentum

  • rmsprop

  • adam

  • 学习率衰减

    • 指数衰减
    • 离散下降
    • 手动衰减
  • 局部最优问题

    • 高维空间,更可能遇到梯度为0的鞍点而不是局部最优点
    • 平稳段是一个问题,它使得学习变得缓慢

超参优化

超参重要性

吴恩达认为的超参重要性:学习率> 动量系数、隐藏单元数、batch size>层数、学习率衰减>其他

网格搜索VS随机搜索

对于传统机器学习算法,由于参数较少,因而网格搜索是可行的。但是在深度学习中,参数很多,而且重要性存在差别,有的参数可能不管取什么值都不会影响结果。在这种情况下,使用网格搜索其实是浪费资源的,比如5×5的网格,假设参数2不论取何值都无显著影响,那么虽然进行了25次尝试,但对于参数1其实只做了5种尝试,剩下的20种尝试都是浪费。因此,在这种情况下,使用随机搜索更加高效。可以排除不重要参数造成的资源浪费。通过分析参数的情况和对应效果,可以更快的定位参数的重要性和合理范围。然后可以在此范围内进行更精细的搜索。

超参随机取值范围与方法

在超参的取值范围内进行随机取值时,一般采用对数随机取值而不是线性随机取值。比方说学习率在0.0001到1范围内进行随机取值,如果采用线性随机取值,那么大部分取值都会大于0.1,但其实我们可能希望他能够在0.0001到0.1这个区间段取值更为密集,在0.1到1这个区间段不需要太密集。之所以会有这样的需求,可能是因为模型效果对参数在0.0001到0.1这个区间的变化更加敏感,而对0.1到0.9范围不是特别敏感。换言之0.1到1段相对更加同质一些。以上只是比方,具体那些参数那些范围内敏感是具体情况而定。对数随机取值就可以解决这个问题。以上问题可以转换为在-4到0之间随机均匀取值k,然后转为$10^k$,得到最终参数值。可以看到,这种方法下,取值在0.1到1之间的概率只有1/4。

BN(batch norm)

BN是指每层先对激活值进行标准化,在放入激活函数中激活。这里的标准化不是纯粹的z标准化,譬如在sigmoid激活时,可能我们希望激活值在0.5附近而不是0附近,从而可以充分利用sigmoid的非线性激活效果。故而,BN在第i层的方式是:
$$
z_{norm} ^{(i)}= \frac{z^{(i)} - \mu}{\sqrt{\sigma^2+\epsilon}}\\
\widetilde{z}_{norm}^{(i)} = \gamma^{(i)} z_{norm}^{(i)} + \beta^{(i)}
$$
对于某个mini-batch,直接基于数据进行均值方差标准化,因此在每个mini-batch训练中,$\mu,\sigma$是有差异的,并非整个训练集的均值方差。$\epsilon$是个微小值,用来做数值计算平滑。

BN的意义:

  • 通过标准化,可以加速学习,尤其是网络较深的情况下,避免梯度消失或爆炸
  • 通过标准化,可以使得每层的输入分布相对稳定,不会因为前面网络参数改变导致本层输入严重的波动。换言之,使得前后网络层之间具有一定的独立性和稳定性。这样也可以避免covariate shift现象。
  • 附带的,可以带来轻微的正则化效果。因为mini-batch训练的时候,均值和方差随mini-batch的具体数据有所差异,引入了噪音,使得模型更稳定。和dropout一样,mini-batch增大,会削弱正则化效果。但需要注意,这只是个附加效果,不要把它作为正则化的手段

同样类似于dropout,测试阶段和训练阶段逻辑有所不同。对于dropout,在测试阶段需要关闭。对于BN,在测试阶段,需要提前指定均值和方差,因此如果只是测试一条数据,是无法计算均值和方差的。均值和方差的测算方法有:

  • 在网络建好后,将整个训练集作为一个batch扔进去,可以得到均值和方差,但一般不使用这种方法
  • 使用指数加权平均来计算。训练过程每个mini-batch均值方差进行指数加权平均,最终可以得到一个测试可用的均值方差

softmax

softmax作为一个激活函数比较特殊。其他激活函数但是入常量出常量,而它是入向量出向量。主要的作用就是激活向量转概率向量:
$$
softmax(t_i) = \frac{e^{t_{i}}}{\sum\limits_i e^{t_i}}
$$

第三门课:结构化机器学习项目

正交化

简单地说,就是让每个参数独立的负责一个方面,不同参数之间正交,不发生共同作用,从而可以快速定位问题和调整方向。

early stopping就是一个不太正交化的参数,它同时影响训练集的拟合效果和开发集的表现。

单一数字评估指标

有时,我们对模型的效果需要从多个角度来考察,比如precision和recall,但是对于这种多指标的情况,如果训练了多个模型要进行优劣评估,就会很困难。因为很难出现某个模型在每个指标上都有压倒性优势。这样的比较困难又会进一步的影响到模型迭代优化的方向。故而,我们需要一个单一指标来清晰的进行优劣对比。比如综合precision和recall的F1-score。

满足和优化指标

有时,我们除了追求accuracy之外,还要求模型的运行时间、资源利用等要满足一定的条件。前者称为优化指标,后者称为满足指标。优化指标用于控制迭代的方向,如上一部分所示最好是单一数字评估指标,满足指标只需要到达要求下限就可以,并不需要在此基础上追求更好的效果,比如运行时间。

数据集配置和划分规则

  • train/dev/test应该服从同一分布
  • 切分比例并没有定数,主要是为了让每个数据集都能有足够的样本来产生足够置信度的结果。传统机器学习时代一般是7/3或者6/2/2的切分,深度学习时代,由于数据量巨大,98/1/1的切分就可以让dev/test有充足的数据。

贝叶斯最优错误率、人类水平、可避免偏差

贝叶斯错误率:理论上可能达到的最优错误率。出于数据或者任务本身的原因,并不能一定达到无错。

人类水平:人类在任务上的能力,可以作为一个基准,来估计贝叶斯错误率。

可避免偏差:模型效果和贝叶斯错误率之间的差距。

当存在较大的可避免偏差时,模型调优以降低偏差为主。

如果train和dev错误率存在较大差距,则需要进行降方差调优。

一般先调偏差、后调方差。

偏差优化:

  • 训练更大更深的模型
  • 尝试其他优化算法(adam、sgd等)
  • 更换网络架构
  • 超参调优
  • ……

方差优化:

  • 增加数据
  • 正则化
  • 更换网络架构
  • 超参调优
  • ……

错误分析

对于模型识别错误的样本,将之随机挑出一部分,观察其错误原因,或者是该部分样本的类型。譬如说猫狗识别中,我们去统计错误识别的样本中有多少是狗识别为猫,有多少是照片模糊,有多少是因为照片加了滤镜等等。这样可以得到具体有哪些错误类型以及其在错误样本中的占比。从而可以知道先解决哪个问题可以得到最高的收益。

标记错误

有一种特殊的情况是标记本身出错。这个可以分数据集来讨论。对于训练集,深度学习一般对于偶发的随机性的标记错误相当健壮。但是,对于系统性错误就会存在问题,譬如训练集中白色的猫都被标记为狗。显然,模型可能会学到这一错误模式。

对于开发集或者测试集,首先要做错误分析,通过错误分析也会发现标记错误的一个大概比例。如果比例很小,那么可以考虑不做修正。如果比例较大,影响到了对于模型的评估,那么就需要进行修正了。需要注意的是,这样的修正应当是在开发集和测试集都进行的,并且是针对所有样本的,而不仅仅是模型判错的样本!

快速搭建第一个系统

通过快速简单的搭建一个系统,然后进行方差/偏差分析和错误分析,你可以清晰的看到你面临的问题和困难,从而选择正确高效的进攻方向。