吴恩达CS230深度学习笔记(三)

2020-02-12

涉及 结构化机器学习项目 。

机器学习(ML)策略

当你尝试优化一个深度学习系统时,你通常可以有很多想法可以去试,问题在于,如果你做出了错误的选择,你完全有可能白费6个月的时间,往错误的方向前进。如果有快速有效的方法能够判断哪些想法是靠谱的,或者甚至提出新的想法,判断哪些是值得一试的想法,哪些是可以放心舍弃的。

搭建建立机器学习系统的挑战之一是,你可以尝试和改变的东西太多太多了。包括,比如说,有那么多的超参数可以调。我留意到,那些效率很高的机器学习专家有个特点,他们思维清晰,对于要调整什么来达到某个效果,非常清楚,这个步骤我们称之为正交化。正交化的意思就是使得各个特征之间互不干扰,可以单独地依次调各个特征,而不是同时要兼顾好几个。

当我训练神经网络时,我一般不用early stopping,这个技巧也还不错,很多人都这么干。但个人而言,我觉得用early stopping有点难以分析,因为这会同时影响你对训练集的拟合,因为如果你早期停止,那么对训练集的拟合就不太好,但它同时也用来改善开发集的表现,所以这个方式没那么正交化。

查准率(precision)、查全率(recall)、$F_1$分数,$F_1$分数的定义是这个公式:$\frac{2}{\frac{1}{P} + \frac{1}{R}}$。我们还需要考虑运行时间,可以将准确度和运行时间组合成一个整体评估指标,比如说时间必须要小于100ms,只要小于就行了。

在分割训练集验证集测试集的时候,需要将所有数据随机打乱,以此来做到让开发集和测试集都来自于同一分布。对于传统的数据量很小的数据,按照7:3或者6:2:2划分是相当合理的。但是对于现在超大规模的数据集,比如100w,可能按照98:1:1的比例就足够了,因为就算这样分那测试集都还有1w个数据呢,足够测试了。

偏差和方差的处理办法

处理机器学习问题时,应该把它切分成独立的步骤,将定义指标看成一步,然后在定义了指标之后,你才能想如何优化系统来提高这个指标评分。比如改变你神经网络要优化的成本函数。如果你在指标上表现很好,在当前开发集或者开发集和测试集分布中表现很好,但你的实际应用程序,你真正关注的地方表现不好,那么就需要修改指标或者你的开发测试集。

贝叶斯错误率或者对贝叶斯错误率的估计和训练错误率之间的差值称为可避免偏差,你可能希望一直提高训练集表现,直到你接近贝叶斯错误率,但实际上你也不希望做到比贝叶斯错误率更好,这理论上是不可能超过贝叶斯错误率的,除非过拟合。而这个训练错误率和开发错误率之前的差值,就大概说明你的算法在方差问题上还有多少改善空间。总结一下,如果你想理解偏差和方差,那么在人类可以做得很好的任务中,你可以估计人类水平的错误率,你可以使用人类水平错误率来估计贝叶斯错误率。所以你到贝叶斯错误率估计值的差距,告诉你可避免偏差问题有多大,可避免偏差问题有多严重,而训练错误率和开发错误率之间的差值告诉你方差上的问题有多大,你的算法是否能够从训练集泛化推广到开发集。

所以我想要让一个监督学习算法达到实用,基本上希望或者假设你可以完成两件事情。首先,你的算法对训练集的拟合很好,这可以看成是你能做到可避免偏差很低。还有第二件事你可以做好的是,在训练集中做得很好,然后推广到开发集和测试集也很好,这就是说方差不是太大。

如果你想用尽一切办法减少可避免偏差,我建议试试这样的策略:比如使用规模更大的模型,这样算法在训练集上的表现会更好,或者训练更久。使用更好的优化算法,比如说加入momentum或者RMSprop,或者使用更好的算法,比如Adam。你还可以试试寻找更好的新神经网络架构,或者说更好的超参数。这些手段包罗万有,你可以改变激活函数,改变层数或者隐藏单位数,虽然你这么做可能会让模型规模变大。或者试用其他模型,其他架构,如循环神经网络和卷积神经网络。新的神经网络架构能否更好地拟合你的训练集,有时也很难预先判断,但有时换架构可能会得到好得多的结果。另外当你发现方差是个问题时,你可以试用很多技巧,包括以下这些:你可以收集更多数据,因为收集更多数据去训练可以帮你更好地推广到系统看不到的开发集数据。你可以尝试正则化,包括$L2$正则化,dropout正则化或者我们在之前课程中提到**的数据增强。同时你也可以试用不同的神经网络架构,超参数搜索,看看能不能帮助你,找到一个更适合你的问题的神经网络架构。

误差分析

当你发现你的模型效果不是很好时,应该怎么办呢?建议手动检查你的错误样例,并且根据种类统计一下比例,比如说绘制一个excel表格。如果某一种类只有极少量错误,那么工作的重心就不应该在这上面而是应该在其他上面。在机器学习中,有时候我们很鄙视手工操作,或者使用了太多人为数值。但如果你要搭建应用系统,那这个简单的人工统计步骤,错误分析,可以节省大量时间,可以迅速决定什么是最重要的,或者最有希望的方向。

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

在做错误分析的时候,有时你会注意到开发集里有些样本被错误标记了,这时应该怎么做呢?

首先,我们来考虑训练集,事实证明,深度学习算法对于训练集中的随机错误是相当健壮的(robust)。只要你的标记出错的样本,只要这些错误样本离随机错误不太远,有时可能做标记的人没有注意或者不小心,按错键了,如果错误足够随机,那么放着这些错误不管可能也没问题,而不要花太多时间修复它们。当然你浏览一下训练集,检查一下这些标签,并修正它们也没什么害处。有时候修正这些错误是有价值的,有时候放着不管也可以,只要总数据集总足够大,实际错误率可能不会太高。我这里先警告一下,深度学习算法对随机误差很健壮,但对系统性的错误就没那么健壮了。所以比如说,如果做标记的人一直把白色的狗标记成猫,那就成问题了。因为你的分类器学习之后,会把所有白色的狗都分类为猫。但随机错误或近似随机错误,对于大多数深度学习算法来说不成问题。

其次,我强烈建议你要考虑同时检验算法判断正确和判断错误的样本,要检查算法出错的样本很容易,只需要看看那些样本是否需要修正,但还有可能有些样本算法判断正确,那些也需要修正。如果你只修正算法出错的样本,你对算法的偏差估计可能会变大,这会让你的算法有一点不公平的优势,我们就需要再次检查出错的样本,但也需要再次检查做对的样本,因为算法有可能因为运气好把某个东西判断对了。这第二点不是很容易做,所以通常不会这么做。通常不会这么做的原因是,如果你的分类器很准确,那么判断错的次数比判断正确的次数要少得多。总结起来就是训练集中标错的数据影响不大,测试集和验证集中的标错数据如果有较大影响就去修正数据,否则就不用。

最后的几个建议:

  • 在构造实际系统时,通常需要更多的人工错误分析,更多的人类见解来架构这些系统,尽管深度学习的研究人员不愿意承认这点。
  • 一些工程师和研究人员不愿意亲自去看这些样本,也许做这些事情很无聊,但实际上这很重要。花了这几分钟,或者几个小时去亲自统计数据,真的可以帮你找到需要优先处理的任务。
  • 如果你想搭建全新的机器学习程序,你应该首先快速设立开发集和测试集还有指标,这样就决定了你的目标所在,然后找到训练集,训练一下,看看效果,开始理解你的算法表现如何,从而进行偏差方差分析、误差分析等步骤。初始系统的全部意义在于,有一个学习过的系统,有一个训练过的系统,让你确定偏差方差的范围,就可以知道下一步应该优先做什么,让你能够进行错误分析,可以观察一些错误,然后想出所有能走的方向,哪些是实际上最有希望的方向。
  • 当出现训练集和测试集数据分布不匹配的时候,你可以通过人工合成数据等办法,向训练集里添加一些和测试集分布相同的数据。

迁移学习

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

经验规则是,如果你有一个小数据集,就只训练输出层前的最后一层,或者也许是最后一两层。但是如果你有很多数据,那么也许你可以重新训练网络中的所有参数。如果你重新训练神经网络中的所有参数,那么这个在初期训练阶段,有时称为预训练(pre-training)。如果你以后更新所有权重,这个过程叫微调(fine tuning)。预训练和微调的权重来源于迁移学习。

为什么这样做有效果呢?有很多低层次特征可以通过预训练很好的学习出来,而这些特征对很多有共通点的任务都是很有帮助的,所以这些预训练特征可能帮助你的其他任务学习得更快一些,或者需要更少的学习数据。迁移学习起作用的场合是,在迁移来源问题$A$中你有很多数据,但迁移目标问题$B$你没有那么多数据,并且二者的输入$x$输入相同。如果$A$的数据没有$B$多,这种情况下增益可能不多。

多任务学习

在迁移学习中,你的步骤是串行的,你从任务里学习只是然后迁移到任务。在多任务学习中,你是同时开始学习的,试图让单个神经网络同时做几件事情,然后希望这里每个任务都能帮到其他所有任务。

比如上图中,假设有四个任务,每个任务都期望得到一个值,具体体现在输出层里画了四个圈圈。那么你现在可以做的是训练一个神经网络,来预测这些$y$值,你就得到这样的神经网络,输入$x$,现在输出是一个四维向量$y$。要训练这个神经网络,你现在需要定义神经网络的损失函数,对于一个输出$\hat y$,是个4维向量,对于整个训练集的平均损失:

$$\frac{1}{m}\sum_{i = 1}^{m}{\sum_{j = 1}^{4}{L(\hat y_{j}^{(i)},y_{j}^{(i)})}}$$

$\sum_{j = 1}^{4}{L(\hat y_{j}^{(i)},y_{j}^{(i)})}$这些表示单个任务预测的损失,$L$可以比如说用Logistic损失。

在这个场合,一张图可以有多个标签。如果你训练了一个神经网络,试图最小化这个成本函数,你做的就是多任务学习。因为你现在做的是建立单个神经网络,然后解决四个问题,然后返回给你四个任务对应的标签。另外你也可以训练四个不同的神经网络,而不是训练一个网络做四件事情。但神经网络一些早期特征,在不同任务中都会用到,然后你发现,训练一个神经网络做四件事情会比训练四个完全独立的神经网络分别做四件事性能要更好,这就是多任务学习的力量。

那么多任务学习什么时候有意义呢?当三件事为真时,它就是有意义的:

  • 第一,如果你训练的一组任务,可以共用低层次特征。
  • 第二,这个准则没有那么绝对,所以不一定是对的。每个任务的数据量要很接近。但我通常会看的是如果你专注于单项任务,如果想要从多任务学习得到很大性能提升,那么其他任务加起来必须要有比单个任务大得多的数据量。如果对于单个任务你已经有1000个样本了,那么对于所有其他任务,你最好有超过1000个样本,这样其他任务的知识才能帮你改善这个任务的性能。
  • 最后多任务学习往往在以下场合更有意义,当你可以训练一个足够大的神经网络,同时做好所有的工作,所以多任务学习的替代方法是为每个任务训练一个单独的神经网络。多任务学习会降低性能的唯一情况,和训练单个神经网络相比性能更低的情况就是你的神经网络还不够大。但如果你可以训练一个足够大的神经网络,那么多任务学习肯定不会或者很少会降低性能,我们都希望它可以提升性能,比单独训练神经网络来单独完成各个任务性能要更好。

所以总结一下,多任务学习能让你训练一个神经网络来执行许多任务,这可以给你更高的性能,比单独完成各个任务更高的性能。但要注意,实际上迁移学习比多任务学习使用频率更高。我看到很多任务都是,如果你想解决一个机器学习问题,但你的数据集相对较小,那么迁移学习真的能帮到你,就是如果你找到一个相关问题,其中数据量要大得多,你就能以它为基础训练你的神经网络,然后迁移到这个数据量很少的任务上来。

端到端网络

端到端学习到底是什么呢?简而言之,以前有一些数据处理系统或者学习系统,它们需要多个阶段的处理。那么端到端深度学习就是忽略所有这些不同的阶段,用单个神经网络代替它,直接从输入到输出映射。

事实证明,端到端深度学习的挑战之一是,你可能需要大量数据才能让系统表现良好,比如,你只有3000小时数据去训练你的语音识别系统,那么传统的流水线效果真的很好。但当你拥有非常大的数据集时,比如10,000小时数据或者100,000小时数据,这样端到端方法突然开始很厉害了。所以当你的数据集较小的时候,传统流水线方法其实效果也不错,通常做得更好。你需要大数据集才能让端到端方法真正发出耀眼光芒。

但是通常来说端到端并不是完全的输入到输出,而是需要先对输入进行一些处理,准确获取那些可以利用的部分,然后再利用端到端网络来进行判断。相当于,第一步找到在哪里,第二步判断是什么。为什么两步法更好呢?实际上有两个原因。第一,你解决的两个问题,每个问题实际上都比原来这整个问题要简单得多。第二,两个子任务的训练数据都很多,那么就更容易训练一个更好的网络。比如对于门禁系统,第一步人脸边缘检测,第二步人脸识别。

应用端到端学习的一些好处,首先端到端学习真的只是让数据说话。所以如果你有足够多的$(x,y)$数据,那么不管从$x$到$y$最适合的函数映射是什么,如果你训练一个足够大的神经网络,希望这个神经网络能自己搞清楚,而使用纯机器学习方法,直接从$x$到$y$输入去训练的神经网络,可能更能够捕获数据中的任何统计信息,而不是被迫引入人类的成见。端到端深度学习的第二个好处就是,所需手工设计的组件更少,所以这也许能够简化你的设计工作流程,你不需要花太多时间去手工设计功能,手工设计这些中间表示方式。

那么缺点呢?这里有一些缺点,首先,它可能需要大量的数据。要直接学到这个$x$到$y$的映射,你可能需要大量$(x,y)$数据。其子任务可能有很多训练数据,但是整体起来的训练数据却很少。另一个缺点是,它排除了可能有用的手工设计组件。机器学习研究人员一般都很鄙视手工设计的东西,但如果你没有很多数据,你的学习算法就没办法从很小的训练集数据中获得洞察力。所以手工设计组件在这种情况,可能是把人类知识直接注入算法的途径,这总不是一件坏事。我觉得学习算法有两个主要的知识来源,一个是数据,另一个是你手工设计的任何东西(知识),可能是组件,功能,或者其他东西。所以当你有大量数据时,手工设计的东西就不太重要了,但是当你没有太多的数据时,构造一个精心设计的系统,实际上可以将人类对这个问题的很多认识直接注入到问题里,进入算法里应该挺有帮助的。

如果你在构建一个新的机器学习系统,而你在尝试决定是否使用端到端深度学习,我认为关键的问题是,你有足够的数据能够直接学到从$x$映射到$y$足够复杂的函数吗?如果你想使用机器学习或者深度学习来学习某些单独的组件,那么当你应用监督学习时,你应该仔细选择要学习的$x$到$y$映射类型,这取决于那些任务你可以收集数据。相比之下,谈论纯端到端深度学习方法是很激动人心的,但是就目前能收集到的数据而言,还有我们今天能够用神经网络学习的数据类型而言,这实际上不是最有希望的方法,或者说这个方法并不是团队想出的最好用的方法。而我认为这种纯粹的端到端深度学习方法,其实前景不如这样更复杂的多步方法。因为目前能收集到的数据,还有我们现在训练神经网络的能力是有局限的。

这与softmax回归的主要区别在于,softmax将单个标签分配给单个样本,而这里是将多个标签分配给单个样本。

【原文出自黄海广博士 deeplearning_ai_books

支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者,更多文章请访问想飞的小菜鸡