老周在城郊有片苹果园,今年引进了一款高价新型肥料。全园平均增产 8%,喜报都印好了,他却犹豫了——翻开记录本:新树增产 20%,老树毫无变化,还有几棵病树反而落叶。平均数字骗了他。在数据世界里,每个“人”都是一棵果树:投放广告、发优惠券、用新药,都像施肥。但不是每个人反应都一样。CausalTree 就是那个聪明的园丁:它反复问“在哪一刀切下去,施肥效果差异最大”,一层层把人切成小块,最终画出一张“施肥地图”——哪群人该重点干预,哪群人少碰为妙。这篇文章会演示:它凭什么判断“切得值不值得”(分裂准则),如何避免“看着答案划线”(诚实分裂),它与普通预测树的差别(对比 CART),最后收获一张能直接工作的子群地图。

岗位关联

掌握 CausalTree,能让你从“平均效果”的幻觉中走出来,专业地回答“干预到底对谁有效”——这是精准营销、个性化医疗等场景的核心竞争力。

目录
  1. 可视化演示
  2. 异质子群问题:同一种肥料,不同的反应
  3. 反事实与增量:一棵树的平行宇宙
  4. 递归划分思想:园丁的逐层切分
  5. 分裂准则设计:专挑效果差异最大的一刀
  6. 诚实分裂机制:试验田分开用
  7. CART 对比分析:看产量还是看增产
  8. 子群解释应用:收获一张施肥地图
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

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

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

异质子群问题:同一种肥料,不同的反应

老周施肥三个月后,产量数据出来了。他把每棵树的编号、品种、树龄、施肥量、产量记在小本子上,惊讶地发现:同样是 200 克肥料,早熟的“红玉”增产两成半,晚熟的“青冠”纹丝不动,还有三棵老树叶子发黄。全园平均增产 8%,但这 8% 就像把一碗热水和一碗凉水倒在一起——温度是温和的,可没有一碗水真正“温和”。他真正该问的是:到底哪批树该施肥?

这个问题在因果推断里叫处理效应异质性(Treatment Effect Heterogeneity):同一个干预(施肥、发券、用药),对不同个体效果不同。如果只知道总体的平均处理效应(ATE=8%),决策就是一刀切的“全园施肥”。可现实往往是:有人受益、有人无感、有人受损。要精准决策,就得把“平均”拆开,识别出效应不同的子群。

CausalTree 建模的目标是条件平均处理效应(CATE)——给定特征 $x$(品种、树龄、土壤)时,处理与对照的期望结果之差:

老周把这句话翻译成园丁语言:“我需要的不是'平均增产 8%'这张海报,而是一张'哪块地增产、哪块地减产'的明细账。”异质子群识别,就是这张明细账的第一页。

τ(x)=E[ Y(1)−Y(0)∣X=x ]\tau(x) = E[\,Y(1) - Y(0) \mid X = x\,]
这个式子的意思是:在特征完全相同的果树里,算“施肥产量 $Y(1)$”和“不施肥产量 $Y(0)$”之差的平均值,得到的就是这群树专属的施肥效果。
  1. 结果变量 $Y(1)$ 与 $Y(0)$处理与对照两种状态下的潜在结果,分别是“施肥后的产量”和“不施肥的产量”。
    Y(1), Y(0)Y(1),\ Y(0)
  2. 个体处理效应 $Y(1)-Y(0)$同一棵树两种结果的差,是理论上最理想的“个人效果”,可惜无法同时观测两路结果。
    Y(1)−Y(0)Y(1) - Y(0)
  3. 条件期望 $E[\cdot \mid X=x]$在特征值恰好等于 $x$ 的果树群中求平均,把随机波动消掉。
    E[  ⋅  ∣X=x]E[\;\cdot\;\mid X=x]
  4. CATE $\tau(x)$特征 $x$ 子群的平均处理效应,这正是 CausalTree 每个叶子要输出的值。
    τ(x)\tau(x)

反事实与增量:一棵树的平行宇宙

老周没法让同一棵红玉树“既施肥又不施肥”。他盯着施过肥的红玉树想:要是没施肥,这棵树能结多少?这问题没法直接回答——观测世界只能走一条路。但园丁知道:同品种、同树龄、同土壤的邻居树,没施肥的产量,差不多就是那个“平行世界”的答案。

这就是反事实推理:我们只能观测到 $Y(1)$ 或 $Y(0)$ 中的一路,另一路是“假如”的平行结果。增量建模(uplift modeling)正是要估计这个差值:对个体 $i$,接受干预带来的增量是 $\Delta_i = Y_i(1) - Y_i(0)$。营销里叫“提升度”,医疗里叫“获益”。

在 Rubin 的潜在结果框架下,当随机实验保证处理分配与潜在结果独立时,全样本的平均处理效应写成:

老周从此不再问“这棵树多高”,只问“若多施肥它多长多少”。反事实,才是增量决策的底层语言。

ATE=E[Y(1)]−E[Y(0)]ATE = E[Y(1)] - E[Y(0)]
这是“全园平均增产 8%”的数学写法。CausalTree 不满足于它,而是在每个子群上重新计算,把 ATE 细化为 CATE。
  1. 期望 $E[Y(1)]$若所有树都施肥,全园产量的平均值——一个假设的平行世界的数字。
    E[Y(1)]E[Y(1)]
  2. 期望 $E[Y(0)]$若所有树都不施肥,全园产量的平均值。
    E[Y(0)]E[Y(0)]
  3. 差值得到 ATE两相相减,得到干预在全园的平均效果。
    E[Y(1)]−E[Y(0)]E[Y(1)] - E[Y(0)]
  4. 从 ATE 到 CATE把“全园平均”换成“特征为 $x$ 的子群平均”,就得到 CATE,也就是 CausalTree 的输出目标。
    E[Y(1)∣X=x]−E[Y(0)∣X=x]E[Y(1) \mid X=x] - E[Y(0) \mid X=x]

递归划分思想:园丁的逐层切分

老周决定动手。第一刀按树龄切:十五年以上的老树归东边,十五年以下归西边;第二刀在西边按土壤酸碱度切,在东边按品种切。越切越细,每个小格里的树越来越“同类”。这就是递归:每一层,对当前地块重复同样的“找特征、定阈值、一分为二”动作。

CausalTree 沿用 CART 式递归二分。每个节点 $P$ 是一群样本,选择某个特征 $j$ 和阈值 $c$,把节点切成左右两块 $P_L$、$P_R$,再对两块递归下去,直到满足停止条件(最大深度、最小样本量等):

老周的果园地图上出现越来越小的格子——也就是候选子群。递归划分,就是“划分—再划分”,直到地图足够精细。

P=PL∪PR,PL={ i:Xij<c },PR={ i:Xij≥c }P = P_{L} \cup P_{R},\qquad P_{L}=\{\,i : X_{ij} < c\,\},\quad P_{R}=\{\,i : X_{ij} \ge c\,\}
一个节点分裂,就是画一条“特征阈值线”:线上的人进左子节点,线下的人进右子节点,人群被逐级切细。
  1. 特征 $X_{ij}$第 $i$ 棵树的第 $j$ 个属性,比如树龄、土壤酸碱度、光照时长。
    XijX_{ij}
  2. 阈值 $c$这一刀切在哪里的分界线,CausalTree 会在候选阈值中挑最优的那条。
    cc
  3. 左子节点 $P_L$属性值小于 $c$ 的样本集合。
    PL={ i:Xij<c }P_{L}=\{\,i: X_{ij} < c\,\}
  4. 右子节点 $P_R$属性值大于等于 $c$ 的样本集合,两者互补且不重叠。
    PR={ i:Xij≥c }P_{R}=\{\,i: X_{ij} \ge c\,\}

分裂准则设计:专挑效果差异最大的一刀

老周每次下刀前都会犯难:沿树龄切,还是沿土壤切?他给每个候选切法打分:切开后两边的“施肥增产差”差多少。差得越悬殊,说明这一刀越能把“反应不同的人”分开,分数越高。

CausalTree 的核心贡献就在分裂准则。普通回归树分裂时最小化结果 $Y$ 的误差;CausalTree 分裂时,目标是最小化处理效应估计误差,等价于最大化左右子节点的效应差异。Athey 与 Imbens 在 2016 年提出基于期望误差的准则,核心直觉可写成:

老周的评分公式很短:“切完之后,两边的肥料反应差得越远越好。”CausalTree 的每一刀,都朝着“异质更清晰”的方向落刀。

max⁡s∑ℓ∈{L,R}Nℓ(τ^ℓ−τ^P)2,τ^ℓ=Yˉℓ,1−Yˉℓ,0\max_{s} \sum_{\ell \in \{L,R\}} N_{\ell}\left(\hat{\tau}_{\ell} - \hat{\tau}_{P}\right)^{2},\qquad \hat{\tau}_{\ell} = \bar{Y}_{\ell,1} - \bar{Y}_{\ell,0}
通俗说:把父节点 $P$ 切开后,左右两块的效应 $\hat{\tau}_L$ 与 $\hat{\tau}_R$ 离父节点效应越远、样本量越大,这一刀的价值越高。
  1. 节点效应 $\hat{\tau}_\ell$叶子 $\ell$ 内处理组均值减对照组均值,即该地块的施肥增量估计。
    τ^ℓ=Yˉℓ,1−Yˉℓ,0\hat{\tau}_{\ell} = \bar{Y}_{\ell,1} - \bar{Y}_{\ell,0}
  2. 父节点效应 $\hat{\tau}_{P}$未切分时整块地的平均增量,是评分的基准线。
    τ^P\hat{\tau}_{P}
  3. 样本量加权 $N_{\ell}$子节点样本越多,其对总体的代表性越强,评分越可信。
    NℓN_{\ell}
  4. 最大化加权平方和让左右子节点效应尽量偏离父节点,差异被拉开,异质更清晰。
    ∑ℓNℓ(τ^ℓ−τ^P)2\sum_{\ell} N_{\ell}\left(\hat{\tau}_{\ell} - \hat{\tau}_{P}\right)^{2}

诚实分裂机制:试验田分开用

老周有个坏毛病:先看产量再划线。他试过在产量高的树周围画圈,圈里当然产量高——可这是作弊。为了让地图真实,他把果园分成两块试验田:A 田只用来定分界线,B 田按 A 定好的线重新测量每格的增产效果。两条线分开,地图才经得起第二年验证。

这就是诚实分裂(Honest Splitting):把样本随机分成训练集与评估集。训练集用于决定树结构(在哪切、切几层),评估集用于估计叶子里的处理效应。结构和效应用不同样本,避免“同一批数据既出题又做题”导致的过拟合偏差。叶子效应按评估集重新计算:

老周的地图从此“结构 A 田定、数值 B 田测”。诚实,是 CausalTree 与普通回归树之间最重要的分水岭。

\hat{\tau}^{\mspace{1mu}\mathrm{honest}}(x) = \frac{1}{N_{1}^{\mathrm{est}}(x)} \sum_{i \in N_{1}^{\mathrm{est}}(x)} Y_i - \frac{1}{N_{0}^{\mathrm{est}}(x)} \sum_{i \in N_{0}^{\mathrm{est}}(x)} Y_i
分子上分别是评估集中处理组、对照组的产量之和,除以各自的样本量,得到两组均值,再相减。这就是“不用切分数据、另用一块地量出来的效应”。
  1. 训练集与评估集分离随机把样本分成两份,结构学习只用训练集。
    Ntr∪Nest=N,Ntr∩Nest=∅N^{\mathrm{tr}} \cup N^{\mathrm{est}} = N,\quad N^{\mathrm{tr}} \cap N^{\mathrm{est}} = \varnothing
  2. 评估集处理组 $N_{1}^{\mathrm{est}}(x)$落在叶子 $x$ 且实际接受处理的样本集合。
    N1est(x)N_{1}^{\mathrm{est}}(x)
  3. 评估集对照组 $N_{0}^{\mathrm{est}}(x)$落在同一叶子但未接受处理的样本集合。
    N0est(x)N_{0}^{\mathrm{est}}(x)
  4. 两组均值之差在评估集上重新量一遍施肥增量,得到诚实无偏的叶子效应。
    1N1∑Yi−1N0∑Yi\frac{1}{N_{1}}\sum Y_i - \frac{1}{N_{0}}\sum Y_i

CART 对比分析:看产量还是看增产

老周买过一套“智能果园软件”,它能预测哪棵树明年产量最高,准得很。但老周要的不是“谁的产量高”,而是“施肥给谁带来的增量大”。红玉树产量本来就高,软件会推荐给所有红玉树施肥——可有些红玉树施肥前后根本没差,肥钱白花了。

CART(分类回归树)是预测模型,学习 $E[Y \mid X]$,分裂时使结果误差最小;CausalTree 是因果模型,学习 $E[Y(1)-Y(0) \mid X]$,分裂时使效应差异最大。前者回答“是什么、是多少”,后者回答“如果干预会怎样”。两者结构相似,目标天差地别:

同样一棵树,普通软件看“结多少果”,CausalTree 看“多施多少肥能多结多少果”。要哪张图,取决于决策是预测还是干预。

CART: min⁡∑i(Yi−μ^(Xi))2,CausalTree: max⁡∑ℓ∈{L,R}Nℓ(τ^ℓ−τ^P)2\text{CART}:\ \min \sum_{i} \big(Y_i - \hat{\mu}(X_i)\big)^{2},\qquad \text{CausalTree}:\ \max \sum_{\ell \in \{L,R\}} N_{\ell}\big(\hat{\tau}_{\ell} - \hat{\tau}_{P}\big)^{2}
左边让“预测产量”与“实际产量”的差距最小;右边让“左右子群效应差异”最大。一个追结果,一个追增量。
  1. CART 的预测值 $\hat{\mu}(X_i)$对特征 $X_i$ 预测结果 $Y_i$,目标是拟合产量本身。
    μ^(Xi)\hat{\mu}(X_i)
  2. CART 的残差平方和预测错了多少,越小说明对产量的拟合越好。
    ∑i(Yi−μ^(Xi))2\sum_{i} \big(Y_i - \hat{\mu}(X_i)\big)^{2}
  3. CausalTree 的效应差左右子节点效应偏离父节点的程度,越大说明异质越明显。
    ∑ℓNℓ(τ^ℓ−τ^P)2\sum_{\ell} N_{\ell}\big(\hat{\tau}_{\ell} - \hat{\tau}_{P}\big)^{2}
  4. 目标决定工具若要做预测性任务(估价、销量),选 CART;若要做干预性任务(投放、开药),选 CausalTree。
    预测 Y vs. 估计 τ\text{预测 } Y \ \text{vs.}\ \text{估计 } \tau

子群解释应用:收获一张施肥地图

深度 3 的树停止生长,老周的施肥地图终于完成了:东北角“高收入低龄树”标注大剂量施肥;西北角“低互动老树”标注停肥;中间“中性区”维持现状。他把地图贴在小屋墙上,每个格子里有一句话规则,任何工人看了都能照做。

叶子即子群,路径即规则。CausalTree 的可解释性让它特别适合业务落地:每个叶子对应一段 if-then 规则,叶子的 $\hat{\tau}_\ell$ 就是该子群的条件平均处理效应。我们可以统计子群大小、效应值、特征分布,形成画像并决定动作。最终预测函数是各叶子效应的加权拼接:

老周的地图被印成手册,第二年春天按图施肥,秋收增产两成。CausalTree 的终点不是一棵树,而是一张能执行、能验证、能解释的“异质子群作战图”。

τ^(x)=∑ℓ=1Lτ^ℓ⋅I(x∈ℓ)\hat{\tau}(x) = \sum_{\ell=1}^{L} \hat{\tau}_{\ell} \cdot \mathbb{I}\big(x \in \ell\big)
新样本来了,先看它最终掉进哪个叶子,就把那个叶子的 $\hat{\tau}_\ell$ 作为它的效应预测——像查地图找到自己的格子,直接读格子上的数字。
  1. 叶子编号 $\ell=1,\dots,L$树的 $L$ 个叶子,每个叶子是一个异质子群。
    ℓ∈{1,…,L}\ell \in \{1,\dots,L\}
  2. 指示函数 $\mathbb{I}(x \in \ell)$当样本 $x$ 落入叶子 $\ell$ 时取 1,否则取 0,相当于“查地图定位”。
    I(x∈ℓ)\mathbb{I}(x \in \ell)
  3. 叶子效应 $\hat{\tau}_{\ell}$该子群的处理效应估计,地图格子上的数字。
    τ^ℓ\hat{\tau}_{\ell}
  4. 求和输出把命中的叶子效应挑出来,得到该样本的效应预测。
    ∑ℓ=1Lτ^ℓ⋅I(x∈ℓ)\sum_{\ell=1}^{L} \hat{\tau}_{\ell} \cdot \mathbb{I}(x \in \ell)

AI 应用场景

场景一:智能营销优惠券投放

阶段:模型训练与决策优化

解决的问题:传统营销按“购买概率”发券,会把券发给本来就会买的自然转化客户,造成预算浪费。CausalTree 估计每个客户群的“发券增量”,把预算集中在增量大的子群,砍掉负增量人群。

具体案例:某电商平台用 CausalTree 对近三个月用户建模,叶子规则显示:“高活跃新客”发券后 GMV 提升 18%,“低活跃老客”发券后反而降低 5%。运营按地图调整:只给前者发大额券,给后者发召回短信而非优惠券,整体 ROI 提升约 35%。

场景二:医疗个性化用药决策

阶段:效果评估与亚组分析

解决的问题:临床试验常报告“平均有效”,但医生更关心“哪类患者真正受益”。CausalTree 对受试者递归划分,识别出获益亚群、无效应亚群与潜在风险亚群。

具体案例:某降压新药三期临床数据显示总体疗效显著,CausalTree 分析发现:依从性高且无糖尿病史的患者收缩压多降 9 mmHg,而糖尿病合并肾损伤患者反而升高 3 mmHg。监管与临床决策据此调整适用人群标签。

场景三:用户召回触达策略

阶段:因果推断与运营策略

解决的问题:运营团队对流失用户统一推送“回归礼包”,打扰了部分本会自然回流的用户。用 uplift 树按用户活跃度与注册渠道划分,找到“推送促活”与“推送反噬”两组人群。

具体案例:某 App 对 30 天未登录用户建模,CausalTree 叶子显示:曾付费但低频用户推送后 7 日回归率提升 12%,高频刚流失用户推送后投诉率上升。策略改为前者推送、后者静默,次月整体留存上升 6%。

Python 演示

python86 行
import numpy as np  class CausalTree:    def __init__(self, max_depth=3, min_leaf=60):        self.max_depth = max_depth        self.min_leaf = min_leaf        self.root = None     def fit(self, X, W, Y, X_es, W_es, Y_es):        self.root = self._grow(X, W, Y, 0)        self._honest_refit(self.root, X_es, W_es, Y_es)        return self     def _grow(self, X, W, Y, depth):        node = {'tau': 0.0, 'leaf': True, 'feat': None, 'thr': None,                'left': None, 'right': None}        treat = W == 1        if treat.any() and (~treat).any():            node['tau'] = Y[treat].mean() - Y[~treat].mean()        n = len(Y)        if depth >= self.max_depth or n < 2 * self.min_leaf:            return node        tau_p = node['tau']        best = -np.inf        for f in range(X.shape[1]):            order = np.argsort(X[:, f])            Xs, Ws, Ys = X[order, f], W[order], Y[order]            for i in range(self.min_leaf, n - self.min_leaf + 1):                if Xs[i] == Xs[i - 1]:                    continue                wl, wr = Ws[:i], Ws[i:]                yl, yr = Ys[:i], Ys[i:]                tl = yl[wl == 1].mean() - yl[wl == 0].mean() if (wl == 1).any() and (wl == 0).any() else 0.0                tr = yr[wr == 1].mean() - yr[wr == 0].mean() if (wr == 1).any() and (wr == 0).any() else 0.0                gain = i * (tl - tau_p) ** 2 + (n - i) * (tr - tau_p) ** 2                if gain > best:                    best = gain                    node['feat'] = f                    node['thr'] = (Xs[i] + Xs[i - 1]) / 2        if best <= 0 or node['feat'] is None:            return node        mask = X[:, node['feat']] < node['thr']        node['leaf'] = False        node['left'] = self._grow(X[mask], W[mask], Y[mask], depth + 1)        node['right'] = self._grow(X[~mask], W[~mask], Y[~mask], depth + 1)        return node     def _honest_refit(self, node, X, W, Y):        if node['leaf']:            treat = W == 1            if treat.any() and (~treat).any():                node['tau'] = Y[treat].mean() - Y[~treat].mean()            return        mask = X[:, node['feat']] < node['thr']        self._honest_refit(node['left'], X[mask], W[mask], Y[mask])        self._honest_refit(node['right'], X[~mask], W[~mask], Y[~mask])     def predict_tau(self, X):        taus = []        for x in X:            node = self.root            while not node['leaf']:                go_left = x[node['feat']] < node['thr']                node = node['left'] if go_left else node['right']            taus.append(node['tau'])        return np.array(taus)  if __name__ == '__main__':    rng = np.random.default_rng(42)    n = 2000    X = rng.uniform(0, 1, size=(n, 2))    W = rng.binomial(1, 0.5, size=n)    tau_true = np.where(X[:, 0] < 0.5, 2.0, -1.0)    Y = 0.5 + W * tau_true + rng.normal(0, 0.3, size=n)    n_tr = n // 2    ct = CausalTree(max_depth=2, min_leaf=60).fit(        X[:n_tr], W[:n_tr], Y[:n_tr],        X[n_tr:], W[n_tr:], Y[n_tr:])    pred = ct.predict_tau(X[n_tr:])    mse = ((pred - tau_true[n_tr:]) ** 2).mean()    sub = X[n_tr:, 0] < 0.5    print('效应预测 MSE: %.3f' % mse)    print('子群A(真实2.0) 估计 tau = %.3f' % pred[sub].mean())    print('子群B(真实-1.0) 估计 tau = %.3f' % pred[~sub].mean())
目的:用纯 Python 实现一个简化版 CausalTree——递归划分时按“处理效应差异最大化”评分,并用独立的评估集重新估计叶子效应(诚实分裂),最后在合成数据上检验子群发现效果。
  1. node['tau'] = Y[treat].mean() - Y[~treat].mean()计算当前节点的处理效应:处理组均值减对照组均值,如同量“施肥地块比不施肥地块多收多少”。
  2. gain = i * (tl - tau_p) ** 2 + (n - i) * (tr - tau_p) ** 2分裂评分:左右子节点效应偏离父节点的加权平方和,越大说明这一刀效果差异越显著。
  3. node['thr'] = (Xs[i] + Xs[i - 1]) / 2取相邻样本的中点作阈值,确保左右子集都非空且划分干净。
  4. self._honest_refit(self.root, X_es, W_es, Y_es)诚实分裂:结构不变,用“另一块试验田”评估数据重估每个叶子效应。
  5. pred = ct.predict_tau(X[n_tr:])新样本沿树走到叶子,返回该子群的效应估计,像查施肥地图上的格子。
  6. Y = 0.5 + W * tau_true + rng.normal(0, 0.3, size=n)合成“平行世界”:结果 = 基线 + 真实效应 + 噪声,一棵树只能走其中一路。

常见误区

误区都是决策树,把是否处理当作普通特征塞进 CART 训练,就等价于 CausalTree。

正解CART 学习的是 $E[Y \mid X]$,分裂时让预测误差最小;CausalTree 学习的是 $E[Y(1)-Y(0) \mid X]$,分裂时让效应差异最大。把 treatment 当普通特征,学到的是“响应模式”而非“因果增量”,还会被选择偏差污染。就像老周只看产量画圈,永远画不出施肥增量图。

为何常见:两者都是树、都递归划分,外观相似让人忽略了目标函数的天壤之别。

误区叶子输出 2.0,就觉得这一组人在干预后产量/转化率是 2.0。

正解$\hat{\tau}$ 是差值:处理组均值减对照组均值。如果处理组是 10.0、对照组是 8.0,差值才是 2.0。预测结果高不代表增量高——红玉树本来产量就高,施肥与否差别不大,增量趋近于零。

为何常见:日常习惯直接看结果数值,忽略了“效应”这个相对概念。

误区把数据按 7:3 分成 train/test,测试集用来报 AUC,这就叫诚实分裂。

正解诚实分裂的特异性在于“结构学习”与“效应估计”的样本分离:训练集只定树的形状,评估集专门重估叶子 $\hat{\tau}$。普通划分的测试集只做性能评估,不参与任何参数重算。类比老周的 A 田画线、B 田量产量,两件事各用各的地。

为何常见:两者都有“两份数据”的外观,容易混淆核心差异——分离的是“任务”而非仅“评估”。

误区选择左右叶子 $\hat{\tau}_L$ 与 $\hat{\tau}_R$ 数值差最大的切法。

正解光看均值差会偏好小样本的极端划分。正确评分还要乘以子节点样本量并使用父节点作基准,即 $N_L(\hat{\tau}_L-\hat{\tau}_P)^2 + N_R(\hat{\tau}_R-\hat{\tau}_P)^2$。一小撮树效应很极端,不代表整块地都该按此划分。

为何常见:“差异大就好”的直觉忽略了估计的稳定性与代表性。

误区CausalTree 把叶子内每个个体的处理效应都预测为同一个 $\hat{\tau}$。

正解$\hat{\tau}$ 是条件平均处理效应,是子群的平均值,不代表个体效应处处相同。就像“低龄高收入”地块平均增产 2.1,但地块里仍有少数树增产 1.2。CausalTree 识别的是“平均效应不同的子群”,不是“内部完全同质”的小组。

为何常见:把“子群平均”误读为“个体一致”,混淆了聚合统计与个体真实值。

面试题

Q1. CausalTree 最核心的目标是什么?

单选题 · easy

  • A.精确预测结果变量 Y 的取值
  • B.识别处理效应不同的子群← 正确答案
  • C.对样本做无监督聚类
  • D.拟合高维非线性曲线

答案:B。CausalTree 的目标是 CATE——找出对同一干预反应不同的异质子群。A 是 CART 的目标,C 是聚类任务,D 是回归拟合,均未抓住“处理效应”这一核心。

Q2. 反事实结果指的是什么?

单选题 · easy

  • A.个体实际观测到的结果
  • B.个体未接受处理时的结果← 正确答案
  • C.模型预测的错误结果
  • D.处理组的平均结果

答案:B。每个个体只能走一条路:施肥或不施肥。另一条路的结果就是反事实,即“如果当初没施肥会怎样”。A 是观测结果,C、D 与反事实无关。

Q3. CausalTree 分裂准则的核心追求是什么?

单选题 · mid

  • A.最小化全园产量预测误差
  • B.最大化子节点间处理效应差异← 正确答案
  • C.最大化信息增益比
  • D.最小化节点基尼系数

答案:B。CausalTree 的分裂目标是让左右子节点的效应差异尽量大,公式为 $\sum N_\ell(\hat{\tau}_\ell-\hat{\tau}_P)^2$。A 是回归树目标,C、D 是分类树指标。

Q4. 诚实分裂机制的主要作用是什么?

单选题 · mid

  • A.显著降低训练时间
  • B.均衡处理组与对照组
  • C.防止结构学习过拟合效应估计← 正确答案
  • D.自动减少特征数量

答案:C。诚实分裂用训练集定结构、评估集测效应,避免同一批数据“既出题又做题”导致的效应估计过拟合。它不直接加速训练,也不平衡样本或删特征。

Q5. 与 CART 相比,CausalTree 的本质差异是什么?

单选题 · mid

  • A.不再使用树形结构
  • B.学习目标是处理效应← 正确答案
  • C.只能处理两个特征
  • D.无需处理缺失值

答案:B。CART 预测 $E[Y\mid X]$,CausalTree 估计 $E[Y(1)-Y(0)\mid X]$。两者都用树形递归,B 才是本质差异;A、C、D 均不符合事实。

Q6. 叶子节点输出 $\hat{\tau}=2.0$ 表示什么?

单选题 · hard

  • A.处理组平均结果等于二
  • B.对照组平均结果等于二
  • C.子群处理比对照高两点← 正确答案
  • D.子群内个体效应都为二

答案:C。$\hat{\tau}$ 是处理组均值减对照组均值,表示该子群的平均增量。它不是单一组的均值(A、B),也不等于组内每个个体的效应(D),个体效应仍有异质性。

Q7. 诚实分裂中,叶子 $\hat{\tau}$ 用哪些样本计算?

单选题 · hard

  • A.训练结构的同一样本
  • B.评估集内的样本均值差← 正确答案
  • C.全量数据的回归斜率
  • D.处理组样本的方差

答案:B。诚实分裂的关键是结构用训练集定、效应用评估集算,所以叶子 $\hat{\tau}$ 用评估集内处理组与对照组的均值差。A 会导致过拟合,C、D 与效应估计无关。

Q8. 处理效应异质性体现在哪些方面?

多选题 · easy

  • A.不同特征子群效应不同← 正确答案
  • B.全体个体效应完全相同
  • C.效应差异可用于精准决策← 正确答案
  • D.处理效应恒定永远不变

答案:AC。异质性意味着不同子群效应不同(A),这种差异正是精准投放、个性化用药的依据(C)。B、D 描述的是“同质性”,与异质性相反。

Q9. CausalTree 的递归划分涉及哪些要素?

多选题 · mid

  • A.特征空间逐级二分切分← 正确答案
  • B.连续特征用阈值切分← 正确答案
  • C.交换处理组与对照组
  • D.根节点逐级分裂子节点← 正确答案

答案:ABD。递归划分是特征空间的逐级二分(A),连续特征用阈值 $c$ 切分(B),从根节点不断分裂出子节点(D)。C 处理组与对照组是固定的事实,不存在交换。

Q10. 关于诚实分裂的训练流程,以下说法正确的是?

多选题 · hard

  • A.结构由训练集决定← 正确答案
  • B.效应由评估集估计← 正确答案
  • C.结构由评估集决定
  • D.效应由训练集估计

答案:AB。诚实分裂把任务拆开:训练集用于生长树的结构(A),评估集用于重新估计叶子处理效应(B)。C、D 把两者颠倒,会引入“看着答案划线”的偏差。