小明家总发生怪事:客厅薯片在减少,同时妹妹被家长训斥。你敢直接说“薯片减少导致妹妹挨训”吗?显然中间藏着“偷吃零食”这个看不见的小捣蛋鬼。因果发现也想在数据里找出“谁导致谁”,但真实数据常常也有类似的小捣蛋鬼——潜在混杂变量。PC 算法像一个只在整洁房间里查案的侦探,默认所有关键角色都在场;FCI 算法则是升级版侦探,它允许有人躲在暗处,依然能画出可信的因果地图。这张地图叫 PAG,上面除了箭头,还有双向边,专门标注“这里有隐藏通道”。掌握 FCI,就掌握了在 AI 系统里对观测数据做更诚实因果推断的钥匙。
学会 FCI 算法,意味着你可以在医疗、推荐、风控等充满未知混杂的真实场景中,仍给出可信的因果结构结论,这是 AI 走向可靠决策的关键能力。
目录
可视化演示
展示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 学会在信息不完整时依然给出诚实的因果结论。
- 联合概率把所有变量一起出现的概率写成一张大联合分布。
- 父节点集合$\mathrm{Pa}(X_i)$ 是 $X_i$ 的直接原因集合,也就是图中指向它的那些节点。
- 链条相乘每个变量的概率只依赖它的直接原因,全部乘起来就还原出联合概率。
潜在混杂:看不见的捣蛋鬼
夏天到了,冰淇淋销量上涨,海边溺水人数也上涨。阿理侦探如果只看数据,会以为“买冰淇淋导致溺水”。然而真正的原因是“高温”——高温既让人买冰淇淋,也让人去游泳。这个“高温”就是看不见的捣蛋鬼:潜在混杂变量。
在因果发现里,潜在混杂指两个变量的共同原因没有被观测到。它会造成伪相关:即使 X 对 Y 没有任何因果效应,数据也会显示 X 与 Y 有关。PC 算法遇到这种情况会出错,因为它把“相关”误当成了“因果箭头”。FCI 则专门设计来应对这种场景,这也是它最核心的价值。
如果真正关心干预效果,混杂会让观测统计量骗人:
回到薯片案件:家长看到“薯片减少”与“妹妹哭”相关,但真正的共同原因是“妹妹偷吃后被抓”。FCI 的本事,就是即使看不到偷吃过程,也能从数据里标出“这里有隐藏通道”,而不是误把薯片当凶手。
- 干预分布$do(T)$ 表示人为固定 $T$ 的值,相当于切断所有指向 $T$ 的因果路径。
- 观测分布$P(Y\mid T)$ 只是被动观察数据中 $T$ 和 $Y$ 同时出现的频率。
- 二者不等只要存在未观测混杂,观测相关就可能高于或低于真实因果效应。
条件独立:侦探的探针
阿理侦探最常用的问话方式是:假如我已经知道你整天都在家,那么“薯片减少”和“妹妹笑”还相关吗?如果不相关,说明之前的相关只是由“在家”这个共同背景引起的。这种问法,就是条件独立性检验。
FCI 把这种问法变成数学测试。对连续变量,常用偏相关配合 Fisher Z 变换:先剔除条件变量 Z 的线性影响,再看 X 与 Y 是否还有相关。若检验结果不显著,就认为 X 与 Y 在给定 Z 时条件独立。
检验统计量写作:
侦探每问一次话,就减少一条可疑线索;FCI 每次做条件独立检验,就决定要不要删除一条边。这个探针的质量,直接决定了最终因果地图是否可信。
- 偏相关$r_{XY\cdot Z}$ 表示剔除 Z 的影响后,X 与 Y 剩下的相关程度。
- Fisher 变换$\frac{1}{2}\ln\frac{1+r}{1-r}$ 把相关系数变成可比较的正态量。
- 样本量校正乘以 $\sqrt{n-|S|-3}$,样本越大、条件集越小,越容易发现微弱相关。
PAG:一张带问号的地图
阿理侦探不会把所有信息都画成实线箭头,他会在不确定的地方画上问号。FCI 输出的 PAG——Partial Ancestral Graph,部分祖先图——就是一张带问号的地图。它不强行给出完整的 DAG,而是诚实标注哪些方向确定、哪些方向未知。
PAG 里的每条边两端有三种标记:尾巴表示“是祖先”,箭头表示“不是祖先”,圆圈表示“暂不确定”。于是常见边形如:
有了这张带问号的地图,侦探不会把“可能有关”说成“一定导致”。这种保守,恰恰是 FCI 在复杂现实中可信的原因。
- 单箭头$X \rightarrow Y$,尾巴在 X,箭头在 Y,表示 X 是 Y 的祖先。
- 双向箭头$X \leftrightarrow Y$,两端都是箭头,表示双方都不是对方的祖先,且存在未观测混杂或选择偏差。
- 圆圈箭头$X \circ\!\!\rightarrow Y$,圆圈一端未知,箭头一端表示“不是祖先”。
FCI 三步走
阿理侦探办案有三步:先排除无关线索,再找对撞结构,最后补上隐藏通道标记。FCI 算法也遵循类似流程,且为了适应大数据,每一步都尽量只检验“可能相邻”的变量,这也是它名字里 Fast 的由来。
第一步,骨架学习:对每对变量做条件独立检验,只要找到某个条件集能让它们独立,就把边删掉。第二步,碰撞器定向:如果 X 与 Y 不直接相邻,但都与 Z 相邻,而且 X 与 Y 在无条件时不独立、给定 Z 后反而独立,就能把 Z 定向为对撞节点。第三步,反复用规则补充箭头和双向边,最终得到 PAG。
骨架保留的核心判据是:
三步结束后,侦探手上的不是“唯一剧本”,而是一组可能剧本的公共交集。PAG 中保留的箭头,是所有剧本都同意的结论;双向边则是所有剧本都承认“有暗门”的风险标记。
- 邻居集合$\mathrm{Adj}(X)$ 是当前图中还与 X 相连的节点。
- 候选条件集在邻居中排除 Y 后取子集,作为“分离集”候选。
- 不存在分离集找不到任何 $S$ 使 $X\perp\!\!\!\perp Y\mid S$,边就保留下来。
输出解读:别把地图当实景
拿到 PAG 后,阿理侦探会先圈出确定信息:哪些箭头在所有可能的因果图中都成立?哪些边只是“嫌疑关系”?如果一张 PAG 上有很多圆圈标记,说明数据本身不足以区分方向,这不算算法失败,反而是诚实的表现。
PAG 的一个重要特点是:同一个 PAG 对应多个可能的 DAG。它像是把一组“都能解释当前数据”的因果图拍了一张合影,只留下共同特征。双向边在这里是特别重要的警示牌:
读图的时候,如果把“双向边”当成“X 和 Y 互为因果”,就误解了地图。它真正的意思是:两者确实有关,但真正的因果方向被暗处的捣蛋鬼挡住了,不能轻下结论。
- 两端箭头$X\leftrightarrow Y$ 表示 X 不是 Y 的祖先,Y 也不是 X 的祖先。
- 隐藏机制箭头同时指向对方,说明背后有未观测的共同原因或选择机制。
- 风险警示它只警告风险,不说明效应方向和大小。
应用边界:侦探的局限
阿理侦探再厉害,也不能凭一张地图判刑。FCI 同样有边界:它能给出结构线索,却不能直接给出“改变 X 会让 Y 变化多少”的数值。假如你想知道“疫苗推广能让发病率降多少”,仅靠 FCI 输出的 PAG 还不够。
FCI 还依赖若干假设:数据满足因果马尔可夫性、忠实性、样本独立同分布,且没有严重选择偏差。样本量太小、变量非线性、测量误差大,都会让条件独立检验失真。因此,FCI 更适合做“结构发现”,而不是“效应估计”。
从 PAG 到因果效应,还差关键一步:
所以现实中的侦探会把 FCI 当“缩小嫌疑范围的助手”,而不是“直接判刑的法官”。结合领域先验、时间顺序和实验数据,才能真正把因果地图变成可靠决策。
- 结构信息$\mathrm{PAG}(X,Y)$ 描述的是因果结构上的关系。
- 干预分布$P(Y\mid do(X))$ 需要额外识别假设或实验设计。
- 无法推出结构线索不等于因果效应,符号 $\not\Rightarrow$ 就是警告。
AI 应用场景
医疗中的隐藏生物标记
阶段:因果发现与潜在混杂识别
解决的问题:电子病历里“用药”和“康复”高度相关,但患者基因、生活方式等关键变量往往未记录。传统统计容易把相关当疗效,导致错误结论。
具体案例:某团队用 FCI 分析数万条病历,发现某药物与康复之间出现双向边,提示存在未观测共同原因。进一步结合基因数据后,才确认真正的因果路径,避免了一次错误药效宣传。
推荐系统的反事实纠偏
阶段:推荐因果建模
解决的问题:用户点击商品并不等于用户喜欢商品,因为“用户当前活跃度”这类时间因素同时影响点击和购买。FCI 可以帮助判断点击与购买之间是否存在隐藏混杂。
具体案例:电商平台用 FCI 学习用户行为 PAG,发现“点击”与“收藏”间存在双向边,说明有未观测的活跃度在起作用;随后用反事实框架调整推荐权重,离线评估的转化率预测更稳。
Python 演示
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())
def generate_data生成“高温 U”同时影响 X、Y,且 X 真正影响 Z 的模拟数据。def _ci用残差化计算偏相关,再经 Fisher Z 判断条件独立。self.ci_count += 1统计条件独立检验次数,体现 FCI 对测试次数的关注。for size in range(0, 2)依次尝试空条件集和单变量条件集,寻找分离集。if not sep:找不到任何分离集时,边保留下来。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 最主要提升了什么能力?
- A.处理隐藏混杂← 正确答案
- B.加快运行速度
- C.绘制彩色图表
- D.无需任何数据
答案:A。FCI 的核心改进是放宽“因果充分性”假设,允许未观测混杂存在。速度提升只是附带特性,绘制图表和无需数据都不是算法本质。
Q2. 下列哪一项是 FCI 输出的图结构?
- A.精确有向无环图
- B.部分祖先图PAG← 正确答案
- C.朴素贝叶斯图
- D.知识图谱结构
答案:B。FCI 输出部分祖先图 PAG,用尾巴、箭头和圆圈标记表示多个可能的 DAG 的共同特征,而不是单个完整 DAG。
Q3. 在 PAG 中,X↔Y 通常表示什么?
- A.确实存在潜在混杂← 正确答案
- B.X一定是Y的祖先
- C.Y一定是X的祖先
- D.没有任何统计关联
答案:A。双向边表示两个方向都是箭头,说明 X、Y 都不是对方的祖先,同时存在未观测混杂或选择偏差。它不是祖先关系,也不是无关联。
Q4. 条件独立性检验中,Fisher Z 用来做什么?
- A.判断偏相关是否为零← 正确答案
- B.计算因果效应大小
- C.估计所需样本量
- D.绘制置信区间图
答案:A。Fisher Z 把偏相关系数转换为近似正态的 z 分数,再通过与阈值比较,判断给定条件集后偏相关是否显著不为零。
Q5. FCI 骨架学习中,如何删除两点之间的边?
- A.找到某条件独立集← 正确答案
- B.计算两两相关系数
- C.使用领域专家指定
- D.按变量出现顺序删
答案:A。只要找到某个条件集使两点条件独立,就可以删除这条边。计算相关系数只是基础,方向删除则是算法自动完成。
Q6. 关于 PAG 中的圆圈标记,正确的是?
- A.表示方向尚未确定← 正确答案
- B.表示存在确定因果
- C.表示该边完全不存在
- D.表示变量取值有缺失
答案:A。圆圈标记对应“未知”端。它不代表边不存在,也不代表数据缺失,而是说明该端是尾巴还是箭头无法确定。
Q7. 关于 FCI 输出 PAG 的说法,正确的是?
- A.一个 PAG 对应一组 DAG← 正确答案
- B.一个 DAG 对应一组 PAG
- C.PAG 中不含任何箭头
- D.双向边表示确定因果
答案:A。PAG 是一族 DAG 的共同特征集合,因此一个 PAG 对应多个可能的 DAG。双向边不表示确定因果,而是表示潜在混杂。
Q8. 下列哪些情况可能使 FCI 产生双向边?
- A.存在未观测的共同原因← 正确答案
- B.存在选择偏差的机制← 正确答案
- C.样本量不够多
- D.变量之间的时间顺序
答案:AB。双向边在 PAG 中主要标记未观测混杂或选择偏差。样本量不足可能造成错误边,但不会在因果语义上产生“双向边”这个标记;时间顺序是辅助定向信息。
Q9. FCI 应用时需要满足哪些假设?
- A.分布满足忠实性← 正确答案
- B.满足因果马尔可夫性← 正确答案
- C.所有变量都必须可观测
- D.样本满足独立同分布← 正确答案
答案:ABD。FCI 仍然需要忠实性和因果马尔可夫性,也需要样本独立同分布。它放宽的是“所有共同原因都可观测”,所以 C 错误。
Q10. 关于 PAG 边标记的解读,下列正确的是?
- A.箭头端表示不是祖先← 正确答案
- B.尾巴端表示是祖先← 正确答案
- C.圆圈端表示未知← 正确答案
- D.双向边代表可确定干预
答案:ABC。PAG 的端标记语义是:尾巴=是祖先,箭头=不是祖先,圆圈=未知。双向边只提示潜在混杂风险,不能据此确定干预效应,因此 D 错误。