“这家供应商去年有三次不合格,今年就该多抽它。”这句话在很多时候是对的,但如果把它当成风险预测的全部,至少会在下面三种情形里判断错。
情形一:新供应商,没有历史
新入库的供应商,历史不合格次数是 0,只看次数的话,它的风险最低。可“没有记录”只说明没检过,不说明没有问题。这类对象往往恰恰是最该关注的:我们对它一无所知。合理的做法是让它继承同品类、同产地、同季节的整体水平,再随着检测结果逐步更新,这一步在抽检历史变成优先级那篇里叫冷启动。
情形二:历史很干净,但环境变了
某产区过去几年一直合格,今年却连续降雨、湿度偏高,或者出现了新的病虫害,用药与储存条件都会随之变化。历史干净的对象在这样的窗口里风险可能上升,只看历史的模型对此毫无反应,因为它的输入里根本没有天气。
情形三:历史很多,但已经整改
一家企业去年多次不合格,之后更换了设备、调整了工艺,近三个月的检测都合格。如果模型把全年次数等权相加,它会一直排在前面,占用本可用在别处的检测资源。时间衰减、“最近一次不合格距今多久”、整改记录,这些都应该进入模型。
哪些因素会进入模型
上面三种情形指向同一件事:风险来自多个层面,历史次数只是其中一层。常见的输入包括:
- 环境:产地的水源、土壤与周边环境,以及产区所处的地理与气候条件。
- 天气:降雨、湿度、气温。高温高湿有利于霉菌生长,进而与霉菌毒素类风险相关;降雨还会改变病虫害压力,也会影响施药与采收的间隔安排。
- 供应链:转运次数、仓储时长、冷链是否有断点、中间商数量。同一批产品经手的环节越多,可能出问题的位置也越多。
- 品类:叶菜、根茎、水产、坚果、乳制品的主要风险类型各不相同,不应被放进同一个池子里训练。
- 加工环节:清洗、烘干、切分、灌装、包装引入的风险各异,同一批原料在不同环节的风险也不一样。
因素之间还有交互。同样是连续阴雨,对露天叶菜和对入库储存的坚果,影响机理并不相同,模型必须把品类和天气放在一起看。树模型可以自动学到这类交互,前提是训练数据里各种组合都出现过,而这又回到了抽检要留一部分随机探索的问题。
这些因素多数是结构化数据,适合用 XGBoost 一类树模型来处理,再用 SHAP 把每个因素对某一批的贡献拆开展示,检测人员才能知道一个高分“从哪里来”,这一点首页风险分数从哪来那篇有具体的示意。
图中历史不合格次数被画得又小又淡,是有意为之:它仍然是有用的输入,只是不该独占位置。
用时间切分防止未来泄漏
风险预测的验证有一个容易被忽略的陷阱。如果把所有历史记录随机打乱,一部分用于训练、一部分用于验证,同一周、同一批的记录会同时出现在两边,模型等于提前看到了“未来”,验证分数会虚高。更贴近实际的做法是时间切分:用较早的时间训练,用之后的时间验证,最好再按供应商分组,让验证集里出现训练时没见过的供应商。还有一个细节:检测结果往往要过几天才出来,特征只能使用做预测那个时点已经掌握的信息,不能把事后才知道的东西混进去。
风险会随时间变化
风险不是贴在供应商身上的固定标签,它随季节、天气和市场情况移动。2026 年 9 月发表在 Frontiers in Food Science and Technology 的一项基于欧盟 RASFF 通报数据的研究,在做严重程度分类时假设数据在时间上平稳,作者也明确指出这一点需要在部署阶段验证,并且它只是原型,不是可直接用于监管的系统。落到日常使用上,就是模型要定期用新数据重新校准,看它对最近一段时间的排序还准不准。
历史不合格次数依然有用,它需要与环境、天气、供应链放在一起权衡。而对多数企业来说,最先卡住的常常不是算法,而是天气和转运记录根本没有与批次对应起来。纵横国际食品安全栏目关注的正是这种落地条件:数据能不能取得、按什么粒度对齐,往往比换一个更复杂的模型更早决定预测的上限。