深度学习策略与经验

深度学习策略与经验

Posted by Jinliang on June 25, 2019

在这章中,我们会学习到一些策略、分析深度学习问题的方法、部署深度学习产品的经验等等。

1. 正交化

为什么标题叫做正交化,正交意味着互成90度,可以理解为互不影响,能够单独调节一个装置,改变某个属性,在此同时对其他属性没有影响。

当我们想要调节我们的深度学习系统,想让他变得更好时,往往有很多方向,但是如果方向错误的话,就会浪费大量的时间,因此,我们要学会的第一件事,就是如何找到系统的问题。

想要一个好的深度学习系统,以监督学习为例,需要满足一下4个条件:

  1. 训练集上表现良好
  2. 开发集上表现良好
  3. 测试集上表现良好
  4. 实际使用中表现良好

通过上面四个条件,我们可以定位是哪一个条件出现了问题,然后再进行系统的调节与优化。

2. 评估指标

我们无论是调整超参数,还是尝试不同的算法,或者尝试不同的搭建系统手段,如果我们能定义一个单实数评估指标,我们的进展会快得多。

我们以查准率和查全率为例,查准率为:在我们的分类器中标记为正的时候,有多少概率为真的正;查全率为对于所有真的正,我们的分类器能识别出的百分比。

查准率与查全率往往需要折衷,当我们有两个系统,其中一个系统查准率高,另外一个系统查全率高,我们很难进行选择,此时我们需要一个新的评估指标,能够结合查准率和查全率。

结合查准率和查全率的方法就是$F_1$分数,,我们可以简单的认为是查准率和查全率的平均值:

\[F_1=\frac{2}{\frac{1}{p}+\frac{1}{R}}=\frac{2}{\frac{R+P}{RP}}=\frac{2PR}{P+R}\tag{2-1}\]

公式2-1中,P、R分别为查准率和查全率,$F_1$函数叫做调和平均数。

当我们有了$F_1$函数,我们可以在两个系统中很快的选择出较好的,这说明有一个单实数指标有多么的重要。


但是在很多情况下,特别是复杂的系统中,单实数评估指标是不现实的,我们该怎么办?

实际上,当我们要考虑$N$个指标时,我们选择其中一个指标为优化指标,尽量优化这个指标,剩下的$N-1$个指标都是满足指标,只要他们达到一定阈值,那我们就不在乎了。

因此,在单实数评估指标很难实现时,我们考虑将指标分为一个优化指标+$N$个满足指标,在满足指标达到时,我们只需要选择优化指标最好的就可以了。

3. 训练、开发、测试集的划分

开发集也被称为保留交叉验证集,一般来说,我们会使用训练集训练不同的模型,然后使用开发集评估不同的思路,然后选择一个,不断迭代去改善开发集的性能,最后得到一个满意的版本,再使用测试集去评估。

我们在划分时,尽量让我们的开发集和测试集来自同一分布,为什么这样呢,原因是在实验中,开发集+单实数评估指标就是我们的目标,我们在进行模型选择与训练时,都以此目标为准,如果测试集与开发集不属于同一分布,那么相当于我们的目标是歪的,最后的结果也不会太好。

在之前,有一条经验法则:将全部数据使用7:3分成训练集与测试集。

当需要开发集时,一般使用6:2:2的比例进行划分训练集、开发集、测试集。

在我们有成千上万个样本时,上述经验是有用的,但是在现代机器学习中,一般使用规模大的多的数据集,当我们有100万个训练样本中,可以使用98:1:1的比例划分训练集、开发集、测试集。

因为在我们有100万个样本时,1%就是10000个样本,对于开发集与测试集完全够用了。

测试集的目的是我们完成系统开发之后,测试集帮我们评估投产系统的性能,一般来说,10000个样本能给我们足够的置信度来给出性能指标了。


当我们的评估指标无法正确评估好算法的排名时,我们需要花时间定一个新的评估指标。评估指标的意义在于,准确的告诉我们多个模型中,哪一个更适合我们,如果我们旧的评估指标达不到这个要求,就应该尝试定义一个新的指标,能够选出最适合的模型。

同理,如果我们的开发集、测试集与我们的系统实际使用的数据关系不大,那么我们应该修改开发、测试集,让他们能够更好的反映模型需要处理好的数据。

4. 关于人的表现(human-level performence)

在很多的学习任务中,都会有这样一个规律:

在我们研究一个系统或者模型时,当我们试图向人类水平努力时,进展比较快;

当我们的算法比人类水平好时,他还可以变得更好,但是准确度上升的斜率会越来越平缓;

再继续训练时,模型越来越大,性能越来越多,但是性能无法超过某个理论的上限,这就是所谓的贝叶斯最优错误率

我们一般认为贝叶斯最优错误率为可能达到的最优错误率。

在上述规律中,为什么超过人类水平后,进展就会变慢呢?

可以从两个方面尝试解答:

  • 人类水平在很多任务中离贝叶斯最优错误率已经不远了,因此超越人类水平后,并没有太多的空间可以提升了
  • 如果我们模型或者算法的表现比人类差,那么我们可以使用某些工具来提高性能,当模型超过了人类水平,这些工具就没有那么好用了。

实际上,当模型的变现没有人类好时,我们可以从以下几个方面进行优化:

  • 可以找人标记数据,这样我们会获得更多的数据喂给算法
  • 通过人类分析算法处理,找出错误出在哪里,尝试理解为什么人能做对,模型做错,即人工错误分析
  • 更好的分析偏差和方差

实际上,贝叶斯最优错误率我们是很难获取的,但是人类水平的错误率我们却可以轻易获取,并且在某些任务上,如语音、图像等等,人类水平与贝叶斯最优错误率是很接近的,因此,我们可以使用人类水平估计贝叶斯最优错误率,以更快的决定出专注于减少偏差的算法还是减小方差的算法。

实际上,目前很多领域人工智能已经超过人类了,但是在结构化数据中居多,在一些非结构化数据中,例如语音图像等等,人类非常擅长,人工智能想要超过人类比较难。

在使用人类水平估计贝叶斯最优错误率的基础上,怎样分析偏差与方差呢?

  • 人类水平错误率

  • 训练错误率

  • 开发错误率

我们需要分别计算人类水平与训练错误率的差值、训练错误率与开发错误率的差值。

我们将人类水平与训练错误率的差值叫做可避免偏差,训练错误率与开发错误率的差值叫做方差。

偏差反映了算法对训练集的拟合程度,方差反映了泛化性能以及是否过拟合。

相对于方差,如果我们的可避免偏差过大时,可以考虑以下策略:

  • 使用规模更大的模型
  • 训练更久
  • 使用更好的优化算法(Momentum、Adam等)
  • 使用新神经网络架构
  • 使用合适的超参数
  • 增加层数或者隐藏单位数

相对于偏差,如果我们的方差过大时,可以考虑以下策略:

  • 使用更多的数据
  • 使用正则化(L2,dropout)
  • 使用其他的神经网络结构
  • 使用合适的超参数

通过上述的流程,我们就可以快速的分析我们的模型、算法,找到正确的方向进行优化。

5. 错误分析

上面提到过,当我们的算法表现没有人类好时,我们可以使用几种方法,其中一种为错误分析

当我们算法的表现不如人类时,我们可以手动检查算法犯的错误,然后给我们接下来要做的工作一个方向,这个过程叫做错误分析。

进行错误分析,我们应该找一组错误样本(如随机挑选100个随机样本),可能在开发集或者测试集中,观察错误标记的样本,看看假阳性(false positives)和假阴性(false negatives),统计属于不同错误类型的错误数量。在这个过程中,我们会受到启发,归纳出新的错误类型。通过统计不同错误标记类型占总数的百分比,我们可以发现哪些问题需要优先解决,或者给我们构造新优化方向的灵感。

以上就是错误分析的具体流程与思想。


但是在错误分析中,我们有可能发现,有些样本被错误标记了,我们应该怎么做?

我们先来考虑训练集

深度学习算法对于训练集中的随机错误是相当健壮的,就是说,当数据集足够大时,就算有一些错误标签,也不影响算法的训练。

但是相反,深度学习算法对于随机误差很健壮,对于系统性的错误却没有那么健壮。丽日我们在标记时,总把某一类标错,那么我们的算法就会出现问题。

再来考虑开发集和测试集

我们可以在错误分析中增加一列,属于是否标记错误的列,如果错误分析后,错误标记的占比很大,那就很有必要去修正错误的标签;如果占比很小,不影响开发集、测试集的评估能力,那么久没必要去修复。

在修正时,有几点需要注意:

  • 同时修正开发集与测试集

    不管使用什么修正手段,都要同时作用于开发集和测试集,之前讨论过,开发集和测试集要属于同一分布。

  • 同时修正算法判断错误和判断正确的样本

    在修正时,我们要忽略算法是否判断正确这个因素,将所有的错误标签都修正,不然对算法偏差的估计会偏大,对算法不公平

  • 在修正时,可适当忽略训练集

6. 数据不匹配

当我们搭建一个深度学习系统时,我们需要大量的数据,但是当前我们只有少量的数据以及类似的从其他地方获取的数据,这部分数据和我们的实际应用场景不太一致,但是我们要训练好一个深度学习系统,又必须使用它。

可以考虑一下两种解决方案:

  • 将两部分数据混在一起,然后按照一定的比例拆分成训练集、开发集、测试集

    这样做有一个好处就是训练集、开发集、测试集都来自于同一分布,符合我们大多数深度学习系统的训练流程;其中的坏处就是使用开发集、测试集对深度学习系统进行评估,评估结果并不是真正的评估结果

  • 将我们的自身数据按照比例分成开训练集、开发集、测试集,然后将从其他地方获取的不标准数据放入训练集

    这样做的好处就是开发集、测试集是我们的真实目标,是我们想要处理的目标;坏处就是训练集与开发集(测试集)的分布不一致。

上述两种方式中,推荐的方式是第二种,我们需要使用自身的数据作为开发集、测试集,给我们的深度学习系统正确的目标,然后使用其他数据作为训练集的一部分,使用更多的训练数据进行训练。


当我们使用第二种方式时,我们的训练集和开发集(测试集)处于不同的分布,分析偏差方差的方式也会不一样,具体看一下:

我们需要一个新的数据集,叫做训练-开发集,它是由我们随机打散训练集,然后分出一部分作为训练-开发集。

通过上面的描述,训练集与训练-开发集来自同一分布。现在我们分析他们之间的差值,来进一步分析偏差与方差问题:

  • 人类水平错误率

  • 训练错误率
  • 训练-开发错误率

  • 开发错误率

在之前提到过,人类水平与训练错误率的差值叫做可避免偏差,训练错误率与开发错误率的差值叫做方差。但是在训练集与开发集、测试集处于不同分布时,我们为了分析偏差方差问题增加了训练-开发错误率,如何使用呢?

现在,我们把训练错误率与训练-开发错误率之间的差值叫做方差;训练-开发错误率与开发错误率之间的差值叫做数据不匹配问题;训练错误率与人类水平错误率叫做偏差

处理偏差与方差比较大问题的策略已经在第4小节讲述,关于数据不匹配问题的解决方法,并没有特别系统的方法,但是也有一些经验和技巧可以使用:

存在数据不匹配问题时,我们可以做错误分析,看看训练集与开发集,试图了解这两个数据到底有什么不同,然后再看看是否有办法收集更多看起来像开发集的数据进行训练。

其中一种收集方法就是人工合成数据,比如需要有噪声的数据,我们可以手机一些声音,在收集一些噪声,将两者合成。但是要注意,在人工合成时,不要在所有可能的空间选择一小部分去合成,例如在合成噪声数据时,不要从始至终使用同一个噪声。

7. 迁移学习与多任务学习

在深度学习中,有时候神经网络可以从一个任务中学习到知识,并将这些知识应用到另一个独立的任务中,这就是迁移学习。

在一个任务中,我们已经训练好了一个神经网络,现在要使用迁移学习,将学习好的神经网络用于另一个任务中,我们的方法是:

  • 如果新任务有大量的数据集

    随机初始化最后一层网络的权重,然后使用新数据训练网络中的所有权重。

  • 如果新任务数据量很小

    随机初始化最后一层网络的权重,然后使用新数据训练最后1层或者2层的权重

在第一个任务上训练网络叫做预训练;使用新数据进行训练,这叫做微调

如果我们想从任务A学习并迁移一些知识到任务B,那么当任务A与任务B有相同的输入时(例如都是图像,都是音频等等),这时迁移学习是有意义的,当任务A的数据比任务B多得多时,迁移学习的意义更大,效果也会更好。


在迁移学习中,任务是串行的,在多任务学习中,是是同时进行多个任务的学习的,试图让神经网络同时做几件事,然后希望每个任务都能帮到其他的任务。

假如我们让4个任务同时学习,那么整个训练集的平均损失为:

\[\frac{1}{m}\sum_{i=1}^m\sum_{j=1}^4L(\hat y_j^{(i)}-y_j^{(i)})\tag{7-1}\]

由于神经网络的早期特征,在执行不同的任务时都会用到,因此训练一个神经网络做4件事会比4个完全独立的神经网络分别作4件事性能要更好,这就是多任务学习的力量。

什么时候多任务是有意义的呢?当满足以下条件时:

  • 训练一组任务,可以共用低层次特征
  • 多任务提供了大量的数据,能够使用很多知识来增强任务的性能
  • 足够大的神经网络(使用足够大的神经网络时,多任务学习肯定不会或者很少降低性能)

在实际中,迁移学习的使用频率要高于多任务学习,因为多任务学习很难找到那么多相似且数据量对等的任务可以用单一神经网络训练。