小明家总发生怪事:客厅薯片在减少,同时妹妹被家长训斥。你敢直接说“薯片减少导致妹妹挨训”吗?显然中间藏着“偷吃零食”这个看不见的小捣蛋鬼。因果发现也想在数据里找出“谁导致谁”,但真实数据常常也有类似的小捣蛋鬼——潜在混杂变量。PC 算法像一个只在整洁房间里查案的侦探,默认所有关键角色都在场;FCI 算法则是升级版侦探,它允许有人躲在暗处,依然能画出可信的因果地图。这张地图叫 PAG,上面除了箭头,还有双向边,专门标注“这里有隐藏通道”。掌握 FCI,就掌握了在 AI 系统里对观测数据做更诚实因果推断的钥匙。

岗位关联

学会 FCI 算法,意味着你可以在医疗、推荐、风控等充满未知混杂的真实场景中,仍给出可信的因果结构结论,这是 AI 走向可靠决策的关键能力。

目录
  1. 可视化演示
  2. 从 PC 到 FCI:侦探升级
  3. 潜在混杂:看不见的捣蛋鬼
  4. 条件独立:侦探的探针
  5. PAG:一张带问号的地图
  6. FCI 三步走
  7. 输出解读:别把地图当实景
  8. 应用边界:侦探的局限
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示FCI算法全流程:条件独立测试构建骨架图、考虑潜在混杂的边定向规则、PAG部分祖先图四种边类型含义,通过步进式交互理解隐混淆下的因果发现机制。

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

骨架发现:CI测试删除边 完全图:初始所有节点对相连 CI测试:条件独立则删除对应边 X⊥Y|S → remove edge(X,Y) 骨架图保留存在依赖关系的节点对

从 PC 到 FCI:侦探升级

阿理侦探以前只接“室内案件”:所有相关嫌疑人都被叫到客厅,没有任何人能躲起来。PC 算法就是这种室内侦探,它基于约束方法办案,先做条件独立性检验,再推出因果箭头。可现实案件里,总有人藏在天花板里,比如“偷吃零食的妹妹”。FCI 全称 Fast Causal Inference,是升级版侦探——它允许有隐藏角色存在,仍能判断谁影响谁。

PC 与 FCI 共享同一套“约束式”探案思路:用数据中的条件独立关系当证词,先画出无向骨架,再给边定向。差别在于,PC 默认“因果充分性”,即所有共同原因都被观测到;FCI 则放弃了这个默认,专门应对存在未观测混杂的场景。正因如此,FCI 的输出不再是完整的有向无环图 DAG,而是一张更谨慎的部分祖先图 PAG。

在因果图中,联合分布可以按因果方向分解为:

PC 像只相信“在场的人”,FCI 则会在证词里寻找“有人躲着”的痕迹。学习 FCI,本质上就是让 AI 学会在信息不完整时依然给出诚实的因果结论。

P(X1,…,Xn)=∏i=1nP(Xi∣Pa(Xi))\displaystyle P(X_1,\dots,X_n)=\prod_{i=1}^{n}P(X_i\mid \mathrm{Pa}(X_i))
这句话的意思是:只要知道每个变量的直接原因(父节点),整张因果图就能完整描述数据分布。
  1. 联合概率把所有变量一起出现的概率写成一张大联合分布。
    P(X1,…,Xn)\displaystyle P(X_1,\dots,X_n)
  2. 父节点集合$\mathrm{Pa}(X_i)$ 是 $X_i$ 的直接原因集合,也就是图中指向它的那些节点。
    Pa(Xi)\displaystyle \mathrm{Pa}(X_i)
  3. 链条相乘每个变量的概率只依赖它的直接原因,全部乘起来就还原出联合概率。
    ∏i=1n\displaystyle \prod_{i=1}^{n}

潜在混杂:看不见的捣蛋鬼

夏天到了,冰淇淋销量上涨,海边溺水人数也上涨。阿理侦探如果只看数据,会以为“买冰淇淋导致溺水”。然而真正的原因是“高温”——高温既让人买冰淇淋,也让人去游泳。这个“高温”就是看不见的捣蛋鬼:潜在混杂变量。

在因果发现里,潜在混杂指两个变量的共同原因没有被观测到。它会造成伪相关:即使 X 对 Y 没有任何因果效应,数据也会显示 X 与 Y 有关。PC 算法遇到这种情况会出错,因为它把“相关”误当成了“因果箭头”。FCI 则专门设计来应对这种场景,这也是它最核心的价值。

如果真正关心干预效果,混杂会让观测统计量骗人:

回到薯片案件:家长看到“薯片减少”与“妹妹哭”相关,但真正的共同原因是“妹妹偷吃后被抓”。FCI 的本事,就是即使看不到偷吃过程,也能从数据里标出“这里有隐藏通道”,而不是误把薯片当凶手。

P(Y∣do(T))≠P(Y∣T)\displaystyle P(Y\mid do(T)) \neq P(Y\mid T)
左边是“把 T 强行设为某个值”后 Y 的分布,右边是“直接看数据中 T 与 Y 的相关”,二者在混杂存在时不相等。
  1. 干预分布$do(T)$ 表示人为固定 $T$ 的值,相当于切断所有指向 $T$ 的因果路径。
    P(Y∣do(T))\displaystyle P(Y\mid do(T))
  2. 观测分布$P(Y\mid T)$ 只是被动观察数据中 $T$ 和 $Y$ 同时出现的频率。
    P(Y∣T)\displaystyle P(Y\mid T)
  3. 二者不等只要存在未观测混杂,观测相关就可能高于或低于真实因果效应。
    ≠\displaystyle \neq

条件独立:侦探的探针

阿理侦探最常用的问话方式是:假如我已经知道你整天都在家,那么“薯片减少”和“妹妹笑”还相关吗?如果不相关,说明之前的相关只是由“在家”这个共同背景引起的。这种问法,就是条件独立性检验。

FCI 把这种问法变成数学测试。对连续变量,常用偏相关配合 Fisher Z 变换:先剔除条件变量 Z 的线性影响,再看 X 与 Y 是否还有相关。若检验结果不显著,就认为 X 与 Y 在给定 Z 时条件独立。

检验统计量写作:

侦探每问一次话,就减少一条可疑线索;FCI 每次做条件独立检验,就决定要不要删除一条边。这个探针的质量,直接决定了最终因果地图是否可信。

ZXY⋅Z=n−∣S∣−3×12ln⁡1+rXY⋅Z1−rXY⋅Z\displaystyle Z_{XY\cdot Z}=\sqrt{n-|S|-3}\times\frac{1}{2}\ln\frac{1+r_{XY\cdot Z}}{1-r_{XY\cdot Z}}
这是把偏相关系数转换成近似正态分布的 z 分数,然后和阈值比较,判断“条件独立”是否成立。
  1. 偏相关$r_{XY\cdot Z}$ 表示剔除 Z 的影响后,X 与 Y 剩下的相关程度。
    rXY⋅Z\displaystyle r_{XY\cdot Z}
  2. Fisher 变换$\frac{1}{2}\ln\frac{1+r}{1-r}$ 把相关系数变成可比较的正态量。
    12ln⁡1+rXY⋅Z1−rXY⋅Z\displaystyle \frac{1}{2}\ln\frac{1+r_{XY\cdot Z}}{1-r_{XY\cdot Z}}
  3. 样本量校正乘以 $\sqrt{n-|S|-3}$,样本越大、条件集越小,越容易发现微弱相关。
    n−∣S∣−3\displaystyle \sqrt{n-|S|-3}

PAG:一张带问号的地图

阿理侦探不会把所有信息都画成实线箭头,他会在不确定的地方画上问号。FCI 输出的 PAG——Partial Ancestral Graph,部分祖先图——就是一张带问号的地图。它不强行给出完整的 DAG,而是诚实标注哪些方向确定、哪些方向未知。

PAG 里的每条边两端有三种标记:尾巴表示“是祖先”,箭头表示“不是祖先”,圆圈表示“暂不确定”。于是常见边形如:

有了这张带问号的地图,侦探不会把“可能有关”说成“一定导致”。这种保守,恰恰是 FCI 在复杂现实中可信的原因。

X→Y,X↔Y,X∘ ⁣ ⁣→Y\displaystyle X \rightarrow Y,\quad X \leftrightarrow Y,\quad X \circ\!\!\rightarrow Y
从左到右分别是:X 是 Y 的祖先、X 与 Y 之间存在隐藏混杂、方向端点仍未确定。
  1. 单箭头$X \rightarrow Y$,尾巴在 X,箭头在 Y,表示 X 是 Y 的祖先。
    X→Y\displaystyle X \rightarrow Y
  2. 双向箭头$X \leftrightarrow Y$,两端都是箭头,表示双方都不是对方的祖先,且存在未观测混杂或选择偏差。
    X↔Y\displaystyle X \leftrightarrow Y
  3. 圆圈箭头$X \circ\!\!\rightarrow Y$,圆圈一端未知,箭头一端表示“不是祖先”。
    X∘ ⁣ ⁣→Y\displaystyle X \circ\!\!\rightarrow Y

FCI 三步走

阿理侦探办案有三步:先排除无关线索,再找对撞结构,最后补上隐藏通道标记。FCI 算法也遵循类似流程,且为了适应大数据,每一步都尽量只检验“可能相邻”的变量,这也是它名字里 Fast 的由来。

第一步,骨架学习:对每对变量做条件独立检验,只要找到某个条件集能让它们独立,就把边删掉。第二步,碰撞器定向:如果 X 与 Y 不直接相邻,但都与 Z 相邻,而且 X 与 Y 在无条件时不独立、给定 Z 后反而独立,就能把 Z 定向为对撞节点。第三步,反复用规则补充箭头和双向边,最终得到 PAG。

骨架保留的核心判据是:

三步结束后,侦探手上的不是“唯一剧本”,而是一组可能剧本的公共交集。PAG 中保留的箭头,是所有剧本都同意的结论;双向边则是所有剧本都承认“有暗门”的风险标记。

edge(X,Y) 保留  ⟺  ∄ S⊆Adj(X)∖{Y} 使 X⊥ ⁣ ⁣ ⁣⊥Y∣S\displaystyle \mathrm{edge}(X,Y)\ \text{保留}\iff \nexists\, S\subseteq \mathrm{Adj}(X)\setminus\{Y\}\ \text{使}\ X\perp\!\!\!\perp Y\mid S
意思是:只要在 X 的邻居里找不到任何集合能让 X、Y 条件独立,这两个变量之间就保留一条边。
  1. 邻居集合$\mathrm{Adj}(X)$ 是当前图中还与 X 相连的节点。
    Adj(X)\displaystyle \mathrm{Adj}(X)
  2. 候选条件集在邻居中排除 Y 后取子集,作为“分离集”候选。
    S⊆Adj(X)∖{Y}\displaystyle S\subseteq \mathrm{Adj}(X)\setminus\{Y\}
  3. 不存在分离集找不到任何 $S$ 使 $X\perp\!\!\!\perp Y\mid S$,边就保留下来。
    ∄ S 使 X⊥ ⁣ ⁣ ⁣⊥Y∣S\displaystyle \nexists\, S \ \text{使}\ X\perp\!\!\!\perp Y\mid S

输出解读:别把地图当实景

拿到 PAG 后,阿理侦探会先圈出确定信息:哪些箭头在所有可能的因果图中都成立?哪些边只是“嫌疑关系”?如果一张 PAG 上有很多圆圈标记,说明数据本身不足以区分方向,这不算算法失败,反而是诚实的表现。

PAG 的一个重要特点是:同一个 PAG 对应多个可能的 DAG。它像是把一组“都能解释当前数据”的因果图拍了一张合影,只留下共同特征。双向边在这里是特别重要的警示牌:

读图的时候,如果把“双向边”当成“X 和 Y 互为因果”,就误解了地图。它真正的意思是:两者确实有关,但真正的因果方向被暗处的捣蛋鬼挡住了,不能轻下结论。

X↔Y  ⇒  存在未观测混杂或选择偏差\displaystyle X\leftrightarrow Y\;\Rightarrow\;\text{存在未观测混杂或选择偏差}
双向边不是一条普通边,而是提醒你:X 与 Y 之间不能轻易断言谁导致谁。
  1. 两端箭头$X\leftrightarrow Y$ 表示 X 不是 Y 的祖先,Y 也不是 X 的祖先。
    X↔Y\displaystyle X\leftrightarrow Y
  2. 隐藏机制箭头同时指向对方,说明背后有未观测的共同原因或选择机制。
    存在未观测混杂\displaystyle \text{存在未观测混杂}
  3. 风险警示它只警告风险,不说明效应方向和大小。
    ⇒风险警示\displaystyle \Rightarrow \text{风险警示}

应用边界:侦探的局限

阿理侦探再厉害,也不能凭一张地图判刑。FCI 同样有边界:它能给出结构线索,却不能直接给出“改变 X 会让 Y 变化多少”的数值。假如你想知道“疫苗推广能让发病率降多少”,仅靠 FCI 输出的 PAG 还不够。

FCI 还依赖若干假设:数据满足因果马尔可夫性、忠实性、样本独立同分布,且没有严重选择偏差。样本量太小、变量非线性、测量误差大,都会让条件独立检验失真。因此,FCI 更适合做“结构发现”,而不是“效应估计”。

从 PAG 到因果效应,还差关键一步:

所以现实中的侦探会把 FCI 当“缩小嫌疑范围的助手”,而不是“直接判刑的法官”。结合领域先验、时间顺序和实验数据,才能真正把因果地图变成可靠决策。

PAG(X,Y)  ⇏  P(Y∣do(X))\displaystyle \mathrm{PAG}(X,Y)\;\not\Rightarrow\;P(Y\mid do(X))
PAG 只告诉你 X 与 Y 之间存在或不存在祖先关系,不告诉你干预后的概率分布。
  1. 结构信息$\mathrm{PAG}(X,Y)$ 描述的是因果结构上的关系。
    PAG(X,Y)\displaystyle \mathrm{PAG}(X,Y)
  2. 干预分布$P(Y\mid do(X))$ 需要额外识别假设或实验设计。
    P(Y∣do(X))\displaystyle P(Y\mid do(X))
  3. 无法推出结构线索不等于因果效应,符号 $\not\Rightarrow$ 就是警告。
    ⇏\displaystyle \not\Rightarrow

AI 应用场景

医疗中的隐藏生物标记

阶段:因果发现与潜在混杂识别

解决的问题:电子病历里“用药”和“康复”高度相关,但患者基因、生活方式等关键变量往往未记录。传统统计容易把相关当疗效,导致错误结论。

具体案例:某团队用 FCI 分析数万条病历,发现某药物与康复之间出现双向边,提示存在未观测共同原因。进一步结合基因数据后,才确认真正的因果路径,避免了一次错误药效宣传。

推荐系统的反事实纠偏

阶段:推荐因果建模

解决的问题:用户点击商品并不等于用户喜欢商品,因为“用户当前活跃度”这类时间因素同时影响点击和购买。FCI 可以帮助判断点击与购买之间是否存在隐藏混杂。

具体案例:电商平台用 FCI 学习用户行为 PAG,发现“点击”与“收藏”间存在双向边,说明有未观测的活跃度在起作用;随后用反事实框架调整推荐权重,离线评估的转化率预测更稳。

Python 演示

python62 行
import numpy as npfrom itertools import combinations  def generate_data(n=2000):    rng = np.random.default_rng(42)    U = rng.normal(size=n)          # 隐藏混杂:高温    X = 0.8 * U + rng.normal(size=n)  # 冰淇淋销量    Y = 0.8 * U + rng.normal(size=n)  # 溺水人数    Z = 1.2 * X + rng.normal(size=n)  # 真正效应 X -> Z    return np.column_stack([X, Y, Z])  class SimpleFCIDemo:    def __init__(self, data, alpha=1.96):        self.data = data        self.alpha = alpha        self.edges = set()        self.ci_count = 0     def _ci(self, i, j, cond):        """返回条件独立是否成立,使用偏相关 + Fisher Z"""        self.ci_count += 1        A = np.column_stack([np.ones(len(self.data))] + [self.data[:, k] for k in cond])        ri = self.data[:, i] - A @ np.linalg.lstsq(A, self.data[:, i], rcond=None)[0]        rj = self.data[:, j] - A @ np.linalg.lstsq(A, self.data[:, j], rcond=None)[0]        r = np.corrcoef(ri, rj)[0, 1]        z = np.sqrt(len(self.data) - len(cond) - 3) * 0.5 * np.log((1 + r) / (1 - r + 1e-12))        return abs(z) < self.alpha     def learn_skeleton(self):        p = self.data.shape[1]        for i, j in combinations(range(p), 2):            sep = []            for size in range(0, 2):                conds = list(combinations([k for k in range(p) if k not in (i, j)], size))                for cond in conds:                    if self._ci(i, j, list(cond)):                        sep = list(cond)                        break                if sep:                    break            if not sep:                self.edges.add((min(i, j), max(i, j)))     def orient_pag(self):        """教学版:双向边标记潜在混杂,方向结合已知先验"""        pag = []        for i, j in self.edges:            if {i, j} == {0, 2}:                pag.append(('X', 'Z', '->'))            else:                pag.append(('X', 'Y', '<->'))        return pag  data = generate_data()demo = SimpleFCIDemo(data)demo.learn_skeleton()print('条件独立性检验次数:', demo.ci_count)print('观测骨架边:', demo.edges)print('PAG:', demo.orient_pag())
目的:用偏相关与 Fisher Z 做条件独立检验,在存在隐藏混杂的数据上还原 FCI 风格的骨架与 PAG。
  1. def generate_data生成“高温 U”同时影响 X、Y,且 X 真正影响 Z 的模拟数据。
  2. def _ci用残差化计算偏相关,再经 Fisher Z 判断条件独立。
  3. self.ci_count += 1统计条件独立检验次数,体现 FCI 对测试次数的关注。
  4. for size in range(0, 2)依次尝试空条件集和单变量条件集,寻找分离集。
  5. if not sep:找不到任何分离集时,边保留下来。
  6. orient_pag对无法分离的边标记为双向边,模拟 PAG 的隐藏混杂签名。

常见误区

误区我觉得 FCI 能把因果图里每个箭头方向都完整还原,输出图和真实 DAG 一模一样。

正解FCI 输出的是 PAG,会有大量圆圈标记和双向边,表示“仅靠观测数据无法确定”。它宁可保留问号也不编造方向,这正是它在隐混杂条件下可信的原因。

为何常见:很多教程先讲 PC,而 PC 在无混杂时能给出较完整有向图,初学者容易把这种印象迁移到 FCI。

误区只要有双向边 X↔Y,就一定存在一个具体的未观测变量 U 同时导致 X 和 Y。

正解双向边只是“存在未观测混杂或选择偏差”的签名,可以是一个 U,也可以是多个 U,甚至是一组复杂机制。它不承诺变量个数或名字。

为何常见:画图时常把一个隐藏节点画成圆圈,让人误以为它对应一个具体变量。

误区条件独立性检验 p 值大于 0.05,就证明 X 和 Y 在给定 Z 下一定独立。

正解检验只能说“没有足够证据拒绝独立”,样本量、非线性、测量误差都可能导致漏检;数据充足且假设成立时结论才可靠。

为何常见:显著性检验的“不显著不等于真实独立”是统计入门常踩的坑,在因果图里更容易被忽略。

误区只要用 FCI,就不需要领域知识,算法全自动得到最终因果图。

正解FCI 仍需忠实性、马尔可夫、无选择偏差等假设;方向常存在多种等价解释,领域先验(时间先后、干预知识)能减少不确定标记。

为何常见:FCI 名字里带 Fast,给人“一键出图”的错觉。

面试题

Q1. FCI 相比 PC 最主要提升了什么能力?

单选题 · easy

  • A.处理隐藏混杂← 正确答案
  • B.加快运行速度
  • C.绘制彩色图表
  • D.无需任何数据

答案:A。FCI 的核心改进是放宽“因果充分性”假设,允许未观测混杂存在。速度提升只是附带特性,绘制图表和无需数据都不是算法本质。

Q2. 下列哪一项是 FCI 输出的图结构?

单选题 · easy

  • A.精确有向无环图
  • B.部分祖先图PAG← 正确答案
  • C.朴素贝叶斯图
  • D.知识图谱结构

答案:B。FCI 输出部分祖先图 PAG,用尾巴、箭头和圆圈标记表示多个可能的 DAG 的共同特征,而不是单个完整 DAG。

Q3. 在 PAG 中,X↔Y 通常表示什么?

单选题 · easy

  • A.确实存在潜在混杂← 正确答案
  • B.X一定是Y的祖先
  • C.Y一定是X的祖先
  • D.没有任何统计关联

答案:A。双向边表示两个方向都是箭头,说明 X、Y 都不是对方的祖先,同时存在未观测混杂或选择偏差。它不是祖先关系,也不是无关联。

Q4. 条件独立性检验中,Fisher Z 用来做什么?

单选题 · mid

  • A.判断偏相关是否为零← 正确答案
  • B.计算因果效应大小
  • C.估计所需样本量
  • D.绘制置信区间图

答案:A。Fisher Z 把偏相关系数转换为近似正态的 z 分数,再通过与阈值比较,判断给定条件集后偏相关是否显著不为零。

Q5. FCI 骨架学习中,如何删除两点之间的边?

单选题 · mid

  • A.找到某条件独立集← 正确答案
  • B.计算两两相关系数
  • C.使用领域专家指定
  • D.按变量出现顺序删

答案:A。只要找到某个条件集使两点条件独立,就可以删除这条边。计算相关系数只是基础,方向删除则是算法自动完成。

Q6. 关于 PAG 中的圆圈标记,正确的是?

单选题 · mid

  • A.表示方向尚未确定← 正确答案
  • B.表示存在确定因果
  • C.表示该边完全不存在
  • D.表示变量取值有缺失

答案:A。圆圈标记对应“未知”端。它不代表边不存在,也不代表数据缺失,而是说明该端是尾巴还是箭头无法确定。

Q7. 关于 FCI 输出 PAG 的说法,正确的是?

单选题 · mid

  • A.一个 PAG 对应一组 DAG← 正确答案
  • B.一个 DAG 对应一组 PAG
  • C.PAG 中不含任何箭头
  • D.双向边表示确定因果

答案:A。PAG 是一族 DAG 的共同特征集合,因此一个 PAG 对应多个可能的 DAG。双向边不表示确定因果,而是表示潜在混杂。

Q8. 下列哪些情况可能使 FCI 产生双向边?

多选题 · hard

  • A.存在未观测的共同原因← 正确答案
  • B.存在选择偏差的机制← 正确答案
  • C.样本量不够多
  • D.变量之间的时间顺序

答案:AB。双向边在 PAG 中主要标记未观测混杂或选择偏差。样本量不足可能造成错误边,但不会在因果语义上产生“双向边”这个标记;时间顺序是辅助定向信息。

Q9. FCI 应用时需要满足哪些假设?

多选题 · hard

  • A.分布满足忠实性← 正确答案
  • B.满足因果马尔可夫性← 正确答案
  • C.所有变量都必须可观测
  • D.样本满足独立同分布← 正确答案

答案:ABD。FCI 仍然需要忠实性和因果马尔可夫性,也需要样本独立同分布。它放宽的是“所有共同原因都可观测”,所以 C 错误。

Q10. 关于 PAG 边标记的解读,下列正确的是?

多选题 · extreme

  • A.箭头端表示不是祖先← 正确答案
  • B.尾巴端表示是祖先← 正确答案
  • C.圆圈端表示未知← 正确答案
  • D.双向边代表可确定干预

答案:ABC。PAG 的端标记语义是:尾巴=是祖先,箭头=不是祖先,圆圈=未知。双向边只提示潜在混杂风险,不能据此确定干预效应,因此 D 错误。