阿芳私房菜馆的灶台边,贴着一张泛黄的卡片:麻辣鸡丁 = 2×鸡腿新鲜度 + 0.5×花椒勺数 + 掌勺手气。客人觉得做菜是玄学,阿芳却信一个死理——只要食材一样、调料一样、手气也一样,出锅的菜就一模一样。这张卡片上写的,就是“结构赋值函数”:它白纸黑字地规定,结果(菜的味道)如何由原因(食材)、条件(调料)和随机扰动(手气)算出来。 因果推断里的结构因果模型(SCM),正是把整个世界想象成一间这样的厨房。每个变量都有一张配方卡,告诉你它的“父母”变量和外生噪声如何生成它。配方可以是线性的,像“2×鸡腿新鲜度”;也可以是非线性的,像“火候的平方”;还可以是离散的,像“超过某温度就出锅”。最妙的是,只要把所有手气值固定,整个厨房的菜就全被唯一确定——正因为这种确定性,我们才能掰着指头算出“如果当初……会怎样”。
学会结构赋值函数,你就掌握了把“因果假设”写成“可计算配方”的能力。这是从事政策评估、医学因果推断、商业策略模拟的 AI 从业者必备的底层思维工具。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
函数定义:一张配方卡
阿芳的私房菜馆灶台边,贴着一张泛黄的卡片:“麻婆豆腐的嫩滑分 = 豆腐含水量 + 0.3×豆瓣酱勺数 + 手气抖动。” 这张卡片规定了因果的“资格”:哪个变量当父亲,哪个变量当孩子,孩子如何被算出来。因果推断中的结构赋值函数,就是给每个内生变量发一张这样的配方卡。
在结构因果模型(SCM)中,每个内生变量 V_i 都拥有一张配方卡,写作:
PA_i 是 V_i 的父节点集合——那些直接影响 V_i 的变量;U_i 是这个变量的外生噪声,代表一切我们看不见的随机与意外;f_i 则是那张配方上白纸黑字的规则。它决定“给定这些原因和意外,结果到底是多少”。
说完这四行,阿芳把卡片翻过来——背面还有一行小字:“先备料,再调味,最后凭手感。” 这恰是赋值函数的工作顺序:父节点与外生噪声先就位,配方再出手,孩子变量应声落地。
- 内生变量 V_i模型中需要解释的结果变量,比如菜品嫩滑分。它的取值由结构决定、被结构赋值函数“算”出来。
- 结构赋值函数 f_i一张确定性的配方规则,把输入映射为唯一的输出。
- 父节点集合 PA_i直接决定 V_i 的原因变量们,相当于这道菜身后的“原料们”。
- 外生噪声 U_i模型之外、不可观测的随机因素,相当于“手气”。没有它,模型无法解释结果的多变性。
函数形式:清蒸、爆炒还是切块
阿芳的菜单从不拘一格:清蒸鱼讲究线性——鱼的新鲜度每涨一分,嫩滑度稳定涨两分;分子料理则非线性——温度超过临界点,口感骤然变化;凉拌菜的“切块”是离散映射——黄瓜大于某个码就切丝,否则切片,没有中间地带。结构赋值函数的写法,决定了因果的强度与方向。
最基础的是线性形式:
β 的符号告诉你方向(正还是负),绝对值告诉你强度(拨动一格 X,Y 动几格)。非线性形式则更贴近现实:
交互项 X1×X2 表示两个原因的搭配效应——比如“好食材×好火候”才能出神作。离散映射用阈值切分:
X 超过阈值 τ,结果就跳成 1,否则是 0。离散函数在决策场景中很常见:是否录取、是否开闸。
选哪种写法?答案是“看机制”。因果关系的形状由现实决定,配方只是把它如实写下来。阿芳不会为了显得高级而做分子料理——她只选能还原食材本真的做法。
- 线性系数 β拨动单位 X 时 Y 的变化量,代表因果的强度与方向。
- 非线性函数 σ(·)如 sigmoid、指数、对数等扭曲映射,刻画复杂机制。
- 交互项 X1X2两个原因的协同效应,反映“搭配”的力量。
- 阈值 τ离散跳变的开关点,超过则取一,否则取零。
确定性机制:同样手气,同样菜
每隔一段时间,阿芳都会做一道“实验菜”:用完全一样的鸡、一样的调料、闭上眼数三秒再揭锅。她说,只要手气一样,这道菜就该是一模一样的。这就是结构赋值函数的确定性:机制本身不掷骰子,随机性全部被赶进外生变量 U 里。
数学上,确定性写成:
给定了父节点 pa_i 和噪声 u_i,v_i 就被唯一钉死。此性质是整个 SCM 的“地基”:正因为函数是确定的,反事实推理才有意义——否则“如果换了 X”就无从算起。变量间的随机起伏全部来自 U 的分布,而不是函数本身。
阿芳常说:“配方不说谎,说谎的是手气。” 这句话揭开了因果模型中概率论的底牌:不确定性不是机制失效,而是手气的分布尚未摸清。
- 前提假设父节点与外生噪声都已知且固定。
- 确定性推演一步代入,结果唯一。
- 随机性归属一切概率都从 U 的分布来,函数本身不掷骰子。
因果效应计算:算一笔“如果”的账
阿芳最常被追问的问题:“要是上次用了土鸡,这道菜会不会更好吃?” 这是反事实问题,也是因果效应计算的核心。把“用土鸡”设为干预 do(X=新值),结构赋值函数就有大用:删掉 X 原本的配方,直接把它固定成干预值,再让其他变量按原配方运转,就能推演“如果”世界。
因果效应的标准表达式:
这个式子把“干预后的期望”拆成所有手气状态下结果的加权平均。实际做干预时,只需将 X 的赋值函数替换为常数 x,其余结构不动,生成的数据就是反事实样本。
回到厨房:阿芳用土鸡(X=10)和冷冻鸡(X=4)各做一遍,其他调料与手气完全一样,两次美味分之差,就是“换土鸡”的因果效应。SCM 把这套手写实验搬进了数学世界。
- do(X=x)外部强制介入,替换 X 的生成规则,删除 X 原配方。
- 条件期望固定 X 和 U 时 Y 的平均水平。
- 噪声加权按 U 的真实分布加权求和,得到干预后的期望。
应用场景:配方救了一个餐饮集团
阿芳的厨房手册被一家连锁餐饮集团收购后,奇迹发生了:新店厨师只要按配方执行,菜品分几乎可精确复现。结构赋值函数的现实价值正是如此——把“机制”变成可计算的资产。在真实世界,它帮三类人下结论。
1. 政策评估:政府想知道“补贴 3000 元对再就业率的影响”。传统上只能观察补贴者的现状,混杂因素太多;有了赋值函数,可以模拟 do(补贴=3000) 与 do(补贴=0) 的差值,得到干净的因果效应。
2. 医学研究:一位患者不可能同时吃药和吃安慰剂。用结构赋值函数给每个患者造一个“反事实的自己”,回答“如果当年用了新药”。
3. 商业决策:电商平台想优化广告预算。广告费、曝光量、销量之间各有一张配方卡,干预模拟不同预算分配,选出最优投放方案。
这三个场景共享同一套逻辑:与其等待自然数据暴露出因果,不如把机制显式写成函数,直接“做实验”。阿芳的配方让连锁店告别了“靠运气开店”的赌博。
- 处理组期望把所有对象都强制置于 X=1 时的平均结果。
- 对照组期望把所有对象都强制置于 X=0 时的平均结果。
- 平均因果效应 ATE两侧相减,获得干预的净效果。
SCM整体框架:一本完整的厨房手册
把阿芳的私房菜馆放大,整间厨房就是一部结构因果模型:墙上贴着菜谱图(因果图 G),说明谁传给谁;抽屉里放着“手气骰子”(外生变量 U);每个灶台前挂着一张配方卡(结构赋值函数 F);每一道出锅的菜(内生变量 V)都由前面三者决定。SCM 的三元组写法:
其中 U 是外生变量的集合——源头噪声,不被模型解释;V 是内生变量的集合——模型要解释的对象;F 是结构赋值函数的集合——把 U 和 V 按图索骥连接起来。因果图 G 则是 V 上父子关系的可视化骨架。
阿芳把手册摊开:菜谱图、配料表、手气骰子、配方卡,四样东西齐了,这家菜馆的一切风味都能被复算。SCM 亦然——有了三元组,你拥有的不再是一堆相关数据,而是一台可以反复“重开世界”的模拟器。
- 外生变量 U模型中“最后一层”的随机扰动,是手气的总集合。
- 内生变量 V被其他变量解释的结点,是模型的待求解对象。
- 赋值函数集 F所有结构方程的集合,每一条都指向一个内生变量。
- 三元组模型 MSCM 的完整定义,配上因果图 G 形成全貌。
AI 应用场景
政策评估中的因果效应估计
阶段:决策前干预模拟(因果效应估计)
解决的问题:传统政策评估依赖试点实验或回归分析,试点成本高、周期长,回归混杂因素难以剥离。结构赋值函数让分析师写下“补贴→求职努力→再就业”的机制方程式,在代码中直接执行 do(补贴=3000) 与对照实验,数分钟内得到因果效应估计。
具体案例:某市人社局要评估就业补贴。数据分析团队构建 SCM:再就业 = f(补贴,求职努力,市场景气度,U),给定外生噪声分布后,模拟 10 万份“反事实居民”,比较补贴组与对照组的再就业率,最终报告引用“因果效应 +3.2 个百分点”。
医学研究中的反事实推演
阶段:个体化治疗估计(反事实推理)
解决的问题:个体患者无法同时经历“用药”和“不用药”。结构赋值函数允许为每位患者构造反事实世界,如实回答“如果用新药,这个患者的 5 年生存率是多少”。
具体案例:肿瘤科团队基于历史患者建立“剂量→肿瘤缩小率→生存期”的结构方程,对每位新患者输入其基线特征和外生噪声,模拟 do(用药=新方案) 与 do(用药=标准方案) 的生存期差值,辅助医生与患者共同决策。
商业决策中的预算优化
阶段:策略模拟与资源配置(干预优化)
解决的问题:广告预算与销量之间存在多重混杂(季节、品牌、竞争对手),普通回归无法区分“广告带销量”还是“旺季带销量”。结构赋值函数把因果关系写成显式方程,干预模拟不同预算分配。
具体案例:某电商平台在大促前为“广告费→曝光→点击→销量”建立 SCM,模拟 do(预算=各方案) 的总销量,选出边际收益最高的分配方案,将营销 ROI 提升 18%。
Python 演示
import numpy as np class StructuralNode: """结构赋值函数节点: 描述父节点与外生噪声如何生成内生变量""" def __init__(self, name, parents, function): self.name = name self.parents = parents self.function = function def compute(self, parent_values, noise): return self.function(parent_values, noise) def build_salary_scm(): """构建一个薪资 SCM, 能力 Ability 影响学历 Education 与薪资 Salary""" def f_ability(parents, u): return u def f_education(parents, u): return 1.5 * parents['Ability'] + u def f_salary(parents, u): return 2.0 * parents['Education'] + 0.5 * parents['Ability'] + u nodes = { 'Ability': StructuralNode('Ability', [], f_ability), 'Education': StructuralNode('Education', ['Ability'], f_education), 'Salary': StructuralNode('Salary', ['Education', 'Ability'], f_salary), } return nodes def simulate(scm, interventions=None, n_samples=1000, seed=42): """按结构赋值函数生成数据, interventions 指定 do() 干预""" rng = np.random.default_rng(seed) records = [] for _ in range(n_samples): values = {} for name, node in scm.items(): if interventions and name in interventions: values[name] = interventions[name] else: parent_values = {p: values[p] for p in node.parents} noise = rng.normal(0, 0.3) values[name] = node.compute(parent_values, noise) records.append(values) return records def main(): scm = build_salary_scm() baseline = simulate(scm, interventions={'Education': 1.0}) treated = simulate(scm, interventions={'Education': 2.0}) mean_base = np.mean([r['Salary'] for r in baseline]) mean_treat = np.mean([r['Salary'] for r in treated]) effect = mean_treat - mean_base print(f"do(Education=1) 平均薪资: {mean_base:.2f}") print(f"do(Education=2) 平均薪资: {mean_treat:.2f}") print(f"教育每提升 1 的因果效应: {effect:.2f}") if __name__ == '__main__': main()
class StructuralNode:用一个小类封装配方卡:节点名、父节点名单、赋值函数def compute(self, parent_values, noise):执行配方:传入父亲们的值和手气,算出孩子f_education(parents, u):学历的结构赋值函数:由能力和噪声决定f_salary(parents, u):薪资的结构赋值函数:由学历、能力和噪声共同决定simulate(scm, interventions=None)沿拓扑序逐个节点算值,支持 do 干预替换if interventions and name in interventions:干预时直接固定节点值,跳过其原生配方,等价于删除赋值函数rng.normal(0, 0.3)每次为节点抽取手气,体现外生噪声的分布effect = mean_treat - mean_base两个干预世界平均薪资之差,就是因果效应
常见误区
误区我理解了,结构赋值函数就是把数据喂给线性回归跑出来的方程,找到 Y = βX + ε 就找到因果了。
正解两者形似而神异。回归方程描述“看到 X 时 Y 平均是多少”,是从数据中总结出的相关模式;结构赋值函数描述“如果把 X 拨到一个值,Y 会怎样”,是研究者依据机制写下的因果法则。以教育→薪资为例,回归只能告诉你“学历高的人平均薪资高”,而结构赋值函数可以告诉你“让一个人多读一年书,他的薪资增加多少”。前者可能被能力等混杂因素扭曲,后者因为显式写出了机制和噪声,可以支持干预推演。
为何常见:因为线性回归方程 Y = βX + ε 与线性结构赋值函数 Y = βX + U 长得太像,初学者很容易把“拟合”当成“因果关系的确立”。
误区既然函数是确定性的,外生变量 U 只是扰动项,为了简化直接去掉,写成 Y = f(X) 就够了。
正解U 是“唯一确定”的必要组成部分。即使 f 确定,若不知道 U,Y 仍不可知。U 的分布还承担着解释观测随机性的职责:为什么相同 X 下 Y 会波动?答案就在 U。去掉 U,模型会误把所有变异都归因于 X,因果效应估计将系统偏离。就像阿芳的配方可以确定“手气固定则菜固定”,但若不知道手气是多少,菜还是算不出来。
为何常见:因为公式视觉上 U 长得像“误差项”,人们下意识觉得它不重要、可以省略。
误区因果模型就是线性的 Y = βX + U,非线性只是花哨包装,实际不重要。
正解结构赋值函数可以是任意函数形式,非线性反而是常态。阈值效应(超过某剂量才有疗效)、交互效应(药物 A 和 B 合用时效果倍增)都无法用线性表达。函数形式的选择应服从机制:现实是什么形状,配方就写什么形状。阿芳做分子料理时,口感对温度就是非线性响应,硬用直线写配方必翻车。
为何常见:教科书里大量用线性作例子,让人误以为线性是 SCM 的默认设定。
误区do(X=2) 不就是把数据库中 X=2 的样本筛出来分析吗?反正 X 的观测值都是 2。
正解观察筛选保留了 X 与混杂因素(如能力、背景)的天然关联;干预 do(X=2) 则会删除 X 的原生赋值函数,阻断所有指向 X 的因果箭头,再固定为 2。两者在面对混杂时结论完全不同。“看到高学历的人薪资高”受能力扭曲,“让所有人学历变高”则消除了这种扭曲。
为何常见:直觉上“看到 X=2”和“让 X=2”似乎没有区别,但混杂的存在让二者分道扬镳。
误区结构赋值函数说给定外生变量就完全确定内生变量,那世界岂不是没有任何随机性?
正解确定性是“机制层面”的:给定 U 和 PA,结果唯一。但观测层面我们永远无法精确知道 U 的值,所以 Y 在观测者看来仍是随机的。打个比方:阿芳知道“手气固定则菜固定”,但她并不知道下一秒手气是多少,所以客人尝到的菜依然千变万化。SCM 的随机性不是来自函数掷骰子,而是来自 U 的未知分布。
为何常见:把“机制确定”与“观测随机”两个层次混为一谈,是初学者最容易踩的思维陷阱。
面试题
Q1. 结构赋值函数在 SCM 中的作用是?
- A.统计变量间相关系数
- B.刻画因果生成机制← 正确答案
- C.替代数据采集过程
- D.消除所有随机噪声
答案:B。结构赋值函数描述父节点和外生噪声如何生成内生变量,是因果机制的规则本身。A 是统计相关工作,C 与数据采集无关,D 夸大其词——噪声并未被消除,而是被显式建模。
Q2. 结构赋值函数的输入通常是?
- A.父节点和外生噪声← 正确答案
- B.子节点和局部噪声
- C.全部节点和系统值
- D.常数项和误差向量
答案:A。V_i = f_i(PA_i, U_i),输入是父节点集合 PA_i 与外生噪声 U_i。B 方向反了,孩子是输出不是输入;C 范围太大,D 把父节点偷换成常数项。
Q3. 哪一项属于离散结构赋值函数?
- A.线性乘积加噪声项
- B.指数函数缩放宽幅
- C.超过阈值取值变为一← 正确答案
- D.对数变换再乘系数
答案:C。离散映射通过阈值切分输出,如 Y = 1[X ≥ τ],超过阈值跳为 1。A、B、D 都是连续的线性或非线性形式,不会产生离散跳变。
Q4. 给定外生变量值后内生变量将?
- A.唯一确定不再随机← 正确答案
- B.依赖样本数量变化
- C.与父节点相互影响
- D.需要工具变量估计
答案:A。确定性机制保证给定 U 和 PA 后 V 唯一确定。样本量只影响统计估计精度,不影响机制取值;C 描述的是因果图的边,不是求解过程;D 是关联数据下的估计手段。
Q5. 干预 do(X=2) 的准确含义?
- A.观察自然情况的样本
- B.固定 X 值并删配方← 正确答案
- C.调整 X 外生噪声值
- D.修改全部函数参数
答案:B。do 操作删除 X 的结构赋值函数,把 X 固定为常数 2,阻断指向 X 的因果箭头。A 是观察而非干预,C 仍保留 X 的机制,D 范围过大且错误。
Q6. 因果效应 E[Y|do(X=x)] 的求法依赖?
- A.按手气分布加权平均← 正确答案
- B.直接观察历史数据
- C.提高样本数量即可
- D.完全回避模型假设
答案:A。干预期望通过 E[Y|do(X=x)] = Σ_u E[Y|X=x, U=u]·P(U=u) 计算,依赖外生噪声的分布加权。观察数据受混杂扭曲,样本量无法替代机制假设。
Q7. 结构赋值函数的确定性意味着?
- A.世界完全没有随机性
- B.机制不再掷出骰子← 正确答案
- C.外生变量不很重要
- D.样本统计失去意义
答案:B。确定性指的是给定 U 和 PA 后结果唯一,即函数本身不引入随机。观测随机性仍来自 U 的分布,所以 A 错、C 错;确定性恰恰让反事实计算有意义,D 错。
Q8. 结构赋值函数的常见形式有哪些?
- A.线性表达因果强度← 正确答案
- B.非线性刻画复杂机制← 正确答案
- C.离散映射设置阈值← 正确答案
- D.纯相关矩阵做统计
答案:ABC。线性(Y=βX+U)、非线性(σ、交互项)、离散(阈值映射)都是合法的赋值函数形式。D 是统计工具,不构成生成机制的结构方程,故排除。
Q9. 结构赋值函数支持因果推断的原因是?
- A.可以模拟干预后的世界← 正确答案
- B.可以构造反事实样本← 正确答案
- C.可以描述机制的方向← 正确答案
- D.可以把噪声完全清零
答案:ABC。确定性函数让 do 干预可执行、反事实世界可重演,且函数形式明确了因果方向与强度。D 错误——噪声没有被清零,只是被显式写进模型并参与加权。
Q10. SCM 三元组 (U,V,F) 中 F 代表?
- A.观测到的数据集合
- B.结构赋值函数集合← 正确答案
- C.外生变量的集合
- D.内生变量映射矩阵
答案:B。F = {f₁, f₂, …, fₙ} 是所有结构赋值函数的集合。A 是数据,C 是 U,D 把 F 误解为单纯的矩阵,忽略了函数的机制语义。