想象你是一位新上任的“因果侦探”,接手了一桩悬案:医院里有三位“嫌疑人”——吸烟习惯、肺部阴影、咳嗽症状,你手里只有一大摞病历数据,却没有目击证人告诉你谁影响了谁。你的任务,是从数据的蛛丝马迹中,还原出那张隐藏在背后的“因果地图”。 贝叶斯网络结构学习,就是这位侦探的核心手艺。它要做的,是从观测数据中“反推”出变量之间的依赖关系网——也就是一个有向无环图(DAG,即 Directed Acyclic Graph)。今天我们要讲的“基于分数方法”,是破案的“正统派”思路:它相信“最好的解释来自最精巧的推理”——给每张候选的因果地图打分,分数越高,说明它越能自圆其说地解释手头的证据。就像侦探不轻信任何单一证词,而是综合所有线索,衡量哪个故事版本最合理。

岗位关联

掌握基于分数的贝叶斯网络结构学习,是成为合格AI工程师的“破案基本功”。它是因果推断、可解释AI和智能决策系统的基石,能让你从“只会跑模型”进阶到“懂得还原数据背后的因果逻辑”。

目录
  1. 可视化演示
  2. 评分函数:给每张“因果地图”打分
  3. 搜索空间:数量爆炸的“嫌疑人组合”
  4. 搜索策略:侦探的破案路线图
  5. 约束式方法:另一条破案路径
  6. 模型评估:怎么知道破案成功
  7. 扩展应用:从课堂到工业界的破案现场
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

三步展示分数式结构学习:BIC/BDeu评分搜索最优DAG、DAG空间超指数组合爆炸与启发式搜索、马尔可夫等价类与v-结构方向不确定性。

本节配有交互动效(进入视口后自动加载,约 3KB)。

分数式结构学习:BIC评分搜索 BIC=拟合度-复杂度惩罚,平衡精度与简洁 最优DAG使BIC分数最大化 BIC = log L(D|G) - (k/2)log(n) 爬山法在DAG空间中迭代搜索,每次尝试加边/删边/反转边,接受提升分数的移动。

评分函数:给每张“因果地图”打分

侦探破案时,会为每个嫌疑人编织一个“故事版本”,然后用证据逐一检验哪个版本最经得起推敲。评分函数,就是那位帮侦探打分的高级顾问——它不关心故事是否动听,只关心每个故事在多大程度上解释了手头的证据。

在贝叶斯网络结构学习中,每个候选的 DAG 就是一个“故事版本”。评分函数衡量的是:在给定这个网络结构的前提下,观测数据出现的概率有多大。这就是“似然”的直觉——数据似然越高,说明这个网络对数据的解释力越强。但这里有个陷阱:网络越是复杂,参数越多,就越容易“过度拟合”——就像一个侦探编造过于离奇的故事去迁就每一个细节,反而失去了说服力。

因此,实用的评分函数必须在“拟合数据”和“模型简洁”之间寻找平衡。贝叶斯信息准则(BIC)就是最常用的一个:

除了 BIC,还有基于贝叶斯理论的评分——它计算的是在数据观测后,网络结构的后验概率 $P(G|D) \propto P(D|G)P(G)$,其中 $P(G)$ 是网络结构的先验。这类评分天然包含了对复杂度的惩罚,就像侦探对“巧合过多的剧本”天然保持警惕。

回到破案的比喻:BIC 评分就是侦探的“证据计分板”,它奖励那些用简洁的因果链条解释了大量证据的网络,惩罚那些为了自圆其说而不断加戏的复杂剧本。

BIC(G∣D)=log⁡P(D∣θ^,G)−d2log⁡n\displaystyle \text{BIC}(G|D) = \log P(D|\hat{\theta},G) - \frac{d}{2}\log n
其中 $\log P(D|\hat{\theta},G)$ 是最大似然对数,衡量网络对数据的拟合程度;$d$ 是参数个数,$\frac{d}{2}\log n$ 是复杂度惩罚,$n$ 是样本量。BIC 分数越高,网络越优。
  1. 最大似然项计算在当前网络结构 $G$ 下,用最优参数 $\hat{\theta}$ 解释数据 $D$ 的概率。
    log⁡P(D∣θ^,G)\displaystyle \log P(D|\hat{\theta},G)
  2. 参数数量$d$ 是网络中所有条件概率表里自由参数的个数,网络越复杂,$d$ 越大。
    d=∑i(ri−1)×qi\displaystyle d = \sum_{i}(r_i - 1) \times q_i
  3. 惩罚系数$\frac{1}{2}\log n$ 是惩罚力度,样本越多,对复杂模型的惩罚越重。
    d2log⁡n\displaystyle \frac{d}{2}\log n
  4. 综合平衡用对数似然减去惩罚项,得到最终的 BIC 分数。
    BIC=对数似然−惩罚项\displaystyle \text{BIC} = \text{对数似然} - \text{惩罚项}

搜索空间:数量爆炸的“嫌疑人组合”

现在侦探面临一个难题:嫌疑人之间有太多可能的“关系剧本”。假设只有 5 个变量,可能的因果指向关系就有上千种;如果变量增加到 10 个,这个数字会膨胀到天文数字!

这里的“搜索空间”是指所有可能的 DAG 结构组成的集合。数学家 Robinson 给出了 DAG 数量的递推公式,其增长速度远超指数:

这意味着,即使只有 20 个变量,我们也绝无可能逐个枚举所有候选网络——这正是结构学习最核心的挑战。就像一个侦探面对几百万个嫌疑人组合,不可能逐一审问,必须制定聪明的搜索策略。

这个“组合爆炸”的现实,催生了各种启发式搜索策略:不追求找到全球最优解,而是希望在合理时间内找到一个足够好的“因果剧本”。

f(n)=∑k=1n(−1)k+1(nk)2k(n−k)f(n−k)\displaystyle f(n) = \sum_{k=1}^{n} (-1)^{k+1} \binom{n}{k} 2^{k(n-k)} f(n-k)
这个公式描述了 $n$ 个节点的 DAG 总数。当 $n=10$ 时,DAG 数量高达 $4.2 \times 10^{18}$;当 $n=20$ 时,这个数字已经超过了宇宙中恒星的数量。
  1. 组合选择先从 $n$ 个节点中选出 $k$ 个放入第一层。
    (nk)\displaystyle \binom{n}{k}
  2. 层间连边第一层的 $k$ 个节点与其余 $n-k$ 个节点之间的单向连边方式。
    2k(n−k)\displaystyle 2^{k(n-k)}
  3. 符号交替$(-1)^{k+1}$ 确保同一 DAG 不被重复计数。
    (−1)k+1\displaystyle (-1)^{k+1}
  4. 递推求解每一层独立成 DAG,递归计算总数。
    f(n)=∑k=1n(⋯ )f(n−k)\displaystyle f(n) = \sum_{k=1}^{n}(\cdots) f(n-k)

搜索策略:侦探的破案路线图

面对庞大的嫌疑人组合,侦探需要一个高效的“搜索路线图”。搜索策略就是解决这个问题的工具——它决定了如何从无数候选 DAG 中找到评分最高的那个。

最直观的方法是贪婪搜索(Greedy Search):从一个初始网络出发(比如空图),每次尝试增加、删除或反转一条边,如果评分提高了,就接受这个改动,否则保持原状。这就像侦探沿着一条线索追查,只要发现新证据指向更合理的剧情,就修正自己的推断。贪婪搜索简单高效,但容易陷入“局部最优”——就像侦探过早锁定了某个嫌疑人,错过了真正的凶手。

为了跳出局部最优,研究者引入了更精妙的策略:

  • 禁忌搜索(Tabu Search):记录最近几步的搜索历史,短期内禁止回到刚刚离开的结构,强迫自己探索新的区域。
  • 模拟退火(Simulated Annealing):模仿金属冷却过程,在搜索早期“温度高”,愿意接受一些评分下降的改动从而逃离局部最优;随着“温度”下降,逐渐变得保守,最终收敛。
  • 遗传算法(Genetic Algorithm):将多个候选网络视为一个“种群”,通过交叉和变异产生新结构,用评分作“适应度”筛选优秀个体,像生物进化那样逐代优化。

这些策略各有千秋,但都遵循同一个核心思想:在“探索”与“利用”之间权衡——既要敢闯新路,又要善用已有线索。这就像老练的侦探,既不会被眼前的线索束缚,也不会漫无目的地乱撞。

无论使用哪种搜索策略,最终都要把候选结构的评分算出来。这引出了一个优雅的分解性质:当评分函数是“可分解的”(如 BIC),整个网络的评分可以分解为各节点局部评分的加和,让搜索过程大幅提速。

约束式方法:另一条破案路径

除了基于分数的“演绎派”,还有一条基于约束的“实证派”路线。这种方法的核心理念是:不要给所有剧本打分,而是直接用统计检验来排除不可能的因果关系。

约束式方法使用条件独立性检验——检查两个变量在给定第三个变量的条件下是否独立。这就像是侦探的“不在场证明检验”:如果你能证明 A 和 B 在给定 C 的条件下完全无关,那么" A→B→C "这条因果链就是假的。其中最经典的算法是PC 算法,它从一个全连通图出发,通过条件独立性检验逐条删除多余的边,最后确定边的方向。

两种方法各有优劣:

  • 分数式方法:通过全局评分搜索最佳结构,鲁棒性好,但计算量大。
  • 约束式方法:依赖独立性检验的统计效力,在小样本下可能失灵,但计算效率更高。

聪明的做法是将两者结合——混合方法:先用约束式方法快速收缩搜索空间,再用分数式方法精细搜索。就像侦探先用法医鉴定排除大部分嫌疑人,再从剩下的候选人中仔细审讯。这种“先粗筛、后精审”的思路,在实际工程中被广泛应用。

模型评估:怎么知道破案成功

侦探破案后,需要让陪审团相信他的结案报告。同样,学到的贝叶斯网络结构也需要经过严格评估,才能确认它不是过拟合的产物。

最常用的评估方法是留出法(Holdout):将数据分成训练集和测试集,用训练集学到的网络结构,在测试集上评估其预测能力。交叉验证(Cross-validation)则将数据分成交叉的子集,轮流训练与测试,结果更加稳健。引导法(Bootstrap)通过对原始数据反复重采样,生成多个“平行世界”的数据集,在每个数据集上学习网络结构,观察哪些边在多次学习中都稳定出现——出现频率越高的边,越可能是真实存在的因果关系。

这些评估手段的共同目的,是回答一个关键问题:学到的结构是数据背后的“真凶”,还是样本噪音造成的“冤案”?稳定性评估能够给出因果关系的置信度——就像法庭上的证据链,单条证据可能出错,但如果多条独立证据都指向同一嫌疑犯,那可信度就大大提升了。

扩展应用:从课堂到工业界的破案现场

掌握了贝叶斯网络结构学习,你的“侦探技能”就具备了广泛的用武之地。最直接的应用是因果推断——学到的 DAG 结构可以直接用于“干预分析”:如果我们改变某个变量,预测其他变量会如何变化。这在医药临床试验、电商定价策略、推荐系统反事实分析中都有重要价值。

在工业级 AI 系统中,这一技术已经得到了验证:广告投放系统用它从用户行为数据中还原“点击意愿”的因果链;智能制造用它从传感器数据中发现设备故障的因果路径;金融机构用它识别欺诈交易背后的驱动因素。结构学习与深度学习的融合也在兴起——将学到的因果结构注入神经网络,让模型不仅“知其然”,还能“知其所以然”。

这个领域还在不断扩展:动态贝叶斯网络将时间维度纳入建模,隐变量模型处理观测不到的混杂因素,因果强化学习用结构信息指导智能体的探索。今天的贝叶斯网络结构学习,正从一门理论工具演变为 AI 系统“靠谱性”的关键保障——毕竟,只有真正理解了数据背后的因果逻辑,AI 才能做出值得信赖的决策。

AI 应用场景

医疗诊断中的因果结构发现

阶段:因果发现(结构学习)

解决的问题:传统电子病历分析用相关性代替因果性,导致误判。比如“吃海鲜”与“腹痛”高度相关,但真正的原因是“过敏体质”。用基于分数方法的结构学习,能从大量病历数据中还原“体质→过敏→症状”的因果链,辅助医生准确诊断。

具体案例:某三甲医院积累 10 万份过敏患者病历,涵盖 50 个症状与暴露因素变量。使用 BIC 评分 + 贪婪搜索,学习出过敏因果网络,发现“遗传家族史”是“过敏性鼻炎”的直接原因,而“空气污染”是间接调节因素。基于该结构,医生制定个性化预防方案,误诊率下降 23%。

推荐系统的因果纠偏

阶段:结构学习 → 因果推断 → 决策优化

解决的问题:传统推荐系统只学“相关关系”,容易陷入“热门偏见”——推荐结果被流行度绑架,用户真正的兴趣被淹没。用贝叶斯网络结构学习还原“真实兴趣→点击行为→物品曝光”的因果路径,可剥离流行度干扰,提升推荐准确性。

具体案例:某电商平台使用 5000 万用户行为数据构建贝叶斯网络,节点包括“用户年龄”“浏览时长”“物品价格”“购买行为”等。学到的 DAG 显示“年龄→消费品类偏好”存在直接因果边,而“价格→购买”边被“收入”混淆。基于因果结构调整推荐策略,转化率提升 15%。

工业设备故障根因分析

阶段:结构学习 → 异常诊断 → 预测性维护

解决的问题:工业传感器数据维度高、噪声大,传统故障诊断用黑箱分类器,无法定位故障根因。贝叶斯网络结构学习能从传感器数据中发现“温度异常→润滑油劣化→轴承磨损”的因果链条,辅助工程师精准排除故障。

具体案例:某风电企业为每台风机部署 120 个传感器,采集温度、振动、转速等数据。通过结构学习得到故障因果网络,发现“齿轮箱温度”与“发电机转速”并非直接相关,而是共同受“润滑系统压力”影响。据此提前 48 小时预警故障,维护成本降低 30%。

Python 演示

python172 行
import itertoolsimport numpy as np  def local_bic_score(data, node, parents, n_bins=5):    """计算单个节点的 BIC 评分片段。    数据已离散化为 0..n_bins-1 的整数。    """    # 将数据离散化(等宽切分成整数)    # 简化为直接使用整数数据    n = data.shape[0]    # 节点的取值状态数    r = len(np.unique(data[:, node]))    cnt = n * 1.0     # 统计父节点状态组合的样本量    if len(parents) == 0:        # 无父节点: 只需统计该节点的各取值频数        counts = np.bincount(data[:, node], minlength=n_bins)        counts = counts[counts > 0]        q = 1  # 父节点状态组合数    else:        # 将父节点状态拼成元组来计数        parent_states = data[:, parents]        unique_combos, inverse = np.unique(parent_states, axis=0, return_inverse=True)        q = len(unique_combos)        counts = []        for combo_idx in range(q):            mask = inverse == combo_idx            child_vals = data[mask, node]            # 在这组父状态下,子节点取值频数            sub_counts = np.bincount(child_vals, minlength=n_bins)            if sub_counts.sum() > 0:                counts.append(sub_counts)        counts = [c for c in counts if c.sum() > 0]     d = 0    loglik = 0.0    for c in counts:        total = c.sum()        if total == 0:            continue        d += (len(c) - 1)  # 该组自由参数数        for cnt_ij in c:            if cnt_ij > 0:                loglik += cnt_ij * np.log(cnt_ij / total)     loglik -= cnt * np.log(n) * 0.0  # 保留loglik为对数似然    # BIC片段 = loglik - d/2 * log n    return loglik - d / 2.0 * np.log(n)  def bic_score(data, dag, n_bins=5):    """计算整个 DAG 的 BIC 分数。dag[u,v]=1 表示 u -> v。"""    n = data.shape[1]    score = 0.0    for v in range(n):        parents = [u for u in range(n) if dag[u, v] == 1]        score += local_bic_score(data, v, parents, n_bins)    return score  def is_dag(dag):    """用拓扑排序判断邻接矩阵是否无环。dag[u,v]=1 表示 u->v。"""    n = dag.shape[0]    indegree = dag.sum(axis=0).astype(int)    queue = [i for i in range(n) if indegree[i] == 0]    visited = 0    while queue:        u = queue.pop(0)        visited += 1        for v in range(n):            if dag[u, v] == 1:                indegree[v] -= 1                if indegree[v] == 0:                    queue.append(v)    return visited == n  def perturb_dag(dag, edge_pool):    """生成邻接 DAG: 随机增/删/反一条边,返回新DAG和操作描述。"""    n = dag.shape[0]    new_dag = dag.copy()    op = np.random.choice(['add', 'remove', 'reverse'])    valid = False    for _ in range(50):        u, v = edge_pool[np.random.randint(len(edge_pool))]        if op == 'add' and new_dag[u, v] == 0:            new_dag[u, v] = 1            if is_dag(new_dag):                return new_dag, op            new_dag[u, v] = 0        elif op == 'remove' and new_dag[u, v] == 1:            new_dag[u, v] = 0            return new_dag, op        elif op == 'reverse':            if new_dag[u, v] == 1:                new_dag[u, v] = 0                new_dag[v, u] = 1                if is_dag(new_dag):                    return new_dag, op                new_dag[u, v] = 1                new_dag[v, u] = 0    return dag, 'none'  def greedy_search(data, n_bins=5):    """贪婪搜索: 从空图出发,每次选择评分提升最大的单边操作。"""    n = data.shape[1]    edge_pool = list(itertools.permutations(range(n), 2))    dag = np.zeros((n, n), dtype=int)    best_score = bic_score(data, dag, n_bins)    improved = True    iters = 0     while improved and iters < 100:        improved = False        iters += 1        best_ops = []        best_delta = 0.0         for op in ['add', 'remove', 'reverse']:            for (u, v) in edge_pool:                new_dag = dag.copy()                if op == 'add':                    if new_dag[u, v] == 1:                        continue                    new_dag[u, v] = 1                elif op == 'remove':                    if new_dag[u, v] == 0:                        continue                    new_dag[u, v] = 0                else:  # reverse                    if new_dag[u, v] == 0:                        continue                    new_dag[u, v] = 0                    new_dag[v, u] = 1                 if not is_dag(new_dag):                    continue                s = bic_score(data, new_dag, n_bins)                delta = s - best_score                if delta > best_delta:                    best_delta = delta                    best_ops = [new_dag]                elif delta > 0 and abs(delta - best_delta) < 1e-9:                    best_ops.append(new_dag)         if best_ops:            dag = best_ops[np.random.randint(len(best_ops))]            best_score += best_delta            improved = True     return dag, best_score  if __name__ == '__main__':    # 构造 4 变量 200 样本的合成数据: A -> B, A -> C, B -> D, C -> D    np.random.seed(42)    n = 200    A = np.random.binomial(1, 0.5, n)    B = np.random.binomial(1, 0.7, n) if np.sum(A) == 0 else (A + np.random.binomial(1, 0.3, n) > 0).astype(int)    B = (A * 0.6 + np.random.rand(n) * 0.4 > 0.5).astype(int)    C = (A * 0.5 + np.random.rand(n) * 0.5 > 0.5).astype(int)    D = (0.6 * B + 0.4 * C + np.random.rand(n) > 0.6).astype(int)    data = np.column_stack([A, B, C, D])     learned_dag, score = greedy_search(data)    print('学到的 DAG 邻接矩阵 (u,v)=1 表示 u->v:')    print(learned_dag)    print('BIC 分数:', round(score, 2))    print('真实结构: A->B, A->C, B->D, C->D')
目的:完整实现了一个基于 BIC 评分的贪婪搜索结构学习器,能从离散数据中还原 DAG 结构。
  1. def local_bic_score(data, node, parents, n_bins=5):计算单个节点的 BIC 评分片段,可分解评分是高效搜索的关键。
  2. unique_combos, inverse = np.unique(parent_states, axis=0, return_inverse=True)统计父节点状态组合,对应条件概率表的每一行。
  3. d += (len(c) - 1)累加该节点局部参数的自由度,用于复杂度惩罚。
  4. loglik += cnt_ij * np.log(cnt_ij / total)累加对数似然,衡量拟合程度。
  5. def is_dag(dag):用拓扑排序验证无环约束,保证候选结构合法。
  6. def perturb_dag(dag, edge_pool):生成邻接 DAG,模拟搜索中的单边操作。
  7. for op in ['add', 'remove', 'reverse']:贪婪搜索的三种基本操作,对应边集的增删反。
  8. if not is_dag(new_dag): continue跳过违反 DAG 约束的候选。
  9. s = bic_score(data, new_dag, n_bins)重新计算整个网络的 BIC 评分。
  10. if delta > best_delta:记录评分提升最大的结构,实现“贪婪”策略。

常见误区

误区我以为 BIC 分数最高的网络一定就是真实的因果网络,分数代表了因果效应的真实性。

正解BIC 只是衡量“结构对数据的拟合能力 + 模型简洁性”,它并不等同于因果真实性。在观测数据上,多个不同的 DAG 可能具有相同的 BIC 分数(等价类),这些结构在统计上不可区分。要确认因果关系,还需要额外假设(如忠实性条件)或设计干预实验。就像侦探的评分体系再合理,也可能有两个剧本同样能解释现场——只有新的证据(干预数据)才能分辨真伪。

为何常见:因为“分数”这个词暗示了“越好越接近真理”,而实际上评分函数的目标只是数据拟合,不是因果真理性。

误区既然贪婪搜索每一步都选择让分数提升最大的操作,那么最终结果就应该是全局最优的。

正解贪婪搜索本质是“爬山法”,它只保证最终结果在“局部”邻居中是最优的,无法保证全局最优。搜索空间中的评分函数呈现出多峰地形,贪婪算法容易陷在某个局部峰顶,错失更高的全局峰。禁忌搜索和模拟退火等策略就是为了克服这一局限。就像侦探沿着一条线索一路追查,可能最终发现只是抓到了从犯,而真凶还在另条线索上。

为何常见:人们的直觉是“每一步都选最好的,总和也是最好的”,但组合优化问题中局部最优不等于全局最优。

误区我觉得基于约束的 PC 算法和基于分数的方法是完全不同、互不相干的两种算法,只能在两者中二选一。

正解两者虽然范式不同,但并非对立关系,完全可以互补。约束方法用统计检验“剪枝”,分数方法用搜索“选优”;混合方法先剪枝后选优,工程上效果更好。而且很多评分函数本身也蕴含了条件独立性的检测——如果一个边是多余的,删掉它的 BIC 分数会提升。两者的底层目标一致:找到能解释数据的简约因果结构。

为何常见:教科书为了教学清晰会刻意区分两大范式,但实际研究是高度融合的;初学者容易被分类标签误导成“你死我活”的关系。

误区我认为只要数据量够多,贝叶斯网络结构学习就能恢复出完美真实的 DAG。

正解样本量增加确实能提高统计稳定性,但不能保证结构的正确性。如果真正的生成过程违反了“忠实性假设”(比如两个变量独立但特定条件下相关),或者数据中存在未观测的混杂变量,即使样本无限多,学习到的结构也可能与真实因果机制不符。这就像侦探有再多的证人证词,如果证人们都系统性说谎(数据有偏),结论依然错误。

为何常见:大数据时代的流行叙事让人们相信“数据越多 = 真理越近”,但结构学习不仅依赖数据量,还依赖数据质量、变量完备性和假设合理性。

面试题

Q1. 贝叶斯网络结构学习中,“基于分数方法”的核心思想是什么?

单选题 · easy

  • A.用统计检验排除条件独立的边
  • B.给候选网络打分并搜索最优结构← 正确答案
  • C.直接用深度学习拟合网络结构
  • D.用专家知识手工设计网络结构

答案:B。基于分数方法的核心是定义评分函数(如 BIC)来评估每个候选 DAG 的优劣,然后通过搜索策略找到分数最优的结构。A 是约束式方法的思路,C 和 D 都不属于经典的结构学习方法。

Q2. BIC 评分函数中,复杂度惩罚项的作用是什么?

单选题 · easy

  • A.让网络结构更加复杂以拟合所有细节
  • B.防止过拟合,平衡拟合度与模型简洁性← 正确答案
  • C.加速搜索算法的收敛速度
  • D.消除变量之间的冗余相关性

答案:B。BIC = 对数似然 − d/2·log n,其中惩罚项 d/2·log n 随参数数量增加而增大,约束模型不要过度复杂。这与奥卡姆剃刀原则一致——没有惩罚项的纯似然评分永远偏向复杂的网络,导致过拟合。

Q3. 当节点数为 10 时,可能的 DAG 数量约为多少?

单选题 · mid

  • A.约 10 的 100 次方
  • B.约 4.2 × 10 的 18 次方← 正确答案
  • C.约 100 的 10 次方
  • D.约 10 的 10 次方

答案:B。根据 Robinson 递推公式,10 个节点的 DAG 总数约为 4.2 × 10^18,这个数量级远超枚举的可行性,因此必须依赖启发式搜索策略。这个数字揭示了为什么暴力枚举不可行。

Q4. 贪婪搜索在贝叶斯网络结构学习中最主要的缺点是?

单选题 · mid

  • A.计算复杂度太低导致结果不可靠
  • B.每一步都需要重新拟合所有参数
  • C.容易陷入局部最优解← 正确答案
  • D.只能处理离散型变量

答案:C。贪婪搜索每一步都选择立即使评分提升最大的操作,这种“短视”策略使它容易收敛到局部最优解而非全局最优。禁忌搜索和模拟退火等策略就是为了克服这个问题而设计的。

Q5. 以下哪些方法可以用来克服贪婪搜索的局部最优问题?

多选题 · mid

  • A.禁忌搜索← 正确答案
  • B.模拟退火← 正确答案
  • C.增大评分函数惩罚项
  • D.遗传算法← 正确答案

答案:ABD。禁忌搜索通过禁止回头访问近期状态来探索新区域;模拟退火通过接受部分劣化解跳出局部最优;遗传算法通过种群进化全局搜索。增大惩罚项只是改变评分函数本身,并不改变搜索策略的局部最优特性。

Q6. 关于 BIC 评分和贝叶斯评分的比较,以下说法正确的是?

单选题 · hard

  • A.贝叶斯评分不需要先验分布假设
  • B.BIC 是贝叶斯评分在大样本下的近似← 正确答案
  • C.BIC 评分一定比贝叶斯评分更准确
  • D.贝叶斯评分不包含复杂度惩罚机制

答案:B。BIC 可以从贝叶斯评分(后验概率)出发,在大样本极限下通过拉普拉斯近似推导出来。两者都包含对模型复杂度的惩罚,区别在于 BIC 的惩罚是确定性的而贝叶斯评分通过先验实现。选项 A、C、D 均有明显错误。

Q7. 贝叶斯网络结构学习中,DAG 必须满足哪些约束?

多选题 · mid

  • A.所有边必须是有向的← 正确答案
  • B.图中不允许存在有向环← 正确答案
  • C.所有变量必须服从高斯分布
  • D.每个节点必须有至少一条入边

答案:AB。DAG 即“有向无环图”,核心约束是所有边有方向且无环。变量分布没有高斯要求(可以是离散、连续、混合),也不是每个节点都需要有父节点(无父节点表示该变量独立于其他变量)。

Q8. PC 算法属于哪种结构学习范式,其核心操作是什么?

单选题 · hard

  • A.分数式方法,核心是评分搜索
  • B.约束式方法,核心是条件独立性检验← 正确答案
  • C.深度学习方法,核心是神经网络的参数学习
  • D.混合式方法,核心是评分与检验交替进行

答案:B。PC 算法是约束式方法的代表,它从完全图出发,通过条件独立性检验剔除多余的边,然后进行边的定向。虽然混合方法也会结合检验与评分,但 PC 算法本身只使用独立性检验,属于纯约束范式。

Q9. 在贝叶斯网络结构学习的模型评估中,以下哪些方法可以用于评估学到的结构?

多选题 · hard

  • A.留出法,在训练集上学习,在测试集上评估← 正确答案
  • B.交叉验证,轮流划分训练/测试集← 正确答案
  • C.引导法,重采样数据并检查边的稳定出现频率← 正确答案
  • D.直接检查残差是否服从正态分布

答案:ABC。留出法、交叉验证和引导法是结构学习中常用的评估手段,用于检验学到的结构是否过拟合以及边的稳定性。检查残差正态性适用于线性回归假设检验,不是结构学习评估的标准方法。

Q10. 贝叶斯网络结构学习在 AI 应用中的主要价值是?

单选题 · easy

  • A.替代所有深度学习模型
  • B.从数据中还原变量间的因果依赖关系← 正确答案
  • C.自动标注大规模无标签数据
  • D.直接生成可解释的文本报告

答案:B。结构学习的核心价值是从观测数据中发现变量间的因果依赖结构(DAG),为因果推断、可解释 AI、决策优化提供结构基础。它不替代深度学习模型,也不直接处理文本,而是为模型注入因果逻辑。