想象你是一位主厨,经营着城里口碑最好的“贝叶斯餐厅”。客人们总说你的招牌酱汁忽咸忽淡——问题在于,你调味的唯一依据是“经验”和“手感”。你有十二年的做菜心得(先验),却从没系统地记录每批客人试吃后的反馈(数据),再把两者严谨地融合成一份新配方(后验)。 直到有一天,朋友给你推荐了一套“智能厨房系统”,名字叫 NumPyro。它的特别之处在于:做菜用的不是普通灶台,而是 GPU 工业炉;菜谱不是手写纸片,而是可自动微分、可批量执行的 JAX 代码;调味也不是凭感觉试一次,而是让 MCMC 采样器在你的后厨里自动反复试味上千次,或者派出一名 SVI 速成学徒快速学会你的手艺。 这篇文章,就是带你以“主厨”的视角,把这套智能厨房从里到外逛一遍:它的动力总成(JAX)、菜谱写法(模型定义)、试味流程(NUTS 采样)、学徒速成法(SVI),再看看它和隔壁餐厅的 PyMC、Stan 两口锅到底怎么选。逛完这趟,你也能把自家的“贝叶斯酱汁配方”写得又快又稳。
掌握 NumPyro,意味着你能在 GPU 上几十分钟内完成原本需要几天才能跑完的贝叶斯推断,这是算法工程师做量化决策、因果分析和高维科学计算的核心竞争力。
目录
可视化演示
展示NUTS采样器轨迹、JIT编译加速对比和SVI变分推断ELBO优化过程,理解JAX后端性能优势。
本节配有交互动效(进入视口后自动加载,约 3KB)。
NUTS采样轨迹 NUTS利用哈密顿动力学探索参数空间 U-turn检测自动调节轨迹长度 H(θ) = -log p(θ) + ½ p² jax.grad自动计算梯度,jit编译加速采样
认识NumPyro:智能厨房的招牌
你后厨的墙上挂着一块牌子:“今日特调,全凭手感。”现在,你决定引进那套智能厨房系统。第一件事是搞清楚它的来头:它并不是小厂商的杂牌厨具,而是知名“餐饮集团”(Pyro 框架)推出的新一代旗舰——把原有的菜谱和流程,全部搬迁到一套更快更省电的“动力平台”(JAX)上重写。招牌还是那个招牌,内里已是全新的机器。
在技术世界,这个“新一代智能厨房”就是 NumPyro——Pyro 的 JAX 后端实现。Pyro 原本依赖 PyTorch 作为张量与自动微分引擎,而 NumPyro 用 JAX 重写了概率编程的三大件:模型定义、MCMC 采样(代表算法 NUTS)、SVI 变分推断。由于 JAX 自带 jit 编译、vmap 向量化和 grad 自动微分,NumPyro 在复杂模型上常常比原版 Pyro 快一个数量级,尤其擅长 GPU 大规模并行。
贝叶斯推断要回答的问题朴素而深刻:在观察到数据 D 之后,我们相信参数 θ 的取值分布是什么?它就是一条公式的事:
有了这条公式打底,你会发现 NumPyro 做的所有事情,本质都是“帮主厨把贝叶斯公式算出来”。它不替你决定配方,而是给你一套又快又稳的设备,去计算那个大多数人算不动的积分。
- 先验 P(θ)数据还没上桌时,你根据十二年经验对配方参数 θ 的判断。它代表“盐量大概在 1~3 勺之间、2 勺最可能”这种带不确定性的初始信念。
- 似然 P(D|θ)给定配方 θ,做出符合客人反馈 D 的概率。盐放 5 勺时客人说“太咸”的概率接近 1,放 1.5 勺时客人满意的概率就高。它刻画“数据如何为某个配方打分”。
- 证据 P(D)在所有可能配方下数据出现的平均概率,本质是归一化常数,确保左侧后验积分等于 1。厨房里,它相当于“各种做法都算上,客人这桌反馈出现的总概率”。
- 后验 P(θ|D)看完客人反馈后修正出的配方分布,是贝叶斯推断的最终答案——“现在我认为盐量最可能在 1.8 勺附近,且范围变得更窄”。
JAX核心引擎:三大动力件
智能厨房与传统厨房最大的差别,是它拥有三件近乎魔法般的动力设备。第一件叫“预制菜卡”(jit):把你写好的整份菜谱一次性编译成机器指令,下次做菜一键出餐,不用再逐行念菜谱。第二件叫“多层烤盘”(vmap):原本一次只能烤一块牛排,现在自动变成一次烤一百块,而你写的还是那一份菜谱。第三件叫“自动探味器”(grad):每放一种调料,它都能立刻告诉你“这勺盐对最终味道的边际影响有多大”。
这三件设备对应 JAX 的核心 API。jax.jit(f) 对函数做即时编译(XLA),把 Python 函数变成高度优化的计算图;jax.vmap(f) 自动向量化,让同一函数批量作用于数组每个元素;jax.grad(f) 通过反向模式自动微分计算函数对输入的梯度。它们可以任意组合,且都保持“纯函数”约束——函数不能有副作用,输入输出必须显式流动。
自动微分是这些能力的地基。一个复杂模型对参数求梯度,本质上是把从输入到输出的每一层运算用链式法则串起来:梯度像“味道的变化”沿着做菜的工序,一步步反向传回每一道配料。公式是链式法则:
有了这三件动力设备,后厨的效率质变:主厨(NumPyro)可以写一次配方,批量喂给一百个烤盘(vmap 并行多条链),同时让自动探味器(grad)在每次调味后立刻指出改进方向。后面讲 NUTS 与 SVI 时,这三件套会反复登场。
- 中间变量 u复合计算链条中的一个中间结果,比如“酱汁先加热、再调味”,u 就是“加热后的酱汁状态”,它是连接输入 x 与输出 y 的桥梁。
- 外层导数 ∂y/∂u输出 y 对中间量 u 的变化率,即“调味对最终味道的影响有多大”,这一项来自最后一步运算。
- 内层导数 ∂u/∂x中间量 u 对输入 x 的变化率,即“加热温度对酱汁状态的影响有多大”,这一项来自前一步运算。
- 链式相乘把两步变化率相乘,就得到输入 x 的微小改变最终如何传导到输出 y。自动微分在反向传播时,正是从输出端把沿途导数依次乘回去。
模型定义艺术:写下配方
设备再好,没有配方也做不出菜。在 NumPyro 这座智能厨房里,“配方”就是一段用 Python 写的模型函数。你不需要像传统编程那样声明一堆类和接口,只需把做菜逻辑平铺直叙地写出来:先放什么料(先验),再根据料计算出菜的口味(期望),最后让客人的反馈参与进来(似然)。
具体来说,NumPyro 用 numpyro.sample("名字", 分布) 声明每一个随机变量,用 dist.Normal、dist.Exponential 这类概率分布模块指定变量的“取值范围和偏好形状”。写模型的心法是:随机变量显式声明,确定性计算直接写,观测数据作为可选参数 y 传入。这让模型既是一份人类可读的菜谱,也是一份机器可执行的采样蓝图。
一个最经典的线性回归模型:先给截距 a、斜率 b 和噪声 σ 各声明一个先验分布,用它们算出均值,再把观测数据 y 作为“客人的真实反馈”绑定到似然上。整个模型的联合分布写成:
在这份配方里,sample 就是菜谱上的“放料步骤”:每一行带名字的 sample 都像可追溯的食材批注,采样结束后你能按名字查看到对应参数的完整后验分布。配方写清楚,后面的“试味”和“学徒速成”才有据可依。
- 参数先验 p(θ)配方参数的先验信念,θ 可能包含截距、斜率、噪声。每一维都要在模型里用 sample 显式声明,相当于菜谱开头列的“食材清单”。
- 连乘 ∏对 n 个样本逐点相乘。它假设数据在给定 θ 后条件独立——每位客人的反馈被视为独立考验,总体的“满意度”是所有考验结果相乘。
- 单样本似然 p(D_i|θ)第 i 份数据在配方 θ 下的出现概率。在线性回归中,它常被设定为正态分布:均值由预测线决定,方差由 σ 决定,样本越贴近预测线,这一项越大。
- 联合分布 p(θ,D)先验与所有似然项的乘积,是采样器和优化器的共同目标函数。若 y 传入,NumPyro 会把观测数据“钉”在似然上,使模型变成一个可求后验的条件分布。
MCMC采样:主厨亲自试味
配方写好了,接下来是最重要的一步:亲自试味。MCMC(马尔可夫链蒙特卡洛)就是主厨拿着一把勺,反复从“后验配方空间”里舀出候选味道,边尝边调整。最简单的 MCMC 像随机游走:每次在当前位置附近挪一小步,好吃就留下,不好吃就按概率退回。这种方法正确但磨蹭,容易在味道空间里原地打转。
NUTS(No-U-Turn Sampler)是 HMC(哈密顿蒙特卡洛)的自动化改进版,也是 NumPyro 的招牌采样器。HMC 的思路很物理:让“试味”像球在碗里滚动一样,带着动量冲向味道空间深处,而不是醉汉式乱走。NUTS 进一步解决了“每次该滚多远”——它自动扩展树深度,直到路径要回头(No-U-Turn)为止,几乎不需要手工调步长。你只需指定预热期和采样期,剩下交给它。
NUTS 使用的物理模型是“能量守恒”。把后验的负对数看成“势能”,再引入一个辅助动量 r 作为“动能”,系统总能量(哈密顿量)为:
采样结束后,别急着下结论。你要像主厨检查酱汁是否稳定一样做诊断:丢弃预热期样本(warmup),观察 R-hat 是否接近 1,检查有效样本量 ESS 是否足够。而 NUTS 加 NumPyro 的默认配置,对绝大多数中低维模型已经“相当能打”。
- 势能 U(θ)把后验公式取负对数得到。后验密度高的地方势能低——就像碗底最稳,采样点自然更容易聚集在那里。NUTS 从势能高处滚向低处,最终停在分布的高概率区域。
- 动能项动量 r 是辅助变量,每次迭代从标准正态采样;质量矩阵 M 通常取单位阵或对角阵。动能让采样器“有惯性”地穿越低概率区域,而不至于卡住。
- Leapfrog 积分NUTS 用蛙跳法把 θ 和 r 同步推进若干小步,交替更新位置与动量,近似模拟哈密顿动态——这是每轮“翻滚”的具体动作。
- 接受与回溯NUTS 用“是否回头”决定树的生长方向,自动扩展路径后从候选集合里均匀抽一个点。这相当于“滚到该回头的地方,在滚过的路径上随手抓一个味道当样本”。
SVI变分推断:速成学徒工
主厨试味再厉害,一天也接待不了几百桌客人。于是你决定培养一个“速成学徒”:不让他像主厨那样一颗颗品尝后验,而是让他用一套“固定套路”——比如一个正态分布——去模仿主厨的真实手艺。这就是变分推断的精髓:把“采样复杂分布”的难题,替换成“在一个简单分布族里找最像的那个”。
在 NumPyro 中,SVI(随机变分推断)做三件事:选择一个变分分布 q(θ)(通常用 AutoNormal 自动为各参数构造独立正态分布)、定义优化目标 ELBO、用 SGD 类优化器迭代更新 q 的参数。随着迭代,q 逐步逼近真实后验,就像一个学徒对照主厨的作品不断修正自己的动作。
优化目标是“证据下界”ELBO:
一句话记住 SVI 和 MCMC 的分工:MCMC 是主厨亲自试味,准确全面但慢;SVI 是学徒速成,快、能扛大数据,但只能保证“够像”。NumPyro 里两者共用同一个模型函数,只是换一套执行引擎——“一份菜谱两种做法”的灵活性,正是概率编程框架最贴心的地方。
- 变分分布 q(θ)学徒的“固定套路”,通常来自 AutoNormal——各参数独立正态。它像一个动作模板,参数 μ 和 σ 就是学徒要练的“基本功参数”。
- 期望算子 E_q在 q 的分布下对所有可能 θ 求平均。执行时 NumPyro 用蒙特卡洛估计:每次从 q 里采一组 θ,算一次目标值,再对多次采样取平均。
- 联合对数项 log p(D,θ)联合概率取对数,即“先验 × 全部似然”取对数。它鼓励 q 去解释数据、贴近先验,是“做出主厨味道”的评分。
- 信息量补偿 -log q(θ)对 q 自身密度取负对数再求期望,恰好等于 q 的熵(加常数)。它防止 q 退化成“只顾尖峰不管宽度”的过拟合,保证探索性。
实战应用场景:接待真实客人
设备齐全、配方在手,现在到了接待第一位真实客人的时刻。我们选一道最家常的菜:线性回归。假设你要研究“广告投放金额 x 与销售额 y”的关系。过去你只会用最小二乘拟合一条线,得到点估计;现在有了 NumPyro,你能得到斜率和截距的完整后验分布——不只告诉你“每多投一元带来 1.3 元销售”,还告诉你“这个 1.3 有多么不确定”。
流程三步走:第一步,用 sample 写模型(先验 + 似然);第二步,跑 NUTS 采样:MCMC(NUTS(model), num_warmup=500, num_samples=1000),得到各参数的样本轨迹;第三步,后验分析:打印摘要、绘制后验直方图、计算可信区间。到这里,你已经完成了从“点估计思维”到“分布思维”的跃迁。
有了后验之后,最常见的下一步是预测新数据。新顾客 x̃ 的销售额会怎么分布?这需要把“每个可能配方下的预测”按后验加权平均:
这道“线性回归”虽家常,却演示了贝叶斯建模的全部精髓:写配方(模型)、试味(采样)、上菜(后验预测)。现实中同样的三步能换无数配方——分层模型、混合模型、时序模型——而 NumPyro 的 API 几乎不变。这就是“一套厨房,万道菜”。
- 新输入 x̃你关心的新数据点,比如“下个月打算投 5 万元广告”。它带着自己的预测目标来到后验预测的舞台中央。
- 单参数预测 p(x̃|θ)在某个具体配方 θ(a, b, σ)下,新销售额 x̃ 的似然分布。对线性回归,它就是均值 a + b·x̃、标准差 σ 的正态分布。
- 后验 p(θ|D)前面 MCMC 或 SVI 得到的参数分布,是“根据已有数据修正后的配方信念”。
- 积分 ∫对所有 θ 加权求和。实现上不用真的算积分——把每个后验样本代入 p(x̃|θ),收集起来就是后验预测样本,画出来就是那条预测带。
生态对比迁移:三家厨房怎么选
逛完自家厨房,你难免好奇:隔壁餐厅在用 PyMC,再隔壁换了更贵的 Stan,我们的 NumPyro 到底处在什么位置?如果把概率编程框架比作厨房品牌:Stan 像一台“德系精密仪器”,功能强大、文档严谨,但需要学一门独立的 Stan 语言;PyMC 像“老牌德系家用厨具”,API 直观、社区庞大,用 with 上下文管理器写模型,十分亲切;NumPyro 则像“新一代智能厨具”,功能与 Stan 比肩,却长在 JAX 生态里,天然拥抱 GPU 与函数式编程。
三个品牌的“试味效率”可以用统一指标衡量:单位时间里获得的有效样本数。有效样本量 ESS 不是采样总次数——因为马尔可夫链相邻样本高度自相关,真正“独立”的信息要少得多:
迁移到 NumPyro 并不痛苦,抓住三个动作:第一,把 with pm.Model(): 的上下文结构改成普通 Python 函数,随机变量用 numpyro.sample("name", dist.xxx) 声明;第二,把 pm.Normal 等分布改成 numpyro.distributions 里的同名类;第三,把 pm.sample() 换成 MCMC(NUTS(model), ...).run(rng_key, data)——数据先转成 JAX 数组,还要显式传入随机数钥匙 PRNGKey。至于先验、似然、诊断指标,概念完全通用。
回到后厨墙上,那块“全凭手感”的牌子可以摘下来了。选择框架本质是选择厨具:要极致性能与 GPU 并行,选 NumPyro;要成熟稳定、社区庞大,选 PyMC;要最严谨的统计研究、不在乎上手成本,选 Stan。而理解它们共用的贝叶斯语言,才是真正的手艺——厨具可以换,手艺永远是你的。
- 总样本数 n采样器输出的原始样本数,在 NumPyro 里由 num_samples 指定,它不等于有效信息量。
- 自相关 ρ̂_k相隔 k 步的两个样本的相关系数。如果链走得慢,相邻样本长得像,ρ̂ 就高——就像学徒每步只挪一点点,动作之间高度重复。
- 分母 1+2∑ρ̂把各阶自相关加起来修正重叠信息。完全独立的理想采样下所有 ρ̂=0,分母为 1,ESS=n。
- ESS 与效率ESS 衡量“真正独立的样本量”。对比 NumPyro、PyMC、Stan 时,用 ESS 除以运行时间得到每秒有效样本,这才公平。
AI 应用场景
增长团队 A/B 测试决策
阶段:数据分析与贝叶斯决策推断
解决的问题:传统 p-value 假设检验只能回答“差异是否显著”,无法直接给出“B 版比 A 版好的概率有多大、提升幅度是多少”。用 NumPyro 构建分层贝叶斯模型,把用户分组与画像特征作为随机效应,直接输出转化率后验分布,辅佐产品决策。
具体案例:某电商平台做首页改版 A/B 测试,20 万用户点击数据进入模型,NUTS 在 GPU 上约 20 分钟完成 4 条链采样,给出“B 优于 A 的概率 97.3%,期望提升 1.2 个百分点”的后验结论,产品经理据此全量上线。
金融波动率与风险建模
阶段:时序建模与参数估计
解决的问题:随机波动率模型含隐变量、无解析解,极大似然估计难以处理;NumPyro 的 GPU 加速支持对大量资产序列批处理建模,vmap 能并行跑几百条 MCMC 链。
具体案例:量化研究员以沪深 300 成分股日收益率为观测,用 NumPyro 拟合随机波动率模型,NUTS 估计波动率后验分布,再用于风险平价组合的权重计算,相比传统 GARCH 给出了更完整的置信区间。
临床试验剂量-反应分析
阶段:生物统计推断
解决的问题:小样本临床试验需要把历史医学知识作为先验纳入推断,贝叶斯方法是监管机构认可的方法论;NumPyro 让统计师快速构建自定义模型(如 Emax 模型),并与 Stan 结果交叉验证。
具体案例:某药企 I 期临床分析“剂量→不良反应概率”关系,统计师用历史动物实验参数作先验,NUTS 估计 ED50 的后验分布,与公司原有 Stan 结果一致,运行时间从 2 小时降到 25 分钟。
Python 演示
import jaximport jax.numpy as jnpimport numpyroimport numpyro.distributions as distfrom numpyro.infer import MCMC, NUTS, SVI, Trace_ELBOfrom numpyro.infer.autoguide import AutoNormal def linear_model(x, y=None): alpha = numpyro.sample("alpha", dist.Normal(0.0, 1.0)) beta = numpyro.sample("beta", dist.Normal(0.0, 1.0)) sigma = numpyro.sample("sigma", dist.Exponential(1.0)) mu = alpha + beta * x numpyro.sample("obs", dist.Normal(mu, sigma), obs=y) def main(): key = jax.random.PRNGKey(0) x = jnp.linspace(-2.0, 2.0, 50) true_a, true_b = 0.5, 1.3 y = true_a + true_b * x + jax.random.normal(key, x.shape) * 0.2 # MCMC with NUTS mcmc = MCMC(NUTS(linear_model), num_warmup=500, num_samples=1000) mcmc.run(key, x, y) mcmc.print_summary() # SVI with AutoNormal guide guide = AutoNormal(linear_model) svi = SVI(linear_model, guide, numpyro.optim.Adam(0.02), Trace_ELBO()) svi_result = svi.run(key, 1500, x, y) print("SVI final loss:", float(svi_result.losses[-1])) if __name__ == "__main__": main()
def linear_model(x, y=None):模型是普通 Python 函数,y 作为可选观测参数注入,模仿“菜谱可做菜也可品菜”。alpha = numpyro.sample("alpha", dist.Normal(0.0, 1.0))声明截距的先验分布,命名为 "alpha" 便于后验追踪。numpyro.sample("obs", dist.Normal(mu, sigma), obs=y)声明似然并用obs=y把观测数据“钉”进去;不传 y 时模型退化为纯生成器。mcmc = MCMC(NUTS(linear_model), num_warmup=500, num_samples=1000)组装主厨试味流水线:500 步热身 + 1000 步正式采样。mcmc.run(key, x, y)传入随机数钥匙和数据,正式开火采样。guide = AutoNormal(linear_model)自动为模型每个参数构造独立正态变分分布,是“学徒模板”。svi = SVI(linear_model, guide, numpyro.optim.Adam(0.02), Trace_ELBO())用 Adam 优化器 + ELBO 目标训练学徒。
常见误区
误区“NumPyro 就是把 import pyro 改成 import numpyro,模型代码几乎原样照搬就能跑,毕竟人家官方自己都说'Pyro 的 JAX 后端'嘛。”
正解NumPyro 是用 JAX 从零重写的概率编程框架,不是 PyTorch 的薄封装。模型必须是纯函数式:没有 with 上下文管理器风格,随机数必须显式传递 PRNGKey,分布 API 也做了重构。就像新厨房不仅换了灶台,连菜谱格式都从手写纸条变成了电子菜谱,想“照搬”必须重写模型与推断流程。
为何常见:“Pyro 的 JAX 后端”这个官方表述容易让人理解为“仅替换后端”,忽略了函数式编程范式的本质差异。
误区“MCMC 是精确方法,SVI 只是近似,所以只要算力允许,就应该永远优先用 MCMC。”
正解MCMC 提供的样本渐近精确,但高维、大数据的后验会让链难以混合;SVI 虽然用简单分布族做近似,但收敛快、可扩展性强,在深度贝叶斯模型和大规模数据集上往往更实用。正确做法是根据问题维度、数据规模和实时性需求做权衡,MCMC 是主厨亲自下厨,SVI 是学徒快速顶班——客人多时学徒也能撑起场面。
为何常见:教科书强调 MCMC 的渐近无偏性,“近似”一词让 SVI 显得低人一等。
误区“NumPyro 内部用了 jit,所以我在模型里随便写 Python 的 for 循环、if 分支,运行也会被自动编译加速。”
正解jit 只对纯函数生效,且被编译函数内不能有依赖运行时值的 Python 控制流。NumPyro 对内置操作做了大量 jit 化,但你自己写的动态循环需要用 jax.lax.scan 或 jax.lax.while_loop 改写,否则 Python 解释器会成为瓶颈。它像“预制菜卡”:只有纯粹的操作序列才能被一键编译,含“看心情放盐”这类动态判断的环节,必须换成可编译的专用指令。
为何常见:看过 jit 加速演示后,容易高估它“普适编译”的魔法,忽略了 JAX 纯函数与静态图的限制。
误区“既然 NumPyro 支持 GPU,那我配一块顶级显卡,跑小模型也能体验几十倍加速。”
正解GPU 的优势来自大规模并行。小数据、低维模型的单链采样里,GPU 启动开销和 CPU↔GPU 数据传输反而可能拖慢速度。NumPyro 的 GPU 收益通常来自三种场景:大模型、大数据、用 vmap 并行跑几百条链。GPU 像超大烤盘:一次烤一千块月饼才划算,只烤一个蛋挞反而要预热半天。
为何常见:“GPU 更快”的直觉忽略了并行粒度与通信开销,属于工程常识类误区。
面试题
Q1. NumPyro 的底层自动微分与向量化引擎是?
- A.TensorFlow 计算图引擎
- B.PyTorch 张量计算库
- C.JAX 自动微分引擎← 正确答案
- D.Theano 静态编译器
答案:C。NumPyro 是 Pyro 官方推出的 JAX 后端实现,张量计算、自动微分和向量化全部由 JAX 引擎承担。PyTorch 只是 Pyro 原版的后端,TensorFlow 与 Theano 和 NumPyro 没有关系。选 C。
Q2. 在 NumPyro 中声明随机变量的核心 API 是?
- A.numpyro.param
- B.numpyro.sample← 正确答案
- C.numpyro.plate
- D.numpyro.observe
答案:B。numpyro.sample 是声明随机变量的核心 API,每个随机变量由“名字 + 分布”确定。param 声明可学习参数,plate 在旧版 Pyro 中做批量标记而 NumPyro 更推荐 vmap,observe 是 Pyro 老 API 的写法。选 B。
Q3. NumPyro 中的 NUTS 采样器属于哪一类算法?
- A.吉布斯采样的变体
- B.拒绝采样法的改进
- C.哈密顿蒙特卡洛改进← 正确答案
- D.粒子滤波方法的变体
答案:C。NUTS(No-U-Turn Sampler)是哈密顿蒙特卡洛(HMC)的自动化改进,它利用梯度构造动量系统高效探索后验,并自动决定探索深度,与随机游走式吉布斯采样、拒绝采样有本质区别。选 C。
Q4. JAX 中 vmap 变换的核心作用是?
- A.把函数批量映射到数组维度← 正确答案
- B.把函数编译为高效机器码
- C.自动计算函数的梯度信息
- D.转换 NumPy 数组为 JAX 数组
答案:A。vmap 将“作用于单个元素”的函数自动向量化,使其批量作用于数组每个元素,等价于隐式批处理。编译由 jit 负责,梯度由 grad 负责,数组转换用 jnp.asarray。选 A。
Q5. 相比 Pyro(PyTorch 版),NumPyro 的性能优势包括?
- A.JIT 编译显著加速推理循环← 正确答案
- B.原生 GPU 加速与自动并行← 正确答案
- C.vmap 轻松并行多条采样链← 正确答案
- D.完全不需要声明随机变量
答案:ABC。NumPyro 基于 JAX,天然获得 jit 编译、GPU 加速与 vmap 批处理,这让它在许多模型上明显快于 PyTorch 版 Pyro。但任何框架声明随机变量都不可或缺,D 错误。选 A、B、C。
Q6. SVI 变分推断优化的核心目标函数是?
- A.最大化数据的对数似然
- B.最大化证据下界 ELBO← 正确答案
- C.最小化预测误差平方和
- D.最大化先验分布的密度
答案:B。SVI 通过最大化 ELBO(证据下界)训练变分分布,最大化 ELBO 等价于最小化 q 与真实后验的 KL 散度。最大似然是频率派思路,不含对先验与 q 熵的权衡。选 B。
Q7. 关于 MCMC 收敛诊断,以下说法正确的有?
- A.预热期样本应被丢弃不用← 正确答案
- B.R-hat 接近 1 表示已收敛← 正确答案
- C.链越长结果一定越准确
- D.ESS 衡量有效样本的数量← 正确答案
答案:ABD。MCMC 诊断三板斧:丢弃 warmup、R-hat≈1 表示链间收敛、ESS 评估有效样本量。链长是必要非充分条件——自相关高的链即使很长,独立信息也有限,故 C 错误。选 A、B、D。
Q8. NUTS 中 target_accept_prob 参数的主要作用是?
- A.决定采样树的扩展最大深度
- B.设定目标接受率以调节步长← 正确答案
- C.控制马尔可夫链迭代总数
- D.指定并行运行链的条数
答案:B。target_accept_prob 设置 NUTS 的目标接受率(默认 0.8),双平均自适应算法据此调节步长大小。树深度由 NUTS 自动决定,迭代总数由 num_samples 控制,链条数由 num_chains 控制。选 B。
Q9. 关于 JAX 纯函数特性对 NumPyro 建模的影响,正确的是?
- A.模型函数不能修改外部全局状态← 正确答案
- B.模型内允许使用 Python 随机数
- C.模型内可以直接修改外部列表
- D.模型函数可依赖未传入的全局变量
答案:A。JAX 的 jit 要求函数纯函数化:无副作用、不依赖外部可变状态、不用 Python 随机数。NumPyro 模型继承此约束,随机性统一由显式传入的 PRNGKey 管理,否则会导致结果不可复现甚至编译报错。选 A。
Q10. 从 PyMC 迁移模型到 NumPyro 时,正确的改造有?
- A.上下文模型改为纯函数式定义← 正确答案
- B.pm.Normal 等分布改为 dist 类← 正确答案
- C.pm.sample 改为 MCMC 加 NUTS← 正确答案
- D.数据转 JAX 数组并显式传钥匙← 正确答案
答案:ABCD。PyMC 用 with 上下文管理器建模,NumPyro 用函数式模型;分布从 pm.xxx 换成 numpyro.distributions 的 dist.xxx;采样流程换成 MCMC+NUTS;输入需转成 JAX 数组并显式传 PRNGKey。四步缺一不可,因此全选。