当前位置: 首页 > 科技观察

建造容易,维护难!谷歌机器学习系统的经验教训

时间:2023-03-15 15:52:04 科技观察

2014年,谷歌一篇关于机器学习背后隐藏的高技术债务的论文一度走红。今天,这篇论文登上了知名技术社区HackerNews的头条。看似时隔4年,人工智能已经进入了新的春天,但困扰机器学习研究者的问题依然相似。作者表示希望本文能够为生产环境中的机器学习系统的开发者和维护者提供一些实用的建议。作者警告说,虽然从头开始构建机器学习系统相对容易,但后续改进可能会出乎意料地困难。阅读本文时,读者会明显感受到其中的心得,来自于作者在谷歌任职期间来之不易的心血。AI前线带你重温这篇经典文章。文章中的一些经验强调“与机器学习相关并不意味着可以完全抛弃良好的软件工程实践”,部分内容属于机器学习特有的常见陷阱。鉴于所有希望建立“X+AI”业务的初创企业都面临的潜在挑战,本文中的建议非常值得认真考虑。本文提到的机器学习系统带来的技术债务主要类别包括:信息隐藏和变更关闭的挑战;胶水代码和配置;以及不断变化的外部世界和分析模型的结论。本文最重要的见解之一是,技术债务应该成为工程师和研究人员关注的问题。以大量增加系统复杂性为代价进行解决方案研究显然是不明智的。即使添加一两个看似无害的数据依赖项也有可能减慢进一步的开发速度。虽然减少技术债务不如证明新定理那么令人兴奋,但它仍然是强大创新的重要驱动力。事实上,为复杂的机器学习系统开发全面而优雅的解决方案是一项具有重大现实意义的工作。信息隐藏和变化的封装……机器学习模型是一个创造复杂纠缠状态的机器,在其改进上不可能有效地隔离工作的分布。作为证明,假设E在一组模型中使用特征x1,...Xn,那么如果我们改变x1中值的输入分布,那么它的重要性、权重或剩余的特征都可能发生变化——不管模型是批处理的无论是以形式进行完整的再训练还是以在线方式进行逐步适应,都是如此。添加新特征xn+1或删除任何特征xj可能会导致类似的变化。没有输入是真正独立的。Shepherd将此称为CACE原则:任何改变都会改变一切。如果我们了解先验的重要性,就不需要用机器学习重复证明!所以这个模型有点像一个巨大的搅拌机,我们把很多信息扔进去得到结果,但是对输入的各种变化具有不可预知的敏感性,并且几乎没有效果隔离。面对如此棘手的问题,我们该怎么办?虽然没有放之四海而皆准的解决方案,但作者提出了三种可能有用的策略。您可以隔离模型,而是提供一个总体结论。然而,纠缠的问题仍然存在于每个模型中。此外,“从规模上看,这种战略可能难以长期维持”。开发方法以深入了解模型预测的行为。您需要投资使模型不再是难以捉摸的黑匣子——例如,为其配备更多可视化工具。此外,我还与几家公司交谈过,其中一些公司表示,他们解释机器学习模型做出的决策的能力——甚至是监管要求——是他们商业模式的重要组成部分。因此,请慎重考虑这方面的要求和实施方式。使用更复杂的正则化方法,以便训练期间使用的目标函数反映性能预测成本增加的任何迹象。“这种方法可能有效,但它只是可能的。此外,它可能会增加系统的复杂性,从而增加债务,这违背了减少纠缠的目的。”寻找偶发耦合的另一种方法是建立隐藏反馈循环,这在未声明的消费者中尤为明显。通过取消声明消费者,系统只是在消费建模输出的输出,我们几乎不知道这些过程的存在。这个隐藏的反馈回路如果根据影响模型的输入参数信息采取一些行动,很容易导致问题:想象一下,在我们的新闻标题点击率预测系统中,系统中的一个组件负责“智能方式”确定用于标题的字体大小。如果这个字体大小模块开始使用点击率作为输入信号,并且字体大小确实会影响用户的点击倾向,那么点击率会增加一个新的隐藏的字体大小反馈回路。可以想象,这样的系统会逐渐无休止地增加所有标题的字体大小。数据依赖问题...虽然可以通过静态分析和链接图等方法相对容易地识别代码依赖,但是处理数据依赖的分析工具很少见。因此,我们可能很难构建一个可以解决大数据依赖链的解决方案。例如,一些输入信号会随时间改变行为。遵循CACE原则,即使将这些变化作为改进的方向,也很难预测后果。另一个数据依赖性是模型中的特征集合,其中一些特征提供的准确性非常有限。我们可以通过多种方式利用其他未充分利用的依赖项-包括已弃用的早期遗留功能的子集,一次添加的功能组合(而不是仅仅挑选出实际工作的功能),或者是为追求准确性而添加,无法证明其自身对复杂性的影响。定期清洁功能非常重要。例如,为简单起见,假设在团队合并后有一轮从旧产品编号到新产品编号的转换,那么两组场景都将在系统中显示。其中,新产品只能获得一个新号码,而旧产品可能同时拥有两个号码。机器学习算法当然也会将旧数字合并到依赖项中。一年后,管理人员清理了用旧数字填充的数据库代码。这种变化不会被回归测试检测到,因为旧号码在清理后直接没有被使用。对于机器学习系统的维护者来说,这显然不是一个好消息……能够理解数据依赖性的工具将帮助我们顺利进行特征清理。Google的一个团队构建了一个自动化功能管理工具:自采用该解决方案以来,该解决方案已帮助Google团队每个季度安全地删除数千行与功能相关的代码,同时自动验证版本和其他问题。该系统还可以有效防止在新型号中意外使用已弃用或损坏的功能。数据依赖性管理的最后一种方法是建立“校正”机制以重用现有模型。这样,您可以快速获得初步结果;但另一方面,你以后对模型的分析和改进将面临更高的成本。其他95%(粘合代码和配置)令人惊讶的是,学术界意识到在大多数机器学习系统中,只有一小部分代码实际上在进行“机器学习”。事实上,一个成熟的系统最终可能最多有5%的代码负责机器学习,剩下的95%或更多只是充当胶水,通过重新实现(而不是重用)来改进原本笨拙的API。.这里要解决的问题是很多机器学习库被打包成独立的工件,这无疑会引入很多胶水代码(比如从Java转R或者matlab)。如果您无法在更广泛的系统架构中找到适合您的资源选项,重新实现算法(5%的代码)并有效减少胶水代码量可能更有意义。一个相关的问题是管道丛林——过于复杂的数据准备管道。管道丛林问题只能通过全面审视数据收集和特征提取来避免。清理管道丛林并从头开始设计清理方法确实是工程层面的一项重大投资,但它也可以显着降低持续成本并加速进一步的创新活动。一旦系统因为胶水代码和流水线丛林问题变得僵化,很多朋友都会忍不住在生产代码中调整实验代码路径,进行额外的实验。这样当然更方便,但是一旦出现频率太高,只会造成更大的混乱。作为一个典型的例子,谷歌最近在一次重大的机器学习系统清理中发现了数万行未使用的实验代码。当用更紧凑的API重写时,这部分“遗留”可以显着减少工作量、生产风险和控制系统的复杂性,从而为新算法的实验铺平道路。在本节末尾,“配置通常是混乱的现实世界干扰美丽算法的工具:”请考虑以下示例。特征A在9月14日至9月17日期间被错误记录。功能B直到10月7日才正式上线。由于记录格式发生变化,11月1日前后的数据计算特征C的代码必须改变。FeatureD没有在生产中使用,所以在livetuning中进行模型查找时必须使用备选D'和D"。如果使用featureZ,那么所有与训练相关的任务都必须获得额外的内存配额,否则它们的训练效率将会降低显着减少。最后,由于延迟限制,特征Q排除了特征R。所有这些混乱的情况使得配置难以正确修改并且难以推理。此外,配置错误还会带来高昂的成本-包括严重的时间浪费,耗尽计算资源或生产问题。配置更改应与代码一样小心处理世界不断变化的本质是机器学习技术债务的重要来源。与其手动设置决策阈值(例如显示或不显示广告),不如考虑评估现有验证数据以发现阈值。还有,因果不明的相关特征可能会引起问题:这似乎不是什么大问题:如果两个特征总是相关的,但只有一个特征属于真正的因果关系,那么归因似乎还是可以的相信两者并通过观察它们的共同现象得出结论。然而,如果这两种特征的共生在外部世界突然消失,预测行为可能会发生巨大变化。用于区分相关效果的综合机器学习策略也超出了我们的讨论范围;[Bottou2013]对此给出了一些很好的建议和参考。结合本文的重点,我们注意到非因果关系是隐性债务的另一个来源。最后,实时监控系统至关重要。该论文提出测量预测偏差,并在系统采取的动作数量超过特定阈值时发出警报。在按预期工作的系统中,预测标签的分布通常应该等同于观察标签的分布。这不需要完全测试,因为它可以通过单个空模型来满足——即直接预测标签的平均值,而不考虑输入特征。然而,这种简单的方法产生了令人惊讶的好结果,并且此类指标的变化通常反映了需要注意的关键问题。……