前几天晚上我躺在床上刷 dev.to,本来是要睡了。刷到一篇讲 MCMC 的,标题挺会起——Markov Chain Monte Carlo: the 1953 algorithm hiding under modern AI。作者标了 AI-assisted。我想就当睡前读物吧。
然后刷到 Vinh Nguyen 的纠错。解决方案架构师,十六年经验。他说文章里那个循环严格说只是 Metropolis,只有提案对称时才成立。对正参数用乘性提案,比如 x * exp(normal(0, 0.7)),就必须把 Hastings 比里的 q 项补上——他顺手跑了个实验。
目标 Gamma(3,1),真均值 3。40 万步,burn-in 2 万。不加 q 修正,链均值 1.995,落在 Gamma(2,1) 上;加上 q 修正,2.999。接受率呢?错的那条 72%,对的那条 66%。
先停一下,问个问题:错得这么离谱的链——均值偏了整整三分之一——为什么接受率反而更高?
我第一眼是不信的。接受率这个东西,你直觉上会拿它当健康指标:接受率高,说明链走起来不费劲,步长调得不错。但 Vinh 的实验把它扒开了——接受率测的不是“走对了没有”,是“走起来顺不顺”。有偏的链困在一个更矮更窄的峰上,每次提议都更容易被接受,因为要爬的坡更缓。轨迹图照样是那种模糊的毛毛虫。所以错的链,看起来调得更好。
这么说吧,接受率、轨迹图、burn-in 那些诊断,全都只回答“链走得像不像样”,没有一个直接回答“链停的地方对不对”。你步长调成什么样,最后都会拿到一个长度 1 万的数组,看起来都像成功跑完了。
再往下呢?q 修正为什么那么容易漏掉?
因为 Metropolis 1953 年那版用的就是对称提案。对称的时候 q(y|x) 和 q(x|y) 上下约掉,接受率里只剩 target ratio。这一步不写进代码也不影响运行——只要你提案恰好对称。Hastings 1970 年才把不对称那项补上。换句话说,q 修正不是算法跑起来需要的,是算法“对起来”才需要的。它只在你选了不对称提案、或者停下来问一句“这么写凭什么对”的时候才冒出来。
他那个实验里,错的那行简单到让人后怕。乘性提案下 q_ratio 不是 1:
# 对称高斯提案:q_ratio = 1,Metropolis 就够了
# 乘性提案:q_ratio = candidate / current
# 漏掉这行,链就停在 Gamma(2,1) 而不是 Gamma(3,1)
log_q_ratio = log(candidate) - log(current)
漏掉这一行,剩下的代码一行都不用改,照样跑,照样返回样本,照样给你一个看着健康的接受率。
AI 辅助的内容对这层天生不敏感。它擅长把标准流程写顺:从当前点抽候选、算 target ratio、按 R 接受或拒绝、burn-in 之后求平均。这套东西在对称提案下跑得通,在无数教程里也长这样。但“非对称提案下这行是错的”——这个判断不在代码形态里,在代码和数学之间那条缝里。那篇文章的作者挺坦白,标了 AI-assisted。但 AI 恰好辅在“把流程写顺”上,没辅在“为什么这么写”上。
这儿得把 MCMC 最漂亮那层的好处摊开看。它不用算归一化常数,只比较候选点和当前点的相对好坏。这个便宜很实在——高维积分没法硬算,10 个参数上网格就是 10^20 次,那根本不是工程优化能绕过的墙。但它有代价:你拿到的那一万个样本,永远只是“某个链的停留记录”,不是“目标分布的直接证据”。确认它真来自目标分布,功夫全在算法外面:q 要自己推,burn-in 要自己判,R-hat 要自己跑,多条链要自己起。算法替你跑,不替你验证。
而这正是 AI 生成内容最不会主动补上的部分。不是它偷懒,是“q 修正为什么必须在这儿”这件事,在语料里出现的频率远低于“Metropolis-Hastings 流程”本身。模型学会了把流程写得像真的,却很难学会“哪里可能悄悄漏了一项”——后者是人在用的时候被问题逼出来的,不是从代码里读出来的。
所以说回来,那文章标题没起错,1953 年的算法确实藏在现代 AI 底下。只是藏得最深的那层,不是算法怎么跑,是那条缝:错误的链看起来更健康,接受率在替一个错的峰背书,而唯一的报警器是你得自己知道 q 该在哪儿。
那天晚上关掉页面前,我在笔记里写了一句话:接受率证明的是链肯不肯走路,不是它走得对不对。之后每次调 MCMC,我先问的不是 accept 了多少,是 q 还该不该在那儿。
