因果推断 | 元学习方法原理详解和代码实操

发布于:2025-07-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

1 引言

小伙伴们,三个月没见,都还好嘛?说实话,我这段时间的小日子有些跌跌撞撞的。

工作方面,我曾经从零开始搭建的项目,因为团队分工的问题,不得不暂时搁置,只好调整心态,转而投入到其他项目中。身体方面,我原以为自己怎么吃都不会胖,没想到体重从刚毕业时的105斤一路飙升到现在的138斤,这也迫使我把健身计划调整为以减体重为主。生活习惯上,原本设定的连续健身60天的小目标却在第59天意外中断,每日阅读的习惯也在坚持了350多天后,倒在了一年这一重要节点前。这些消极的事情积少成多,对我来说,委实变成了一个大打击。

不过,我始终是那种即使身处低谷,也会努力向光而行的人。虽然工作中有一段时间很迷茫,但最近被安排负责的在线优化问题,让我重新燃起了兴趣。减肥方面,不仅在体重秤上看到了积极的变化,还带动了老婆一起健身,现在她的成果甚至比我还要显著。至于生活习惯,重启永远不晚:新的连续运动天数已经达到了76天,正在向100天的目标迈进;新的连续阅读也坚持了49天,小目标依然是一整年。

解决了这些问题后,我又回来继续学习了。之前涉猎了很多方向(因果推断、不确定性优化、大模型),但都没有形成系统的理解,感觉有些零散。今年下半年,我打算将重心放在因果推断上,争取在年底前形成一次系统性的总结。

本篇文章的主题是因果推断中的元学习方法。此前我曾跳过这部分内容,直接学习了双重机器学习方法(DML),主要是因为DML在业界应用更为广泛。但最近发现,元学习方法在一些团队的特定项目中依然有实际价值。此外,从知识体系的完整性和基础性角度来看,理解元学习方法的原理与实践仍然十分重要。

正文如下。

2 元学习方法

2.1 基本概念

在介绍元学习方法之前,首先需要明确相关概念。机器学习中的元学习(Meta-learning)与因果推断领域常用的元学习器(Meta-learner)虽名称相似,但侧重点略有不同。目前,关于二者关系的讨论还不多。根据主流大模型的解释,机器学习中的 Meta-learning 更强调方法论,即“如何高效学习”;而因果推断中的 Meta-learner,则是这种思想在因果推断任务中的具体实现方式。

因此,要理解因果推断中的元学习方法,首先需要掌握机器学习领域的 Meta-learning 概念。其核心思想是“学习如何学习”,即让算法能够通过不断积累经验,提升自身的学习能力。例如,如果目标是提高考试成绩,元学习关注的是掌握高效的学习方法,从而全面提升各科成绩;而传统机器学习则可能只关注某一科目的成绩提升,较少考虑方法的迁移和泛化能力。

在因果推断领域,元学习方法的应用体现在:模型先在不同干预条件下学习结果分布,再基于这些学习结果评估因果效应。具体实现包括 S-learner、T-learner、X-learner 和 R-learner 等多种算法。

2.2 S-learner

S 代表 Single,即单一模型。S-learner 使用一个模型同时对实验组和对照组的数据进行建模,将处理变量(treatment)作为特征输入。此时,条件平均处理效应(CATE)的表达式为:
τ ( x ) = E [ Y ∣ X = x , T = 1 ] − E [ Y ∣ X = x , T = 0 ] = μ 1 − μ 0 \tau(x) = E[Y|X=x, T=1] - E[Y|X=x, T=0] = \mu_1 - \mu_0 τ(x)=E[YX=x,T=1]E[YX=x,T=0]=μ1μ0

下图中, M ( Y ∼ ( X , T ) ) M(Y \sim (X, T)) M(Y(X,T)) 表示以 ( X , T ) (X, T) (X,T) 为特征、 Y Y Y 为标签的任意机器学习模型(如线性回归、XGBoost 等)。

2.3 T-learner

T 代表 Two,即两个模型。T-learner 分别针对实验组和对照组,各自训练一个模型以预测响应值。此时,CATE 的表达式为:
τ ( x ) = E [ Y ( 1 ) − Y ( 0 ) ∣ X = x ] = E [ Y ( 1 ) ∣ X = x ] − E [ Y ( 0 ) ∣ X = x ] = μ 1 − μ 0 \tau(x) = E[Y(1) - Y(0)|X=x] = E[Y(1)|X=x] - E[Y(0)|X=x] = \mu_1 - \mu_0 τ(x)=E[Y(1)Y(0)X=x]=E[Y(1)X=x]E[Y(0)X=x]=μ1μ0

下图中, M 0 ( Y 0 ∼ X 0 ) M_0(Y^0 \sim X^0) M0(Y0X0) M 1 ( Y 1 ∼ X 1 ) M_1(Y^1 \sim X^1) M1(Y1X1) 分别是对照组和实验组的响应预测模型,模型类型可灵活选择。

2.4 X-learner

X 代表 Cross,强调交叉估计。X-learner 通过分别估计实验组的平均处理效应(CATT)和对照组的平均处理效应(CATC),再加权平均得到最终的 CATE 估计。

  1. 第一步:与 T-learner 类似,分别用 M 1 M_1 M1 M 2 M_2 M2 针对对照组和实验组拟合响应。
    M 1 ( Y 0 ∼ X 0 ) , M 2 ( Y 1 ∼ X 1 ) M_1(Y^0 \sim X^0), \quad M_2(Y^1 \sim X^1) M1(Y0X0),M2(Y1X1)
  2. 第二步:用对照组模型预测实验组的个体处理效应(ITE),用实验组模型预测对照组的 ITE。
    D ^ 1 = Y 1 − M 1 ( X 1 ) , D ^ 0 = M 2 ( X 0 ) − Y 0 \hat D^1 = Y^1 - M_1(X^1), \quad \hat D^0 = M_2(X^0) - Y^0 D^1=Y1M1(X1),D^0=M2(X0)Y0
  3. 第三步:以 X X X 为特征,第二步得到的 ITE 为标签,分别在对照组和实验组上训练模型 M 3 M_3 M3 M 4 M_4 M4,最终以加权平均作为 CATE 的估计。权重可采用倾向得分等方法。
    τ ^ 0 = M 3 ( D ^ 0 ∼ X 0 ) , τ ^ 1 = M 4 ( D ^ 1 ∼ X 1 ) \hat \tau_0 = M_3(\hat D^0 \sim X^0), \quad \hat \tau_1 = M_4(\hat D^1 \sim X^1) τ^0=M3(D^0X0),τ^1=M4(D^1X1)
    τ ( x ) = g ( x ) τ ^ 0 + ( 1 − g ( x ) ) τ ^ 1 \tau(x) = g(x)\hat \tau_0 + (1 - g(x))\hat \tau_1 τ(x)=g(x)τ^0+(1g(x))τ^1

2.5 R-learner

R-learner 与双重机器学习(DML)方法类似。回顾 DML 的基本表达式,其通过对残差进行线性回归,估计平均因果效应 τ \tau τ
Y − Y ^ = τ ⋅ ( T − T ^ ) Y - \hat Y = \tau \cdot (T - \hat T) YY^=τ(TT^)

R-learner 是 DML 思想的拓展,目标是寻找一个函数 τ ( x ) \tau(x) τ(x),使得以下损失最小化:
[ Y − Y ^ − τ ( x ) ⋅ ( T − T ^ ) ] 2 [Y - \hat Y - \tau(x) \cdot (T - \hat T)]^2 [YY^τ(x)(TT^)]2

由于 R-learner 在实际应用中较为少见,本文不再展开介绍。

3 代码实现

本节将进入代码实操环节,分为两个部分:第一部分手动实现一种元学习方法,以加深理解;第二部分通过实例对比不同元学习方法在因果推断中的表现。

3.1 S-learner 原理实现

以 S-learner 为例,分别采用两种方式实现:第一种是直接调用 causalml 工具包中的 BaseSRegressor 模块;第二种是手动编写机器学习模型实现。

import numpy as np
from sklearn.linear_model import LinearRegression
from xgboost import XGBRegressor
from causalml.inference.meta import BaseSRegressor
from causalml.dataset import synthetic_data

def calc_by_package(X, treatment, y):
    # 使用线性回归模型
    learner_s = BaseSRegressor(learner=LinearRegression())
    learner_s.fit(X=X, treatment=treatment, y=y)
    ate_s = learner_s.predict(X)

    # 使用 XGB 回归模型
    learner_t = BaseSRegressor(learner=XGBRegressor())
    learner_t.fit(X=X, treatment=treatment, y=y)
    ate_t = learner_t.predict(X)

    print('s-learner, by package, LR: {:.04f}, XGB: {:.04f}'.format(np.mean(ate_s), np.mean(ate_t)))

def calc_by_self(X, treatment, y):
    # 构造训练数据
    X_train = np.hstack((X, treatment.reshape((len(treatment), 1))))
    X_1 = np.hstack((X, np.ones((X.shape[0], 1))))
    X_0 = np.hstack((X, np.zeros((X.shape[0], 1))))

    # 线性回归模型
    lr = LinearRegression()
    lr.fit(X_train, y)
    y_1_lr = lr.predict(X_1)
    y_0_lr = lr.predict(X_0)
    predict_y_lr = y_1_lr - y_0_lr

    # XGB 回归模型
    xgb = XGBRegressor()
    xgb.fit(X_train, y)
    y_1_xgb = xgb.predict(X_1)
    y_0_xgb = xgb.predict(X_0)
    predict_y_xgb = y_1_xgb - y_0_xgb

    print('s-learner, by self, LR: {:.04f}, XGB: {:.04f}'.format(np.mean(predict_y_lr), np.mean(predict_y_xgb)))

if __name__ == '__main__':
    np.random.seed(20)
    # y-观测结果;X-样本特征;treatment-处理变量;tau-个体处理效应
    y, X, treatment, tau, b, e = synthetic_data(mode=1, n=10000, p=8, sigma=1.0)

    calc_by_package(X, treatment, y)
    calc_by_self(X, treatment, y)

从输出结果可以看出,手写实现与工具包实现的结果完全一致:

true causal effect: 0.5002480680349228
estimated causal effect, by package, LR: 0.6801, XGB: 0.5382
estimated causal effect, by self, LR: 0.6801, XGB: 0.5382

3.2 不同元学习器效果对比

下面的代码对比了 S-learner、T-learner、X-learner 和 DML 在同一数据集上的表现,主要评估指标为 ATE 和 AUUC。

import numpy as np
import pandas as pd
from xgboost import XGBRegressor
from causalml.inference.meta import BaseSRegressor, BaseXRegressor, BaseTRegressor
from causalml.dataset import synthetic_data
from econml.dml import CausalForestDML
from causalml.metrics import auuc_score, plot_gain
import matplotlib.pyplot as plt

def plot_sorted_tau_and_preds(df):
    # 真实 tau 与四个模型预测结果
    tau = df['tau_true'].values
    preds = {
        'S-learner': df['s-learner'].values,
        'T-learner': df['t-learner'].values,
        'X-learner': df['x-learner'].values,
        'DML': df['DML'].values
    }
    idx = np.argsort(tau)
    tau_sorted = tau[idx]
    preds_sorted = {k: v[idx] for k, v in preds.items()}
    x = np.arange(len(tau))

    y_all = [tau_sorted] + [preds_sorted[k] for k in preds]
    ymin = min([arr.min() for arr in y_all])
    ymax = max([arr.max() for arr in y_all])

    fig, axes = plt.subplots(2, 2, figsize=(16, 12))
    model_names = list(preds.keys())
    for i, ax in enumerate(axes.flatten()):
        ax.scatter(x, tau_sorted, label='True tau', color='black', s=10, alpha=0.7)
        ax.scatter(x, preds_sorted[model_names[i]], label=model_names[i], color='tab:blue', s=10, alpha=0.7)
        ax.set_title(f'{model_names[i]}')
        ax.set_xlabel('Sample (sorted by tau)')
        ax.set_ylabel('CATE')
        ax.set_ylim(ymin, ymax)
        ax.legend()
    plt.tight_layout()
    plt.show()

def calc_by_package(X, treatment, y, tau):
    # S-learner
    learner_s = BaseSRegressor(learner=XGBRegressor())
    ate_s = learner_s.fit_predict(X=X, treatment=treatment, y=y)
    print('estimated causal effect, by S-learner: {:.04f}'.format(np.mean(ate_s)))

    # T-learner
    learner_t = BaseTRegressor(learner=XGBRegressor())
    ate_t = learner_t.fit_predict(X=X, treatment=treatment, y=y)
    print('estimated causal effect, by T-learner: {:.04f}'.format(np.mean(ate_t)))

    # X-learner
    learner_x = BaseXRegressor(learner=XGBRegressor())
    ate_x = learner_x.fit_predict(X=X, treatment=treatment, y=y)
    print('estimated causal effect, by X-learner: {:.04f}'.format(np.mean(ate_x)))

    # DML
    cf_dml = CausalForestDML(model_t=XGBRegressor(), model_y=XGBRegressor())
    cf_dml.fit(y, treatment, X=X)
    ate_dml = cf_dml.effect(X)
    print('estimated causal effect, by DML: {:.04f}'.format(np.mean(ate_dml)))

    # 合并结果
    df = pd.DataFrame({
        'y': y,
        'treat': treatment,
        's-learner': np.ravel(ate_s),
        't-learner': np.ravel(ate_t),
        'x-learner': np.ravel(ate_x),
        'DML': np.ravel(ate_dml)
    })
    df['tau_true'] = tau
    print('true causal effect: {}'.format(np.mean(tau)))

    # AUUC
    auuc = auuc_score(df, outcome_col='y', treatment_col='treat', normalize=True, tmle=False)
    print(auuc)

    return df

if __name__ == '__main__':
    np.random.seed(0)
    y, X, treatment, tau, b, e = synthetic_data(mode=1, p=25)

    result_df = calc_by_package(X, treatment, y, tau)
    plot_sorted_tau_and_preds(result_df)

    # 绘制 AUUC 曲线
    plot_gain(
        result_df,
        outcome_col='y',
        treatment_col='treat',
        normalize=True,
        random_seed=10,
        n=100,
        figsize=(8, 8)
    )
    plt.show()

结果解读:

  • ATE 结果:X-learner 的预测最接近真实值,DML 和 S-learner 次之(前者整体偏低,后者整体偏高),T-learner 偏差最大。
    estimated causal effect, by S-learner: 0.6588
    estimated causal effect, by T-learner: 0.7255
    estimated causal effect, by X-learner: 0.5699
    estimated causal effect, by DML: 0.3762
    true causal effect: 0.5116507638464373
    
  • ITE 区分度:DML 的预测区分度最低,S-learner 和 X-learner 中等,T-learner 区分度最高。
  • AUUC 指标:排序能力从高到低依次为排序能力从高到低依次为 T-learner > S-learner > X-learner > DML。
s-learner    0.898929
t-learner    1.049052
x-learner    0.811587
DML          0.727232
tau_true     0.461501

基于以上案例的模型评估结果,有两个值得进一步讨论的点:

  1. ATE 和 AUUC 指标有时结论不一致
    ATE 关注平均因果效应的准确性,而 AUUC 关注模型对个体排序的能力。举例来说,假设有 5 个个体,真实 ITE 分别为 [2, 1, 0, -1, -2],真实 ATE=0。如果一个模型预测 ITE 为 [0, 0, 0, 0, 0],则 ATE=0(与真实值一致),但排序能力极差(AUUC 很低);另一个模型预测为 [2, 1.5, 1, 0.5, 0],ATE 不为 0,但排序能力很好(AUUC 高)。实际场景中,指标选择要结合业务目标,比如广告投放更关注排序能力,此时 AUUC 更具参考价值。

  2. 真实 ITE 的 AUUC 值未必最高
    理论上,用真实 ITE 排序后计算 AUUC 应该最优,但实际由于我们无法观测到所有个体的潜在结果,AUUC 的计算通常基于观测数据的分桶均值差,具体逻辑可参考因果推断 | 潜在结果框架的基础知识

4 总结

正文到此结束,核心内容总结如下:

  1. 元学习器在因果推断中的三种常见模型包括:S-learner、T-learner 和 X-learner,各自具有不同的建模思路和适用场景。

  2. 在模型评估方面,AUUC 和 ATE 两项指标关注的重点不同,可能导致评估结论不一致。ATE关注整体平均因果效应的准确性,而AUUC更强调模型对个体排序能力的优劣,实际选择应结合具体业务需求。

5 相关阅读

X-learner: https://arxiv.org/pdf/1706.03461

基于元学习的因果效应估计方法:https://zhuanlan.zhihu.com/p/644413135

causalml-meta learn:https://causalml.readthedocs.io/en/latest/examples/meta_learners_with_synthetic_data.html

因果推断 | 潜在结果框架的基础知识:https://mp.weixin.qq.com/s/8YrNm99g2BaGJusQKd_U7A


网站公告

今日签到

点亮在社区的每一天
去签到