自以为是套路,结果反生技术债,开源代码应该注意这五大误区!

  • 2020 年 4 月 19 日
  • AI

作者 | 蒋宝尚

编辑 | 丛末

人工智能的研究该不该开源代码,一直是社区热议的话题。毕竟,一项机器学习研究不仅包括理论,算法和应用也是机器学习研究的重要内容。

ICML、ICLR 和 NeurIPS 都在尝试将实验代码和数据作为评审材料的一部分提交,鼓励作者在评审或出版过程中提交代码。事实证明,结果能够复现的研究往往也更能引起讨论,也更能促进学科领域的进步发展。

但是,却一直存在着这样问题:开源研究中的代码应该如何写?提交的时候应该注意什么样的事项,才能帮助其他研究者更容易复现论文结果?

一位机器学习研究员,在reddit上发出了倡议,提出了机器学习研究中开源代码时的五大反面教材(反模式),呼吁在开源代码的时候,尽量避免一些错误。

以下为他的倡议原文:

大家好,鉴于这个话题,我必须先声明一下:我也很佩服发表论文并有勇气开放源代码的研究员,他们非常了不起,我个人也从开源代码库中改编过一些代码,即用于自己的项目,也用于生产代码。我对此表示最深刻的敬意。

但是,这些代码有时候也出现问题 **runs for cover**

下面是我个人的笔记,里面包括五个反面教材,如果你有补充,欢迎评论留言,如果你不同意,指出来哪一个,我们展开讨论。

在敲机器学习相关研究代码,或者其他的啥领域代码的时候,请尽量避免:

1.做一个单一的配置对象,让所有的函数不断传递给你。配置文件非常棒,但是如果把他们加载到字典里面,然后到处mutating,那么这就会变成一场噩梦。

注意,在顶层这么操作通常不会有问题,也可以和你的CLI绑定在一起。

2.使用argparse当然可以,但是不要像上面note1那样使用,另外让我们废除 “from args import get_args(); cfg = get_args()”的模式。用更加直接的方法解析命令行的参数。(例如,如果你使用 argh,自然会让你围绕着可重用的函数来架构你的代码)

3.请不要让你的CLI接口泄露到你的实现细节中去,首先创建库,然后将其公开为CLI。这会让所有东西都更具可重用性。

4.除非有充分的理由(提示,很少有),否则不要使用文件作为进程间通信。如果你调用一个函数保存了一个文件,然后在下一行代码中加载这个文件,那就说明出了很大的问题。如果这个函数是来自不同的repo,可以考虑cloning它,修复后再PRing回来,使用修改后的形式。当然,还是有副作用的,往往会引起一个“无声”的Bug。

5.在几乎所有的情况下,除了最琐碎的情况,做一个事物列表上操作的函数比在单个事物上操作的函数更麻烦。所以,如果真的需要一个接受列表的接口,可以直接做一个新的函数,调用单个函数就可以了。

网友评论:还真是教科书级别的错误!

帖子放到reddit上面之后,立即引起了各路网友反响,大家似乎在一些学术论文中或多或少都遇到了这些问题。

(雷锋网)

哈哈,期初以为我只会在学术论文中遇到这些问题,随后我进入业界的时候,发现机器学习中的技术欠债是真实存在的。

(雷锋网)

在编写个人研究代码的时候,我并未总是提前对最终结果有个清晰的想法,接口需要不断更改,以前有意义的库可能在一些改变之后不再有意义。我使用反模式,通常是为了赶DDL时候,加快实现速度。

(雷锋网)

一个项目中往往有两种代码,一种是作为基础设施的代码,另一种是作为研究工作流的代码。前者应该是相当静态的,明确的,有很好的软件工程。后者应该是灵活的,且有可能是混乱的,能够优化为快速迭代。

这个话题非常重要,但是,你必须说明为什么这些事情是坏的,还必须提出替代方案。

机器学习中的反模式与技术债务

上面提到的五点写代码的反模式与机器学习技术债务息息相关,一般来说,反模式一开始用起来很爽,但是维护起来却有非常大成本开销。 

机器学习不同于其他,2015年谷歌曾经贡献过一篇年度顶级论文《机器学习系统,隐藏多少技术债?》,里面详细介绍了机器学习系统一些常见的的反面模式。其中过包括:

粘合代码 :机器学习研究者倾向于开发普遍适用的解决方案作为自给自足的包(packages)。采用通用软件包经常会导致粘合代码的系统设计模式,在这种系统设计模式中,包含了大量支持数据写入通用软件包或者数据从通用软件包中输出的代码。

粘合代码的代价从长远来看是很高的,因为这会让机器学习系统非常局限,如果需要测试其他方法,成本就会变得不可避免的昂贵。

对抗粘合代码的重要策略之一就是,将黑盒包装进普通的应用程序接口,以便更多地重复利用,降低更换包的成本。

管道丛林:这是粘合代码的一种特殊情况,经常出现在数据预备阶段。稍不注意,在机器学习系统良好的格式下,预备数据的结果系统可能会成为一个充满碎片的丛林,经常也会有中间输出文件在其中。

只有从整体上考虑数据收集和特征提取的过程,才能够避免“管道丛林”现象的发生。从头再来的方式虽然初始投资巨大,但却能够大幅度减少持续增加的成本。

失效的实验代码路径:在主要的生成代码中,通过执行实验代码路径作为条件分支来演示具有选择性方法的实验过程,短期内很有诱惑力,但是随着时间的推移,后台兼容性的维护会非常困难。

结果办法是周期性地重复检查每个实验分支,果断舍弃废物分支非常有益

抽象化债务:当时谷歌认为,明显缺少强抽象来支持机器学习系统。基于的观察是:机器学习领域的文献中,没有哪一篇将相关数据库(relational database)作为基本抽象(basic abstraction)的论文得到的结果能达到接近成功的地步。

常见异味(smell):即在一个系统中或者系统中的一个部件中存在的潜在问题。主要包括:1、POD类(Plain-Old-Data)异味,即机器学习系统采用浮点数、整数等普通的数据类型进行编码。2、多语言异味,用多种语言编写系统经常会增加测试成本,并且会增加将所有权让渡给其他人的难度;3、原型异味,定期地依赖原型环境意味着机器学习系统的脆弱性,时间压力会促使一个原型系统变成了产品解决方案,所以长远看需要付出更多的成本。