毕业论文的研究问题:按位置(门将、后卫、中场、前锋)分别训练的随机森林模型,能否比单一的通用模型更准确地预测球员的 Sofascore 评分?
第一个版本给每个模型输入的是球员在这场比赛本身的数据:射门、传球、对抗、抢断、触球,一共 63 列。验证过程是干净的:用第 1 到 30 轮训练,用第 31 到 38 轮测试,用按比赛分组的 GroupKFold 调超参数,保证同一场比赛的球员不会同时出现在一个折的两边。一个只预测该位置平均评分的基线给出了下限。
| 模型 | MAE | R² |
|---|---|---|
| 位置平均 | 0.391 | |
| 通用模型 | 0.203 | 0.72 |
| 位置专用模型 | 0.207 | 0.72 |
前锋的 R² 达到了 0.80。把基线误差砍掉一半,写进论文里很好看。但这说不通:在足球里,单场比赛的数据不应该能解释这么多东西。
不是数据泄漏,是循环论证
第一个怀疑对象是训练集和测试集之间的泄漏。结果并没有。数据划分和交叉验证的折都保持每场比赛完整,没有任何测试数据碰到训练过程。
问题出在预测目标上。Sofascore 评分来自一个不公开的公式,而这个公式的输入和那 63 个特征非常相似:进球、传球、对抗、抢断。随机森林学到的并不是"什么是好的表现",而是在逼近产生这个数字的公式。高 R² 衡量的只是模型模仿 Sofascore 算法的程度,仅此而已。
这个实验仍然回答了一个更窄的问题(在已知比赛数据的情况下,位置专用模型只在门将上胜出,而门将恰好是统计特征最独特的位置)。但它回答不了真正重要的问题:能不能在比赛开始前就知道评分?
重新设计
第二个实验禁止使用被预测的那场比赛的任何数据。每个特征都必须在开球前就已经存在:
- 主场还是客场;
- 对手实力:对手在之前比赛中的进球数和失球数;
- 球员自己的历史:61 项数据的赛季平均值,以及评分和出场时间的赛季平均值和近 5 场平均值,全部只用严格早于本场的比赛计算。
"严格早于"正是 一个赛季的 Sofascore 数据,以及它在哪里说谎 中的轮次顺序不再是小细节的原因。还没有历史数据的球员(14.9% 的球员整个赛季只踢了 3 场或更少)会得到 NaN,scikit-learn 的随机森林从 1.4 版本起可以原生处理。同时还会加入一个此前出场次数的特征,让模型学会更相信 20 场的历史,而不是 1 场。
验证方式改为前向滚动:用一批 10 场比赛之前的全部数据训练,预测这一批,再把它加入训练集,继续下一批,覆盖赛季的最后 80 场比赛。超参数搜索在训练窗口内使用按时间排序的折,始终在晚于训练数据的比赛上验证。
R² 降到了 0.10。这是预期中的结果,不是退步。用接近评分公式本身的数据去解释一个已经产生的评分,是一回事;在比赛前预测它,是另一件难得多的事。结果见 一场比赛的评分,大部分无法预测。