生产机器学习系统:部署测试#
你已经准备好把那个“预测独角兽出没”的模型部署上线了。但是在部署的时候,你需要保证整条机器学习(ML)流水线都得能跑起来、能持续更新、能对外提供服务,而且不出岔子。
想象一下,要是部署模型能像按下一个大大的“Deploy”按钮那么简单就好了。遗憾的是,部署一套完整的机器学习系统通常需要下面这些测试:
校验输入数据
校验特征工程
校验新模型版本的质量
校验服务基础设施
测试流水线各组件之间的集成
不少软件工程师偏爱测试驱动开发(TDD)——先写测试,再写源码。可 TDD 到了机器学习这里有点棘手。比如模型还没训练之前,你根本没法写一个测试去校验损失值;只能先在模型开发过程中摸清楚损失能降到多少,再拿新的模型版本去对标这个“可达损失”。
关于独角兽模型#
这一节讲的内容都是围绕那个独角兽模型展开,因此你需要先知道这些:
你正在用机器学习算法构建一个分类模型,用来预测独角兽的出没情况。你的数据集记录了 10,000 次独角兽出现,以及 10,000 次独角兽没有出现的情况。数据集中包含了位置、一天中的时段、海拔、温度、湿度、树木覆盖率、有没有出现彩虹,以及另外若干特征。
用可复现的训练过程来测试模型更新#
你多半还想继续改进独角兽模型。比如,你对某个特征又做了一轮特征工程,然后重新训练模型,指望这次能拿到更好的结果(至少不比原来差)。
遗憾的是,模型训练这件事有时很难复现。想提高可复现性,我们推荐这么几条:
MLWM: 这里是说,想用新加入的特征来提高 A 版本模型的效果,那么得需要保证除了新加入特怎以外的其它因素能和之前一样保持不变。例如你用同一个数据集训练10次模型,那么每次得到的模型的效果有时候差距还挺大,原因就在于尽管数据集一样,但是某些随机初始化的权重参数并不一样,这就导致了最终结果的差异。
确定性地给随机数生成器设置种子(MLWM: 类似我们常见的
random_state参数),细节可以参考数据生成中的随机化按固定顺序初始化模型的各个组件,保证每次运行时,各组件从随机数生成器拿到的都是同一个随机数
取模型多次运行的平均值
用版本控制管理,哪怕还只是早期迭代阶段,这样排查模型或流水线问题时,你能精确定位到代码和参数
当然,即便照上面这些都做了,仍可能存在其他来源的不确定性。
测试机器学习模型的 API 调用#
当调用机器学习模型的 API 更新以后我们又该如何进行测试呢?你当然可以重新训练一遍模型,但这样做太耗时间了。更省事的做法是写一个单元测试,然后随机生成输入数据,最后计算一次梯度下降的过程。只要这一步没有报错,那 API 的改动大概没把你的模型弄坏。
为流水线组件写集成测试#
在一条 ML 流水线里,改动一个组件,可能会让别的组件产bug。想要确认这些组件能正常地协同工作,那就写一个集成测试,把整条流水线从头到尾跑一遍。
除了持续跑集成测试,推送新模型、新软件版本时也应该跑一遍,很多你以为不会出现问题的地方往往会产生问题。不过整条流水线跑起来很慢,这让持续集成测试变得颇为棘手。想把集成测试跑快一点,可以只用数据的一个子集来测试,或者使用更简单的模型;具体怎么取舍,取决于你的模型和数据。想要拿到持续的覆盖,就把那些跑得快的测试调整成每个新模型版本或软件版本都会触发的方式,同时把跑得慢的测试放到后台持续运行。
上线前前先校验模型质量#
把新模型版本推上线之前,要检查下面两类潜在的退化情况:
突然退化:新版本里的一个 bug,就可能让模型效果明显掉下来。校验新版本的办法,是拿它的效果跟上一个版本作比较。
缓慢退化:用来发现突然退化的那套测试,未必能察觉模型效果在多个版本之间慢慢流失。所以要换一种做法,确保模型在验证集上的厕测试结果达到一个固定阈值。如果你的验证集已经偏离了线上实时数据,那就更新验证集,同时确认模型仍然能够满足同一个效果的阈值。
上线前校验模型与基础设施的兼容性#
如果模型更新的速度比服务器更快,那么模型和服务器装的软件依赖可能就不一样了,从而可能会引发不兼容的问题(例如深度学习框架的更新和底层驱动版本之间的兼容性问题)。要确保模型用到的那些操作在服务器上确实存在,就得先把模型放进服务器的一个沙箱版本里预演一遍。
关键术语:
- 训练-服务偏离(Training-serving skew)
- Z-score 归一化(Z-score normalization)