简介:面向从事多准则决策、权重估计与贝叶斯统计建模的研究者与学生,这套 MATLAB 贝叶斯最佳—最差方法实现提供了可复用的代码框架,可用于在成对比较数据中融合先验信息并估计准则权重。包内共 8 个文件,以 6 个 .m 脚本为核心,覆盖主算法、图件绘制、JAGS 接口与链重构及收敛处理,另含 1 个 readme.md 和 1 个 txt 数据说明,压缩包约 15KB,轻量易查阅。已有 2174 人学习或下载,说明其在相关课程与研究中具有一定参考基础。读者可借助脚本理解 Bayesian BWM 的计算流程,结合示例数据替换准则名称与最佳—最差比较向量,并据 readme 完成 JAGS 环境配置,从而快速复现实验、核对结果并扩展自己的决策分析。
1. 从一组专家打分到后验权重:BayesianBWM 到底解决什么
评审会上最怕一种场面:专家对指标重要性打了分,BWM 算出一版权重,排序也出来了,有人追问“这个第一名的置信度有多高”,现场只能沉默。这套 matlab贝叶斯决策代码把 BayesianBWM 做成可运行工程,核心不是再写一遍最佳最差法,而是把专家比较判断里的噪声显式建模,输出权重的后验均值、区间和排序概率。它适合做供应商选择、选址评价、方案排序、指标赋权的人,尤其是被问过“权重稳不稳”的从业者。作为一套系统开源出来的 MATLAB 工程,价值在于把 BWM 的点估计变成可检验的后验分布,让权重不再是一个黑匣子数字。
2. 把 BWM 写成可计算的模型:决策变量、约束与贝叶斯层
2.1 最优最差比较向量的矩阵化
BWM 的输入不复杂,但方向最容易翻车。先确定准则集 (C={c_1,\dots,c_n}),挑出最优准则 (c_B) 和最差准则 (c_W)。然后给两个向量:最优准则相对其他准则的重要性 (A_B=(a_{B1},\dots,a_{Bn})),其中 (a_{BB}=1);其他准则相对最差准则的重要性 (A_W=(a_{1W},\dots,a_{nW})^T),其中 (a_{WW}=1)。标度常用 1 到 9,数值越大表示前者比后者越重要。经典 BWM 求权重时把最大偏差压小,写成线性规划就是最小化 (\xi),约束里出现 (|w_B-a_{Bj}w_j|\le \xi w_j) 和 (|w_j-a_{jW}w_W|\le \xi w_W),再加上权重归一化和非负约束。矩阵化之后,MATLAB 里不必手写一堆循环,把 (A_B) 和 (A_W) 当成行向量、列向量分别存,后面采样和一致性检验都围绕这两个向量展开。
我一般会把原始判断单独放一张表,不直接写进脚本。原因是后面做灵敏度分析时,改一个 (a_{Bj}) 就要能追溯到底是哪个专家、哪个准则改的。表里至少有四列:准则名、是否最优、是否最差、相对最优或最差的标度。下面这段 MATLAB 代码演示怎么把一行行记录整理成 BWM 需要的两个向量,并做最基本的维度检查。代码里的criteria是字符串数组,bestIdx和worstIdx是整数索引,ab和aw后续会传给采样函数。
% 准则名与最优/最差索引 criteria = ["成本", "交付", "质量", "服务", "风险"]; bestIdx = 3; % 质量被选为最优准则 worstIdx = 1; % 成本被选为最差准则 % 最优准则相对其他准则的标度,a_BB = 1 ab = [3, 2, 1, 4, 5]; % 对应 成本,交付,质量,服务,风险 % 其他准则相对最差准则的标度,a_WW = 1 aw = [1, 4, 6, 2, 8]; % 对应 成本,交付,质量,服务,风险 % 基本检查:长度一致、最优位为1、最差位为1 n = numel(criteria); assert(numel(ab) == n && numel(aw) == n, "比较向量长度与准则数不一致"); assert(ab(bestIdx) == 1, "最优准则自比较必须为1"); assert(aw(worstIdx) == 1, "最差准则自比较必须为1"); % 转成列向量,后续线性约束和采样都按列处理 ab = ab(:); aw = aw(:);这段代码的关键不是语法,而是把“谁是最优、谁是最差”固定成索引,而不是靠列顺序猜。bestIdx和worstIdx一旦写错,后面权重排序会整体反掉,而且代码不一定报错。ab和aw的长度必须等于准则数,两个自比较位置必须是 1,这两个断言能挡掉大部分低级错误。参数上,标度不要出现 0 或负数,1 到 9 之外的值在 BWM 里没有标准解释,除非团队自己定义了扩展标度并在报告里写清楚。
2.2 贝叶斯层怎么加:先验、似然与后验采样
贝叶斯 BWM 和经典 BWM 的差别在于:经典方法把 (a_{Bj}) 当成确定值,贝叶斯方法把它当成带噪声的观测。常见做法是把比较值看成围绕真实权重比值的随机变量,比如用对数正态似然,让 (a_{Bj}) 落在 (w_B/w_j) 附近,误差用尺度参数控制;权重本身用 softmax 变换后的正态先验,或者直接用 Dirichlet 先验,保证非负且和为 1。这样后验分布里既有专家判断的中心趋势,也有判断不一致带来的宽度。工程上不一定要上很复杂的概率编程工具,MATLAB 里用 Metropolis-Hastings 或切片采样就能跑,关键是把先验参数、似然尺度和初值写清楚。
下面是一段简化后的采样骨架,用来展示参数怎么落到代码里。logPost是自定义的对数后验函数,输入是未归一化的权重向量z,输出对数后验密度;nsamples是保留样本数,burnin是预热次数,thin是抽稀间隔。实际工程里logPost里会同时计算先验项和两个比较向量的似然项,这里只保留调用骨架,方便看清采样循环的结构。
% 采样参数 nsamples = 20000; burnin = 5000; thin = 5; nPar = n; % 权重维度,实际用 n-1 个自由参数也可 proposal = 0.05; % 随机游走步长,太大拒收率高,太小链走得慢 % 初值:均匀权重加一点扰动 z = ones(1, nPar) / nPar + 0.01 * randn(1, nPar); z = z / sum(z); samples = zeros(nsamples, nPar); logp = logPost(z, ab, aw, bestIdx, worstIdx); for it = 1:(burnin + nsamples * thin) zProp = z + proposal * randn(size(z)); zProp = zProp / sum(zProp); % 保持权重和为1 logpProp = logPost(zProp, ab, aw, bestIdx, worstIdx); if log(rand) < (logpProp - logp) z = zProp; logp = logpProp; end if it > burnin && mod(it - burnin, thin) == 0 idx = (it - burnin) / thin; samples(idx, :) = z; end end这段循环做的是随机游走 Metropolis:每次在当前位置附近生成候选权重,归一化后算对数后验,再用接受率决定要不要跳过去。proposal是最需要手调参数,设成 0.2 以上拒收率会明显升高,链容易原地打转;设成 0.005 以下接受率很高但样本高度相关,后验区间会假性变窄。burnin不能省,初值离后验远的时候前几千步不能算有效样本。thin是为了降低存储和自相关,通常设 5 到 20,样本量够大时不必抽太狠。判断参数是否合理,不要只看最终权重,先看接受率和迹图,接受率长期低于 10% 或高于 80% 都要回头调步长。
2.3 MATLAB 工程目录与入口脚本
这套工程常见做法是把“数据、模型、采样、诊断、导出”拆开,入口脚本只负责串流程。目录不必复杂,但每个文件职责要清楚,否则改一个先验参数会牵动半个工程。下面这张表是我会保留的最小结构,名字可按团队习惯改,职责别混。
| 目录或文件 | 职责 | 改它的时候注意什么 |
|---|---|---|
data/ | 放准则表、比较向量、方案得分 | 编码统一用 UTF-8,列名不要带空格 |
src/bwm_linear.m | 经典 BWM 线性规划,做基准对照 | 只依赖优化工具箱,别混入采样逻辑 |
src/log_post_bayes.m | 对数后验,先验和似然都在这里 | 改先验必须同步改文档 |
src/sample_bwm.m | 采样循环与接受率统计 | 步长、链长从这里传,不写死 |
src/check_consistency.m | 一致性比率和输入检查 | 报错信息要带准则名 |
run_bwm_demo.m | 入口脚本 | 只做参数组装和结果保存 |
入口脚本我一般写成“读表、校验、采样、诊断、导出”五段,不把采样循环塞进来。下面这段代码是入口的简化版,readtable读 CSV,check_consistency先挡明显矛盾,sample_bwm返回样本矩阵和接受率,最后把后验均值和 95% 区间写回表。opts里放采样参数,方便一次跑多组配置做对比。
% run_bwm_demo.m dataFile = fullfile("data", "criteria_demo.csv"); T = readtable(dataFile, "TextType", "string"); % 从表中取索引和比较向量,列名按实际表改 bestIdx = find(T.is_best); worstIdx = find(T.is_worst); ab = T.ab; aw = T.aw; % 先做一致性检查,不通过就别浪费采样时间 cr = check_consistency(ab, aw, bestIdx, worstIdx); fprintf("一致性比率 CR = %.4f\n", cr); opts.nsamples = 20000; opts.burnin = 5000; opts.thin = 5; opts.proposal = 0.05; [samples, accRate] = sample_bwm(ab, aw, bestIdx, worstIdx, opts); fprintf("接受率 = %.2f\n", accRate); postMean = mean(samples, 1)'; postLo = prctile(samples, 2.5, 1)'; postHi = prctile(samples, 97.5, 1)'; T.weight_mean = postMean; T.weight_lo = postLo; T.weight_hi = postHi; writetable(T, fullfile("out", "weights_posterior.csv"));入口脚本里最值得保留的是opts这层参数对象,而不是一长串散落的变量。dataFile和输出路径都走fullfile,避免换机器后路径分隔符出问题。check_consistency放在采样之前,是因为一致性比率超标时继续采样也能出结果,但那个结果只是在拟合一组自相矛盾的判断。writetable把均值、下界、上界一起导出,后面写报告不用再回 MATLAB 里翻变量。接受率要打印出来,低于 10% 或高于 80% 时先调proposal,不要急着解释权重。
提示:入口脚本不要写死准则数量,
n从表里读,否则新增一个指标就要改三处索引。
3. 跑通第一组权重:数据录入、参数设置与结果校验
3.1 指标与比较向量的录入格式
数据录入阶段最容易埋雷。CSV 表里如果只有准则名和标度,没有最优/最差标记,脚本就得靠列顺序猜,换个人整理数据立刻翻车。我习惯让表至少包含criterion、ab、aw、is_best、is_worst五列,其中ab是最优准则相对该准则的标度,aw是该准则相对最差准则的标度。最优行的ab必须是 1,最差行的aw必须是 1,其余行按 1 到 9 填。方案得分另放一张表,准则名必须和权重表完全一致,不要出现“成本”与“成本费用”这种看起来像但匹配不上的写法。
下面是一段录入后的校验代码,重点检查三件事:最优/最差标记是否唯一、两个向量在对应位置是否为 1、是否存在非正标度。T是readtable读进来的表,criterion是字符串列。校验不通过时直接报出准则名,比只报“索引越界”有用得多。
% 校验准则表 assert(sum(T.is_best) == 1, "最优准则必须且只能有一个"); assert(sum(T.is_worst) == 1, "最差准则必须且只能有一个"); bestIdx = find(T.is_best); worstIdx = find(T.is_worst); assert(T.ab(bestIdx) == 1, "最优准则行的 ab 必须为 1"); assert(T.aw(worstIdx) == 1, "最差准则行的 aw 必须为 1"); assert(all(T.ab > 0) && all(T.aw > 0), "标度必须为正数"); % 准则名不能重复,否则方案得分匹配时会错位 assert(numel(unique(T.criterion)) == height(T), "准则名存在重复");这段校验挡住的是“数据看起来能跑、结果却不可信”的问题。sum(T.is_best) == 1防止有人把两个准则都标成最优,T.ab(bestIdx) == 1防止最优行填了 3 却没人发现。标度为正数这条看起来多余,但实际表里出现 0 或空值时,readtable可能读成NaN,后面采样会直接崩。准则名唯一性检查是为了方案得分表能按名字对齐,不靠列顺序。参数上没有可调空间,这些是硬约束,不满足就回到原始判断表改,不要在脚本里做静默修正。
3.2 采样参数怎么定:链长、burn-in、thinning
采样参数没有一套放之四海皆准的值,但有可操作的起点。链长nsamples决定后验均值和区间的稳定性,维度在 5 到 9 个准则时,我一般从 20000 条保留样本起步;准则更多或后验形状更偏时加到 50000。burnin通常取保留样本的 20% 到 30%,初值离合理权重越远越要加长。thin取 5 到 10 能明显降自相关,但别用抽稀来掩盖链走得慢的问题。proposal按接受率调,目标是 20% 到 40% 的接受率,太低就减小步长,太高就增大步长。
下面这段代码演示怎么用两组不同步长各跑一次,比较接受率和后验均值差异。sample_bwm返回样本矩阵和接受率,postMean是后验均值。两次结果差异很大时,不要直接选好看的那组,先看哪组接受率更合理、迹图是否充分混合。
optsA = struct("nsamples", 20000, "burnin", 5000, "thin", 5, "proposal", 0.02); optsB = struct("nsamples", 20000, "burnin", 5000, "thin", 5, "proposal", 0.08); [sA, accA] = sample_bwm(ab, aw, bestIdx, worstIdx, optsA); [sB, accB] = sample_bwm(ab, aw, bestIdx, worstIdx, optsB); fprintf("proposal=0.02 接受率 %.2f,权重均值 %s\n", accA, mat2str(mean(sA,1), 3)); fprintf("proposal=0.08 接受率 %.2f,权重均值 %s\n", accB, mat2str(mean(sB,1), 3));这段对比不是为了选一个“看起来更准”的权重,而是检查采样器有没有把后验走歪。proposal=0.02时接受率如果高到 90%,样本会高度相关,后验区间偏窄;proposal=0.08时接受率如果低到 5%,链大部分时间在原地,均值可能还没离开初值附近。nsamples和burnin在这段里保持一致,只改步长,才能把差异归因到步长上。实际调参时先固定链长调步长,再固定步长加链长,不要一次改三个参数。
3.3 一致性检验与权重解读
BWM 的一致性比率用来衡量专家判断是否自相矛盾。常见计算方式是先求经典 BWM 的 (\xi^),再用 (\xi^) 除以一致性指标得到 CR。不同文献的一致性指标表略有差异,工程里要么按团队固定一张表,要么在脚本里把表写成常量并注明来源。CR 阈值常用 0.1 或 0.2,超过阈值不是代码错,而是判断本身需要回访专家。权重解读时不要只报均值,后验区间宽度同样重要:两个准则均值差 0.02,但区间大面积重叠,硬说一个比另一个重要就不稳。
下面这段代码把一致性比率、后验均值和 95% 区间整理成一张表,按均值降序排。cr来自check_consistency,samples是采样输出。排序用sortrows,但报告里要保留原始准则名,不要只给索引。
cr = check_consistency(ab, aw, bestIdx, worstIdx); postMean = mean(samples, 1)'; postLo = prctile(samples, 2.5, 1)'; postHi = prctile(samples, 97.5, 1)'; result = table(T.criterion, postMean, postLo, postHi, ... "VariableNames", ["criterion", "mean", "lo", "hi"]); result = sortrows(result, "mean", "descend"); fprintf("CR = %.4f\n", cr); disp(result);这段输出里cr要先看,超过阈值时后面的权重排序只能当参考,不能当结论。postMean是后验均值,postLo和postHi是 2.5% 与 97.5% 分位点,区间宽度反映专家判断的不确定性。排序按均值降序只是方便阅读,真正比较两个准则时要看区间是否重叠。参数上,分位点可以改成 5% 和 95% 做更保守的区间,但要在报告里写清楚,不能两套区间混着用。
注意:一致性比率超标时,先回查最优/最差准则是否选得合理,再查标度是否填反,不要靠调采样参数把 CR“跑下去”。
4. 常见坑排查:从“跑不动”到“结果不可信”
4.1 比较矩阵方向反了:权重排序整段翻转
现象很直观:脚本能跑完,接受率也正常,但成本类准则权重高得离谱,和质量类准则的排序完全反了。原因通常是把 (a_{Bj}) 和 (a_{jW}) 的方向记混了。(a_{Bj}) 是最优准则相对准则 (j) 的重要性,数值越大表示最优准则越重要;(a_{jW}) 是准则 (j) 相对最差准则的重要性,数值越大表示准则 (j) 越接近最优端。有人把“成本比质量重要 3 倍”写成 (a_{jW}=3),方向就翻了。解决方法是回到原始判断表,用一句话复述每个数字的含义,再对照bestIdx和worstIdx检查。代码里可以加一条打印:把最优准则名、最差准则名和两个向量一起输出,跑之前肉眼过一遍。
4.2 先验太强导致后验收缩:区间窄得不像话
现象是后验区间窄得几乎是一条线,专家判断明明分歧很大,输出却像所有人都达成一致。原因多半是先验尺度设得太小,似然还没起作用,后验已经被先验拽住了。贝叶斯 BWM 里权重的先验如果用一个很小的方差,等价于提前认定权重就在某个窄范围里,数据再多也拉不回来。解决方法是把先验尺度调大,或者先用无信息先验跑一版,看看后验区间是否合理,再逐步加先验。检查时看先验参数和似然尺度的相对大小,先验标准差比似然噪声还小,基本就会压住数据。报告里要写清楚用了什么先验,不能只报后验均值。
4.3 MCMC 不收敛:链在初值附近打转
现象是迹图前半段和后半段均值不一致,或者多条链从不同初值出发后始终不混合,接受率低到个位数。原因可能是步长太小、后验曲面太陡、初值离高密度区太远,或者模型里存在不可识别的参数。解决顺序是:先把proposal调大看接受率是否回到 20% 以上;再加长burnin;再换几个分散的初值各跑一条链,比较链间方差。如果多条链还是各走各的,不要继续加样本量,先检查似然函数里的权重比是否写错,尤其是最优准则和最差准则的索引有没有在采样过程中被交换。诊断上至少看迹图、接受率和多链均值差,三个都过再谈结果。
4.4 一致性比率超阈值:不是代码错,是判断自相矛盾
现象是 CR 明显高于阈值,权重仍然能算出来,但排序在相邻准则之间来回跳。原因通常是最优/最差准则选得不合适,或者某个专家把“稍微重要”填成了“极端重要”。BWM 的一致性检验就是用来暴露这种矛盾的,代码没有义务把它修好。解决方法是回访判断来源,优先检查最优准则和最差准则相关的几行,因为这两个向量对 CR 影响最大。如果专家坚持原判断,可以在报告里保留 CR 并说明局限性,而不是悄悄改标度。工程上可以把 CR 检查做成硬门槛,超过阈值直接停止导出权重,避免有问题的结果流到下游。
4.5 维度变化后索引错位:新增指标后权重对不上
现象是加了一个新准则后脚本还能跑,但导出的权重和准则名错位,或者方案得分表匹配不上。原因多半是代码里写死了准则数量或列位置,新增一行后bestIdx、worstIdx和向量长度没同步更新。解决方法是所有维度从表里读,索引用find按标记取,不要用ab(3)这种硬编码。方案得分表按准则名合并,不按列顺序合并。每次改数据后先跑一遍维度断言:准则数、ab长度、aw长度、方案得分表列数必须一致。这个坑不一定会报错,但会让结论悄悄偏掉,属于典型的血泪经验。
5. 把贝叶斯 BWM 嵌进方案排序:灵敏度分析与场景对比
5.1 权重后验如何转成方案得分
权重跑出来只是中间结果,真正要回答的是“哪个方案排第一”。常见做法是先用方案在准则上的得分矩阵 (S),乘以权重向量 (w),得到综合得分。贝叶斯版本不只用后验均值乘一次,而是从后验样本里抽一组权重算一次得分,重复很多次,得到每个方案综合得分的分布。这样不仅知道谁排第一,还能知道排第一的概率有多大。方案得分矩阵要提前归一化,成本型指标和效益型指标方向要统一,否则权重再准,排序也会被量纲带偏。
下面这段代码从后验样本里抽 2000 组权重,和方案得分矩阵相乘,输出每个方案的均值、区间和第一名概率。S是方案得分矩阵,行是方案,列是准则,列顺序必须和T.criterion一致;samples是后验权重样本。winProb统计每组权重下得分最高的方案。
nDraw = 2000; drawIdx = randperm(size(samples, 1), nDraw); W = samples(drawIdx, :); % nDraw × nCriteria scores = S * W'; % nAlt × nDraw scoreMean = mean(scores, 2); scoreLo = prctile(scores, 2.5, 2); scoreHi = prctile(scores, 97.5, 2); [~, winner] = max(scores, [], 1); winProb = accumarray(winner', 1, [size(S,1), 1]) / nDraw; fprintf("方案得分均值:%s\n", mat2str(scoreMean, 3)); fprintf("第一名概率:%s\n", mat2str(winProb, 3));这段代码的关键是S * W'这一下矩阵乘法,把“每组权重下的方案得分”一次性算完,不用两层循环。drawIdx从后验样本里随机抽,避免直接用全部样本导致计算量过大;抽 2000 组通常够看分布,方案多时加到 5000。scoreLo和scoreHi给的是综合得分的 95% 区间,winProb给的是排序概率。参数上,nDraw越大结果越稳,但不要抽到超过样本数;S的列顺序必须和权重列严格一致,最好在代码里加一条准则名比对断言。
5.2 灵敏度分析:改一个比较值看排序是否翻转
灵敏度分析不是把所有权重重新跑一遍,而是盯住最敏感的那几个比较值。做法是选一个 (a_{Bj}),在合理范围内上下浮动,比如从 3 改到 2 和 4,每次重跑采样,观察第一名方案是否变化、目标准则的权重区间是否移动。如果只改一个标度排序就翻转,说明结论对那个判断很敏感,报告里要重点说明。反过来,标度在 2 到 4 之间变动排序都不变,结论更稳。下面这段代码演示对一个位置做三档扫描,abList存三组不同的ab,循环调用采样并记录第一名方案。
baseAb = ab; pos = 2; % 要扫描的比较值位置 values = [2, 3, 4]; abList = repmat(baseAb, numel(values), 1); abList(:, pos) = values(:); winName = strings(numel(values), 1); for k = 1:numel(values) [sK, ~] = sample_bwm(abList(k,:), aw, bestIdx, worstIdx, opts); Wk = sK(randperm(size(sK,1), nDraw), :); scoreK = S * Wk'; [~, winK] = max(mean(scoreK, 2)); winName(k) = T.criterion(winK); % 这里换成方案名更合适 fprintf("a_B%d = %d,第一名索引 %d\n", pos, values(k), winK); end这段扫描里pos是要改的准则位置,values是三档标度,abList每行是一组完整比较向量。每次采样后重新抽权重算方案得分,winK是当前档位下的第一名方案。代码里用T.criterion(winK)只是占位,实际应换成方案名数组。参数上,扫描范围不要超出 1 到 9,也不要把最优准则自己的位置改掉;pos对应的abList(:,pos)如果不是最优准则行,含义会变,要在报告里写清楚改的是哪个准则相对最优准则的标度。
5.3 批量实验与结果导出
批量实验最容易乱在结果表结构。每次跑完只存一个权重矩阵,后面根本分不清哪组参数对应哪个场景。我一般让结果表至少包含:场景名、改动位置、改动值、接受率、CR、各准则后验均值、各准则区间下界、各准则区间上界、第一名方案、第一名概率。字段多但值得,因为写报告时经常要回头查“那次排序翻转是不是因为 CR 超标”。下面这段代码把一次批量结果拼成表并写 CSV,scenario是场景名,results是每轮结果的结构体数组。
rows = cell(numel(results), 1); for i = 1:numel(results) r = results(i); rows{i} = table( ... string(r.scenario), r.pos, r.value, r.accRate, r.cr, ... r.postMean(1), r.postMean(2), r.postMean(3), ... r.winProb, ... "VariableNames", ["scenario","pos","value","accRate","cr", ... "w1","w2","w3","winProb"]); end outTable = vertcat(rows{:}); writetable(outTable, fullfile("out", "sensitivity_summary.csv"));这段导出代码的重点是字段对齐,而不是表有多漂亮。rows{i}里每个字段都来自同一轮实验,vertcat合并后列名一致才能写表。w1、w2、w3只是示意,实际准则多时应该用循环生成列名,或者把权重存成字符串列。winProb建议保留,因为只看第一名方案会丢掉“第一名优势有多大”的信息。参数上,scenario命名要能一眼看出改了什么,比如ab_pos2_2,不要用test1、test2。
6. 进阶技巧:用后验区间做决策而不是只看均值
6.1 后验区间与排序概率
权重均值适合写摘要,不适合单独做决策。两个准则均值差 0.03,但 95% 区间大面积重叠时,硬排先后没有意义。更稳的读法是看排序概率:从后验样本里统计“准则 A 权重高于准则 B”的比例,比例接近 0.5 就说明数据分不出高低。方案层面同理,第一名概率 0.52 和 0.91 是两种结论,前者要补判断或加专家,后者可以直接推进。把区间和排序概率一起放进报告,比只给一张权重条形图更能挡住追问。
6.2 收敛诊断的固定检查表
采样跑完不要只看权重。下面这张检查表我每次都会过一遍,任何一项不过,结果先不进报告。
| 检查项 | 通过标准 | 不通过先动哪里 |
|---|---|---|
| 接受率 | 20% 到 40% | 调proposal |
| 迹图 | 前后段均值无明显漂移 | 加burnin或换初值 |
| 多链均值差 | 不同初值链的均值接近 | 查似然和索引 |
| 后验区间 | 不为零宽度,也不宽到无信息 | 查先验尺度和似然噪声 |
| CR | 低于团队阈值 | 回查比较判断 |
6.3 一个可复用的验收脚本
验收脚本不追求功能多,只做三件事:重跑一次固定种子、比对关键输出、打印不通过项。下面这段代码把接受率、CR 和权重区间宽度做成硬检查,任何一项越界就报错,避免有问题的结果被顺手导出。
function ok = validate_run(samples, accRate, cr, crLimit) ok = true; if accRate < 0.1 || accRate > 0.8 warning("接受率 %.2f 不在合理范围", accRate); ok = false; end if cr > crLimit warning("CR %.3f 超过阈值 %.3f", cr, crLimit); ok = false; end ciWidth = prctile(samples, 97.5, 1) - prctile(samples, 2.5, 1); if any(ciWidth < 1e-4) warning("存在后验区间过窄的准则"); ok = false; end end这段验收的价值在于把“我觉得差不多”变成可执行门槛。accRate越界先查步长,cr越界先查判断,ciWidth过窄先查先验是不是压得太狠。固定随机种子后重跑,关键输出应该高度接近;差异大说明链长不够或采样器不稳定。从那以后我每次交付权重表前都强制走一遍这个脚本,宁可多花十分钟,也不把一条没混合好的链写进结论。希望帮到你。
本文还有配套的精品资源,点击获取