细数机器学习的三个“大坑”,如何避免入“坑”?
作者头像
  • 高杰
  • 2020-05-29 07:16:03 5

俗话说,“吃一堑,长一智”,这句话同样适用于工业机器人的学习。今天,我们将一起探讨机器学习中常见的三个“陷阱”,以及如何避免陷入这些“陷阱”。

数据科学家Archy de Berker在本文中详细描述了自己和周围同事在机器学习探索过程中遇到的问题。这些问题也是很多人在学习过程中可能遇到的常见错误。Berker希望通过这篇文章让大家了解一些有趣的错误——这些错误只有深入了解机器学习领域后才能意识到。

这并不是一篇入门级的文章,要想理解本文内容,最好在Pytorch或TensorFlow等平台上多加练习。

本文主要关注绿色分布区域的错误,但紫色和黄色分布区域的错误也会有所涉及。

一、机器学习中的常见错误

Berker将机器学习中出现的各类错误归纳为三个等级,按照严重程度从轻到重排列。

1、浪费时间的错误

计算科学中最难解决的两个问题之一是命名问题,图中的一条推文对此进行了高度概括。形状错误是最常见且最棘手的错误,通常是因为矩阵尺寸不匹配导致的错误。

本文不会过多讨论这类错误,因为这类错误很容易识别和纠正。大家常常会发现自己不断地重复同样的错误,然后修复,再犯,再修,如此循环往复。

2、导致结果不准确的错误

这类错误可能导致模型结果不准确,给项目带来巨大损失。例如,2015年一架从澳大利亚悉尼飞往马来西亚吉隆坡的亚航航班因技术故障在墨尔本紧急降落。如果模型的结果不准确,就像这架飞机的技术故障,最终会导向错误的结果。

举个例子,如果你在模型中增加了一个特征并添加了许多参数,但在未进行超参数调优的情况下对比之前的性能,发现模型性能变差,那么结论是新增特征降低了模型性能是不准确的。实际上,你需要更加规范的操作以寻求更具有表现力的模型。

错误对模型的影响会随着时间积累,导致实验结果越来越不准确。因此,尽早发现并纠正这些错误非常重要。

3、让你误认为模型已经完美的错误

这是一种严重的错误,会让你高估模型的性能。这类错误通常很难发现,因为人们不愿意承认看似完美的模型可能是假象。

当模型表现很差时,我们倾向于怀疑并再次测试;然而,当模型表现很好时,我们往往会相信并自我满足。这就是所谓的确认偏差,即个人无论是否合理,都倾向于偏好支持自己的观点。

模型看似“完美”的情况通常是由于过拟合造成的,导致训练数据失去代表性,或者评价指标选择不当。这两种情况在下文会有详细解释。

如果从本文中只带走一点,那就是:发现模型的实际效果远不如预期时,那种尴尬和失望感是最令人沮丧的。

二、机器学习的生命周期

机器学习就像香肠机的三个阶段:获取数据、将数据输入模型、通过一些目标来量化输入。

接下来我们将讨论每个阶段中的一些常见错误。

1、输入什么:评价指标

机器学习可以归结为不断减少损失函数值的过程。

然而,损失函数并不是最终的优化目标,它只是一个近似值。例如,在训练分类任务时,通过交叉熵损失函数来优化训练集或验证集,但实际上我们更相信在测试集上的结果,即F1、AUC等评价指标。

在实际优化目标的数据差异很小的情况下,采用低置信度来加速评价过程可能会导致更大的问题。

无论在何种情况下,如果损失函数已经不能代表模型的真实表现,那么麻烦就大了。

此外,还有些做法会让模型变得更糟糕:

1)混合训练集和测试集

混合训练集和测试集很容易实现,而且通常会训练出不错的效果,但这样的模型在复杂的真实环境中表现会很差。

因此,训练集、验证集、测试集的数据是不能相交的,每部分需要包含不同的样本数据。我们需要考虑模型需要怎样的泛化能力,这最终会通过测试集的表现来衡量。

以商店收据为例,使用商店收据进行分析预测,那么测试集显然需要包含以前没见过的新数据,但测试集是否也需要包含以前没见过的新商品以防止模型对特定商店的过度拟合?

最好的方法是一次性将数据分为训练集、验证集和测试集,并分别存储在不同的文件夹中,文件名应清晰明确,例如TrainDataLoader和TestDataLoader。

2)错误使用损失函数

错误使用损失函数的情况较少,因为已有大量资料教我们如何正确使用损失函数。最常见的两种错误包括混淆损失函数中的概率分布与对数,以及混淆回归函数和分类函数。

即使在学术界,混淆回归函数和分类函数也非常普遍。例如,Amazon Reviews数据集经常被顶级实验室用于分类任务,但这实际上是不正确的,因为5星评价显然更接近4星评价,应该采用有序回归。

2、选择错误的评价指标

不同的任务需要不同的损失函数,而在模型性能验证时,我们通常会结合多个侧重不同的评价指标来评估模型性能。例如,机器翻译首选BLEU作为评价指标,自动摘要使用ROUGE来验证性能,而对于其他任务,可以选择准确性、精度或召回率作为评价指标。

通常,评价指标比损失函数更容易理解。一个好思路是在日志中尽可能多地记录信息。

仔细考虑如何划分不相交的训练集、测试集和验证集,使模型具备良好的泛化能力。

在训练过程中,可以使用评价指标来测试模型性能,而不必等到最后才开始使用测试集。这样有助于更好地了解模型当前的训练结果,避免问题到最后才暴露。

选择评价指标时要注意。例如,你不能用简单的准确性来评价序列模型的性能,因为序列间的非对齐情况会导致准确性为零。因此,对于序列数据,应采用间隔来评价。选择错误的评价指标是一件非常痛苦的事情。

以序列模型为例,请确保移除所有特殊字符,因为特殊字符通常是序列的开头、结尾和填充。如果忘记移除特殊字符,可能会得到看起来不错的模型性能,但实际上这样的模型只能预测充满填充字符的长序列。

还有一个让作者印象深刻的错误,他曾在做语义解析时,目的是将自然语言句子转换为数据库查询,回答诸如“明天从蒙特利尔到亚特兰大有多少趟航班?”这样的典型SQL问题。

为了评价模型的准确性,他将模型生成的SQL查询发送到数据库,检查返回的内容是否与真实查询匹配。他设置了一种情况,如果向数据库发送无意义的查询,数据库返回“error”。然后,他发送了已损坏的预测SQL和真实SQL到数据库查询,两者都返回“error”,模型将这种情况计算为100%准确。

这就引出了指导原则:你犯的任何错误只会使性能变差。要坚持检查模型实际做出的预测,而不仅仅是关注评价指标的结果。

3、避免评价指标的错误

1)首先运行所有的评价指标

在没有任何训练的情况下,如果模型表现很好,那一定有问题。

2)记录所有过程的日志

机器学习是一门定量学科,但数字有时会骗人,所有可以想到的数字都需要记录日志,但要以易于理解的方式记录。

在NLP中,这通常意味着你需要反转标记,这是一个复杂的过程,但绝对值得。日志提供了模型训练过程中的定性解释。例如,语言模型通常从学习输入类似“eeeeeeeeee”开始。

如果是处理图像任务,那么日志会更加复杂,因为你不能将图片以文本方式存储为日志。可以通过使用ASCII来处理这个问题,在OCR训练过程中使用ASCII保存日志,从而可以可视化输入的图像数据:

图像

训练期间,尽可能可视化模型的输入和输出。

3)研究验证集

使用测试评价指标来确定集合中性能最佳和最差的样本。了解样本状况,使用一些量化置信度的方法(如softmax),了解模型在哪些分布上表现良好、哪些分布上表现较差。

在回归任务中,残差分析非常有用。

但请记住,正如Anscombe四重奏所示,平均值可能会误导你。

Anscombe Quartet

Anscombe四重奏:所有四个模型的均值和方差相同,且它们都拟合了同一条回归线t。因此,不要过分依赖统计结果,要了解数据本身。

如果遇到多维问题,尝试绘制错误与单个特征的关系图以找出原因。是否存在模型表现非常差的输入空间区域?如果是这样,你可能需要在这个数据区域补充更多数据或进行数据增强。

考虑消融和干扰在模型性能中的影响。诸如LIME和Eli5等工具可以帮助简化模型。下面这篇文章很好地描述了扰动分析,揭示了用于X射线分类的CNN模型使用X射线机本身引入的标签来确定患者是否患有肺炎,而不是X射线机的使用本身与患病率之间的相关性。

三、模型

现在许多课程和文章都强调建模的重要性。但实际上,作为机器学习从业者,大部分时间都花费在处理数据和目标上,而不是研究创新的算法。

深度学习错误中的大多数都是形状错误,从而导致明显的错误发生。

1、模型错误类型

模型错误类型很多,包括:

1)包含不可微分运算操作的模型

在深度学习模型中,一切都必须是端到端可微分的,以支持反向传播。因此,你可能希望不可微分操作可以在TensorFlow等深度学习框架中被明确标识。这是不对的,正如Berker曾对Keras Lambda层感到困惑,因为它可以破坏反向传播。一种解决办法是使用model.summary()来检查,以验证大多数参数是可训练的,如果有不可训练参数的层,则可能破坏了自动微分能力。

2)测试时没有关闭Dropout

我们知道,在测试数据时需要关闭dropout,否则可能会得到随机结果。这可能会非常令人困惑,特别是对于正在部署模型并开始运行测试集的人来说。

这个问题可以通过eval()来解决。另外需要注意的是,在训练模型时dropout可能会导致一个奇怪的现象——模型在验证集上的准确性高于训练集上的准确性。这是因为验证集上使用了dropout,这看起来像是欠拟合,可能会引起一些头疼的问题。

3)维度参数错误

不同框架在样本数(batch size)、序列长度(sequence length)和通道数(channels)上有不同的约定,有些框架允许在这三个方面进行调整,但其他框架不允许随意调整,否则会导致错误。

维度参数错误可能会导致一些奇怪的现象。例如,如果你弄错了样本数和序列长度,那么最终可能会忽略部分样本的信息,并且无法随着时间保存信息。

2、避免模型的错误

1)模块化、可测试

如果发现有不可训练参数的层,则可能破坏了自动微分能力。

编写结构合理的代码并进行单元测试有助于避免模型错误。

将模型分为几个独立的代码块,每个代码块有明确的功能定义,就可以对其进行有效的测试。

2)维度结论

Berker倾向于在ML代码中加入维度结论,让读者可以清楚地知道哪些维度应该更改,哪些不应该更改。当然,如果发生意外,它会引发错误。

富有表达力的Tensorflow代码

3)小数据简单模型的过拟合问题

技巧:先确保模型在非常小的一部分数据集上进行过拟合训练,短时间内排除明显的错误。

尽量让模型能够轻松通过配置文件进行配置,并指定最少的参数测试配置。然后在CI/CD中添加一个步骤,检查非常小的数据集的过拟合,并自动运行它。这将有助于捕获破坏模型和训练管道的代码改动。

四、数据

1、首先要了解数据

在开始建模之前,你应该已经厌倦了数据探索吧。

大多数机器学习模型都在尝试复制人脑的某些形式识别能力。在开始编写代码之前,需要熟悉数据,训练模式识别能力,使你的代码更加高效!了解数据集有助于整体架构的设计和目标的选择,还可以快速识别可能出现性能问题的地方。

一般来说,数据本身就能识别一些问题:数据不平衡、文件类型问题或数据偏见。数据偏见很难通过算法识别,除非你有一个非常“聪明”的模型能识别这些偏见。例如,一个“聪明”的模型能自行认识到偏见:“所有猫的照片都是在室内拍摄的,所有狗的照片都是在室外拍摄的,所以我可能在训练一个室内/室外分类器,而不是识别猫和狗的分类器。”

正如Karpathy所说,数据探索系统可以完成数据查看、数据切片和数据切块。2018年在伦敦举行的KDD会议上,他在演讲中强调,许多Uber的机器学习工程师不是在编写代码来优化模型,而是在编写代码来优化数据标签。

要了解数据,首先需要明白以下三种数据分布:

  • 输入数据的分布,例如平均序列长度、平均像素值、音频时长。
  • 分类失衡是一个大问题。
  • 输入/输出的分布,这通常是你需要建模的内容。

2、选择如何加载数据

有效地加载和预处理数据是机器学习工程中比较痛苦的环节之一,往往需要在效率和透明度之间权衡取舍。

像TensorFlow Records这样的公共数据结构可以将数据打包成大数据集,减少对磁盘的频繁读写,但这种方法会影响透明度:这些结构很难进一步研究或分解数据,如果你想要添加或删除一些数据,则必须重新序列化。

目前,Pytorch Dataset和DatasetLoader是平衡透明度和效率较好的方法,公共程序包torchtext处理文本数据集,torchvision处理图像数据集,这些程序包提供了相对有效的加载方式,填充并批处理每个域中的数据。

3、加快数据加载的方法

以下是Berker在加快数据加载方面的经验教训:

1)不要加载正在加载的数据

这是因为你最终可能会丢失数据或加载重复数据。Berker曾经踩过的坑:

  • 编写正则表达式从文件夹中加载某些文件,但在添加新文件时没有更新正则文件,这意味着新文件无法成功加载。
  • 错误计算一个epoch中的步数导致跳过了一些数据集。
  • 文件夹中有递归符号,导致多次加载相同的数据(在Python中,递归限制为1000)。
  • 无法完全遍历文件层次结构,因此无法将数据加载到子文件夹中。

2)错误存放数据

不要把所有数据放在一个目录中。

如果你有上百万个文本文件全部放在一个文件夹中,那么任何操作都会非常非常慢。有时甚至只是查看或计算的动作,都需要等待大量的文件夹加载,从而大大降低了工作效率。如果数据不在本地,而是远程存储在数据中心,使用sshfs挂载目录,情况会更糟。

另一个错误陷阱是在预处理时没有备份数据。正确的做法是将耗时的预处理结果保存到磁盘中,这样就不必在每次运行模型时都重来一遍,但要确保不覆盖原数据,并需要不断跟踪在哪些数据上运行了哪些预处理代码。

示例

3)不恰当的预处理

在预处理中出现数据滥用的情况是常见的,尤其是在NLP任务中。

非ASCII字符的错误处理是一个很大的痛点,这种状况不常出现,因此很难发现。

分词也会导致很多错误。如果使用基于词的分词,很容易基于一个数据集构建词汇表,结果在另一个数据集上使用时却发现,大量的词汇在词汇表上找不到。这种情况下模型不会报错,但它在其他数据集上表现不佳。

训练集和测试集之间的词汇差异也是一个问题,因为那些只出现在测试集中的词汇是没有经过训练的。

因此,了解数据并尽早发现这些问题是非常有价值的。

4、避免数据处理的错误

1)尽可能多记录日志

确保每次数据处理时都有样本数据的日志,不应只记录模型结果日志,还应记录过程日志。

2)熟悉模型超参数

你需要非常熟悉模型的超参数:

  • 有多少样本?
  • 一次训练所选取的样本数有多大?
  • 一个epoch有多少批处理?

这些也应记录日志,或添加一些结论以确保所有内容都被涵盖。

3)预处理过程中记录所有形状

某些预处理步骤需要使用或创建工件,因此需要记得将其保存。例如,使用训练集的平均数和方差对数值数据进行正则化,并保存平均数和方差,以便在测试时使用相同的变换。

同样,在NLP中,如果不保存训练集的词汇表,就无法在测试时以相同的方式进行分词。如果在测试中构建新的词汇表并重新分词,可能会产生有意义的结果,因为每个单词都将得到一个完全不同的标记。

4)降采样

当数据集非常大(例如图像和音频)时,将数据输入到神经网络中,期望模型能够学习到最有效的预处理方法。如果有有限的时间和计算能力,那么这可能是个好方法,但在实际情况中,降采样是比较合适的选择。

我们不需要全高清图像来训练狗/猫分类器,可以使用扩张卷积来学习降采样器,或者使用传统的梯度下降来完成降采样。

降采样可以更快地完成模型拟合和评价,是一种较好的节约时间的做法。

五、结论

总结一下在机器学习中应遵守的五条指导原则:

  1. 从小处着手,实验会进行得很快。 减少循环时间可以及早发现问题并更快地验证假设。
  2. 了解数据。 不了解数据就无法做好建模的工作。不要浪费时间在花哨的模型上,要沉心静气地完成数据探索工作。
  3. 尽量多地记录日志。 训练过程的信息越多,就越容易识别异常并进行改进。
  4. 注重简单性和透明性,而不仅仅是效率。 不要为了节省大量时间而牺牲代码的透明性。了解不透明代码所浪费的时间比低效算法的运行时间多得多。
  5. 如果模型表现优秀令人难以置信,那很可能有问题。 机器学习中存在许多错误可能会“捉弄”你,成为一名优秀的科学家意味着要理性地发现并消除这些错误。
    本文来源:图灵汇
责任编辑: : 高杰
声明:本文系图灵汇原创稿件,版权属图灵汇所有,未经授权不得转载,已经协议授权的媒体下载使用时须注明"稿件来源:图灵汇",违者将依法追究责任。
    分享
大坑避免机器三个如何学习
    下一篇