从开发到产出:关于机器学习的七则干货建议
笔者去初创公司工作主要是为了做项目,所以尽管任务紧急、团队又小,还伴随着频繁改动的项目要求以及其它琐事,笔者还是去了。不走运的是,笔者作为一名机器学习工程师着手的第一份项目并不那么容易上手。
那是一个大项目,背后有个大客户,意味着要担很大的责任,但是却只有一个小团队负责此项目,团队其中一员就是笔者这个新手。项目负责人安排笔者去处理十二个模型的那一天,光是想想都心有余悸。更糟糕的是,当笔者团队中的一个人被调去另一个项目时,他的工作量落到了笔者头上。
但笔者不是来向你们倾吐苦水的。尽管这个过程颇为曲折,但是也让笔者收获了很多意料之外的经验。
不断研究
笔者犯过最大的一个错误就是仅仅因为某种方法最适合一些特定的数据,就用这一种方法去处理所有数据。换句话说,笔者仅在项目伊始做了一个试验。在找到捷径以后,即使数据增长的越来越大,笔者依然坚持使用那一种方法。
笔者认为那是当时最有效的方法。然而作为一名机器学习工程师,不仅要建构一个系统,还要持续不断地提升它的性能。笔者花了一个月的时间才接受这一事实。
笔者的前任主管曾说,机器学习工程师既是软件工程师也是研究员。研究和软件工程可不一样。就算系统已经构建成功且没有 bug,研究也不会就此止步。研究是一个永无止境的过程,只要睁大双眼,总会发现可以尝试的新事物。
不要在模型学习的时候无所事事
“模型运行着呐! 就让我休息一会儿吧!”随着数据增长的越来越大,使用的方法也变得越来越复杂。一个完整的训练过程可以达到 2 至 3 小时。
当程序运行的时候,笔者常常借此拖延工作,现在我无比悔恨。有好几次都出现了意外,比如运行进程接近尾声的时候出现了程序错误、得分比之前更低了、内存错误以及其它在快结束时才意识到的愚蠢失误。
在模型学习的这段时间,明明可以做许多有意义的事情。
可以提前计划工作步骤。一个程序开始成功运行后,就用不同的方法着手下一个程序。等第一个程序运行完毕,另一个程序就可以开始运行了。在新程序运行的时候,则可以评估分析前一个程序运行的结果。所以如果发现前一个模型的任何可改进之处,就可以立即修正然后重新运行。
这个时间用来和主管或者其它工程师讨论新方法、新点子也是极好的。总之不要浪费时间。
做出明智的选择
某些情况下,不同的方法得出的结果差别可能不是那么明显。产出的成果相较其它方法的成果稍微优秀一点,这样的方法是不是就足够好到成为最终选择了? 不是。许多因素都应该被考虑在内。分析的过程不仅仅是从一个单一的角度找出最佳方法,分析是一个细水长流的过程。
例如,假设有两个垃圾邮件分类模型,它们的 F1 值分别为 0.95 和 0.96。第二个模型的精确率很低,召回率却很高,而第一个模型的精确率和召回率则比较平衡。
在这种情况下,第一个模型比第二个更好,因为它能更好地处理垃圾邮件分类。第二个模型更容易把将普通文档归类为垃圾邮件,这样就会有更多的非垃圾邮件文档将被划为垃圾邮件,这非常糟糕。
其他需要考虑的是与产出有关的重要事宜。某些情况下,如果资源和时间有限,选择一个简便的模型会助你一臂之力。前提是模型的性能仍然良好,并且与较复杂的模型没有太大区别。
切莫混淆数据
数据显然是影响模型性能的重中之重。因此,在项目执行之初就必须一丝不苟地建立一个详备的数据集,包括需要提取的信息类型、注释指南、各个数据类别之间的平衡以及视需求而定的其它重要事项。
如果使用标注工具 (annotator) 来构建数据集,特别是对于 NLP 模型,则要确保技术和语言学两方面认知的同步。有时候,语言学人员不明白某个特定的标注方法对现有的模型是否可行。同样地,工程师有时候也不理解语言学的内容。
需要注意的是,并不是数据越多越好。如果不能有效地反映所有的真实情况,再多的数据训练也是白费功劳。除此之外,数据标签的不一致也是个大问题。
另外,每次实验,训练、验证以及测试数据的结构和比例必须相同,特别是当数据逐渐增多时。为了确保模型的性能,还必须考虑案例类型的分布。如果有专门的测试数据再好不过,这有助于添加训练数据后比较模型性能。
把步骤衔接起来
能够构建端到端系统是作为机器学习工程师必备的另一项重要技能。更重要的是,如果同时处理多个模型,那么使用管线 (pipeline) 会方便很多。加载数据集、预处理和特征提取、训练和评估模型,以及做出预测都可以简化到只使用单个指令。
不要指望一次就能得到满意的结果。此外,长时间重复做某事确实会有压力。因此,使机器学习工作流程自动化非常重要,这样能节省时间、减轻压力。
其 **** 他框架和工具
笔者也会因为出现错误而大呼小叫。在把系统部署到生产环境这一步骤出错后,笔者再一次陷入懊恼。因此,了解一些可用于部署的实用框架和有效工具十分有必要。可以把一些接触过的框架和工具拿来做个比较,再根据需要选出最适合的。
对于笔者来说,Docker 在部署中非常重要。它帮助笔者在容器中部署多个模块。除此之外,Tensorflow Serving 也非常好用,它可以很方便地使训练好的模型应用于预测请求。
使人人都可以理解
写文档不仅是为了自己,也是为了他人。如果其他工程师想要继续试验,或者只是想使用你的程序,你创建的文档都能帮上他们。如果文档也方便专业之外的人员阅读和理解,那就更好了。
一份好的文档通常包含超参数 (hyperparameter) 的配置、时期(epoch)、方法、数据和分数等信息。此外,提供自动化的脚本来运行程序对其他工程师很有帮助,这样就不必先阅读整个代码了。
诚然,笔者还有许多东西需要学习。但最应该明白的是,不要害怕失败。因为研究和试验本身就是一个尝试、失败、再尝试的过程。