CS221 · 人工智能:原理与技术
Lecture 13 · 贝叶斯网络 II:Gibbs 采样与条件独立
源文件:lecture-13.md
Lecture 13 · 贝叶斯网络 II:Gibbs 采样与条件独立
CS221: Artificial Intelligence — Principles and Techniques · Stanford · Autumn 2025
📅 Nov 3 · 💻gibbs_sampling.py
承上启下:从拒绝采样到 Gibbs 采样
上一讲 Lecture 12 · 贝叶斯网络 I:建模与推断 完成了从「状态型模型」到「变量型模型」的转身:
- 用一组随机变量 $X = (X_1, \dots, X_n)$ 描述世界;
- 贝叶斯网络 = 有向无环图(DAG)+ 每个节点的局部条件分布 $p(x_i \mid x_{\mathrm{Parents}(i)})$,联合分布是它们的乘积:$P(X_1, \dots, X_n) = \prod_i p(x_i \mid x_{\mathrm{Parents}(i)})$;
- 概率推断 = 在联合分布上「执行查询」(像 SQL):条件化证据、边缘化其余变量;
- 近似推断的第一招是拒绝采样(rejection sampling):从概率程序前向采样,丢掉不匹配证据的样本。
本讲(贝叶斯网络 II) 沿着两条线推进:
- 算法线:拒绝采样的痛点是「证据罕见时几乎所有样本都被丢弃」。本讲引入 Gibbs 采样(Gibbs sampling)——一种 MCMC 方法,固定证据、轮流重采样每个变量,样本永不被拒绝;再用马尔可夫毯(Markov blanket)让每步重采样只做局部计算。
- 理论线:反过来审视图结构本身——条件独立(conditional independence):图如何编码「谁与谁无关」,为什么观测一个变量会「打开」或「堵住」信息通路。
提示
拒绝采样「每次从头生成、生成时不看证据、事后检查」;Gibbs 采样「从一个已经满足证据的样本出发,一次只改一个变量、证据永远焊死」。前者的样本彼此独立但大量浪费,后者的样本全部保留但前后相关。
下一讲 Lecture 14 · 贝叶斯网络 III:参数学习 会回答最后一个悬而未决的问题:这些局部条件概率本身从哪里来。
1. 回顾:联合分布与拒绝采样
1.1 警报网络(Alarm)
本讲反复用同一个玩具例子:盗窃 $B$(Burglary)、地震 $E$(Earthquake)、警报 $A$(Alarm)。
graph TD
B["B 盗窃"] --> A["A 警报"]
E["E 地震"] --> A
style B fill:#e1f5fe,stroke:#0277bd
style E fill:#e1f5fe,stroke:#0277bd
style A fill:#ffe0b2,stroke:#e65100
构造联合分布的四步与上一讲一致:定义变量 → 连边 → 每个节点一个局部条件分布 → 相乘:
$$P(B = b, E = e, A = a) = p(b)\, p(e)\, p(a \mid b, e)$$
from bayes import ProbTable, Bernoulli
from einops import einsum
# 3. 每个节点一个局部条件分布
p_b = ProbTable("B", [0.95, 0.05]) # p(b):盗窃先验,5% 概率发生
p_e = ProbTable("E", [0.95, 0.05]) # p(e):地震先验,5% 概率发生
p_a_given_be = ProbTable("A | B E", # p(a | b, e):警报 = (盗窃 OR 地震)
lambda b, e, a: a == (b or e), shape=(2, 2, 2))
# 4. 联合分布 P(B, E, A) = p(b) p(e) p(a | b, e)
P_BEA = ProbTable("B E A",
einsum(p_b.p, p_e.p, p_a_given_be.p, "b, e, b e a -> b e a"))
精确推断回答 $P(B \mid A = 1)$:条件化证据取切片、边缘化其余变量、再归一化:
$$P(B = b \mid A = 1) = \frac{\sum_{e} P(B = b, E = e, A = 1)}{\sum_{b', e} P(B = b', E = e, A = 1)}$$
P_BEA1 = ProbTable("B E A=1", P_BEA.p[:, :, 1]) # 条件化 A=1:P(B, E, A=1)
P_BA1 = ProbTable("B A=1", einsum(P_BEA1.p, "b e -> b")) # 边缘化 E:P(B, A=1)
P_A1 = ProbTable("A=1", einsum(P_BA1.p, "b ->")) # 证据概率 P(A=1)(归一化常数)
P_B_given_A1 = ProbTable("B | A=1", P_BA1.p / P_A1.p) # 归一化:P(B | A=1)
上一讲手算过这个数:$P(B = 1 \mid A = 1) = 0.05 / 0.0975 \approx 0.51$。
注意
构造完整联合分布可能指数级慢:$n$ 个二值变量的联合表有 $2^n$ 项,先乘出整个张量再边缘化,空间和时间都随变量数指数增长。玩具网络无所谓,变量一多就必须转向采样这类近似方法——这正是本讲的动机。
1.2 概率程序 + 拒绝采样
一个概率程序(probabilistic program)返回联合分布的一个样本,从而定义了这个分布:
def alarm():
B = Bernoulli(0.05) # 采样盗窃
E = Bernoulli(0.05) # 采样地震
A = B or E # 警报是确定性函数
return {"B": B, "E": E, "A": A}
拒绝采样:反复采样,只保留满足证据的样本,再把查询值计数归一化:
def rejection_sampling(program, query, evidence, num_samples):
"""program: 返回一个样本;query: sample→关注值;evidence: sample→是否保留"""
counts = defaultdict(int)
for _ in range(num_samples):
sample = program()
if evidence(sample): # 不满足证据 → 直接丢弃
counts[query(sample)] += 1
total_count = sum(counts.values())
probs = {q: counts[q] / total_count for q in counts}
return probs
# P(B | A = 1)
query = lambda sample: sample["B"]
evidence = lambda sample: sample["A"] == 1
result = rejection_sampling(alarm, query, evidence, num_samples=300)
注意
采 $N$ 个样本,期望只有 $N \cdot P(E = e)$ 个通过 if evidence(sample) 这道闸门。警报例子里 $P(A = 1) \approx 0.0975$,300 个样本平均只留下约 29 个;若证据概率是 $10^{-4}$ 量级,有效样本几乎为零——拒绝采样会慢到不可用。
2. Gibbs 采样:一种 MCMC
2.1 从「重来」到「续用」
对比两种采样的样本利用方式:
graph LR
subgraph 拒绝采样["拒绝采样(大量被丢弃)"]
R1["sample"] -."丢弃".-> RX1["✗"]
R2["sample ✓"]
R3["sample"] -."丢弃".-> RX3["✗"]
R4["sample ✓"]
end
subgraph Gibbs["Gibbs 采样(永不丢弃)"]
G1["sample"] --> G2["sample"] --> G3["sample"] --> G4["sample"]
end
style R2 fill:#c8e6c9,stroke:#2e7d32
style R4 fill:#c8e6c9,stroke:#2e7d32
style RX1 fill:#ffcdd2,stroke:#c62828
style RX3 fill:#ffcdd2,stroke:#c62828
style G1 fill:#e1f5fe,stroke:#0277bd
style G2 fill:#e1f5fe,stroke:#0277bd
style G3 fill:#e1f5fe,stroke:#0277bd
style G4 fill:#e1f5fe,stroke:#0277bd
| 拒绝采样 | Gibbs 采样 | |
|---|---|---|
| 样本关系 | 每个样本相互独立 | 样本前后相关(马尔可夫链) |
| 生成方式 | 每次从头生成整个样本 | 在上一个样本上改一个变量 |
| 是否用证据 | 生成时不看证据 → 常被拒绝 | 固定证据,永远满足 → 不拒绝 |
| 代价 | 证据罕见时几乎全丢弃 | 样本相关,需要更多样本降方差 |
Gibbs 采样基本思想:从一个满足证据的任意完整赋值开始,一次改一个变量——对每个非证据变量 $X_i$,在「给定其余所有变量当前值」的条件分布下重采样:
$$X_i \sim P(X_i \mid X_{-i} = x_{-i})$$
其中 $X_{-i}$ 表示除 $X_i$ 外的全部变量(含被固定的证据变量),$x_{-i}$ 是它们的当前取值。它是马尔可夫链蒙特卡洛(Markov chain Monte Carlo, MCMC)的一个特例。
提示
每一步重采样都定义了状态空间上的一个随机转移,整个过程是一条马尔可夫链——与 Lecture 7 · 马尔可夫决策过程 里的链是同一个数学对象,只是这里没有动作和奖励。可以证明:这条链的平稳分布(stationary distribution)恰好是我们想要的后验 $P(X_{\text{非证据}} \mid E = e)$。只要链能充分「混合」(mix),走得足够久后,样本出现的频率就趋于后验概率——于是「数样本」就等于「算后验」。
2.2 例子:电话链 A → B → C
graph LR
A["A"] -->|"p(b∣a)"| B["B"]
B -->|"p(c∣b)"| C["C"]
style A fill:#c8e6c9,stroke:#2e7d32
style B fill:#e1f5fe,stroke:#0277bd
style C fill:#ffe0b2,stroke:#e65100
直觉:A 发一个比特给 B,B 再转发给 C,每一步传话都有 20% 的概率被「噪声」翻转——就像传话游戏。
p_a = ProbTable("A", [0.5, 0.5]) # p(a):均匀先验
p_b_given_a = ProbTable("B | A", lambda a, b: 0.8 if b == a else 0.2, shape=(2, 2)) # 80% 保真
p_c_given_b = ProbTable("C | B", lambda b, c: 0.8 if c == b else 0.2, shape=(2, 2)) # 80% 保真
推断目标:$P(A \mid C = 1)$。直觉:$C = 1$ 会推高 $A = 1$ 的概率,但证据要反向穿过两步「腐蚀」,所以关联被削弱——后验应该高于 0.5 但远低于 0.8。
先跑拒绝采样作对照:
def telephone():
A = Bernoulli(0.5)
B = Bernoulli(0.8 if A == 1 else 0.2)
C = Bernoulli(0.8 if B == 1 else 0.2)
return {"A": A, "B": B, "C": C}
query = lambda sample: sample["A"]
evidence = lambda sample: sample["C"] == 1
rejection_probs = rejection_sampling(telephone, query, evidence, num_samples=100)
2.3 条件分布:用联合概率之比算出来
现在跑 Gibbs 采样。先给一个满足证据 $C = 1$ 的任意初始赋值:
x = {"A": 1, "B": 0, "C": 1} # C = 1 是证据,固定不动;只重采样 A、B
关键一步:如何在「给定 $A = a$、$C = c$」下对 $B$ 重采样?把条件概率写成联合概率之比——分子是把 $B$ 设成某个值后的联合概率,分母对被采样变量 $B$ 求和(归一化常数):
$$P(B = b \mid A = a, C = c) = \frac{P(A = a, B = b, C = c)}{\sum_{b'} P(A = a, B = b', C = c)}$$
而每个联合概率就是局部条件概率的乘积,逐点可算:
$$P(A = a, B = b, C = c) = p(a)\, p(b \mid a)\, p(c \mid b)$$
def joint_prob(x, var, value):
# x 是当前赋值,如 {A:0, B:0, C:1};(var: value) 是要尝试的改动,如 "B": 1
y = x | {var: value} # 更新后的赋值 {A:0, B:1, C:1}
# 用局部条件概率之积算 P(y)
return p_a.p[y["A"]] * p_b_given_a.p[y["A"], y["B"]] * p_c_given_b.p[y["B"], y["C"]]
提示
精确推断要把 $2^n$ 项的联合张量整个乘出来;Gibbs 只需要「对一个具体赋值求联合概率」——沿着网络把 $n$ 个局部因子逐点相乘,代价 $O(n)$。归一化也只对被采样那一个变量的取值域求和(二值变量只需算 2 个数)。指数运算被拆成了大量廉价的局部运算。
手动走一遍对 $B$ 的一次重采样($B$ 会被「拉向 1」,因为当前 $A = 1$ 且 $C = 1$,两头都偏好 $B = 1$):
p_ab0c = joint_prob(x, "B", 0) # P(A=1, B=0, C=1)
p_ab1c = joint_prob(x, "B", 1) # P(A=1, B=1, C=1)
p_ac = p_ab0c + p_ab1c # P(A=1, C=1):只对 B 求和
x["B"] = sample_dict({0: p_ab0c / p_ac, 1: p_ab1c / p_ac}) # 从条件分布采样
2.4 Gibbs 采样通用实现
def gibbs_sampling(init_x, vars, query, joint_prob, num_iterations):
"""从初始赋值 init_x 出发,在 vars(不含证据变量)上循环 num_iterations 次,
返回 query 值的估计分布。"""
x = dict(init_x) # 拷贝一份(好习惯)
counts = defaultdict(int) # 记录 query 值的计数
for _ in range(num_iterations):
for var in vars: # 轮流处理每个非证据变量
# 对该变量的每个取值算未归一化概率 P(..., var=value)
probs = {value: joint_prob(x, var, value) for value in [0, 1]}
probs = normalize_dict(probs) # 归一化成条件分布
x[var] = sample_dict(probs) # 从条件分布采样,就地更新
counts[query(x)] += 1 # 记录当前查询值
return normalize_dict(counts) # 计数归一化 → 估计分布
跑起来:
# 一次迭代
gibbs_probs = gibbs_sampling(x, vars=["A", "B"], query=query,
joint_prob=joint_prob, num_iterations=1)
# 多次迭代收敛
gibbs_probs = gibbs_sampling(x, vars=["A", "B"], query=query,
joint_prob=joint_prob, num_iterations=100)
# 与拒绝采样结果对比:两者都在同一「量级」内
运行时间:$O(\text{迭代数} \times \text{变量数} \times |\text{取值域}| \times \text{变量数})$。最后那个「变量数」来自 joint_prob 每次都要乘上所有局部条件概率——下一节要把它优化掉。
说明
核心节奏:一次只改一个变量;用「联合概率之比」得到它的条件分布;只需对这个变量的取值域求和。
实践中常见的两个改进(讲义未展开,属常识补充):初始若干步样本受初始赋值影响大,通常丢弃(称 burn-in);相邻样本高度相关,可每隔 $k$ 步取一个样本(thinning)以降低相关性。
3. 马尔可夫毯:只算局部
3.1 动机:只有一个变量在变
考虑更长的链 $A \to B \to C \to D \to E$,联合概率是 $p(a)\, p(b \mid a)\, p(c \mid b)\, p(d \mid c)\, p(e \mid d)$。每次采样一个变量时都乘上全部因子太浪费——因为只有一个变量在变,大多数因子根本不含这个变量,对结果毫无影响。
回到电话链 $A \to B \to C$,设正在采样 $P(A \mid B = b, C = c)$:
$$\begin{aligned} P(A = 0, B = b, C = c) &= p(a{=}0) \cdot p(b \mid a{=}0) \cdot \color{gray}{p(c \mid b)} \\ P(A = 1, B = b, C = c) &= p(a{=}1) \cdot p(b \mid a{=}1) \cdot \color{gray}{p(c \mid b)} \end{aligned}$$
提示
因子 $p(c \mid b)$ 不含 $A$,在两种情形里取值完全相同,归一化相除时被约掉,对 $P(A \mid B{=}b, C{=}c)$ 毫无影响——直接忽略它!一般规律:采样 $X$ 时,只需保留公式里出现 $X$ 的那些局部因子($X$ 自己的 $p(x \mid \mathrm{parents})$,以及每个子节点的 $p(\text{child} \mid \dots, x, \dots)$)。
3.2 定义与性质
规则:采样某变量时,只需包含涉及该变量的局部条件概率。要评估这些因子,需要知道哪些其他变量的值?这组变量就叫该变量的马尔可夫毯(Markov blanket)。
说明
一个节点 $X$ 的马尔可夫毯 $\mathrm{MB}(X)$ = 它的所有父节点、所有子节点,以及子节点的其它父节点(co-parents)。关键性质:给定马尔可夫毯,$X$ 与网络中其余所有变量条件独立:
$$P(X \mid X_{-X}) = P(X \mid \mathrm{MB}(X))$$
三类成员各有来历:父节点出现在 $X$ 自己的因子 $p(x \mid \mathrm{parents})$ 里;子节点与 co-parents 出现在每个子节点的因子 $p(\text{child} \mid \text{其全部父})$ 里。链结构里没有 co-parents,所以容易忘掉第三类——v-结构(两个父共享一个子)才用得上,这与 §5 的 explaining away 是同一件事。
在电话链 $A \to B \to C$ 中:
$$\mathrm{MB}(A) = \{B\}, \qquad \mathrm{MB}(B) = \{A, C\}, \qquad \mathrm{MB}(C) = \{B\}$$
graph TD
P1["父节点"] --> V(("X"))
P2["父节点"] --> V
V --> C1["子节点"]
V --> C2["子节点"]
CoP["子节点的<br/>其它父节点"] --> C1
subgraph MB["X 的马尔可夫毯"]
P1
P2
C1
C2
CoP
end
style V fill:#ffe0b2,stroke:#e65100
style P1 fill:#e1f5fe,stroke:#0277bd
style P2 fill:#e1f5fe,stroke:#0277bd
style C1 fill:#f3e5f5,stroke:#7b1fa2
style C2 fill:#f3e5f5,stroke:#7b1fa2
style CoP fill:#fce4ec,stroke:#c2185b
3.3 用马尔可夫毯加速
把 joint_prob 换成只含「涉及该变量的因子」的 markov_prob:
def markov_prob(x, var, value):
y = x | {var: value}
if var == "A":
return p_a.p[y["A"]] * p_b_given_a.p[y["A"], y["B"]] # 不含 p(c | b)
elif var == "B":
return p_b_given_a.p[y["A"], y["B"]] * p_c_given_b.p[y["B"], y["C"]] # 不含 p(a)
else:
raise ValueError(f"Unknown variable: {var}")
# 用同一个 gibbs_sampling,只是换了 joint_prob
x = {"A": 0, "B": 0, "C": 1}
query = lambda sample: sample["A"]
probs = gibbs_sampling(x, vars=["A", "B"], query=query,
joint_prob=markov_prob, num_iterations=100)
运行时间的改进——把最后一个「变量数」换成了「马尔可夫毯大小」:
| 版本 | 运行时间 |
|---|---|
joint_prob(乘全部因子) |
$O(\text{迭代数} \times \text{变量数} \times \lvert\text{取值域}\rvert \times \textbf{变量数})$ |
markov_prob(只乘相关因子) |
$O(\text{迭代数} \times \text{变量数} \times \lvert\text{取值域}\rvert \times \lvert\textbf{马尔可夫毯}\rvert)$ |
提示
若各节点的马尔可夫毯都很小(图稀疏),每步重采样就是常数代价,与网络总规模无关——这是 Gibbs 采样能在大型贝叶斯网络上实用的关键。反过来,稠密图(比如 Lecture 12 · 贝叶斯网络 I:建模与推断 里全连接的自回归语言模型)马尔可夫毯就是全体变量,局部化不省任何东西。
3.4 在警报网络上跑 Gibbs
p_b = ProbTable("B", [0.95, 0.05])
p_e = ProbTable("E", [0.95, 0.05])
p_a_given_be = ProbTable("A | B E", lambda b, e, a: a == (b or e), shape=(2, 2, 2))
x = {"B": 1, "E": 1, "A": 1} # 满足证据 A = 1 的初始赋值
def compute_prob(x, var, value):
y = x | {var: value}
return p_b.p[y["B"]] * p_e.p[y["E"]] * p_a_given_be.p[y["B"], y["E"], y["A"]]
query = lambda sample: sample["B"] # 关注 P(B | A = 1)
probs = gibbs_sampling(x, vars=["B", "E"], query=query,
joint_prob=compute_prob, num_iterations=200)
# 注意:这里的估计其实不太准 —— 引出下一节的坑
注意
讲义特意指出警报网络上的 Gibbs 估计不够准(精确值应约为 0.51)。原因在下一节:警报网络里 $B$、$E$、$A$ 之间存在确定性 OR 约束,条件在 $A = 1$ 上之后 $B$ 与 $E$ 高度耦合——「一次改一个变量」的链在这种地形上挪动得非常慢。
4. 何时用哪种采样
4.1 拒绝采样怕:罕见证据
# A → B,证据 B = 1 极其罕见
p_a = ProbTable("A", [0.5, 0.5])
p_b_given_a = ProbTable("B | A", [[0.9999, 0.0001],
[0.9998, 0.0002]])
# 目标 P(A | B = 1)
例子
证据概率 $P(B = 1) = 0.5 \times 0.0001 + 0.5 \times 0.0002 = 0.00015$——平均每 6667 个样本才留下 1 个。而精确后验很好算:$P(A = 1 \mid B = 1) = \frac{0.5 \times 0.0002}{0.00015} = 2/3$。拒绝采样想把这个 $2/3$ 估准,需要的总样本量是天文数字;Gibbs 采样直接把 $B = 1$ 焊死在赋值里,从不拒绝,每一步都是有效样本。
4.2 Gibbs 采样怕:高度相关
# A → B,B 完全复制 A(确定性)
p_a = ProbTable("A", [0.5, 0.5])
p_b_given_a = ProbTable("B | A", lambda a, b: a == b, shape=(2, 2))
# 目标 P(A)(无证据)
x = {"A": 0, "B": 0}
def joint_prob(x, var, value):
y = x | {var: value}
return p_a.p[y["A"]] * p_b_given_a.p[y["A"], y["B"]]
query = lambda sample: sample["A"]
gibbs_probs = gibbs_sampling(x, vars=["A", "B"], query=query,
joint_prob=joint_prob, num_iterations=50)
因为 $B$ 恒等于 $A$:从 $(A{=}0, B{=}0)$ 出发,采 $A$ 时 $B = 0$ 逼得 $A = 0$,采 $B$ 时 $A = 0$ 逼得 $B = 0$……链永远卡在 $(0,0)$,探索不到 $A = 1$,50 次迭代给出严重偏差的答案(正确答案显然是均匀的 $[0.5, 0.5]$)。反倒是拒绝采样每次从头生成、无证据可拒,完美无偏。
graph LR
S00["(A=0, B=0)"] -."卡住".-> S00
S11["(A=1, B=1)"]
S00 -. "无法到达" .-x S11
style S00 fill:#ffcdd2,stroke:#c62828
style S11 fill:#c8e6c9,stroke:#2e7d32
注意
「Gibbs 收敛到后验」的前提是马尔可夫链遍历(ergodic)——任意两个正概率状态之间可以互达。确定性(或零概率)的局部分布会把状态空间切成互不连通的岛,链困在初始岛里,收敛保证失效。即便不是严格确定、只是「强相关」,链也会混合(mixing)极慢:理论上用混合时间(mixing time)刻画需要多少步才能接近平稳分布。实践补救:多条链不同初始化、随机重启、或一次联合重采样一组强耦合变量(block Gibbs)。
4.3 对比总结
| 场景 | 拒绝采样 | Gibbs 采样 |
|---|---|---|
| 一般精确性 | 无偏、独立样本 | 渐近正确、样本相关 |
| 罕见证据 | ❌ 几乎全被拒绝,极慢 | ✅ 固定证据,工作正常 |
| 高度相关变量 | ✅ 从头生成,无偏 | ❌ 卡住、混合慢 |
| 是否复用证据 | 否 | 是 |
说明
Gibbs 采样是 MCMC 家族的一个特例:每步的「提议」就是精确的单变量条件分布,因此接受率恒为 1。更一般的是 Metropolis-Hastings——从任意提议分布(proposal distribution)采候选样本,再按接受率纠偏。理论上用混合时间刻画有效运行时间(样本相关程度);实践中 Gibbs 简单有效,但在病态地形上可能非常慢。
5. 条件独立:图结构编码的独立性
到此为止,推断算法对图的具体结构基本无感(马尔可夫毯已经露出一点苗头)。现在正面回答:图结构本身告诉了我们哪些独立性?
提示
独立性是贝叶斯网络的「负空间」:图里画的边说「谁可能影响谁」,图里没画的边说「谁与谁无关」。它一方面解释了参数为什么省(Lecture 12 · 贝叶斯网络 I:建模与推断 的紧凑性)、马尔可夫毯为什么成立(§3),另一方面直接指导建模——画错独立性比参数估错更致命。
5.1 独立与不独立
独立(independence):$A$ 与 $B$ 独立(记作 $A \perp B$),当且仅当对所有取值 $a, b$:
$$P(A = a, B = b) = P(A = a)\, P(B = b)$$
用四个最小网络看图结构与独立性的关系:
- 例1($A \quad B$,无边):$P(a, b) = p(a)\, p(b)$,按定义独立 ✅。
- 例2($A \to B$):$P(a, b) = p(a)\, p(b \mid a)$,一般不独立 ❌(除非 $p(b \mid a)$ 恰好不依赖 $a$)。
- 例3($A \to C \leftarrow B$,v-结构/碰撞点 collider):
$$P(a, b) = \sum_c p(a)\, p(b)\, p(c \mid a, b) = p(a)\, p(b) \underbrace{\sum_c p(c \mid a, b)}_{=1} = p(a)\, p(b)$$
→ $A$ 与 $B$ 独立 ✅(虽然它们通过 $C$ 相连)! - 例4($A \leftarrow C \to B$,公共原因 common cause):
$$P(a, b) = \sum_c p(c)\, p(a \mid c)\, p(b \mid c) \neq p(a)\, p(b) \text{(一般)}$$
→ $A$ 与 $B$ 不独立 ❌(共同原因引起相关)。
5.2 条件独立
条件独立(conditional independence):给定 $C$,$A$ 与 $B$ 条件独立(记作 $A \perp B \mid C$),当且仅当对所有 $a, b, c$:
$$P(A = a, B = b \mid C = c) = P(A = a \mid C = c)\, P(B = b \mid C = c)$$
把例3、例4「条件在 $C$ 上」再看一遍,结论恰好反转:
| 结构 | 含义 | 无条件 | 给定 C |
|---|---|---|---|
| 例4 $A \leftarrow C \to B$ | 公共原因(common cause) | 不独立 | 条件独立 ✅ |
| 例3 $A \to C \leftarrow B$ | 公共结果/碰撞点(v-结构) | 独立 | 不条件独立 ❌ |
- 例4 给定 $C$:$P(a, b \mid c) = \dfrac{p(c)\, p(a \mid c)\, p(b \mid c)}{P(C = c)} = p(a \mid c)\, p(b \mid c)$($C$ 是根节点故 $P(C{=}c) = p(c)$,约掉)→ 条件独立。观测到公共原因,$A$、$B$ 就「解耦」了。
- 例3 给定 $C$:$P(a, b \mid c) \propto p(a)\, p(b)\, p(c \mid a, b)$,最后那个因子把 $a, b$ 绑在一起,无法分解 → 不条件独立。这就是 explaining away(解释消去):观测到公共结果,会让两个本来独立的原因之间产生依赖。
提示
$B \to A \leftarrow E$ 正是 v-结构:盗窃与地震无条件独立(先验上互不相干),但给定 $A = 1$(警报响了)就不再独立——此时若再得知是地震,就会降低对盗窃的怀疑。上一讲算过数值:$P(B{=}1 \mid A{=}1) \approx 0.51$ 但 $P(B{=}1 \mid A{=}1, E{=}1) = 0.05$。条件独立理论给这个现象补上了图结构层面的解释。
5.3 判定算法(d-分离)
问题:给定证据集 $C$,判断 $A$ 与 $B$ 是否条件独立?讲义给出的图算法(即 d-分离(d-separation) 的可操作版本):
flowchart TD
S1["1. 把证据变量 C 涂灰(shade)"] --> S2["2. 递归删除所有未涂灰的叶节点"]
S2 --> S3["3. 把每个节点的父节点两两相连(marry parents)"]
S3 --> S4{"4. A 到 B 是否存在<br/>不经过任何涂灰节点的路径?"}
S4 -->|存在路径| DEP["不独立"]
S4 -->|无路径| IND["独立"]
style S1 fill:#e1f5fe,stroke:#0277bd
style S3 fill:#ffe0b2,stroke:#e65100
style DEP fill:#ffcdd2,stroke:#c62828
style IND fill:#c8e6c9,stroke:#2e7d32
- 涂灰证据变量 $C$;
- 递归删除未涂灰的叶节点——未被观测的叶子只会被边缘化掉,不影响独立性(这一步等价于只保留 $A$、$B$、$C$ 的祖先图 ancestral graph);
- 把每个节点的父节点两两相连(”marry parents”,术语叫道德化 moralization)——对应 v-结构里两个父在条件于共同子时产生的依赖;
- 看 $A$ 到 $B$ 是否存在一条不经过任何涂灰节点的路径(把边视为无向):有则不独立,无则独立。
说明
这一步专门捕捉 explaining away:当共同子节点(或其后代)被观测/保留时,两个父节点之间要视为直接相连——否则第 4 步的「路径被涂灰节点阻断」规则会漏掉「碰撞点被观测反而打开通路」这一反直觉情形。注意顺序:先删未观测的叶子,再结婚——若碰撞点是未观测的叶子,它会在第 2 步被删掉,其两个父就不会被连上,正确保持独立。
5.4 医疗诊断例子
graph TD
C["C 感冒"] --> H["H 咳嗽"]
A["A 过敏"] --> H
A --> I["I 眼睛痒"]
style C fill:#e1f5fe,stroke:#0277bd
style A fill:#e1f5fe,stroke:#0277bd
style H fill:#ffe0b2,stroke:#e65100
style I fill:#f3e5f5,stroke:#7b1fa2
变量:感冒 $C$、过敏 $A$、咳嗽 $H$、眼睛痒 $I$(沿用 Lecture 12 · 贝叶斯网络 I:建模与推断 的网络)。用上面的算法逐一判定:
- $C \perp A$ ✅:唯一通路 $C \to H \leftarrow A$ 是未观测的碰撞点,$H$ 作为未涂灰叶节点先被删除,通路断开;
- $C \perp I$ ✅:$I$ 只由 $A$ 引起,而 $C$ 与 $A$ 之间无通路(同上),故 $C$ 影响不到 $I$;
- $C \perp I \mid A$ ✅:$A$ 被涂灰后,路径 $C \cdots A \to I$ 在 $A$ 处被阻断;
- $C \perp I \mid \{A, H\}$ ✅:观测 $H$ 会「结婚」$C$ 与 $A$(explaining away 打开 $C$–$A$ 依赖),但去往 $I$ 的必经点 $A$ 已被涂灰,通路仍被阻断。
注意
上面第四条若只观测 $H$(不观测 $A$):$C \not\perp I \mid H$——碰撞点 $H$ 被观测打开了 $C$–$A$ 依赖,信息可以沿 $C \to H \leftarrow A \to I$ 流动。这正是上一讲「眼睛痒降低感冒概率」的图论解释。判独立性时务必把证据集写全,一个变量之差结论完全相反。
一句话记忆:链($X \to C \to Y$)与分叉($X \leftarrow C \to Y$)——观测中间/公共原因会「阻断」通路;碰撞点($X \to C \leftarrow Y$)相反——观测碰撞点(或其后代)反而「打开」两个父之间的依赖。
6. 总结
mindmap
root((贝叶斯网络 II))
精确推断
构造联合分布
条件化 + 边缘化(einops)
指数级慢
近似推断
拒绝采样
样本独立、无偏
怕罕见证据
Gibbs 采样(MCMC)
固定证据、逐变量重采样
条件分布 = 联合概率之比
样本相关、怕高相关变量
马尔可夫毯
父 + 子 + 子的其它父
只算局部条件概率
稀疏图上大幅提速
条件独立
图结构编码独立性
公共原因:给定则独立
碰撞点:给定则不独立
d-分离判定算法
关键要点:
- 精确推断(构造联合分布 + 边缘化/条件化)在变量多时指数级慢($2^n$ 项联合表),需要近似方法。
- Gibbs 采样 = MCMC:固定证据变量,轮流对每个非证据变量按 $P(X_i \mid X_{-i})$ 重采样;链的平稳分布恰是后验,迭代足够久后样本频率趋于后验概率。
- 条件分布用联合概率之比算:$P(X_i = v \mid X_{-i}) = \dfrac{P(X_i = v, X_{-i})}{\sum_{v'} P(X_i = v', X_{-i})}$——只需对被采样变量的取值域求和,无需实例化整个联合分布。
- 马尔可夫毯 = 父 + 子 + 子的其它父;给定马尔可夫毯,节点与其余变量条件独立,故采样只需毯内因子,复杂度里的「变量数」降为「毯大小」,稀疏图上大幅提速。
- 拒绝采样 vs Gibbs:前者无偏但怕罕见证据(有效样本 $\propto P(E{=}e)$);后者复用证据但怕高度相关变量——确定性耦合会破坏遍历性、卡死在初始状态,强相关则混合极慢。
- 条件独立由图结构决定:公共原因($A \leftarrow C \to B$)给定 $C$ 则独立;碰撞点($A \to C \leftarrow B$)无条件独立、给定 $C$ 反而不独立(explaining away)。判定用 d-分离:涂灰证据 → 删未涂灰叶(祖先图)→ 连父(道德化)→ 看是否有避开灰节点的通路。
下一讲预告:目前我们假设局部条件概率 $p(x_i \mid \mathrm{parents})$ 是已知的——如果只有数据、要学出这些参数怎么办?Lecture 14 · 贝叶斯网络 III:参数学习:最大似然与计数、拉普拉斯平滑(Laplace smoothing)、隐变量与 EM 算法。
复习自测
题目
它把证据变量直接固定在赋值里、只重采样其余变量,每个新赋值天然满足证据,所以没有「事后检查再丢弃」这一步。代价是样本来自一条马尔可夫链、前后高度相关:估计的有效样本量远小于迭代次数,需要更多步数降方差;且收敛速度受混合时间支配,强相关变量下可能极慢甚至(确定性耦合时)根本不收敛。
题目
$P(X_i = v \mid X_{-i}) = \dfrac{P(X_i = v, X_{-i})}{\sum_{v'} P(X_i = v', X_{-i})}$,其中联合概率是全部局部因子之积。不含 $X_i$ 的因子在分子分母中取值相同、被约掉,剩下的只有 $X_i$ 自己的因子 $p(x_i \mid \mathrm{parents})$ 和每个子节点的因子 $p(\text{child} \mid \text{其全部父})$;评估它们只需父、子、子的其它父的取值——即 $\mathrm{MB}(X_i)$。
题目
$\mathrm{MB}(A) = \{C, B\}$:$C$ 是子节点,$B$ 是子节点 $C$ 的另一个父($D$ 不在毯内——它是子的子)。必须包含 co-parent 的原因:采样 $A$ 时要评估因子 $p(c \mid a, b)$,其取值依赖 $B$;直觉上这正是 explaining away——$C$ 已知时 $A$ 与 $B$ 相互影响,忽略 $B$ 会算错条件分布。
题目
公共原因 $A \leftarrow C \to B$:无条件不独立(共同原因造成相关,如「冰淇淋销量与溺水人数」同受夏天驱动),给定 $C$ 后独立(知道了季节,两者不再互通信息)。v-结构 $A \to C \leftarrow B$:无条件独立(如盗窃与地震),给定 $C$ 后不独立(警报响了,得知地震会降低盗窃嫌疑——explaining away)。两者方向恰好相反,是 d-分离规则的核心。
题目
从 $(0,0)$ 出发,单变量翻转会产生联合概率为 0 的状态($A \neq B$),条件分布把所有质量都压在「不变」上,链永远停在初始岛——被破坏的是遍历性(不可约性):状态空间被零概率区域切成互不连通的部分,平稳分布论证失效。缓解办法:块采样(把强耦合变量作为整体联合重采样)、多链不同初始化/随机重启,或给确定性分布加微小噪声恢复连通性。
参考资料
- 💻 gibbs_sampling.py — 本讲源码
- 💻 bayes.py —
ProbTable、Bernoulli、拒绝采样(上一讲) - 📄 Stochastic Relaxation, Gibbs Distributions, and the Bayesian Restoration of Images (Geman & Geman, 1984) — Gibbs 采样的奠基论文
- 📄 Equation of State Calculations by Fast Computing Machines (Metropolis et al., 1953) — MCMC / Metropolis 算法源头
- 📄 Monte Carlo Sampling Methods Using Markov Chains (Hastings, 1970) — Metropolis-Hastings 推广
- 📖 Koller & Friedman: Probabilistic Graphical Models (2009) — 第 12 章(Gibbs / MCMC)、d-分离
- 📖 Bishop: Pattern Recognition and Machine Learning (2006) — 第 8.3 节马尔可夫毯、第 11.3 节 Gibbs 采样
- 🌐 CS221 课程主页 — 讲义与作业