快速交付机器学习项目的经验

电子说

1.2w人已加入

描述

编者按:Insight Data Science AI负责人Emmanuel Ameisen和前百度硅谷人工智能实验室主管Adam Coates分享了快速交付机器学习项目的经验。

由于机器学习(ML)日益成为每个产业的重要组成部分,对机器学习工程师的需求增长迅猛。机器学习工程师结合机器学习技术与软件工程知识,为给定的应用寻求表现优异的模型,同时处理随之而来的实现的挑战——从创建训练基础设施到为部署模型做准备。虽然网上不断出现训练工程师创建ML模型并解决遇到的各种软件挑战的资源,而新ML团队最常遇到的一个障碍却是保持和传统软件工程同等水准的进度。

这一挑战最关键的原因是开发新ML模型的过程从一开始就是高度不确定的。毕竟,在最后的训练完成之前,很难知晓模型表现有多好,更别说大量的调参和采用不同的建模假定对模型表现有什么影响了。

多种职业人士面临类似的情况:软件和商业开发者,寻求产品-市场契合的初创企业,处于信息有限的演习之中的飞行员。每种职业人士采用一种常见框架以帮助团队在不确定的情况下高效作业:软件开发的敏捷原则,精益创业,美国空军的OODA循环。机器学习工程师可以遵循类似的框架,应对不确定性,迅速交付伟大的产品。

ML工程循环

本文将介绍机器学习的OODA循环:ML工程循环,其中ML工程师不断进行以下四个步骤:

分析

选型

实现

测量

从而快速、高效地发现最佳模型,并适应未知数据。此外,我们也为每个阶段提供了具体的窍门,并将介绍如何优化整个过程。

ML团队的成功常常意味着交付满足给定限制的表现优异的模型——例如,在满足内存占用、推理时间、公平性等限制的前提下,达到较高的预测精确度。表现是由和最终产品最相关的测度定义的,不管它是精确度,速度,还是输出的多样性。出于简单性,下文的表现测度我们选择了最小化“误差率”。

刚开始着手一个新项目时,你应该精确地定义成功标准,之后将其转换为模型测度。用产品术语来说,服务需要具备什么级别的表现才有用?例如,如果新闻平台向用户分别推荐5篇文章,其中需要多少篇用户相关的文章,我们又如何定义相关性?给定表现标准和现有数据,你可以创建的最简单的模型是什么?

ML工程循环的目的是提供一个开发过程中牢记的心智模型,简化决策过程,以集中精力处理下一个重要步骤。随着从业者经验的积累,这一过程变为从业者的第二本能,可以快速果断地在分析和实现间切换。话是这么说,不过,当不确定性增加时,即使是最有经验的工程师也会发现这一框架价值非凡——例如,模型出乎意料地没能满足需求,团队目标突然调整(例如,为了体现产品需求的变动,测试集做了改动),团队进程因缺乏目标而停滞。

开始

为了启动这一循环,你应该先从一个基本不涉及不确定性的最小实现开始。通常我们想要尽可能快地“得到一个数字”——创建一个足以评估表现并开始迭代的系统。这通常意味着:

开始设置训练、验证、测试集。

实现一个可以工作的简单模型。

例如,如果我们需要创建一个树检测器以调查某一区域的树木,我们也许可以使用相关Kaggle竞赛中现成的数据作为训练集,并使用我们自行收集的一组目标区域的照片作为验证集和测试集。我们接着可以在原始像素上运行逻辑回归,或者在训练图像上运行预训练好的网络(比如ResNet)。这里的目标不是一下子完结这个项目,而是开启我们的迭代循环。下面是一些有助于你达成这一点的窍门:

窍门

关于良好的测试集:

由于团队的目标是在测试集上取得较优表现,测试集实质上描述了团队的目标。因此,测试集应当反映产品或业务的需求。例如,如果你正创建一个基于自拍检测皮肤状况的应用,你可以在任意图像上训练,但需确保测试集包含光照和画质不佳的图像,因为一些自拍很可能出现这类情况。

改变测试集意味着调整团队的目标,所以最好及早固定测试集,仅当项目、产品、业务目标发生变动时才改动测试集。

尽量使测试集和验证集足够大,这样才能得到足够精确的表现测度,从而很好地区分不同的模型。如果测试集太小,你最终将基于噪声数据得出结论。

类似地,你应该在实际情况允许的情况下,尽可能确保测试集和验证集的标签和标注足够精确。错误标注的测试集差不多等于没有正确说明的产品需求。

了解人类或现存/竞争系统在测试集上的表现很有帮助。这给出了最优误差率的界限,也就是你可能取得的最佳表现。

对许多任务而言,达到和人类相当的水平经常是一个很好的长期目标。在任何情况下,最终目标都是使测试表现尽可能接近我们猜测的最佳表现。

关于验证集和训练集:

验证集是测试表现的代理,可用于调整超参数。因此,验证集的分布应当和测试集一致。不过,理想情形下,验证集和测试集应该来自不同组别的用户/输入,这可以避免数据泄露。确保这一点的一个好办法是首先积累大量样本,然后打乱顺序,之后将其分割为验证集和测试集。

如果在你的设想中,产品数据会有很多噪声,确保训练集考虑到了噪声问题(比如使用数据增强或数据劣化)。你不能期望完全在锐利图像上训练的模型能很好地推广到模糊图像。

实现了初始原型之后,你应该在训练集、验证集、测试集上测试它的表现。这标志着你度过了循环的第一个(退化的)周期。评估测试表现和有用的产品所需表现之间的差距。现在到了开启迭代的时刻了!

分析

识别表现瓶颈

在实践中,可能有许多交叉的问题导致了当前的结果,但你的目标是首先找出最明显的问题,这样你就可以快速解决它。不要拘泥于试图完全理解所有缺陷——转而理解最关键的因素,因为在你改进模型之后,许多小问题会改变,甚至消失。

我们下面列出了一些常见的诊断。选择从哪方面开始多多少少是门手艺,但随着ML工程循环的进行,你将逐渐获得尝试哪个的直觉。

对所有分析而言,一个很好的起点是查看训练集、验证集、测试集上的表现。我们建议你写代码在每次试验后自动进行这一比较,养成习惯。平均来说,我们有:训练集误差 <= 验证集误差 <= 测试集误差 (如果三个数据集中的数据遵循同一分布)。基于上一次试验的训练、验证、测试误差率,你可以快速地获知哪个因素是当前最大的限制。例如,如果训练误差和验证误差存在一定差距,那么训练误差是提升表现的瓶颈。

诊断

如果训练集误差是当前的限制因素,那么可能的问题有:

优化算法(例如,深度神经网络的梯度下降)没有调整准确。看看学习曲线,损失有没有下降。检查下是否能够过拟合一个小很多的数据集(例如,在单个minibatch甚至单个样本上训练)。你可以可视化神经元反应的直方图,看看它们有没有饱和(这可能会导致梯度消失)。

训练集可能有标注错误或毁坏的数据。在传给训练算法前,手工检查一些训练样本。

模型可能过小、过于简单。例如,如果你在高度非线性的问题上应用线性回归,你的模型无法很好地拟合数据。我们称为高偏差或欠拟合。

如果验证集误差是当前的限制因素,那么可能的问题有:

模型可能过大、过于复杂,或者正则化不够。我们称为高方差或过拟合。

训练数据不足。

训练数据的分布和验证集、测试集分布不同。

模型的超参数设得不好。如果你搜索最佳超参数(比如特征集、正则项),那可能是搜索方法难以找到较好的选择。

模型编码的“归纳先验”和模型匹配不好。例如,如果数据集用一个线性函数表示更自然,使用最近邻方法也许很难推广,除非你有海量训练数据。

如果测试集误差是当前的限制因素,这常常是因为验证集太小,或者团队在多次尝试的过程中过拟合验证集了。

不管是上面哪种情况,你都可以通过手工检查一组模型出错的随机样本,理解模型的缺陷(一般情况下你不应该在测试集上这么做,以避免在测试样本上“训练”系统)。

通过可视化数据尝试识别常见类型的误差。然后检查样本,记录每种类型的误差出现的频率。分类问题可以看下混淆矩阵,找出表现最差的分类。接着你就可以集中精力解决导致最多错误的那类误差。

有些样本可能错误标注了,或者有多种合理的标签。

有些样本可能比其他样本更难判断,或者缺乏做出判断需要的上下文。在若干种误差同样常见的时候,将一些样本标记为“很难”也许有助于你将精力花在容易得到改进的地方。类似地,将一些样本标记为“很容易”也许有助于你找出系统中细小的错误,导致模型在容易的情形上犯错。这有点像在数据的不同子集上估计“最优误差率”,接着深入进展空间最大的子集。

注意,上面的许多诊断有着直接、明显的解决方案。例如,如果训练数据太少了,那就获取更多训练数据!我们发现在心智上分隔分析阶段和(下面的)选型阶段是有帮助的,因为我们很容易陷入随机尝试各种方法,而没有真正分析背后问题的状况。另外,保持开阔的思路,勤于返回误差分析阶段,经常能够揭示有用的洞见,有助于改善你的决策。

例子

众所周知,卫星数据噪声很多,常常需要仔细检查

例如,Insight Data Science的Jack Kwok在创建一个帮助灾后重建的分割系统时,注意到尽管分割模型在卫星图像训练集上表现良好,在验证集上的表现却很差。这是因为验证集包含受到飓风袭击的城市,这些飓风图像的画质比训练数据差,更加模糊。通过在训练阶段增加一个额外的数据增强步骤,在图像上应用模糊滤镜,有助于降低训练集和验证集的表现差异。

在语音识别系统上,对验证集的深入误差分析可能揭示具有和大多数用户很不一样的浓重口音的说话人贡献了不成比例的误差数量。那么接着就可以检查下训练集,看看是否具备足够比例的类似口音样本,这些样本是否正确标注,训练算法能否成功拟合这些样本。部分用户类别代表性不够或者错误标注是一个机器学习偏见的例子。Google的语音系统采取的一种解决方案是主动向口音很重的用户请求更多训练数据。

选型

找出处理瓶颈的最简单方式

完成分析之后,你对模型造成了哪些类型的误差和影响表现的是哪些因素已经心中有数。就给定的诊断而言,也许有若干潜在的解决方案,下一步就是把它们列举出来,并制定优先级。

有些诊断直接导向潜在的解决方案。例如,如果你的优化器看起来没有调好,你可以尝试不同的学习率,或者考虑干脆换一种优化算法。如果训练数据集过小,收集更多的训练数据可能是合理、快速、容易的解决方案。

我们建议ML工程师及其团队列出尽可能多的也许有效的想法,接着采用简单、快速的解决方案。如果现有的解决方案可能有效(例如,使用你的工具箱中已经实现的另一种优化算法),就从现有方案开始。尽管更复杂的方法也许看起来能在一次巨大的转变中解决更多问题,我们常常发现多次快速迭代带来的改进超过了摸索当前最先进技术带来的收益。如果你可以在标注1000个数据点和研究最前沿的无监督学习方法中做选择,我们觉得你应该选择收集和标注数据。如果你可以从一些简单的启发式算法开始,就这么干。

窍门

取决于你的瓶颈,这里是一些常见的解决方案。

如果你需要调整优化器,以更好地拟合数据:

对数值优化器而言,尝试调整学习率或动量设定。从一个小动量(0.5)开始通常是最容易奏效的方法。

尝试不同的初始化策略,或者从预训练模型开始。

尝试一种容易调参的模型。在深度学习中,残差网络和带批归一化的网络可能训练起来要容易一点。

如果模型不能很好地拟合训练数据:

使用更大、更有表达力的那类模型。例如,使用决策树时,你可以配置更深的决策树。

检查训练集中模型出错的样本,看看有没有错误标注或缺失项。花时间清洗训练数据可以显著改善结果。

如果模型难以推广至验证集:

增加更多训练数据。注意,也许需要集中添加和验证集中所见误差类别类似的训练样本。

基于真实训练样本,生成新样本,以增强数据。例如,如果你注意到树检测器在有雾的图像上一贯表现糟糕,那么可以加上增强步骤,使用OpenCV让图像看起来雾蒙蒙的。

扩大搜索超参数的范围,或者进行更细致的搜索,确保找到在验证集上表现最佳的模型。

尝试不同的正则化方式(例如神经网络的权重衰减、dropout,决策树的剪枝)。

尝试不同类型的模型。不同类型的模型可以改变拟合和推广的表现,所以很难知道什么时候这个方法有用。深度学习的一大优势在于,有范围很广的神经网络构件可供尝试。如果你使用传统的模型(例如决策树或高斯混合模型),切换不同类型的模型需要花费更多精力。如果新模型内含的假定更正确,那么这一改变也许会有帮助——但最好先从容易的模型开始尝试。

实现

只创建需要创建的,快速创建

你知道应该尝试什么,并且已经简化了问题,现在只不过是实现而已……“只不过”。这一阶段的目标是快速创建原型,这样你就可以测量结果,并从中学习,然后快速开启下一轮循环。因此,我们建议你集中精力创建当前试验需要的东西。注意,尽管这一阶段的目标是快速学习,而不是打磨一切,你的实现仍然需要是正确的,这样你就可以频繁地检验代码是否能如期望一般工作。

窍门

在收集和标注数据时:

经常查看数据。查看原始数据,查看预处理后的数据,查看标签。这些再强调也不为过!仅仅是在收集、标注数据的流程中留心一点,就能捕捉许多误差。在Insight Data Science,我们经常碰到和数据清洗、包围盒坐标、分割掩码相关的bug。

标注、清洗数据是日常任务。大多数人高估了收集、标注数据的代价,却低估了在数据匮乏的环境下解决问题的难度。如果方法得到,很容易就在一分钟内标注用于分类问题的20张图像。你是愿意花一小时标注图像,得到一个包含1200张图像的数据集,再花一小时解决一个简单的分类问题,还是花3周时间尝试让一个模型基于5个样本学习?

当你为新模型写代码时,从一个类似的现有实现开始。许多论文现在都公布代码——所以在重新实现论文中的一个想法前,先获取配套的代码,因为论文常常漏掉一些细节。这会为你节省几小时乃至几天。如果可能的话,不管是什么问题,我们都建议遵循以下步骤:

寻找一个解决类似问题的模型实现。

在本地重现现有模型的实现(同样的数据集和超参数)

慢慢调整模型实现和数据工作流以匹配你的需求

重写需要重写的部分

编写测试以检查梯度、张量值、输入数据、标签的格式对不对。刚开始设置模型的时候就写测试,这样,如果捕捉到了错误,就不会再犯。

测量

打印测试结果和决定是否可以发布需要考虑的其他测度(例如,生产环境限制)。

如果表现某种程度上在提高,那么你也许处于正确的方向上。这种情况下,现在也许是完善你知道工作良好的那些部件的时机。另外,确保团队里的其他成员能够重现你的试验结果。

另一方面,如果表现在下降,或者没怎么提高,你需要决定是再试一下(重返分析阶段)还是放弃当前想法。如果ML循环的每一步都相对廉价,那就更容易做出决策:你不必花费过多精力让你的代码趋于完美,再尝试一次也不会花费太多时间——所以你可以根据想法的风险和收益做出决策,而不会受到沉没成本的干扰。

窍门

有用的表现测度包括ML方面的精确度和损失,以及商业价值测度。记住商业价值测度是最终的测度,因为正是它们决定了你创建的产品的有用程度。如果测试测度(ML代码优化的目标)偏离了商业测度,那么测量阶段结束之后,应该停下来考虑修改优化标准或测试集。

既然每个开发循环的末尾总是需要打印测度,同时计算分析阶段所需的其他测度会比较方便。

经常情况是,你最终创建了一个类似“控制面板”的东西,其中包含测试测度和商业测度,还有其他有用的数据。这在团队协作中特别有用。

优化循环

尽管机器学习任务具有内在的不确定性,上面的ML工程循环将帮助你有条不紊地迈向一个更好的模型。不幸的是,它一点也不神奇——你需要发展在每个阶段做出良好决策的能力,例如识别表现瓶颈,决定尝试哪种解决方案,如何正确地实现它们,以及如何测量应用的表现。你也需要习惯快速迭代的节奏。因此,你也应该花时间思考如何提高迭代的质量和速度,这样可以在每次循环中最大化进展,并且可以快速地完成多次迭代。

窍门

如果分析阶段拖慢了你的进度,那就创建一个脚本,总结每次试验结果,从训练集和验证集搜集误差,并以良好的格式打印出来。这种包含常用诊断信息的“控制面板”可以帮助你战胜“啊!我又得手工运行这些分析了……不如试试这个随机解决方案吧”这样的想法。

如果你觉得自己对到底尝试什么毫无头绪,那就随便选一样。一下子尝试做太多事情会拖慢你的进度。有时候你可以在运行试验的时候回过头来尝试另一个想法!

收集数据是取得更好表现的常用方式。如果获取更多数据听起来很痛苦,但是确实可以改变结果,那么也许应该投资让数据更容易收集、清洗、标注的工具。

如果你觉得自己陷入了困境,诊断不出瓶颈,或者选不出接下来试验的模型,考虑向专家咨询。领域专家经常能够就误差分析提供宝贵的洞见(例如,指出导致某些情形困难或容易的微妙差别),而研究论文或有经验的ML从业者也许可以为你提供值得尝试的创造性解决方案(如果你可以和他们分享详细的分析,他们可以更好地帮助你)。

良好的实现技能很重要,代码规范可以防止bug产生。话是这么说,由于相当大比例的想法会失败,因此在迭代过程中,在试验代码中使用一些临时性的不规范做法也没什么大不了的,毕竟失败的代码最终会被丢弃。一旦你确信自己取得了有用的进展,你可以在下一次循环之前根据规范清理代码。

如果试验时间过长,考虑花点时间看看能不能优化代码。或者和系统专家讨论下如何让训练更快。拿不定主意的时候,考虑升级GPU,或者并行运行更多试验,这些是ML试验“时忙时闲”问题久负盛名的解决方案。

和其他决策一样,仅当可以解决当前痛点的时候才致力于这些事项。有些团队花了太多时间创建“完美”的框架,最后发现真正的问题在别的地方。

结语

ML项目内在地具有不确定性,我们上面推荐的方法可以作为引导你前进的扶手。由于试验的命运不确定,你很难为达成特定的精确度目标而负责,但你至少可以负责完成误差分析,列出想法列表,编码实现,看看它们表现如何。就我们的经验而言,拒绝那些闪闪发光的模型的召唤,果断地集中精力于取得递增的进展,可以在研究和应用中导向杰出的成果。这曾经转变了很多团队,让无数Insight Data Science的工程师得以交付前沿项目。

如果你有什么要问的,欢迎留言,或通过Twitter联系两位作者(EmmanuelAmeisen和adampaulcoates)。

感谢Lacey Cope和Adam Coates对本文草稿给出的反馈。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分