← 返回估值榜
Python · XGBoost · Grabit (Tobit GBT) · SHAP · GroupKFold CV进行中

NBA 自由球员估值模型

个人研究项目 · 2026 年 6 月至今

0.881交叉验证 R²2024–26 赛季, n = 316
0.858前瞻 R²滚动起点法,模型实际推理的表现
$3.08M前瞻 MAE滚动起点法, n = 316
877训练合同数按球员分组的选择 CV

NBA 球队每年花超过 40 亿美元在球员薪资上,而最常被引用的估值框架 Hollinger 的 BORD$ 是不公开的。这个项目是一个开源、可复现的替代方案, 利用公开数据预测每位球员值工资帽的百分之几。

这个项目有意思的地方在于截断问题。CBA 从两个方向限制薪资:顶薪根据球龄和表现封顶在 工资帽的 25–35%,底薪也有最低线。一个实际上可能值 40% 的球员只能拿 35%, 所以顶薪的实际薪资是未知的。如果只用这些数据训练机器学习模型,模型只会学到顶薪的上限。 我把信用风险领域的 Grabit 框架(Sigrist & Hirnschall, 2019)适配过来: 被截断的行用逆 Mills 比的梯度把潜在预测推过观测值的边界;未被截断的行保持普通的平方误差。

难点在于判断哪些行算「被截断」,而且两边的判断逻辑不同。在上限这边, 不是每个拿顶薪的球员都被工资帽影响了,所以模型只在 P(顶薪) ≥ 0.52 时才用grabit做额外推升。 在下限这边逻辑反过来:好球员选择拿底薪不算被截断(甚至可以说是反过来的,但是情况太复杂暂时不处理),但潜在价值低于底薪线的确实被托住了。 最后还有一个「已知签约路径」阶段单独处理续约,因为续约的涨幅上限和签约方式有关(鸟权无视工资帽签约合同普遍更大,提前续约有不同的上限等)。 整体模型通过真实价值,cba下的价值,已知签约方式后的价值,三个阶段共同预测球员在市场上会拿到的合同。

双向截断

顶薪合同是右截断观测,老将底薪是左截断观测。两种情况下,实际薪资都是一个边界。 Grabit 用截断正态似然替换这些行上的平方误差,Stage 1 恢复出被边界遮住的真实身价。

后面两个阶段有一个清晰的分界线:这个信息在签约之前就知道,还是签完才知道? Stage 2 处理前者:在 P(顶薪) ≥ 0.52 时推向上限,然后用球龄和赛季就能算出的 CBA 地板和天花板做裁剪。Stage 3 处理后者:一旦知道球员是通过 Early Bird 签的, 或者是续约,CBA 就会施加事前特征无法预见的限制。 每个阶段都只会把预测往下调,最后再重新套一次合法范围。

grad = −σ · φ(z) / Φ(z)      其中  z = (pred − observed) / σ

latent → push (τ = 0.52) → clip[地板, 天花板]
       → 签约偏移 → 机制上限 → 续约上限 → 重新裁剪

stage1 得到的真实价值 是核心市场估值;后面所有步骤都是 CBA 规则。 只有四种资格路径(Bird、薪资空间、Early Bird、Non-Bird)有签约偏移。 MLE、BAE 和底薪由合同金额本身决定,以它们为条件会泄漏目标变量。

结果

选择用的是 10 种随机种子下的 5 折 GroupKFold 汇总,按球员分组确保同一球员不会同时出现在训练和测试集。 前瞻表现单独用滚动起点法衡量(用 T 赛季之前的所有赛季训练,预测 T), 因为这才是推理管线真正在做的事情。

基线 XGBoostGrabit v8.15x
选择 CV R²(n = 877)0.8090.856
选择 CV MAE$3.29M$2.60M
CV R²(2024–26, n = 316)0.8390.881
前瞻 R²(滚动起点)0.8110.858
校准斜率1.0410.966

前瞻 R² 的 95% 置信区间为 [0.80, 0.90]。 基线阶梯提供参照:仅用场均上场时间可以达到 0.605, 完整模型的 0.856 说明其余二十项特征贡献了最后的 0.252。 15% 球员的确认分割(从未参与任何选择决策)得分 0.820, 对比选择池的 0.862。

在起作用的地方衡量干预效果

汇总指标会把每个定向干预的效果平均到它根本没碰过的行上, 所以 v8 在总体指标旁边维护了一个分区计分卡。 在顶薪截断区,69 行中 65 行改善,MAE 下降 $3.70M。 在底薪区,259 行的 MAE 下降 $0.75M。 Stage 3 移动了 13 个续约行,改善了 11 个,续约区 MAE 下降 $0.48M。 完整冠军模型依然通过了汇总选择检查:比仅裁剪模型 R² 高 +0.023, 签约偏移单独贡献了 +0.015。 每个干预都在它的算术能够改变预测的地方被衡量。

签约路径能修正什么

按 CBA 签约机制分组残差,能暴露出表现特征看不到的结构。 持有 Bird 权的球队可以超帽留人,支付一个特征无法捕捉的留队溢价。 底薪和中产特例的方向相反。

很自然的想法是:既然知道了机制,就把它建模进去恢复残差。这行不通。 机制本身就是球员特征的函数,所以把折叠诚实的 P(机制 | x) 喂给模型, CV R² 反而降了 0.0073。即使用泄漏的 oracle 直接告诉模型真实机制, 也只提升 +0.0137。残差缺口来自一个真正的双峰条件分布: 相同的输入下,球员要么拿到市场价的合同,要么接受底薪, 条件均值恰好落在两个峰之间。

解决思路是做一个区分:签约路径可以修正输出,但不能进入输入。 签约之后路径是事实,Stage 3 给四种由球队资格决定的路径各加一个常数偏移。 下表是修正后的结果。被修正的路径现在都在零的 $0.75M 以内, 而底薪仍然带着 +$1.65M,因为底薪由合同金额定义, 以它为条件会泄漏目标变量。

签约机制n残差偏差是否修正
Bird Rights305−$0.38M
薪资空间80−$0.43M
Early Bird53−$0.72M
Non-Bird14+$0.19M
MLE136+$0.52M
底薪259+$1.65M

负偏差表示模型低估。先签后换归入 Bird Rights(发起方须持有 Bird 或 Early Bird 权)。 签约机制始终不进入特征集,所以这些残差可以作为诊断指标使用。

一个值得直说的修正

早期版本报告模型低估顶级球员最多 $6M,那是按实际薪资分箱读出的残差。 那个梯度是假象。按带噪声的目标变量分箱,即使校准完美的模型也会产生它, 因为分箱边界在噪声上做了选择。换成按预测值分箱, v8 冠军模型的校准斜率为 0.966,25%+ 区间残余偏差 +$1.47M。 折叠诚实的重新校准依然无法改善模型。

同一个陷阱还有第二种形式:比较两个模型时, 如果用其中一个模型自己的预测值来划分区间, 区间的样本构成会在两个模型之间漂移,制造出一个本不存在的差异。 这里的模型比较用的是两个模型都没有生成的东西来划分区间。

亮点

  • 把 Grabit(梯度提升 Tobit)从信用风险建模适配到 NBA 薪资, 将双向截断与概率门控的顶薪推升和已知路径的续约上限组合起来, 每个阶段回答一个不同的 CBA 问题
  • 端到端搭建了完整管线:抓取 Basketball Reference、Spotrac 和 nbarapm 的数据, 从 40+ 候选中通过消融筛选出 21 项特征, 以及一个四层评估协议分离结构估计和预测、选择和确认
  • 建立了一个经过量化的否定性结论:签约机制的残差模式是真实存在的, 但无法通过特征恢复,任何修复的上限是实测的