这两年机器学习和AI越来越火,很多公司都开始在业务中使用机器学习模型,我所在的团队也不例外,去年我们在推荐系统中引入了机器学习模型,效果还不错。但是上线不久,就出了一次惊心动魄的故障,推荐系统突然异常,推荐的内容完全不对,用户投诉量暴增,整个团队紧急排查了好几个小时,最后发现是一个很低级的基础问题导致的。今天就来复盘一下这次机器学习故障的经历,聊聊排查过程、根本原因,以及我们总结的经验教训。

一、故障发生:推荐系统突然崩了

那天是个周五,本来是个很平常的日子,下午我正在写代码,突然运维群里有人@我,说推荐系统出问题了,用户反馈推荐的内容完全不对,都是一些不相关的东西,而且很多用户都在反馈,投诉量在快速上升。

我当时心里一紧,赶紧打开监控看,果然,推荐系统的点击率断崖式下跌,从平时的10%左右降到了1%都不到,说明推荐的内容确实完全不对,用户根本不点。而且错误日志也在疯狂增长,很多请求都超时了。

当时第一反应是,是不是模型服务挂了?我赶紧去看模型服务的状态,发现服务还在运行,CPU、内存都正常,没有挂。那是不是模型文件出问题了?我检查了一下模型文件,大小、修改时间都正常,没有被篡改。那是不是特征出问题了?我又去看特征服务,特征服务也正常,特征值看起来也没问题。

这就奇怪了,所有组件看起来都正常,但是推荐结果就是不对。这时候用户投诉还在增加,领导也在催,问什么时候能修好,当时压力真的很大,心里很慌,但是又不能表现出来,只能硬着头皮继续排查。

二、排查过程:一步步缩小范围

冷静下来之后,我开始系统性地排查,一步步缩小范围。

首先,我找了一个出问题的用户ID,手动调用推荐接口,看看返回的结果是什么。手动调用之后,发现返回的推荐列表确实很奇怪,都是一些很老的、不相关的内容,而且排序也不对,分数高的内容排在后面,分数低的排在前面,就好像排序反了一样。

这个发现很重要,说明不是推荐不出来,而是排序出了问题,模型预测的分数可能有问题,或者排序的时候用错了字段。

然后我去看模型预测的分数,把这个用户的特征输入模型,看看模型输出的分数是多少。一看吓一跳,模型输出的分数都是负数,而且绝对值很大,正常的分数应该是0到1之间的概率值,现在全是负数,这显然不对。

那为什么模型输出的分数是负数呢?我又去检查模型的输入特征,把特征值打印出来,一看就发现问题了,特征值全是乱的,有的特别大,有的是0,有的甚至是NaN(非数字),这显然不对。

特征值为什么会乱呢?我又去查特征服务,特征服务返回的特征值看起来是正常的,但是传到模型服务那边就乱了。这时候我突然想到,是不是特征的顺序不对?因为机器学习模型对特征的顺序是有要求的,训练的时候特征是什么顺序,预测的时候也要是什么顺序,如果顺序错了,特征就对不上,预测结果自然就乱了。

我赶紧去对比训练时的特征顺序和预测时的特征顺序,一对比就发现了问题,果然!预测的时候,有两个特征的顺序搞反了,而且还有一个特征漏掉了,没有传进去。这就是问题的根源!

那为什么特征顺序会错呢?我去查了代码提交记录,发现头天晚上有个同事提交了代码,修改了特征工程的部分,调整了特征的顺序,但是只改了特征服务那边,模型服务那边的特征顺序没有同步改,而且改的时候还不小心漏掉了一个特征。因为是晚上提交的,测试也没测出来,第二天上线之后就出问题了。

找到问题之后,修复就简单了,把模型服务那边的特征顺序改过来,把漏掉的特征加上,重新部署,重启服务,推荐结果马上就恢复正常了,点击率也慢慢回升了。从故障发生到修复,一共花了三个多小时,虽然不是特别长,但是影响很大,那几个小时的推荐效果很差,用户体验很不好,还流失了一些用户。

三、根本原因:不是模型的问题,是基础工程的问题

这次故障看起来是机器学习模型出了问题,但是深入分析之后,发现根本不是模型的问题,而是最基础的工程问题,是代码变更没有做好测试和验证,是特征顺序没有统一管理,是上线流程不规范导致的。

总结一下,这次故障的根本原因有几个:

第一,特征顺序没有统一管理。训练模型的时候,特征顺序是在训练脚本里定义的,预测的时候,特征顺序是在模型服务里定义的,两边是分开的,没有统一的配置,也没有校验机制,改了一边另一边不知道,很容易出错。这是最根本的原因。

第二,代码变更没有充分测试。那个同事修改特征顺序之后,只是自己简单测了一下,没有做完整的回归测试,也没有对比修改前后的预测结果是否一致,就提交了代码。而且是晚上提交的,第二天直接上线了,没有经过测试环境的充分验证。

第三,上线流程不规范。上线的时候,没有灰度发布,直接全量上线了,一上线就影响了所有用户。而且上线之后也没有及时监控,故障发生了半个多小时,还是用户反馈了才发现,监控告警没有及时触发。

第四,缺乏模型预测结果的校验。模型服务拿到特征之后,直接就预测了,没有对特征做校验,比如特征值的范围是否正常,有没有NaN,特征数量对不对,这些都没有校验,如果有校验的话,特征不对的时候直接报错,而不是用错误的特征去预测,至少不会返回错误的推荐结果。

这些都是最基础的工程问题,和机器学习本身没有关系,但是就是这些基础问题,导致了一次严重的线上故障。这也说明,机器学习系统,大部分问题都不是算法或者模型的问题,而是工程的问题,基础工程做好了,才能保证机器学习系统稳定运行。

四、经验教训和改进措施

故障修复之后,我们做了详细的复盘,总结了经验教训,并且做了一系列的改进措施,避免类似的问题再次发生。

第一,统一特征管理。我们把所有的特征定义、特征顺序、特征类型、特征取值范围,都统一放到一个配置文件里,训练脚本和模型服务都从这个配置文件读取特征定义,保证两边一致。而且配置文件变更的时候,需要经过评审,不能随便改。这样就从根源上避免了特征顺序不一致的问题。

第二,增加特征校验。在模型服务里,增加了特征校验逻辑,预测之前先校验特征的数量、顺序、取值范围,有没有NaN或者异常值,如果校验不通过,直接报错,并且用默认的推荐结果兜底,不会用错误的特征去预测,避免返回错误的推荐结果。

第三,完善测试流程。代码变更之后,必须做完整的回归测试,不仅要测功能是否正常,还要对比修改前后的模型预测结果是否一致,误差在允许范围内才能通过。测试环境充分验证之后,才能上线。而且禁止晚上提交代码,所有代码变更都在白天提交,有问题能及时发现和处理。

第四,规范上线流程。上线的时候,必须灰度发布,先发布到一小部分机器,观察一段时间,确认没有问题之后,再全量发布。而且上线之后,要密切监控关键指标,比如点击率、错误率、延迟,有异常马上回滚。

第五,完善监控告警。增加了更多的监控指标,比如模型预测分数的分布、特征值的分布、预测延迟、错误率,这些指标都有监控,有异常马上告警,不用等用户反馈才发现问题。而且告警的阈值设置得更合理,避免漏报。

第六,增加模型结果的对比监控。我们会定期抽样,对比模型预测的结果和预期结果是否一致,如果差异超过阈值,就告警,这样即使有问题,也能及时发现,不会等影响了很多用户才知道。

经过这些改进之后,我们的推荐系统稳定了很多,再也没有出过类似的故障。而且整个团队的工程意识也提升了,大家都认识到,机器学习系统,工程基础非常重要,不能只关注模型和算法,基础工程做好了,系统才能稳定。

五、写在最后

机器学习基础故障复盘:一次惊心动魄的经历。

以上就是我们那次机器学习故障的完整复盘,从故障发生、排查过程、根本原因,到经验教训和改进措施,都做了详细的总结。这次故障虽然影响很大,但是也给我们上了深刻的一课,让我们认识到了机器学习系统中工程基础的重要性。

很多人做机器学习,都只关注模型和算法,觉得算法越复杂越好,模型越高级越好,但是忽略了最基础的工程问题。实际上,机器学习系统,80%的工作都是工程,数据处理、特征工程、模型部署、监控运维,这些工程工作做好了,机器学习系统才能稳定运行,才能真正发挥价值。如果基础工程没做好,再好的模型也没用,上线之后各种问题,效果还不如简单的规则。

所以,做机器学习的同学,一定要重视工程基础,不要只盯着算法和模型,把数据、特征、部署、监控这些基础工作做好,系统才能稳定,才能真正产生价值。而且很多故障,都是很低级的基础问题导致的,只要我们多一点细心,多一点规范,很多故障都是可以避免的。

希望我们的这次故障复盘能给大家一些警示,做机器学习系统的时候,一定要重视工程基础,规范流程,完善测试和监控,避免类似的故障发生。也希望大家都能从故障中学习,不断进步,把系统做得越来越稳定。

最后用一句话结尾:"基础不牢,地动山摇。"不管做什么系统,基础都是最重要的,机器学习系统也不例外。把基础打牢,才能走得更远。