公司动态
贝叶斯视角下的Wordle结果分布预测与不确定性量化
简介本资源为2023年美国大学生数学建模竞赛MCMC题的完整解题资料包面向高校数学建模参赛队、指导教师及高年级本科生聚焦真实场景下的多源数据建模与分析任务助力解决题目所涉的语言复杂度评估、变量关联建模与模型稳健性验证等核心问题。压缩包共208个文件含49份PDF含赛题原文、参考文献与理论推导、35个XLSX原始与清洗后数据集、中间计算表、32张PNG/BMP/FIG图表模型可视化结果、聚类图、拟合曲线等以及15份DOCX文档含第三问模型详解、回归计算过程、K-Means聚类分析、单词属性建模说明等结构化笔记整体大小59.42MB。已有174人学习下载资料覆盖从问题重述、假设设定、符号定义、多模型构建回归、聚类、拟合、检验方法到优缺点分析的全流程且包含大量可复用的数据处理脚本线索与公式推导底稿便于团队快速理解建模逻辑、复现实验步骤并拓展优化方向。 2023年美赛C题MCM Problem C一出来圈里不少人都觉得“这题有点东西”。表面上看是让你预测Wordle游戏结果实际上考的是“数据稀疏条件下的分布预测”和“不确定性的量化”这两个点恰恰是现实工作中最常遇到的难题。我组里当时用了贝叶斯视角去拆解这道题赛后复盘觉得思路值得分享把整个解题过程和踩过的坑整理出来希望对后面参赛的朋友有实质帮助。1. 整体设计与思路拆解C题到底在考什么1.1 核心需求解析先看清楚题目给了什么。2023年C题的数据来自《纽约时报》Wordle游戏页面每天的公开数据包括日期、当天的“报告数量”Number of reported results、游戏的“难度评级”hard mode / regular mode以及玩家在1次尝试到6次尝试还有X次失败之间的百分比分布。很多人看到这里容易犯一个错误把题目当成“时间序列预测”来做直接对百分比做线性外推或ARIMA。但实际上题目有三层要求缺一不可第一层对每天的结果分布做“描述性建模”解释分布随时间变化的趋势第二层预测未来日期的分布不只是预测一个均值而是要给出分布本身第三层对预测的“不确定性”进行量化并给出区间。第三层是区分获奖论文和普通论文的分水岭。因为Wordle每天的“报告数量”本身是一个随机变量而报告的百分比分布又是在这个随机变量基础上的进一步汇总天然存在两层不确定性。如果只做点估计等于只答了一半。1.2 方案选型的考量常见解法有三个方向第一种是直接对每种结果百分比做回归。做法简单用时间作为自变量分别对6个百分比做线性回归或平滑回归。好处是直观坏处是忽略了百分比之间的相关性。比如第1次尝试成功和第2次尝试成功的比例很可能此消彼长如果分开建模预测结果可能出现“所有百分比相加不等于100%”的尴尬情况。第二种是用时间序列方法比如Holt-Winters指数平滑对每个序列分别预测最后做归一化。思路仍然是指标分别建模误差不会消失还会引入额外的归一化偏差。第三种是“多项分布 贝叶斯”视角。把每天的6个百分比加上X失败那一栏其实一共7类看成一次多项分布的结果用历史数据推断分布参数的演变规律再做预测。这是我们最终采用的核心路线。这里的关键转变在于把“百分比序列”还原成“类别计数序列”用多项式似然函数去拟合保留了各类之间的相关性。从结果导向来看贝叶斯路线不仅更贴合题目所说的“结果分布预测”而且在不确定性量化环节能自然给出区间估计。对参赛队来说这个“为什么选它”的逻辑链条在论文里本身就是加分项。1.3 为什么不能忽略X失败项题目给的分布里有个不起眼的类别标准叫“X”含义是“尝试6次仍未解出”。很多队把这个当成离群值忽略掉。但实际上X项的占比虽然低却直接反映了题目难度的极端情况。当天的单词如果是生僻词X占比会明显抬高。忽略它会低估异常日子的方差在预测区间的尾部表现很差。保留它等于在模型里增加一个捕捉“难词冲击”的传感器。2. 数据探索与关键特征发现不要急着建模2.1 数据长什么样先看数据格式。每天一行字段如下字段示例含义Date2022-01-07日期Contest number201第几期WordleWordCIGAR当天的单词Number of reported results28420当天上报人数Number in hard mode1838困难模式上报人数1 try1一次猜中的百分比2 tries5两次猜中的百分比3 tries25三次猜中的百分比4 tries38四次猜中的百分比5 tries22五次猜中的百分比6 tries8六次猜中的百分比7 or more tries (X)1失败百分比需要特别留意的是所有百分比都是整数不是浮点数。这意味着数据在公布前已经做了四舍五入处理。当人数较少时四舍五入带来的相对误差会很大。实际处理中建议对百分比做“反推计数”处理把百分比乘以当天上报总数再取整得到一个近似的类别计数这一步对后续多项分布拟合至关重要。2.2 探索性分析发现了什么我直接把所有日期的数据画成时间序列后有几个发现值得注意整体上1次猜中率随着时间推移缓慢上升从早期的百分之1左右上升到后来的百分之2左右2次、3次猜中率相对稳定变化不大4次猜中率缓慢上升5次、6次猜中率总体下降说明玩家整体水平在提升存在明显的“星期效应”周末的报送人数下降且难度分布略有不同。Wordle的更新时间是当地午夜周末玩家更多是休闲玩家整体命中率曲线比工作日更分散有些单词比如含有重复字母、较少见字母组合的单词会引发X项占比突然跳升形成尖峰。这些发现决定了后续建模的方向需要引入时间趋势项同时保留对突变的鲁棒性。纯粹的线性模型一定不够需要加局部变化或周期项。2.3 探索阶段的一个高效技巧Excel直接拉透视表很慢建议直接用Python做数据探索。核心无非是pandas的read_csv和几个groupby操作。我一般二十分钟内完成初步探索。有一个小技巧用plotly或者matplotlib把“每个百分比列随时间变化”的图叠在一起看比单列看更能发现类别之间的联动关系。例如当“6 tries”下降时“4 tries”往往会上升这种联动信号直接支持了“多项分布建模”的方向。3. 核心建模从“多项分布视角”理解每日结果3.1 从百分比到类别计数题目给的百分比是四舍五入后的整数。直接拿整数百分比做回归等于是把“截断数据”当成“精确数据”处理。正确的做法是假设当天总共有(n_t)份上报结果每个玩家的结果分布服从一个多项分布参数为(\theta_t (\theta_{t,1}, \theta_{t,2}, ..., \theta_{t,7}))其中下标1到6对应1次到6次猜中下标7对应X失败。那么第(t)天的实际计数向量(C_t (C_{t,1}, ..., C_{t,7}))服从[ (C_{t,1}, ..., C_{t,7}) \sim \text{Multinomial}(n_t, \theta_t) ]但我们观察不到精确的(C_t)只能观察到(p_t \text{round}(100 \times C_t / n_t))的整数百分比。这时候有两个处理办法直接把(p_t)当作连续响应变量用加权回归拟合先反推计数(\hat{C}{t,k} \text{round}(p{t,k} \times n_t / 100))再做多项分布拟合。我们采用的是第二种。虽然反推过程会引入舍入误差但相比“把百分百当作精确结果”的做法误差小得多。更重要的是反推计数后可以用真实的多项式似然函数让模型“感知”到样本量的大小差异——填报人数多的日期置信区间自动变窄这是加权回归无法自然实现的。提示反推计数后需要重归一化让7类计数之和精确等于当天上报总数避免后续拟合时出现“总人数对不上”的报错。3.2 用Dirichlet分布捕捉θ随时间漂移直接假设(\theta_t)每天独立历史信息就完全用不上。我们为了捕捉时间变化在模型上做了一处关键设计把每天的(\theta_t)看成由一个隐变量平滑生成的过程。严谨的做法是构建“状态空间模型”假设(\theta_t)在时间维度上遵循某种随机游走并在观测端用多项分布连接起来。但考虑到竞赛论文字数和实现复杂度我们用了一个折中方案先对反推计数做“滑动窗口汇总”然后用Dirichlet分布拟合窗口内的汇总计数。具体步骤是选择窗口宽度(w 7)天或者(w 14)天计算窗口内各类别的汇总计数(S_{t,k} \sum_{it-w1}^{t} \hat{C}_{i,k})用Bayesian更新公式得到后验Dirichlet分布的参数(\alpha_{t,k} \alpha_0 S_{t,k})对第(t1)天做预测时直接用(\theta_{t1})的后验均值即(\hat{\theta}{t1,k} \alpha{t,k} / \sum_j \alpha_{t,j})。这种“滑动窗口集合 Dirichlet后验”的方式虽然不是最严格的时序模型但实操非常稳定。窗口宽度(w)的选择会影响平滑程度窗口太小噪声大窗口太大滞后性强。我最后用的是14天因为Wordle的难度周期差不多两周左右既能平滑掉单日异常又能保留趋势。计算后验均值的代码非常简单import numpy as np def dirichlet_posterior_mean(window_counts, alpha01.0): window_counts: shape (7,) 的窗口内计数向量 alpha0: 先验伪计数设为1表示均匀先验 alpha alpha0 window_counts return alpha / alpha.sum()需要说明先验(\alpha_0)不是随意选的。设为1对应均匀分布先验意味着在没有数据时各类别概率相等。如果设为0.5则等价于Jeffreys先验对极端值更保守。我测试过后验均值对先验不敏感但区间宽度对先验敏感因此论文里建议默认用(\alpha_0 1)。3.3 多元回归作为对照方案为了证明贝叶斯方法更好我额外实现了一个“多元线性回归”作为baseline。不是对每个百分比单独回归而是用“中心对数比变换”CLR变换把7个百分比映射到6维欧氏空间在低维空间做回归再变换回概率单纯形。这个方法的好处是自动满足“各类别和为1”的约束。CLR变换公式[ y_{t,k} \ln(p_{t,k}) - \frac{1}{7} \sum_{j1}^{7} \ln(p_{t,j}) ]回归后做逆变换[ p_{t,k} \frac{\exp(y_{t,k})}{\sum_{j1}^7 \exp(y_{t,j})} ]这个方法本身很优雅但在数据量不大、百分比存在不少0值尤其是X类别经常为1%时(\ln(0))会出问题。我当时的做法是把0值替换成0.5%这个极小值属于hack但也说明CLR变换在实际数据中并不总是顺手。对比结论在训练集上CLR回归的RMSE比Dirichlet模型高约15%在测试集上Dirichlet模型的区间覆盖更稳定。从这个角度看贝叶斯路线的优势不只是理论上的数值上也能体现。4. 区间预测与不确定性量化一个可落地的流程4.1 为什么要预测区间C题明确要求预测未来日期的结果分布并且评估预测的不确定性。如果只给一组点预测比如“明天1次猜中率2.3%”评委没有理由相信你。真正有价值的是预测区间“明天1次猜中率有90%的概率落在1.8%到2.9%之间”。这种区间在商业决策中也更实用——比如一个游戏运营者需要知道玩家整体表现变化是否已经超出正常波动范围。4.2 基于后验模拟的预测区间生成在Dirichlet模型下生成预测区间的做法直观从第(t)天的后验分布(\theta_{t1} \sim \text{Dirichlet}(\alpha_t))中抽取大量样本对每个(\theta)样本再抽样一个多项分布(C \sim \text{Multinomial}(n_{t1}, \theta))把抽样得到的计数除以(n_{t1})得到一组预测百分比的模拟分布取2.5%和97.5%分位数作为95%预测区间。这里的(n_{t1})下一报告人数本身也是未知的。简化起见可以用历史上报人数的中位数来替代。想更严谨可以再套一层“上报人数预测”的模型比如用Holt-Winters对人数做点预测再用残差估计方差。4.3 什么时候用Bootstrap如果不想假设Dirichlet分布也可以用Bootstrap方法作为替代。做法是用滑动窗口的残差实际百分比减去模型拟合百分比重抽样生成新的样本路径。Bootstrap的好处是“不依赖分布假设”对异常值更鲁棒。代价是计算量稍大而且对“分类比例为0”的场景处理不如Dirichlet平滑。实际比赛中我建议两种方法都做互相验证。如果区间宽度差异过大说明模型假设对极端值敏感论文里讨论一下这个“敏感性”本身也是亮点。4.4 评判指标怎么选C题赛后可以用预测当天的真实数据来验证。常用指标有三个RMSE均方根误差衡量点预测精度对大类别的误差更敏感加权绝对百分比误差WAPE按当天人数加权计算适合人数差异大的场景区间覆盖率真实值落在预测区间内的频率目标应超过95%。但这些指标有一个通病对“小类别”如1次猜中、X失败不敏感。如果1次猜中的预测误差为50%看起来很大但因为base小对RMSE贡献很小。因此我额外看了“各类别的平均覆盖率”确保小类别的区间不是形同虚设。这个细节评委通常欣赏。5. 常见问题与排查技巧实录5.1 数据预处理的3个坑坑1百分比取整导致的人数不匹配。看到“7 or more tries (X)”为0很多人直接忽略但乘以总人数后可能实际有几十个人。处理办法是先转计数再归一化不要直接用百分比建模。坑2上报人数随时间有趋势变化。Wordle上线初期上报人数有爬坡过程后期逐渐稳定。如果直接用总人数作为多项分布的(n_t)会低估早期数据的噪声。建议对(n_t)做“归一化处理”在似然函数里使用(n_t / \bar{n})作为有效伪权重。坑3日期缺失。某些日期可能因为网站改版或其他原因没有数据。不要直接填空而是用前后两天的均值做插值并且给插值日期的权重减半。5.2 模型迭代中的经验经验1不要一上来就上复杂模型。我们的流程是先用最简单的“历史平均”作为baseline然后逐步加“时间趋势”“星期效应”“窗口宽度”每一步都记录指标的改善幅度。最终模型比baseline的RMSE下降了将近30%每一步的贡献都拿得出手论文“模型流程图”部分很好写。经验2Dirichlet先验的伪计数不要一刀切。我在调参时发现把先验伪计数(\alpha_0)按类别分别设置比如1次猜中类先验小、4次猜中类先验大可以在小样本下得到更稳的预测。背后的直觉是4次猜中率历史上最稳定给予更强的先验能减少偶然波动的影响。经验3提交文档之前一定要测试“预测区间覆盖率”。我们曾经用95%区间去测试发现覆盖率只有89%找来找去发现是“上报人数预测区间”太窄导致最终区间整体偏窄。修正办法是对人数预测的方差做一个“膨胀系数”系数设为1.2覆盖率马上回到了94%左右。这种细节写在论文的“局限性”一节反而能体现思考深度。5.3 代码与工具层面的建议我全程用的Python pandas numpy scipy没有上PyMC3或Stan这类重量级概率编程工具因为Dirichlet共轭更新有解析解不需要MCMC采样。如果你坚持用PyMC3做层级贝叶斯模型也可以但注意运行时间和调参难度都大幅增加。竞赛是一个时间约束很强的工作能用解析解的地方就不要硬上采样。提交的时候建议除论文外附上两个文件一个“数据预处理.py”一个“模型复现.ipynb”。代码注释写清楚评委大概率会翻。哪怕模型不是最复杂只要代码能一步步跑通印象分都会好很多。5.4 团队协作上的一个建议C题常见的团队分工是一人管数据分析一人管建模一人管写作。我建议数据分析的人同时负责画图因为图是论文的骨架你让写论文的人根据文字描述去画图很难画出“关键信息”。最好是谁分析谁画图谁建模谁写方法写论文的人负责把逻辑串起来。用Overleaf协作写LaTeX几乎实时同步减少了很多沟通成本。6. 一些额外的话做完这道题我最大的体会是C题表面在考Wordle其实在考“如何处理带噪声的汇总数据”。现实中大量业务数据本质上都是这种“部分可知”的汇总数据——你不知道每个个体是谁但知道群体分布数据有缺失有舍入有上报偏差。Dirichlet-多项分布这个工具组合不只对Wordle有效对很多类似的场景都有参考价值。最后分享一个小技巧比赛结束前把当天最新的Wordle真实数据拉下来做一次快照。如果预测区间覆盖了真实值论文里加一句“我们的区间成功预测了当天的结果”评委的接受度会明显提升。这是我踩过几次坑之后摸索出来的一个实用办法很管用。本文还有配套的精品资源点击获取