因果感知与竞争性SCM:机器学习公平性的工程化落地
2026/9/21 7:37:44 网站建设 项目流程

如果你在一个风控或招聘算法项目里做过公平性改造,大概率见过这个场景:合规团队看了一眼模型,说“性别特征删掉吧”。你删了,重新训练,AUC 几乎不变,然后你把结果发回去。对方看完却回复:“预测分数在男女群体上的分布差异还是很大,你确认公平了?”

更麻烦的是,当你顺着结果去排查,会发现一个此前被忽略的问题——你根本说不清这些差异到底来自哪条因果路径。教育背景是性别和结果之间的中介变量,还是压跟无关的并行因素?模型是用教育背景“间接复现”了性别信息,还是面试流程本身存在偏好?两种解释对应完全不同的公平性结论,而数据本身往往无法替你回答。

这正是“Causal Perception(因果感知)+ 竞争性 SCM + 情境化公平(Situated Fairness)”要解决的问题。我的核心判断是:公平性不能只在预测层打补丁,必须上升到因果层建模;而因果层本身就存在多个模型“竞争”的不确定性,最终只能靠情境化公平来完成裁决。

读完这篇文章,你会理解:为什么公平性必须建立在因果结构上,而不是相关性上;同一份数据为什么会同时支持多个互相矛盾的因果模型;以及如何把“情境化公平”这种听起来很理论的概念,落地成可操作、可评审、可监控的工程流程。

1. 这篇文章真正要解决的问题

1.1 三个常见误区

第一个误区:删掉敏感特征就等于消除歧视。只要数据里存在与敏感属性强相关的代理变量,模型就会用代理变量“暗度陈仓”。这是公平性项目里最常被纠正的认知。

第二个误区:想办法让两个群体的预测均值相等,就完成了公平。现实中公平性远比“统计均等”复杂。医疗分诊里承认群体风险差异是合理的,招聘初筛里让群体间结果完全均等却可能损害效率。同一个指标,换个场景意义完全不同。

第三个误区:一张因果图就是真相。很多团队做完因果图,就直接对着图算效应、写审计报告。但观测数据对因果图的辨识能力是有限的,多张结构不同的图可能对数据拟合得同样好。这样的图如果恰好画错了,后面的公平性结论全部站不住。

1.2 读者画像与阅读收益

这篇文章适合以下读者:

  • 算法工程师:正在处理模型公平性、偏差审计,想知道怎么区分“直接歧视路径”和“间接影响路径”。
  • AI 产品经理与合规成员:需要一种比“删特征”更严谨的公平性评估框架。
  • 风控、招聘、医疗 AI 等领域的从业者:业务决策会对不同群体产生不同影响,需要结构性分析手段。
  • 研究公平性与因果推断的学生:想理解 SCM 理论在真实工程中的定位与局限。

读完本文,你的收获不是“多学一个库”,而是掌握一套从数据到模型选择、再到公平性裁决的完整方法论。

2. 三个关键词:因果感知、SCM 与情境化公平

2.1 因果感知不是“更聪明的预测”

因果感知的源头在认知科学。心理学家发现,人类观察两个台球碰撞时,并不是先感知“球 A 在动、球 B 随后动”,再推断“A 导致 B”;而是在知觉层面直接就“看到”了因果关系。这种自动、快速、几乎不可抑制的因果体验,就是因果感知。

在 AI 工程里借用这个词,我的理解是:我们希望机器学习系统不只学习特征之间的相关性,而是在表示层面显式编码因果结构。通俗地说,模型回答的不只是“看到 X 升高,Y 会怎样”的关联问题,而是“如果我们主动干预 X,Y 会变成什么”的干预问题。

因果感知不是某个开源工具的名字,而是一种建模视角。落到工程上,它的最小实现单位就是结构因果模型(SCM)。

2.2 SCM:公平性审计的数学骨架

结构因果模型(Structural Causal Model,SCM)由 Judea Pearl 系统提出。一个 SCM 包含三部分:

  • 一组外生变量(未解释的噪声项)和内生变量(模型中要被解释的变量);
  • 一组结构方程,描述每个内生变量如何由其父节点和外生变量决定;
  • 外生变量的联合概率分布。

SCM 通常用有向无环图(DAG)可视化,箭头指向表示“因果方向”。它在公平性中的价值,是把“歧视”这种模糊的社会概念翻译成精确的因果量。比如“性别导致结果差异”可以进一步拆分:

  • 总效应(Total Effect):敏感属性对结果的整体影响;
  • 直接效应(Direct Effect):不经过任何中介变量,敏感属性对结果的直接影响;
  • 间接效应(Indirect Effect):通过中介变量传导的影响。

直接效应更接近“面试官偏好”这类歧视证据,间接效应则对应“教育背景差异导致结果差异”这类背景性影响。SCM 给了我们一个把这两种效应干净拆开的数学框架。

2.3 情境化公平:从公式回到场景

传统公平性研究喜欢定义普适公式,比如 demographic parity 要求两个群体获得正向结果的概率相同,equalized odds 要求模型误差在不同群体间一致。这些指标计算简单,但现实效果常常不尽人意。

情境化公平的核心观点是:公平不是脱离场景的抽象量,而是在具体决策情境、具体利益相关者和具体社会后果中被临时定义出来的。同一个差异,在贷款场景可能是红线,在医疗分诊场景可能反而是合理的风险分层。

工程后果是:公平性审计不该以“跑出一张指标表”结束,而要在模型开发之前就定义清楚——谁来决策、影响谁、哪些因果路径是被允许的、哪些是不被允许的。这些约束,最终会反过来参与因果模型的选择。

为了便于理解,把三个概念先放在一张表里:

概念回答的问题在本文中的角色
因果感知系统如何理解因果关系?目标层:让公平性判断基于因果结构
SCM用什么形式化表示因果结构?建模层:搭建竞争性候选模型
情境化公平多模型冲突时听谁的?决策层:用场景与利益相关者约束裁决

3. 为什么公平性必须走到因果层:一个招聘筛选案例

3.1 传统做法为什么失效

假设一个招聘初筛场景,我们有三个关键变量:

  • G:敏感属性,代表性别等受保护属性;
  • E:教育背景,例如受教育年限;
  • Y:是否通过面试。

传统公平性改造通常是三步:删掉 G;重新训练模型;比较模型预测结果在两个群体上的分布差异。如果差异变小就宣布“公平”,如果差异没变小就继续调阈值。

问题出在第二步和第三步之间。如果 G 和 E 强相关,删掉 G 之后,模型照样能从 E 中学到足够多的“群体信息”。你看到的分布差异,既可能来自面试官对特定群体的直接偏好(G 直接影响 Y),也可能来自两个群体在教育背景上的结构性差异(G 通过 E 影响 Y)。

在预测层做任何操作,都无法区分这两条路径。而这恰恰是公平性判断最需要的信息。

3.2 从预测层上升到因果层

用因果语言重新描述上面场景。候选因果图有两种常见版本:

  • 路径一:G → E → Y,性别只通过教育背景影响结果;
  • 路径二:G → E → Y 且 G → Y,性别在影响教育背景的同时,还直接作用于面试结果。

在路径一只剩教育路径的情况下,如果教育机会本身是公平的,那么结果差异更多来自背景差异,招聘流程本身未必需要整改。在路径二存在的情况下,直接效应就是“歧视路径”的量化证据,需要在流程层面干预。

这就是从“预测层公平”到“因果层公平”的关键跃迁:先问清楚差异来自哪条路径,再决定要不要改。没有因果模型,你连“差异是什么”都说不清。

4. 竞争性 SCM:同一份数据,多个因果解释

4.1 什么是竞争性 SCM

竞争性 SCM,指变量集合相同但因果边集合不同的多个结构因果模型,都能对同一份观测数据给出合理的拟合。它们对“敏感属性如何影响结果”的解释互相冲突,但都是数据上的“合法候选”。

这不是理论上的边缘情况。在观测数据中,X→Y 和 X←Y 这两种方向在特定条件下会产生等价的分布特征,没有额外实验或领域知识就无法区分。更不用说真实业务里还存在未观测混杂因素、测量误差和样本选择偏差。

换句话说,因果模型不是“画完就结束”,而是“画完才开始”。工程上要做的是同时保留多个候选模型,而不是只相信一个。

4.2 竞争模型导致公平性结论冲突

回到招聘案例。SCM-A 认为 G 不直接影响 Y,SCM-B 认为 G 直接指向 Y。假设数据来自 SCM-B 的真实世界,但你只按 SCM-A 建模,直接效应会被漏掉,你会得出“流程不存在直接歧视”的错误结论。

反过来,如果数据实际来自 SCM-A,你却强行按 SCM-B 估计,就会把原本由中介路径解释的差异误判为歧视证据,导致业务方被迫修改本来没有问题的流程。

公平性问题在这里有了新的复杂性:不仅需要估计效应,还需要报告“模型之间的不确定性”。

4.3 直接效应、间接效应与总效应

为了评估竞争 SCM,需要先统一度量。以招聘案例为例,把 G 对 Y 的总效应写成 TE:

  • TE = 总效应,即 G 变化单位时 Y 的平均变化;
  • DE = 直接效应,即控制 E 之后,G 对 Y 的剩余影响;
  • IE = 间接效应,即通过 E 传导的那部分影响;TE = DE + IE(在线性模型中成立)。

工程上,直接效应近似等于“把 E 加入回归后 G 的系数”,间接效应等于“总效应减直接效应”。注意这是线性近似,严谨的因果推断需要借助匹配、工具变量或 do-演算,但分解思路是一样的。

有了效应分解,竞争 SCM 之间的差异就变成可比较的数字:如果 DE 不显著,SCM-A 更可信;如果 DE 显著,SCM-B 更可信。下一步,再由情境化公平来判断哪些路径允许存在。

5. 环境准备与核心流程拆解

5.1 环境与依赖

本文代码以 Python 演示,环境建议 Python 3.8 以上,暂无特殊版本要求。核心依赖如下:

pip install pandas numpy networkx matplotlib statsmodels

如果希望做更严格的因果识别,可以额外安装:

pip install dowhy

DoWhy 在本文不是必需项,核心逻辑用 statsmodels 的线性模型即可说清楚。公司内网无法安装第三方库时,pandas 与 numpy 已经能完成大部分效应分解计算。

5.2 六步核心流程

从一份观测数据到情境化公平判断,我建议按下面六步推进:

第一步:定义决策场景与利益相关者。明确谁在什么场景下做决策,决策影响谁。这一步输出的是“公平性评审会议”的参会名单。

第二步:梳理变量与因果假设。列出所有相关变量,画第一版 DAG。明确哪些是敏感属性、哪些是中介变量、哪些是混杂因子。

第三步:构建候选 SCM 集合。至少构建“无直接效应”和“有直接效应”两版模型,不要只画一张图。

第四步:估计因果效应。对每个候选 SCM,估计总效应、直接效应和间接效应。

第五步:计算公平性指标。将效应分解映射到公平性指标,例如群体通过率差异、直接效应显著性。

第六步:情境化裁决与持续监控。让领域专家和利益相关者参与选择允许路径,并把结论记录到模型卡。

5.3 流程中的常见错误

常见错误之一是:在第二步就把中介变量当混杂因子控制。比如为了估计 G 对 Y 的直接效应,把 E 强行当作混杂因子放入回归,这是对的;但如果目标是估计总效应,就不能控制 E,否则会阻塞 G 到 Y 的间接路径。

另一个错误是:第三步只画一张“看起来合理”的图,然后按一个模型出结论。竞争性 SCM 的要点就是承认存在多个合理模型,并显式比较它们。

6. 完整示例与代码实现

6.1 生成演示数据

我们用模拟数据解释实现思路。真实业务里的数据往往更复杂,但代码逻辑是通用的。

import numpy as np import pandas as pd np.random.seed(42) n = 2000 # 敏感属性:0 与 1 代表两个受保护群体 G = np.random.binomial(1, 0.5, n) # 教育背景:与 G 存在相关 E = 12.0 + 1.2 * G + np.random.normal(0, 1.5, n) # 面试分数:E 影响较大,G 存在一点直接效应 Y_score = 0.15 * G + 0.45 * (E - 12.0) + np.random.normal(0, 1.0, n) Y = (Y_score > np.median(Y_score)).astype(int) df = pd.DataFrame({"G": G.astype(int), "E": np.round(E, 2), "Y": Y}) df.head()

这段代码生成 2000 条模拟记录。真实世界中 G 对 Y 存在 0.15 的直接效应,但模型和数据不会直接告诉你这一点,需要靠候选 SCM 比较来发现。

6.2 可视化两个竞争 SCM

用 NetworkX 把两个候选模型画出来。

import networkx as nx import matplotlib.pyplot as plt def draw_scm(edges, title, filename): g = nx.DiGraph() g.add_edges_from(edges) pos = {"G": (0, 1), "E": (1, 1), "Y": (0.5, 0)} nx.draw(g, pos, with_labels=True, node_color="#d0e6ff", node_size=1800, font_size=14, arrowsize=20) plt.title(title) plt.savefig(filename) plt.show() draw_scm([("G", "E"), ("E", "Y")], "SCM-A:G 不直接影响 Y", "scm_a.png") draw_scm([("G", "E"), ("G", "Y"), ("E", "Y")], "SCM-B:G 直接影响 Y", "scm_b.png")

SCM-A 是“无直接歧视”假设,SCM-B 是“存在直接歧视路径”假设。接下来用数据判断哪个更可信。

6.3 效应分解

用线性概率模型近似分解直接效应、间接效应和总效应。这里 Y 是二值变量,严格来说应使用 logit 模型或专门的因果推断方法,但线性近似足以演示分解逻辑。

import statsmodels.api as sm def total_effect(df): X = sm.add_constant(df[["G"]]) model = sm.OLS(df["Y"], X).fit() return model.params["G"] def direct_effect(df): X = sm.add_constant(df[["G", "E"]]) model = sm.OLS(df["Y"], X).fit() return model.params["G"] te = total_effect(df) de = direct_effect(df) ie = te - de print(f"总效应(TE): {te:.4f}") print(f"直接效应(DE): {de:.4f}") print(f"间接效应(IE,通过 E 中介): {ie:.4f}")

如果数据生成过程与真实世界一致,我们会看到直接效应为正。这意味着 SCM-B 中“G 直接指向 Y”的路径有数据支持,不能在审计时忽略。

6.4 公平性指标与情境化裁决

先计算最基本的群体通过率差异:

def demographic_parity(df, pred_col="Y", group_col="G"): rates = df.groupby(group_col)[pred_col].mean() return abs(rates[1] - rates[0]) dp = demographic_parity(df) print(f"群体通过率差异(Demographic Parity Gap): {dp:.4f}")

再模拟“只保留 E 到 Y 路径”时,如果不存在直接效应,通过率差异会下降多少:

df_no_direct = df.copy() X = sm.add_constant(df[["E"]]) model = sm.OLS(df["Y"], X).fit() df_no_direct["Y_pred"] = (model.predict(X) > np.mean(model.predict(X))).astype(int) dp_no_direct = demographic_parity(df_no_direct, pred_col="Y_pred") print(f"仅教育中介路径下的通过率差异: {dp_no_direct:.4f}")

最后,用一个函数演示情境化裁决的输出结构:

def situational_fairness_decision(te, de, context=None): if context is None: context = { "legal_direct_discrimination": True, "allow_indirect_path": True, "concern_group": "G" } print("=== 情境化公平评估 ===") print(f"总效应(TE): {te:.4f}") print(f"直接效应(DE): {de:.4f}") print(f"间接效应(IE): {te - de:.4f}") direct_discrimination_detected = abs(de) > 0.01 if context["legal_direct_discrimination"] and direct_discrimination_detected: print("结论:检测到直接效应,按当前情境应视为需要整改的歧视路径。") elif context["allow_indirect_path"]: print("结论:未发现显著直接效应;结果差异可由中介路径解释,情境允许。") else: print("结论:即使差异来自中介路径,在当前情境下也不被允许,需要进一步处理。")

调用:

situational_fairness_decision(te=te, de=de, context={ "legal_direct_discrimination": True, "allow_indirect_path": True, "concern_group": "G" })

这里 0.01 只是一个演示阈值。真实项目中,阈值应由领域专家结合业务影响、法规要求和群体后果共同确定,而不是拍脑袋写死。

7. 运行结果与效果验证

7.1 预期输出

基于上面设定的模拟参数,合理预期输出为:

  • 总效应(TE):约 0.12 到 0.20;
  • 直接效应(DE):0.05 左右,显著不等于 0;
  • 间接效应(IE):约 0.05 到 0.10;
  • 群体通过率差异:约 0.10 到 0.15;
  • 仅教育中介路径下的通过率差异:低于原始差异,但仍可能不为 0。

具体数值以实际运行为准。关键是判断趋势:DE 显著不等于 0,说明“G 直接指向 Y”这条路径不能忽略,SCM-B 比 SCM-A 更能解释数据。

7.2 Bootstrap 置信区间

单次效应估计不够可靠,建议做 Bootstrap 置信区间,判断 DE 是否稳定地不等于 0:

rng = np.random.default_rng(7) boot_de = [] for _ in range(500): idx = rng.integers(0, len(df), size=len(df)) d = df.iloc[idx] X = sm.add_constant(d[["G", "E"]]) m = sm.OLS(d["Y"], X).fit() boot_de.append(m.params["G"]) lower, upper = np.percentile(boot_de, [2.5, 97.5]) print(f"直接效应 Bootstrap 95% CI: ({lower:.4f}, {upper:.4f})")

如果 CI 不包含 0,可以更放心地认为存在直接效应。

7.3 敏感性分析思路

Bootstrap 只能处理抽样不确定性,处理不了“模型本身画错了”这种结构性不确定性。敏感性分析的思路是:假设存在一个未观测混杂变量 U,考察 U 对 G 和 Y 的影响强度在什么范围内会翻转 DE 的结论。

工程上可以做一个参数扫描:逐步增大 U 对 G 和 Y 的效应系数,观察 DE 的变化轨迹。如果 DE 在小扰动下就变号,说明结论脆弱,不应直接写进审计报告。

DoWhy 等库也提供敏感性分析方法,但这类分析的核心不是工具本身,而是对模型假设透明度的坚持。

8. 常见问题与排查思路

问题现象可能原因排查方式解决方案
数据无法区分两个竞争 SCM因果图存在马尔可夫等价查看模型等价类,寻找变量集合上的条件独立约束差异结合领域知识选择方向,或设计随机实验补充信息
直接效应估计结果与业务直觉不符中介变量与结果之间存在未观测混杂检查是否错误控制了对撞变量,审查 DAG 遗漏边补全 DAG,使用工具变量或敏感性分析验证
多个 SCM 拟合度接近,BIC/AIC 无法决策数据信息量不足以支撑因果方向识别查看效应估计的置信区间是否相互覆盖报告模型集合,不上调单一结论;引入干预数据
情境化公平被质疑太主观利益相关者价值诉求冲突记录各方意见和决策依据引入结构化算法影响评估,先试点再全量
没有领域专家参与建模组织缺少跨学科评审机制识别业务侧关键决策人先用探索性分析给出候选模型,再邀请专家评审
二值结果用线性回归近似线性概率模型可能超出 0-1 区间检查预测值是否越界改用 logit/probit 或专门因果推断库

9. 最佳实践与工程建议

第一,建模前先完成因果图评审。因果图不是算法工程师的自嗨产物,它会影响审计结论,必须由懂业务的专家和法务人员一起看。把评审记录留档,作为模型卡的一部分。

第二,把公平性指标绑定到因果路径,而不是只绑定预测值。不要只报告“群体通过率差异”,还要报告这个差异可以被哪个因果模型解释,以及 SCM 之间的不确定性有多高。

第三,保留候选模型集合。很多团队只训练一个 SCM,然后在上面做精细推断。真正稳妥的做法是同时保留“无直接效应”和“有直接效应”两个模型,对比它们的估计结果和稳定性。

第四,模型卡中记录以下信息:变量定义、因果假设、候选模型列表、效应估计与置信区间、敏感性分析结论、参与评审的人员。这份文档既要给算法团队看,也要给合规和业务团队看。

第五,敏感属性数据的获取和使用必须谨慎。涉及受保护属性的数据处理,应遵循数据最小化原则,在合法合规的前提下进行匿名化和去标识化。因果分析需要敏感属性参与建模,不代表可以把原始数据散落到各处。

第六,因果结论不要直接上线。未经敏感性分析的因果推断,只是模型假设下的推断。上线前至少要做小范围灰度,并设计好回滚机制。

第七,建立持续监控。公平性不是一次性指标。模型上线后模型分布会漂移,敏感属性与预测结果的关系也会变化,需要定期重算直接效应和群体差异指标。

10. 总结与后续学习方向

这篇文章真正想讲的是一件事:公平性不能靠删特征和调阈值解决,而要在因果结构上回答“差异从哪条路径来”;当多个 SCM 给出相互矛盾的因果解释时,数据本身未必能帮你站队,最终需要情境化公平来完成决策。

从工程角度看,你已经具备了一套可执行的框架:

  • 用 SCM 形式化因果假设;
  • 用效应分解区分直接路径和间接路径;
  • 用 Bootstrap 和敏感性分析评估结论稳健性;
  • 用情境化评审决定哪些路径允许存在;
  • 用模型卡和持续监控维持结论的生命力。

如果希望在因果推断方向上继续深入,建议从下面几个方向入手:

  • 安装 DoWhy 或 EconML,把一个真实业务表格跑成完整的因果推断流程;
  • 阅读 Pearl 的因果推断入门书,重点关注 do-演算、反事实与中介分析;
  • 找一次真实的公平性审计项目,试着从数据中构造多个候选 SCM,而不是直接采用别人的结论。

最后提醒:情境化公平意味着公平性永远是一个持续校准的过程,而不是一个可以一次性交付的算法属性。不要用“删掉敏感特征”这种表面动作消耗团队和业务方对算法治理的信任。因果模型也许不能给你完美的答案,但它至少能让你在说“这个模型公平”时,知道自己是在什么假设下说的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询