城市刑侦队接到一起复杂的连环案:有 5 个关键人物都在案发时段出现过。队长面前的白板上,先把这 5 个人的名字两两用线连起来——破案之前,任何人都可能与任何人存在直接关联。这种“全嫌疑”的工作台,正是 PC 算法在数据面前的第一步:完全连通图。它不预设任何答案,只给所有变量先挂上“可能有关系”的标签。 真正的难题是:侦探没法回到过去重演案件,也不能抓人来反复试验——现实里,我们能拿到的往往只有被动观察数据。于是侦探开始用“不在场证明”挨个排查:如果 A 与 B 之间的关联,在引入中间人 C 之后完全消失,那就说明 A 与 B 之间没有直接的单线联系,可以在白板上划掉这条线。反复排查后,白板上的线越来越少,剩余的就是真实的“关系骨架”。 随后,侦探盯上一种反直觉的三点结构:A 与 B 互不认识,却分别都与 C 有联系。这种“两个独立线索汇聚于同一人”的结构,只能有一种合理指向——箭头指向 C。确定了这个关键枢纽约束,剩下箭头的方向就像数独一样,一条条被推演出来。 这整个过程,就是基于约束的 PC 算法:用条件独立性检验做“不在场证明”来剪枝,再用 v-结构与方向传播规则来定向。它不需要实验干预,只从观测数据出发,就能告诉你谁影响谁;同时也会诚实地告诉你,哪些方向是现有证据根本无法区分的。

岗位关联

掌握 PC 算法,是进入因果推理与可解释 AI 的第一块敲门砖:它教你仅凭观察数据回答“谁影响谁”,是构建可解释因果模型最经典的起点,也是数据科学、医疗 AI、风控等领域从业者的核心基本功。

目录
  1. 可视化演示
  2. 因果发现意义
  3. 条件独立检验
  4. 骨架学习流程
  5. v-结构定向
  6. 方向传播规则
  7. 马尔可夫等价类
  8. 算法特性局限
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示PC算法三阶段:完全图条件独立性检验删边学习骨架、v-结构碰撞点定向、Meek规则全局方向传播,输出马尔可夫等价类CPDAG。

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

骨架学习阶段 从完全连通图出发逐步删除条件独立边 条件独立性检验基于偏相关系数或卡方检验 X⊥Y|S → remove edge(X,Y) 分离集S记录用于后续定向

因果发现意义

直觉的因果常常骗人。侦探翻开城市统计局报告,发现冰淇淋销量高的日子,溺水人数往往也高——可如果把“冰淇淋销量”当作“溺水人数”的因,显然荒唐。它们共同被“夏天”这个隐藏因素驱动。真实世界只给侦探“看到的数据”,不会允许他把谁抓来重新摆布一遍;现实中的 AI 面对的数据,绝大多数也是这样被动观察来的。

AI 工程师手里往往只有用户看了什么、点了什么、买了什么;病历里有什么症状、什么检验指标。要从这些观察数据中回答“谁影响谁”,必须借助因果发现。PC 算法是其中基于约束方法的代表:它不像打分派方法那样给每个图算一个分数,而是像侦探查案一样,用一系列“约束”(条件独立性)过滤掉不合格的解释。

形式化地说,“观察”与“干预”在统计上往往不一致:

所以,PC 算法就是你身边那位不满足于“看见相关”的侦探:它要从观察数据里找出隐藏的因果链条,而不是简单地把同时出现的事说成谁导致了谁。这也是“约束式方法”名字的由来——它相信数据中的独立性约束,能像侦探的证据链一样,一步步锁定真相。

P(Y∣X=x)  ≠  P(Y∣do(X=x))\displaystyle P(Y \mid X = x) \;\neq\; P(Y \mid \mathrm{do}(X = x))
左边是“看见 X 等于 x 时 Y 的分布”(相关),右边是“人为把 X 强制设为 x 时 Y 的分布”(因果效应)。两者不同,正是单纯观察数据不能直接当因果用的根本原因。
  1. 条件概率 $P(Y \mid X=x)$表示观察到 X 取某个值 x 时,Y 的分布。它描述的是相关性,比如看到有人吃冰淇淋时,溺水的比例会偏高。
    P(Y∣X=x)P(Y \mid X = x)
  2. 干预算子 $\mathrm{do}(X=x)$表示强制把 X 改为 x 的实验操作,对应随机对照试验,比如规定所有人都必须吃一支冰淇淋。
    do(X=x)\mathrm{do}(X = x)
  3. 不等号 $\neq$观察与干预通常不相等——吃冰淇淋的人溺水率高,但强制吃冰淇淋并不会提升溺水率,因为真正起作用的是共同隐藏的“夏天”。
    ≠\neq
  4. 整体含义观察数据只能反映相关,不能直接当作因果证据,因此才需要 PC 算法从观察数据中恢复结构。
    P(Y∣X=x)≠P(Y∣do(X=x))P(Y \mid X=x) \neq P(Y \mid \mathrm{do}(X=x))

条件独立检验

侦探手里的第一张王牌叫“不在场证明排查”。如果 A 与 B 之间的每次联系,都能通过中间人 C 的日程完整解释——A 先联系 C,再由 C 转达给 B——那么 A、B 之间就只是间接关系,就不必在他们之间画直接连线。在统计里,这个“通过中间人解释”就叫条件独立。

PC 算法把“两变量之间是否有直接关系”翻译成一个可以检验的问题:给定条件集 Z 后,X 与 Y 是否仍然独立。若独立,说明关系是间接的,图中没有 X-Y 这条边;若不独立,则边暂时保留,继续寻找更大的条件集来“挑战”它。这个反复检验的过程,构成了整个算法的核心驱动力。

条件独立有严格的数学定义:

侦探靠不在场证明划掉一条条嫌疑,PC 算法靠条件独立检验划掉一条条多余边:显著性水平 α 仿佛是侦探的取证标准——标准太严会漏掉真线索,标准太松又会冤枉无辜者。这正是“基于约束”四个字的精髓所在。

X⊥ ⁣ ⁣ ⁣⊥Y∣Z  ⟺  P(X,Y∣Z)=P(X∣Z)⋅P(Y∣Z)\displaystyle X \perp\!\!\!\perp Y \mid Z \;\Longleftrightarrow\; P(X, Y \mid Z) = P(X \mid Z) \cdot P(Y \mid Z)
这个式子说的是:把 Z 的值固定住以后,X 的分布不再随 Y 改变。实际工作中,连续变量常用偏相关系数检验、离散变量用卡方检验,显著性水平 α 控制“多强的证据才承认独立”。
  1. $X \perp\!\!\!\perp Y$表示 X 与 Y 相互独立,在统计学上不互为依赖。
    X⊥ ⁣ ⁣ ⁣⊥YX \perp\!\!\!\perp Y
  2. 条件符号 $\mid Z$表示“在已知 Z 取值的条件下”,相当于侦探把中间人 C 的行程信息一律视作已知。
    ∣Z\mid Z
  3. 联合分布分解 $P(X, Y \mid Z) = P(X \mid Z) \cdot P(Y \mid Z)$等式右边是两个边际概率的乘积,只有当 X、Y 在给定 Z 后互不影响时才成立。
    P(X,Y∣Z)=P(X∣Z)⋅P(Y∣Z)P(X, Y \mid Z) = P(X \mid Z) \cdot P(Y \mid Z)
  4. 等价关系 $\Longleftrightarrow$这个等价式就是 PC 算法的判据——独立成立就删边,不成立就保留边等待下一步检验。
    ⟺\Longleftrightarrow

骨架学习流程

侦探在白板上把所有嫌疑人都两两相连,得到一张“人人都有直接联系”的完全图。接下来他规定排查顺序:先看两人之间在没有任何中间人的情况下能否脱钩,再试引入一个中间人、两个中间人……直到所有可能的解释都被检验过,再停止。

这就是 PC 骨架学习的三层递进:从完全连通图出发,按条件集大小 l = 0, 1, 2, …… 依次做条件独立检验;一旦发现某个条件集能够让 X 与 Y 独立,就删除这条边,并记录当时的条件集(称为分离集 sep-set),留给后续定向阶段使用;如果所有条件集都无法使它们独立,则边被保留。

骨架学习阶段的保留标准可以写成:

整张白板从密密麻麻的完全连线,最终只剩下几条“查不出不在场证明”的骨干关系。这些骨干里可能包含真实因果边,也可能包含方向相反的等价边——但至少,无关变量之间的假联系已经被系统地清除了。

保留边 X−Y  ⟺  ∀ Z⊆V∖{X,Y}, X̸ ⁣⊥ ⁣ ⁣ ⁣⊥Y∣Z\displaystyle \text{保留边 } X - Y \;\Longleftrightarrow\; \forall\, Z \subseteq \mathbf{V} \setminus \{X, Y\},\ X \not\!\perp\!\!\!\perp Y \mid Z
意思是:只有当“任何中间人组合都无法解释 X 与 Y 的关联”时,才认为它们之间存在直接联系。算法按条件集阶数由小到大推进,最终输出一张只含无向边的骨架图。
  1. 无向边 $X - Y$表示 X 与 Y 之间可能直接相关,但方向尚未确定。
    X−YX - Y
  2. 条件集 $Z$从其余变量中选出的中间人组合,用于尝试解释 X 与 Y 之间的关联。
    Z⊆V∖{X,Y}Z \subseteq \mathbf{V} \setminus \{X, Y\}
  3. 全称量词 $\forall$要求所有可能的条件集都被检验过;只要有任何一组使 X、Y 独立,边就会被删除。
    ∀ Z\forall\, Z
  4. 给定 Z 后仍相关 $X \not\!\perp\!\!\!\perp Y \mid Z$关联无法被任何中间人完全解释,因此这条边被保留进无向骨架。
    X̸ ⁣⊥ ⁣ ⁣ ⁣⊥Y∣ZX \not\!\perp\!\!\!\perp Y \mid Z

v-结构定向

白板上一处特殊结构让侦探眼前一亮:A 与 B 之间没有直接连线,两人互不相识;但 A 与 C 有联系,B 与 C 也有联系。更关键的是,当侦探把 C 纳入考虑后,A 与 B 反而呈现出相关——仿佛两人都在朝同一个“枢纽”输出影响。这种反直觉的汇聚结构,只能解释为 A、B 分别是独立的原因,C 是它们共同的后果。

图论中,这种三元关系叫对撞结构(v-结构),写作 A→C←B。PC 算法在骨架上逐个检查三元组:先找到一条无向路径 A-C-B,且 A 与 B 不相邻;再查看 A 与 B 的分离集是否包含 C。如果 C 不在分离集中,说明 A、B 本来独立,给定 C 后才相关,于是箭头必须指向 C。

判定条件可以写成:

侦探找到了第一颗铁定的铆钉:两个互不相识的人,偏偏都联系了同一个人——这条线索指向的不是中间传话人,而是一个“汇合点”。v-结构是 PC 算法定向阶段的第一类锚点,它为后续所有方向推理提供了出发点。

(A⊥ ⁣ ⁣ ⁣⊥B)  ∧  ¬(A⊥ ⁣ ⁣ ⁣⊥B∣C)  ⇒  A→C←B\displaystyle (A \perp\!\!\!\perp B) \;\land\; \lnot (A \perp\!\!\!\perp B \mid C) \;\Rightarrow\; A \to C \leftarrow B
第一条要求 A、B 无条件独立;第二条强调给定 C 后这种独立被打破。这正是对撞结构与“中间传导”结构(A→C→B 或 A←C→B)的分水岭:传导结构中,给定中间节点后独立;对撞结构中,给定共同结果后反而相关。
  1. 无条件独立 $A \perp\!\!\!\perp B$A 与 B 之间没有直接关联,也没有共同原因。
    A⊥ ⁣ ⁣ ⁣⊥BA \perp\!\!\!\perp B
  2. 否定式 $\lnot (A \perp\!\!\!\perp B \mid C)$给定 C 后 A 与 B 不再独立,说明 C 是连接二者的“铰链”——但这种汇聚关系不同于传导。
    ¬(A⊥ ⁣ ⁣ ⁣⊥B∣C)\lnot (A \perp\!\!\!\perp B \mid C)
  3. 合取符号 $\land$两个条件缺一不可,只有同时满足才能判定对撞结构。
    ∧\land
  4. 结论 $A \to C \leftarrow B$C 是两个独立原因的共同效应,箭头都指向它,这就是 v-结构。
    A→C←BA \to C \leftarrow B

方向传播规则

侦探已经确认 A→C←B 这个枢纽,但白板上还有几条线的方向不明。此时他不再需要新证据,只需要逻辑衍生:已知 X 影响了 Y,而 Y 与 Z 之间还有一条未定向边,X 与 Z 之间没有边,那么 Y→Z 是唯一不产生矛盾的选择。这就像数独——先填好的格子,决定周围格子的唯一解。

PC 算法完成 v-结构定向后,剩余边交给 Meek 规则(R1-R4)传播。最常用、也最体现思想的是规则 R1:

规则像推理题一样一环扣一环,直到白板上所有能定方向的地方都定完。Meek 规则的意义在于“避免新环”与“完备定向”:它不引入新证据,只做逻辑完备的事,确保最终结果既忠实于已有判定,又尽可能多地还原因果方向。

(A→B)  ∧  (B−C)  ∧  ¬(A−C)  ⇒  B→C\displaystyle (A \to B) \;\land\; (B - C) \;\land\; \lnot(A - C) \;\Rightarrow\; B \to C
这条规则说:只要出现“已定向的 A→B 边、B 与 C 之间的无向边、A 与 C 不相邻”这一组合,B-C 的方向就必然为 B→C;否则会创造出新的对撞结构或环,违背已有的判定。
  1. 前提一 $A \to B$已有的定向边,相当于侦探已落定的箭头事实。
    A→BA \to B
  2. 前提二 $B - C$B 与 C 之间的边仍然无向,关系存在但方向未知。
    B−CB - C
  3. 前提三 $\lnot(A - C)$A 与 C 之间没有边;若让 C 指向 B,就会形成一个新的 v-结构,与已知骨架矛盾。
    ¬(A−C)\lnot(A - C)
  4. 结论 $B \to C$只有 B 指向 C 才不产生新环、不引入新对撞,因此方向被唯一确定。
    B→CB \to C

马尔可夫等价类

有些案件,现有证据只能证明 X 与 Y 之间存在单线联系,却无法证明是谁先联系谁——X 指向 Y,还是 Y 指向 X,对外部观察者来说完全一样。侦探不会瞎猜,而是把所有可能的方向打包成一族“证据允许的关系图”。这就是马尔可夫等价类的朴素含义。

PC 算法的一个核心事实是:它不能保证输出唯一的因果 DAG,而是输出一个等价类——所有条件独立关系完全一致的 DAG 集合。实际操作中,这个集合用 CPDAG(部分有向无环图)表示:有箭头的边是可确认的方向,无向边代表等价类内部不同 DAG 可以互换方向的部分。

Verma-Pearl 定理给出了等价类的精确刻画:

侦探把“证据无法分辨的方向”如实标注,这看起来保守,却正是严谨:在因果推断里,承认“不知道”比假装“知道”更可贵。CPDAG 里留下的无向边,不是算法的失败,而是数据本身的信息上限。

D1≡D2  ⟺  Skel(D1)=Skel(D2)  ∧  Col(D1)=Col(D2)\displaystyle \mathcal{D}_1 \equiv \mathcal{D}_2 \;\Longleftrightarrow\; \mathrm{Skel}(\mathcal{D}_1) = \mathrm{Skel}(\mathcal{D}_2) \;\land\; \mathrm{Col}(\mathcal{D}_1) = \mathrm{Col}(\mathcal{D}_2)
这个定理说明:两个因果图只要拥有相同的骨架和相同的 v-结构集合,它们在观察层面就完全不可区分。PC 算法最终给出的,正是这样一个“不可区分家族”的代表——CPDAG。
  1. 等价 $\mathcal{D}_1 \equiv \mathcal{D}_2$两个 DAG 在马尔可夫意义上等价,编码的条件独立关系完全相同。
    D1≡D2\mathcal{D}_1 \equiv \mathcal{D}_2
  2. 骨架相同 $\mathrm{Skel}(\mathcal{D}_1) = \mathrm{Skel}(\mathcal{D}_2)$两组变量之间的邻接关系一一对应。
    Skel(D1)=Skel(D2)\mathrm{Skel}(\mathcal{D}_1) = \mathrm{Skel}(\mathcal{D}_2)
  3. 对撞结构集合相同 $\mathrm{Col}(\mathcal{D}_1) = \mathrm{Col}(\mathcal{D}_2)$所有 v-结构的指向模式一致。
    Col(D1)=Col(D2)\mathrm{Col}(\mathcal{D}_1) = \mathrm{Col}(\mathcal{D}_2)
  4. 合取条件 $\land$两个条件需同时成立,缺少任何一个都不能判定等价。
    ∧\land

算法特性局限

侦探的推理有前提:不在场证明记录完整、没有故意伪造、排查顺序得当。现实案卷总有缺页、误记,甚至恰好的巧合——比如两个人明明没有直接联系,却因为一个共同的未记录因素表现得相关。PC 算法同样受这些现实条件束缚。

PC 算法有三大敏感面:第一,样本量有限时,条件独立性检验本身会出错,导致边被误删或误留;第二,它依赖忠实性假设,即数据中的条件独立模式必须与真实图完全一致,一旦被破坏,剪枝就会失真;第三,变量处理顺序不同,检验轨迹不同,有限样本下可能得到不同骨架。它更适合变量数不多、关系较规范、数据质量尚可的探索性分析。

忠实性假设可以形式化为图中条件独立关系与概率分布中条件独立关系的一致:

PC 算法不是万能侦探,但它给了我们一套在观察世界中严谨办案的基本盘:先剪枝、再定点、再传播,最后坦诚地标注哪些方向永远无从确定。理解它的局限,才能在任何实际项目中正确使用它——这正是一切可解释 AI 因果能力的基石。

I(G)=I(P)\displaystyle I(\mathcal{G}) = I(P)
这个等式要求:理论因果图里推出的所有条件独立性,都能在数据中被检验到;反过来,数据中发现的每条独立性,在图上也有对应解释。当它不成立时,PC 算法的输出可能包含错误边。
  1. 图的条件独立集合 $I(\mathcal{G})$由因果图结构推导出的全部条件独立关系,是理论上的“不在场证明”列表。
    I(G)I(\mathcal{G})
  2. 分布的条件独立集合 $I(P)$从实际数据中用统计检验发现的全部条件独立关系,是侦探真实拿到的证据清单。
    I(P)I(P)
  3. 等号 $=$假设两者完全一致——每条理论上的独立性都能被数据确认,且数据中每条独立性都有理论依据。
    ==
  4. 假设的本质这个等式是理想化的忠实性假设,当存在未记录的隐藏变量或恰好抵消的路径时会被破坏。
    I(G)=I(P)I(\mathcal{G}) = I(P)

AI 应用场景

医疗诊断:从电子病历中还原症状因果链

阶段:特征发现与因果结构建模

解决的问题:传统统计只能给出“哪些指标与疾病相关”,医生仍然分不清症状之间谁引发谁。PC 算法从大量观察性病历数据中自动提取症状、检验指标之间的因果骨架,帮助识别真正关键的早期信号。

具体案例:在 MIMIC 重症监护数据集上,用 PC 算法对“高血压、肾功能指标、心衰标志物、住院时长”等变量跑骨架学习,得到“高血压→肾功能指标异常→心衰风险”的因果路径,比单纯的相关性热力图更接近临床机理。

推荐系统:识别用户行为的真实因果漏斗

阶段:用户行为理解与漏斗优化

解决的问题:平台观察到“收藏→加购→下单”高度相关,但传统协同过滤无法判断哪个环节真正驱动了下一个环节。PC 算法从行为日志中学习因果结构,发现某些环节只是“相关性气泡”而非真实因果驱动。

具体案例:某电商团队对“曝光、点击、收藏、加购、下单”五个事件做 PC 因果发现,v-结构定向后发现“收藏”与“下单”之间没有直接因果边,两者都被“购买意愿”这个隐藏因素驱动,据此调整了运营策略,把资源从收藏引导转移到加购引导。

金融风控:寻找违约的驱动型因子

阶段:可解释风控建模

解决的问题:信贷审批中,大量特征与违约相关,但监管要求解释“为什么拒绝”。PC 算法帮助建模团队理清特征间的因果骨架,保留真正影响违约的直接原因,剔除只起陪跑作用的共线特征。

具体案例:某银行对“收入稳定性、负债率、征信查询次数、逾期违约”建立 PC 因果图,发现征信查询次数与违约的关联会经收入稳定性“中间传导”,因此在模型中降低其权重,提升了模型的稳定性和可解释性。

Python 演示

python70 行
import itertoolsimport numpy as np def generate_data(n=5000, seed=42):    """按真实的因果结构生成 4 变量数据。    DAG: X1 -> X3 <- X2, X3 -> X4    """    rng = np.random.default_rng(seed)    x1 = rng.normal(0, 1, n)    x2 = rng.normal(0, 1, n)    x3 = 0.8 * x1 + 0.7 * x2 + 0.3 * rng.normal(0, 1, n)    x4 = 0.9 * x3 + 0.4 * rng.normal(0, 1, n)    return np.stack([x1, x2, x3, x4], axis=1) def partial_corr(data, i, j, k_list):    """计算偏相关系数:剥离 k_list 影响后 i 与 j 的相关系数。"""    n = data.shape[0]    if len(k_list) == 0:        Zc = np.ones((n, 1))    else:        Zc = np.hstack([np.ones((n, 1)), data[:, k_list]])    def residuals(y, X):        beta, _, _, _ = np.linalg.lstsq(X, y, rcond=None)        return y - X @ beta    ri = residuals(data[:, i], Zc)    rj = residuals(data[:, j], Zc)    denom = np.sqrt(np.sum(ri ** 2) * np.sum(rj ** 2))    if denom == 0:        return 0.0    return float(np.sum(ri * rj) / denom) def ci_test(data, i, j, k_list, alpha=0.05):    """Fisher Z 检验:|z| * sqrt(n - |k| - 3) 小于阈值则判独立。"""    n = data.shape[0]    r = np.clip(partial_corr(data, i, j, k_list), -0.999, 0.999)    z = 0.5 * np.log((1 + r) / (1 - r))    threshold = 1.959963984540054    return abs(z) * np.sqrt(n - len(k_list) - 3) < threshold def learn_skeleton(data):    p = data.shape[1]    edges = {(a, b) for a in range(p) for b in range(a + 1, p)}    sepset = {}    l = 0    while l <= p - 2:        changed = False        for (a, b) in list(edges):            adj_a = {x for x in range(p)                     if x != a and (min(a, x), max(a, x)) in edges}            for s in itertools.combinations(adj_a - {b}, l):                if ci_test(data, a, b, list(s)):                    edges.remove((a, b))                    sepset[(a, b)] = set(s)                    changed = True                    break        if not changed:            l += 1    return edges, sepset if __name__ == '__main__':    data = generate_data()    edges, sepset = learn_skeleton(data)    names = ['X1', 'X2', 'X3', 'X4']    print('学习到的无向骨架:')    for (a, b) in sorted(edges):        print(f'  {names[a]} -- {names[b]}')    print('被删除的边:')    for (a, b) in sorted(sepset):        sep_names = sorted(names[x] for x in sepset[(a, b)])        print(f'  {names[a]} -- {names[b]}  分离集: {sep_names}')
目的:用合成数据验证 PC 骨架学习——从 4 个变量的完全连通图中,通过条件独立性检验自动还原真实因果边(X1-X3、X2-X3、X3-X4),并剔除三条虚假边。
  1. x3 = 0.8 * x1 + 0.7 * x2 + 0.3 * rng.normal(0, 1, n)按真实因果机制生成数据:X3 由 X1、X2 和噪声共同决定
  2. Zc = np.hstack([np.ones((n, 1)), data[:, k_list]])把条件变量和截距拼成回归设计矩阵,用于剥离中间人影响
  3. ri = residuals(data[:, i], Zc)剥离 Z 的影响后,i 变量剩余的变化部分
  4. z = 0.5 * np.log((1 + r) / (1 - r))Fisher Z 变换,把相关系数映射到近似正态分布
  5. return abs(z) * np.sqrt(n - len(k_list) - 3) < threshold检验统计量小于阈值时接受“条件独立”
  6. adj_a = {x for x in range(p) if x != a and (min(a, x), max(a, x)) in edges}获取当前与 a 相邻的节点,作为候选中间人池
  7. for s in itertools.combinations(adj_a - {b}, l):枚举所有大小为 l 的条件集,PC 算法按阶数由小到大搜索
  8. if ci_test(data, a, b, list(s)):只要找到一组条件集能让 a、b 独立,就执行删除
  9. sepset[(a, b)] = set(s)记录分离集,这是后续 v-结构定向要用到的关键证据

常见误区

误区跑完 PC 算法拿到一张所有边都带箭头的图,以为每一个方向都被数据实锤了。

正解PC 只能确定骨架和 v-结构,其余边靠 Meek 规则尽量定向;如果数据无法区分方向,剩下的边会保持无向,输出的是 CPDAG 而非唯一 DAG。无向边代表“证据不足”,不是算法偷懒。

为何常见:教程和论文示例常展示完全定向的样例图,容易让初学者误以为 PC 总能输出唯一完整的因果 DAG。

误区两个变量条件独立,就认为它们之间绝对没有任何关联。

正解条件独立是指在给定中间人集合 Z 之后,两者不再有统计关联——它们之间的影响完全可以通过 Z 传导。这恰好说明关系是间接的,而不是“没关系”。

为何常见:日常语言里的“独立”容易让人联想到“无关”,忽略了“条件”二字才是关键前提。

误区既然 PC 在理想情况下能恢复正确骨架,那么怎么排列变量、按什么顺序检验都无所谓。

正解在无限样本和完美检验的假设下确实顺序无关,但有限样本下条件独立性检验会出错,不同的变量顺序会让错误沿不同路径传播,导致最终骨架不同。实践中常建议打乱顺序多次运行,取稳定出现的边。

为何常见:算法课本通常先讲理想化理论,再把有限样本问题放入“工程细节”,让学习者误以为顺序问题不重要。

误区只要把样本量堆到很大,PC 算法就不会出错,因果图一定正确。

正解样本增大能降低采样噪声,但无法解决忠实性假设被破坏、隐藏变量遗漏、检验方法偏差等问题。这些错误源与样本量无关,属于结构性局限。

为何常见:人们容易把所有失败都归因于“数据不够多”,忽略了模型假设本身可能不成立。

误区有了 PC 算法,就不需要做 AB 测试或随机对照试验了。

正解PC 只处理观察数据,输出的是马尔可夫等价类,方向不完整;随机对照实验仍是确定因果效应的金标准。PC 的价值在于从已有观察数据中快速形成因果假设,再用实验去验证,两者是互补关系。

为何常见:对可识别性边界理解不足,误以为“数据驱动”可以绕过一切实验设计。

面试题

Q1. PC 算法属于哪一类因果发现方法?

单选题 · easy

  • A.基于约束的因果发现← 正确答案
  • B.基于分数的结构学习
  • C.基于干预实验的因果推断
  • D.基于深度强化学习的搜索

答案:A。PC 算法以条件独立性检验为约束来剪枝和定向,属于基于约束的方法;打分派用分数搜索结构,实验派依赖干预数据,深度强化学习属于另一类搜索策略。就像侦探靠“不在场证明”排除嫌疑,而不是靠给嫌疑人打分或者重演案件。

Q2. PC 算法骨架学习阶段使用什么作为初始图?

单选题 · easy

  • A.随机生成的稀疏图
  • B.完全连通图(两两相连)← 正确答案
  • C.没有任何边的空图
  • D.以根节点展开的树图

答案:B。PC 算法不预设任何先验邻接关系,从所有变量两两相连的完全连通图出发,再通过条件独立检验逐条删边。这相当于侦探初始假设“任何两个人都有可能直接关联”,再靠证据逐一排除。

Q3. 条件独立性 X⊥Y|Z 的含义是?

单选题 · mid

  • A.X 与 Y 绝对无关
  • B.控制 Z 后 X 与 Y 不再相关← 正确答案
  • C.X 是 Y 的直接原因
  • D.Z 必为 X 与 Y 的共同结果

答案:B。条件独立强调的是“在给定 Z 的条件下”不再相关,不代表无条件无关。A 忽略了条件前提;C 是因果方向推断,不能由独立性得出;D 把条件集强行解释为共同结果,实际上 Z 可以是中间传导者。就像 A、B 都通过 C 转达消息,控制 C 后两人自然“无话可说”。

Q4. v-结构 A→C←B 的典型统计特征是?

单选题 · mid

  • A.A 与 B 在给定 C 后独立
  • B.A、B 无条件独立,给定 C 后却相关← 正确答案
  • C.A、B、C 三者两两独立
  • D.C 是 A 与 B 信息的中间传递者

答案:B。对撞结构中 A、B 是两个独立原因,无条件独立;但共同结果 C 一旦被固定,A、B 会表现出额外相关。这正是它与中间传导结构的区别:传导结构给定 C 后反而独立。B 完美描述了这种“汇聚”特征。

Q5. PC 算法最终输出的是什么?

单选题 · mid

  • A.唯一的完整因果 DAG
  • B.马尔可夫等价类对应的 CPDAG← 正确答案
  • C.所有变量的聚类标签
  • D.每个变量的条件概率表

答案:B。PC 无法区分等价类内部方向,输出的是部分有向无环图(CPDAG),其中无向边表示等价类中可以互换的方向。A 过于绝对;C 是聚类任务;D 是参数学习的结果,不是结构学习的输出。

Q6. Meek 规则 R1 的作用是什么?

单选题 · mid

  • A.判断两个变量是否条件独立
  • B.为剩余无向边定向并避免产生新环← 正确答案
  • C.删除不满足条件独立的边
  • D.为每个节点计算 BIC 分数

答案:B。R1 通过“已知 A→B、B 与 C 无向、A 与 C 不相邻”推出 B→C,是方向传播的基本规则,目的是在已定向上继续推出无矛盾的新方向。A、C 属于骨架学习阶段,D 属于打分派方法。

Q7. 忠实性假设的核心含义是?

单选题 · hard

  • A.数据必须来自随机对照实验
  • B.图与分布的条件独立关系完全一致← 正确答案
  • C.所有变量都服从正态分布
  • D.样本量必须足够大且完整

答案:B。忠实性假设要求因果图中的条件独立关系与真实概率分布中的条件独立关系逐一对应,记作 I(𝒢)=I(P)。它不要求随机实验,也不要求正态分布,是“结构恰好能解释所有独立性”的理想前提。

Q8. PC 算法的两个主要阶段是?

多选题 · easy

  • A.学习无向骨架← 正确答案
  • B.为边定向← 正确答案
  • C.计算贝叶斯信息准则
  • D.做随机干预实验

答案:AB。PC 先通过条件独立检验从完全图中剪出无向骨架,再通过 v-结构和 Meek 规则定向。C 是打分派工具,D 是实验设计手段,都不属于 PC 的两阶段框架。

Q9. 关于 PC 算法,以下说法正确的有?

多选题 · hard

  • A.相关关系不等同于因果关系← 正确答案
  • B.CPDAG 中未定向的边表示方向不可区分← 正确答案
  • C.PC 对检验误差完全不敏感
  • D.v-结构是边定向的第一类锚点← 正确答案

答案:ABD。A 是因果发现的根本动机;B 是 CPDAG 的本质;D 正确,v-结构是唯一能直接从无向骨架确定的锚点。C 错误,有限样本下检验误差会传播成结构错误,这正是 PC 的已知局限。

Q10. 关于 PC 算法的局限,下列说法正确的有?

多选题 · hard

  • A.变量顺序可能影响骨架学习结果← 正确答案
  • B.有限样本下条件独立性检验会出错← 正确答案
  • C.忠实性假设被破坏时可能输出错误边← 正确答案
  • D.PC 可以取代随机对照实验

答案:ABC。A、B、C 都是 PC 的公认局限:顺序影响检验轨迹,有限样本带来统计误差,忠实性假设不成立会直接导致剪枝失真。D 错误,PC 只能处理观察数据,随机对照实验仍是因果效应的金标准,二者互补而非替代。