#import "labtemplate.typ": * #show: nudtlabpaper.with( author: "程景愉", id: "202302723005", title: "贝叶斯网络编程实验", training_type: "普通本科生", grade: "2023级", major: "网络工程", department: "计算机学院", advisor: "祝恩", jobtitle: "教授", lab: "306-707", date: "2025.12.31", header_str: "贝叶斯网络编程实验报告", simple_cover: true, ) #set page(header: [ #set par(spacing: 6pt) #align(center)[#text(size: 11pt)[人工智能实验报告]] #v(-0.3em) #line(length: 100%, stroke: (thickness: 1pt)) ],) #show heading: it => if it.level == 1 [ #v(1em) #set text(font: hei, size: 16pt) #it.body #v(0.5em) ] else [ #v(0.8em) #set text(font: "New Computer Modern", weight: "bold", style: "italic") #it.body #v(0.5em) ] #set heading(numbering: none) #outline(title: "目录",depth: 2, indent: 1em) #set enum(indent: 0.5em,body-indent: 0.5em,) #pagebreak() = 实验介绍 #para[ 本次实验旨在实现并比较两种贝叶斯网络近似推理算法:拒绝采样(Rejection Sampling)和似然加权(Likelihood Weighting)。 实验使用的贝叶斯网络模型包含四个布尔变量:Cloudy (C), Sprinkler (S), Rain (R), WetGrass (W)。 网络结构为 $C \to S, C \to R, S \to W, R \to W$。 实验目标是根据给定的条件概率表(CPTs),在已知证据 $S=T$ 和 $W=T$ 的情况下,估计 $R=T$ 的后验概率 $P(R=T | S=T, W=T)$。 ] = 实验内容 #para[ 实验主要包含以下内容: ] + 根据题目描述的贝叶斯网络结构和条件概率表,构建数据结构表示该网络。 + 实现拒绝采样算法:从先验分布中采样,拒绝与证据不一致的样本,利用保留样本中查询变量的频率作为后验概率的估计。 + 实现似然加权算法:固定证据变量的值,对非证据变量进行采样,并根据证据变量的条件概率计算样本权重,利用加权频率估计后验概率。 + 比较两种算法在不同采样数量(N=100, 1000, ...)下的估计结果与真实值的偏差,分析算法收敛性。 = 实验情况 + 使用 Python 语言实现算法。 + 代码需清晰易读,包含必要的注释。 + 计算并输出 $P(R=T | S=T, W=T)$ 的精确值以便对比。 + 撰写实验报告,展示核心代码和实验结果。 = 实验步骤与实现 == 网络定义与概率表 首先定义网络的条件概率表(CPTs)。 ```python # CPTs P_C = {True: 0.5, False: 0.5} # P(S | C) P_S_given_C = { True: {True: 0.10, False: 0.90}, # C=T False: {True: 0.50, False: 0.50} # C=F } # P(R | C) P_R_given_C = { True: {True: 0.80, False: 0.20}, # C=T False: {True: 0.20, False: 0.80} # C=F } # P(W | S, R) P_W_given_SR = { (True, True): {True: 0.99, False: 0.01}, (True, False): {True: 0.90, False: 0.10}, (False, True): {True: 0.90, False: 0.10}, (False, False): {True: 0.00, False: 1.00} } ``` == 拒绝采样 (Rejection Sampling) 拒绝采样通过生成先验分布的样本,并丢弃那些与证据不符的样本来工作。 ```python def rejection_sampling(query_var, query_val, evidence, num_samples): consistent_count = 0 query_match_count = 0 for _ in range(num_samples): sample = get_prior_sample() # 从联合分布采样 # 检查样本是否与证据一致 is_consistent = True for var, val in evidence.items(): if sample[var] != val: is_consistent = False break if is_consistent: consistent_count += 1 if sample[query_var] == query_val: query_match_count += 1 if consistent_count == 0: return 0.0 return query_match_count / consistent_count ``` == 似然加权 (Likelihood Weighting) 似然加权强制证据变量取观测值,并将样本权重设为证据变量在其父节点给定情况下的似然概率乘积。 ```python def likelihood_weighting(query_var, query_val, evidence, num_samples): weighted_counts = {True: 0.0, False: 0.0} for _ in range(num_samples): weight = 1.0 sample = {} # 以 C, S, R, W 的拓扑顺序采样/赋值 # 若变量在证据中,则赋值为证据值并更新权重 # 若变量不在证据中,则根据父节点采样 # (代码细节略,参见附件源程序) # ... # 累加权重 weighted_counts[sample[query_var]] += weight total_weight = sum(weighted_counts.values()) if total_weight == 0: return 0.0 return weighted_counts[query_val] / total_weight ``` = 实验结果 #para[ 首先通过精确计算(枚举联合概率分布)得到理论真实值: $P(R=T | S=T, W=T) approx 0.320388$ ] #para[ 下表展示了在不同采样数量 $N$ 下,两种算法的估计结果: ] #table( columns: (auto, auto, auto), inset: 10pt, align: center, [*样本数量 N*], [*拒绝采样结果*], [*似然加权结果*], [100], [0.480000], [0.377902], [1000], [0.353741], [0.311148], [10000], [0.320734], [0.317552], [100000], [0.318791], [0.321722], [1000000], [0.319916], [0.319471], ) = 实验总结 + *收敛性*:从实验结果可以看出,随着采样数量 $N$ 的增加,拒绝采样和似然加权算法的结果都逐渐收敛于真实值 $0.320388$。 + *算法比较*: - *拒绝采样*:实现简单,直接利用先验分布采样。但在证据概率较低时(本例中证据发生的概率约为 0.278,尚可),会拒绝大量样本,导致计算资源浪费。在样本量较小(如 N=100)时波动较大。 - *似然加权*:利用了证据信息,所有生成的样本都是有效的(权重不为0),因此在相同采样数量下,通常能提供比拒绝采样更稳定的估计,尤其是当证据发生概率很低时优势更明显。 + *结论*:两种算法均能有效进行近似推理。对于本实验中的简单网络和非极端概率证据,两者在 $N=10000$ 以上时均能给出较好的估计结果。