你的AI产品在六个月前推出了一个代理模式选择加入功能。你进行了倾向性分析,根据参与度层级和查询置信度进行了调整,并报告了任务完成率干净利落地提升了8个百分点。这个数字进入了季度业务回顾,大家都很满意。
不可避免地,一位严谨的数据科学家会提出一个令人不安的问题。你有多大把握倾向性模型捕获了所有混杂因素?如果你遗漏了某些东西,逻辑回归估计的选择概率是错误的怎么办?如果你的结果回归也被错误设定,因为任务完成与查询置信度之间存在非线性关系,而线性模型无法捕捉,那又怎么办?
你有两个模型,不确定哪个是正确的,而两者都承载着关键作用。
选择加入的AI产品默认就会撞上这堵墙。在没有随机化的基于LLM的实验中进行因果推断时,你拥有选择加入和未选择加入用户的结果数据。
复杂之处在于这些群体是自我选择的。你为恢复因果效应而构建的每一个模型,都是对未知真相的近似。
如果倾向性模型是错误的,单独的倾向性加权就会失败。如果结果模型是错误的,单独的回归调整就会失败。每种方法都把一切押在单一模型被正确设定之上。
双重稳健估计,特别是增强逆概率加权(AIPW)估计器,做出了不同的赌注。它将倾向性模型和结果模型结合成一个单一的估计器,只要其中一个被正确设定,它就能保持一致。要让AIPW失效,你需要两个模型同时失败。
这一保证来自于该估计器背后的半参数效率理论,这是一个融入其构造中的数学性质。可以把它视为因果估计的冗余工程。它依赖于同样的容错逻辑,这种逻辑使得分布式系统在单个节点故障时仍能保持在线。
在本教程中,你将使用scikit-learn从零开始实现AIPW,添加自举置信区间,并通过故意一次破坏一个模型来证明双重稳健性质,以展示估计器仍然成立。对于在嘈杂的AI产品实验中运行、每个模型都是近似值的数据科学家来说,这个框架使你的估计能够幸存下来。
本教程中的每个代码块都可以在配套笔记本中端到端运行,网址为github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm/tree/main/12_doubly_robust/。笔记本文件是aipw_demo.ipynb。
倾向性评分方法要成功只需一件事:一个正确捕获所有混杂因素的倾向性模型。回归调整要成功只需一件事:一个正确捕获协变量与结果之间关系的结果模型。两者在实践中都是很强的条件,而且你很少知道自己是否满足其中任何一个。
在LLM选择加入分析中,倾向性模型以三种特定方式失效。首先,事件日志中的特征位于选择加入决策本身的下游。用户的查询置信度得分反映了模型在收到查询时的评估。选择加入的根本动机完全处于你的测量系统之外。
其次,逻辑回归无法自动捕获非线性交互。如果企业计划中的重度用户与个人计划中的重度用户以完全不同的比率选择加入,主效应逻辑模型将完全错过这一细微差别。第三,未测量的混杂因素在构造上是不可见的。如果阅读你工程博客的高级用户比不阅读的同等用户选择加入的比例高得多,而你缺乏博客阅读量信号,那么无论你如何调整倾向性模型,它都会给他们分配错误的权重。
结果模型因不同原因而失败。LLM系统中的任务完成取决于查询复杂性,而查询复杂性是出了名的嘈杂。它取决于模型版本,你可能没有将版本作为协变量捕获。它还取决于用户是否处于具有自定义系统提示的企业工作区中(这个因素可能根本不在你的日志中)。对这些协变量进行线性回归会在某处错误设定函数形式,而偏差的方向是不可预测的。
实际问题是,你无法运行一个明确确认任一模型正确的设定检验。平衡诊断揭示了倾向性质量,而它们的作用也就到此为止。它们无法检测未测量的混杂。残差图确认了结果模型对观测数据的拟合程度。但它们无法揭示协变量遗漏了什么。你可以改进两个模型,但仍然不知道是否解决了根本问题。这比听起来更难。
任何基于倾向性的因果分析都以三个识别假设为基础。在AIPW或任何其他估计器能给出有效因果效应之前,这三个假设都必须成立。
无混杂性(也称为强可忽略性):所有同时影响选择加入概率和任务完成的变量都被测量并包含在你的模型中。
重叠性(正性):每个用户必须有一个非零概率被分到处理组或对照组。没有任何亚组可以完全确定选择加入或不加入。
SUTVA:每个用户的潜在结果不受其他用户处理状态的影响,并且处理只有一种版本。AIPW放宽了你的模型正确捕获这些假设的要求,但它并不会让假设本身消失。它们仍然必须在数据中成立,再多的方法论技巧也改变不了这一点。
AIPW提供了单一模型方法无法提供的数学保证。只要倾向性评分模型或结局模型中有任意一个被正确设定,估计量就能保持一致性。只要至少有一个模型成立,估计器就能成功运行;只有两个模型同时失效时,估计器才会崩溃。
AIPW估计器以所有具有重叠倾向性评分的用户的平均处理效应(ATE)为估计目标。这与倾向性匹配所针对的处理组平均处理效应(ATT)不同。将倾向性评分截断至[0.01, 0.99]可将有效人群缩小至具有充分重叠的用户,但估计目标仍然是该特定重叠区域上的ATE。
公式如下:
ATE_AIPW = mean( m1(X) - m0(X) + T*(Y - m1(X)) / e(X) - (1-T)*(Y - m0(X)) / (1 - e(X)) )
其中:
e(X) 是倾向得分:在给定协变量的情况下选择加入的预测概率
m1(X) 是在干预(选择加入)情况下的预测结果
m0(X) 是在对照组(未选择加入)情况下的预测结果
T 是处理指示符(1 = 选择加入,0 = 未选择加入)
Y 是观测到的结果
本教程中的所有小数值都表示比例。估计值0.08等于任务完成率上的8个百分点。
该表达式由两个主要部分组成。第一部分,m1(X) - m0(X),是纯回归调整:它直接对比两个预测结果。第二部分,即IPW修正项,计算实际发生情况与回归预测之间的加权残差。
如果结果模型完美,残差评估为零,修正项消失。如果结果模型有误,只要倾向模型被正确设定,IPW修正项就会对预测误差进行调整。
反过来推演这个逻辑:如果倾向模型正确,IPW修正项本身就能产生无偏估计,而结果模型只需要减小方差。两条路径中的任何一条都足够。只有两者都失败,估计器才会失败。
这个性质被称为双重稳健性,它带来了实际影响。当两个模型都被正确设定且正则条件成立时,AIPW渐近地达到半参数效率界:在大样本中,它能像任何正则估计器一样从数据中提取到尽可能多的统计信息。在实践中,这种效率收益意味着无需收集更多数据即可获得更窄的置信区间。
你需要Python 3.11或更高版本,熟悉pandas和scikit-learn,并对回归和逆概率加权有基本了解。
安装本教程所需的包:
pip install numpy pandas scikit-learn scipy
预期输出:
Successfully installed numpy pandas scikit-learn scipy
这四个包是仅有的依赖项。scikit-learn提供逻辑回归和线性回归模型。scipy用于图表脚本中的KDE。你不需要任何因果推断专用的库。AIPW估计器足够简单,可以从头构建。
克隆配套仓库以获取合成数据集:
git clone https://github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm.git
cd product-experimentation-causal-inference-genai-llm
python data/generate_data.py --seed 42 --n-users 50000 --out data/synthetic_llm_logs.csv
预期输出:
Generated 50000 users → data/synthetic_llm_logs.csv
数据生成器创建了50,000个合成用户,包含参与层级、查询置信度分数、选择加入标记和任务完成结果。代理模式选择加入的真实因果效应为+8个百分点,这被内置到生成器中,以便您可以验证每个估计器是否准确恢复该效应。
该数据集模拟了一个SaaS产品,用户可以选择加入由更强大模型驱动的代理模式。五万名用户的加入率因参与层级而差异显著:重度用户的加入率为65%,中度用户为35%,轻度用户为12%。
任务完成的真实因果效应为+8个百分点。由于选择偏差,加入用户与未加入用户之间的朴素比较将差异夸大了近三倍(AIPW正是专门设计用于纠正这种扭曲的)。
加载数据并计算朴素估计:
import numpy as np
import pandas as pd
df = pd.read_csv("data/synthetic_llm_logs.csv")
T = df["opt_in_agent_mode"].values
Y = df["task_completed"].values
naive_ate = Y[T == 1].mean() - Y[T == 0].mean()
print(f"Naive ATE (unadjusted): {naive_ate:+.4f}")
print(f"N treated: {T.sum()}, N control: {(1-T).sum()}")
预期输出:
Naive ATE (unadjusted): +0.2106
N treated: 13451, N control: 36549
你加载数据集,提取二元治疗指标(opt_in_agent_mode)和二元结果(task_completed),并计算治疗组与对照组之间结果均值的原始差异。
朴素估计达到 +0.2106,超过21个百分点,严重被选择偏差夸大。高参与度用户选择加入的频率远高于低参与度用户,而且无论他们使用哪个模型,他们本来就会完成更多任务。
这种朴素差距主要反映了谁选择了加入。模型变更只贡献了观测差异的一小部分。
这种保证在理论上听起来很简单,而且在数据中也成立:当你运行步骤5中的误设检验时,你会确切看到 +0.2106 中有多少是选择噪声,多少是真实处理效应。
倾向得分是用户在给定其可观测特征的情况下选择加入的预测概率。基于参与度层级和查询置信度的逻辑回归是该数据集正确的起点。
from sklearn.linear_model import LogisticRegression
# Build covariate matrix
X_df = pd.get_dummies(
df[["engagement_tier", "query_confidence"]],
drop_first=True
).astype(float)
X = X_df.values
# Fit propensity model
ps_model = LogisticRegression(max_iter=1000, C=1.0)
ps_model.fit(X, T)
e_hat = ps_model.predict_proba(X)[:, 1]
# Trim extreme propensities for numerical stability
e_hat = np.clip(e_hat, 0.01, 0.99)
print(f"Propensity range: {e_hat.min():.3f} to {e_hat.max():.3f}")
print(f"Mean propensity (treated): {e_hat[T == 1].mean():.3f}")
print(f"Mean propensity (control): {e_hat[T == 0].mean():.3f}")
预期输出:
Propensity range: 0.114 to 0.675
Mean propensity (treated): 0.401
Mean propensity (control): 0.220
predict_proba 方法返回每个用户的类别1概率,即倾向得分。你将值裁剪到 [0.01, 0.99],以防止当倾向值落在边界附近时AIPW公式中出现除零错误(这正是裁剪在实际中所防止的)。
合理性检查确认,处理组的平均倾向得分(0.401)高于对照组(0.220),这与你所预期的选择模式相符,因为重度用户出现在处理组中的频率要高得多,而模型正确地为它们分配了更高的概率。
倾向得分范围0.114至0.675确认了重叠假设成立:没有用户的倾向得分接近0或1,因此每个用户都有实际概率属于任一组。
在接触估计器之前,先运行倾向得分范围检查。像0.114至0.675这样的窄范围确认重叠成立,而接近0或1的值则表明违反假设。
结果模型分别预测处理组和对照组的任务完成情况。
你训练两个独立的回归模型:第一个仅使用处理组用户训练,第二个仅使用对照组用户训练。然后你使用这两个模型来预测数据集中每个用户在每个假设处理分配下的结果。
from sklearn.linear_model import LinearRegression
# Fit outcome model for treated users
m1_model = LinearRegression()
m1_model.fit(X[T == 1], Y[T == 1])
# Fit outcome model for control users
m0_model = LinearRegression()
m0_model.fit(X[T == 0], Y[T == 0])
# Predict counterfactual outcomes for all users
m1_hat = m1_model.predict(X) # predicted outcome if every user were treated
m0_hat = m0_model.predict(X) # predicted outcome if every user were control
# Regression adjustment estimate (outcome model only, no propensity)
ate_regression = (m1_hat - m0_hat).mean()
print(f"Regression adjustment ATE: {ate_regression:+.4f}")
预期输出:
Regression adjustment ATE: +0.0847
你对处理组用户拟合一个线性回归,以了解协变量与该组结果之间的关联,并对对照组用户拟合另一个独立回归。然后你在完整数据集上预测每个用户在接受处理(m1_hat)和未接受处理(m0_hat)条件下的结果。
回归调整估计值对这些预测差异取平均:结果为+0.0847,比简单估计的+0.2106更接近真实值+0.08。
回归调整在此发挥了作用,利用结果模型为每个用户填补缺失的反事实。0.0847与0.0800之间的剩余差距反映了结果模型自身的局限性,而AIPW中的倾向得分修正正是针对这一差距介入的。
在获得倾向得分和两组结果预测后,你可以将它们整合为AIPW公式:
from typing import Tuple
def calculate_aipw_ate(
Y: np.ndarray,
T: np.ndarray,
e_hat: np.ndarray,
m1_hat: np.ndarray,
m0_hat: np.ndarray
) -> Tuple[float, np.ndarray]:
"""
Augmented Inverse-Probability Weighting (AIPW) estimator.
Parameters
----------
Y : array-like, observed outcomes
T : array-like, binary treatment indicators
e_hat : array-like, estimated propensity scores P(T=1|X)
m1_hat : array-like, predicted outcomes under treatment
m0_hat : array-like, predicted outcomes under control
Returns
-------
float : estimated average treatment effect (ATE)
"""
# IPW correction for treated observations
ipw_treated = T * (Y - m1_hat) / e_hat
# IPW correction for control observations
ipw_control = (1 - T) * (Y - m0_hat) / (1 - e_hat)
# AIPW influence function per observation
phi = (m1_hat - m0_hat) + ipw_treated - ipw_control
return phi.mean(), phi
ate_aipw, phi_obs = calculate_aipw_ate(Y, T, e_hat, m1_hat, m0_hat)
print(f"AIPW ATE: {ate_aipw:+.4f}")
print(f"Naive ATE: {naive_ate:+.4f}")
print(f"Regression-only ATE: {ate_regression:+.4f}")
print(f"Ground truth: +0.0800")
预期输出:
AIPW ATE: +0.0847
Naive ATE: +0.2106
Regression-only ATE: +0.0847
Ground truth: +0.0800
该函数为每个观测值计算AIPW影响函数。第一项m1_hat - m0_hat是回归调整。第二项T * (Y - m1_hat) / e_hat是对已处理用户的IPW校正:它取用户实际结果与模型预测之间的残差,然后以倾向得分的倒数对该残差进行加权。由于那些看起来不太可能选择加入的用户在处理组中代表性不足,因此他们会获得较大的权重以进行补偿。第三项对对照组用户应用对称的校正。
将逐观测影响值求平均即可得到AIPW估计值。在该数据集上,结果落在+0.0847,与仅回归估计值一致。当两个模型都被正确设定时,这正是你所预期的:两种方法的结果一致,都接近+0.08的真实值,并且都与朴素的+0.2106相去甚远。该函数还返回phi_obs,即你在第5步的误设定检验中所需的逐观测影响值。
没有置信区间的点估计是不完整的。最简洁的生产级方法是非参数Bootstrap:有放回地对数据进行重抽样,从头开始重新拟合所有内容,然后取各次重抽样估计值分布的百分位数。
def bootstrap_aipw_ci(
df: pd.DataFrame,
X_cols: list,
treatment_col: str,
outcome_col: str,
n_bootstrap: int = 500,
seed: int = 7
) -> Tuple[np.ndarray, float, float]:
"""
Bootstrap AIPW ATE with 95% percentile confidence interval.
Refits propensity model, both outcome models, and AIPW
from scratch on each resample.
"""
rng = np.random.default_rng(seed)
n = len(df)
boot_estimates = []
X_all = pd.get_dummies(df[X_cols], drop_first=True).astype(float).values
T_all = df[treatment_col].values
Y_all = df[outcome_col].values
for _ in range(n_bootstrap):
# Resample with replacement
idx = rng.integers(0, n, size=n)
X_b, T_b, Y_b = X_all[idx], T_all[idx], Y_all[idx]
# Re-fit propensity
ps = LogisticRegression(max_iter=1000, C=1.0)
ps.fit(X_b, T_b)
e_b = np.clip(ps.predict_proba(X_b)[:, 1], 0.01, 0.99)
# Re-fit outcome models
m1 = LinearRegression().fit(X_b[T_b == 1], Y_b[T_b == 1])
m0 = LinearRegression().fit(X_b[T_b == 0], Y_b[T_b == 0])
m1_b = m1.predict(X_b)
m0_b = m0.predict(X_b)
# AIPW on bootstrap sample
ate_b, _ = calculate_aipw_ate(Y_b, T_b, e_b, m1_b, m0_b)
boot_estimates.append(ate_b)
boot_estimates = np.array(boot_estimates)
ci_low = np.percentile(boot_estimates, 2.5)
ci_high = np.percentile(boot_estimates, 97.5)
return boot_estimates, ci_low, ci_high
boot_dist, ci_lo, ci_hi = bootstrap_aipw_ci(
df,
X_cols=["engagement_tier", "query_confidence"],
treatment_col="opt_in_agent_mode",
outcome_col="task_completed",
n_bootstrap=500,
seed=7,
)
print(f"AIPW ATE: {ate_aipw:+.4f}")
print(f"95% Bootstrap CI: [{ci_lo:+.4f}, {ci_hi:+.4f}]")
print(f"Bootstrap std dev: {boot_dist.std():.4f}")
预期输出:
AIPW ATE: +0.0847
95% Bootstrap CI: [+0.0744, +0.0952]
Bootstrap std dev: 0.0053
你通过有放回地重采样50,000行来抽取500个自助法样本。在每次重采样中,你都从头重新拟合倾向性模型,从头重新拟合两个结果模型,并在新数据上计算AIPW估计值。
在每次重采样中重新拟合所有模型很重要:如果你只对固定模型的残差进行重采样,就会低估由模型估计误差引起的变异性。
95%置信区间为[+0.0744, +0.0952],该区间宽裕地包含了真实值+0.0800,并以很大余量排除了朴素的+0.2106。自助法标准差为0.0053,因此你估计中典型的抽样波动约为半个百分点。
双重稳健性质在实践中是成立的。你可以通过一次故意错误设定一个模型,并观察AIPW是否仍然成立,在自己的数据集上进行实证验证。
将所有用户的估计倾向得分替换为一个常数0.3。无论用户的参与度层级或查询置信度如何,每个用户都获得相同的权重,这使得该倾向性模型按设计成为最大程度错误设定的模型。单独的IPW应该会失效。由于结果模型被正确设定,AIPW应该不受影响。
# Scenario 1: constant propensity (e = 0.3 for everyone)
e_wrong = np.full(len(df), 0.3)
# IPW with wrong propensity
t_mask = T == 1
c_mask = T == 0
ate_ipw_wrong = (
(Y[t_mask] / e_wrong[t_mask]).sum() / (1 / e_wrong[t_mask]).sum()
- (Y[c_mask] / (1 - e_wrong[c_mask])).sum() / (1 / (1 - e_wrong[c_mask])).sum()
)
# AIPW with wrong propensity but correct outcome models
ate_aipw_wrong_ps, _ = calculate_aipw_ate(Y, T, e_wrong, m1_hat, m0_hat)
print("=== Scenario 1: constant propensity (e = 0.3) ===")
print(f"IPW with wrong propensity: {ate_ipw_wrong:+.4f} (should be wrong)")
print(f"Regression adjustment (unchanged): {ate_regression:+.4f} (should be ~0.085)")
print(f"AIPW with wrong propensity: {ate_aipw_wrong_ps:+.4f} (should stay ~0.085)")
print(f"Ground truth: +0.0800")
预期输出:
=== Scenario 1: constant propensity (e = 0.3) ===
IPW with wrong propensity: +0.2106 (should be wrong)
Regression adjustment (unchanged): +0.0847 (should be ~0.085)
AIPW with wrong propensity: +0.0847 (should stay ~0.085)
Ground truth: +0.0800
你将每个用户的固定倾向得分替换为0.3,并计算两件事。首先,仅使用错误倾向得分的纯IPW:它产生朴素的+0.2106,因为它无论参与层级如何都对每个人进行同等加权,未能纠正选择模式。
其次,使用错误倾向得分但保留正确拟合的结果模型的AIPW:估计值仍为+0.0847。结果模型项承载着估计的推进,而IPW校正引入的噪声在样本中平均化抵消。一个臂失效了,另一个臂则支撑着估计器继续前行。
现在保留正确估计的倾向得分,但将两个结果模型都替换为常数。对所有用户设置m1_hat = m0_hat = 0.5,这是对每个人50%任务完成率的无信息预测。仅回归调整应归于零。AIPW应不受影响,因为倾向得分模型被正确设定。
# Scenario 2: constant outcome models (m1 = m0 = 0.5 for everyone)
m1_wrong = np.full(len(df), 0.5)
m0_wrong = np.full(len(df), 0.5)
# Regression adjustment with wrong outcome models
ate_regression_wrong = (m1_wrong - m0_wrong).mean()
# Pure IPW with correct propensity (for comparison)
ate_ipw_correct = (
(Y[t_mask] / e_hat[t_mask]).sum() / (1 / e_hat[t_mask]).sum()
- (Y[c_mask] / (1 - e_hat[c_mask])).sum() / (1 / (1 - e_hat[c_mask])).sum()
)
# AIPW with correct propensity but wrong outcome models
ate_aipw_wrong_out, _ = calculate_aipw_ate(Y, T, e_hat, m1_wrong, m0_wrong)
print("=== Scenario 2: constant outcome models (m1 = m0 = 0.5) ===")
print(f"Regression with wrong outcome models: {ate_regression_wrong:+.4f} (should be 0.0)")
print(f"IPW with correct propensity: {ate_ipw_correct:+.4f} (should be ~0.085)")
print(f"AIPW with wrong outcome models: {ate_aipw_wrong_out:+.4f} (should stay ~0.085)")
print(f"Ground truth: +0.0800")
预期输出:
=== Scenario 2: constant outcome models (m1 = m0 = 0.5) ===
Regression with wrong outcome models: +0.0000 (should be 0.0)
IPW with correct propensity: +0.0851 (should be ~0.085)
AIPW with wrong outcome models: +0.0849 (should stay ~0.085)
Ground truth: +0.0800
当结果模型被设定为常量0.5时,回归调整项m1_hat - m0_hat恰好归零,产生一个完全无用的估计值。使用正确设定的倾向性模型的纯IPW方法本身即可恢复+0.0851的估计结果。
使用错误的结果模型但正确的倾向性模型的AIPW方法同样可以恢复+0.0849的估计结果,因为此时IPW校正项承担了全部权重:残差Y - 0.5通过逆倾向性权重被正确重新加权,并平均化到正确的答案。错误的结果模型只会增加方差,估计量仍然保持一致性。
运行这两种情景可以为你提供一种可在任何内部分析文档中纳入的合理性检验。它将双重稳健性从理论性质转化为一个可以展示给怀疑者的具体数字。
AIPW为你提供了一层针对模型误设的保护,但在展示结果之前,值得明确指出其真正的局限性。
双重稳健保证覆盖的是至少有一个模型正确的情况。如果你的倾向性模型遗漏了一个核心混杂变量,而你的结果模型也未能捕捉真实的函数形式,那么AIPW将携带误设程度较轻的那个模型的偏差。
令人不安的现实是:AIPW会原封不动地将未测量的混杂因素带入估计结果。它给你一次免费犯错的机会,而这个极限恰好是一次。
由于部分用户的倾向性得分接近0或1,AIPW公式中的IPW校正项会急剧膨胀。倾向性得分为e_hat = 0.02的用户会产生Y / 0.02 = 50 * Y的校正项,如果该用户的结果观测值异常,这一项可能会主导整个估计量。
如本文所示,将倾向性得分截断(clip)到[0.01, 0.99]区间只能提供最低限度的保护。倾向性修剪(propensity trimming)——即将具有极端得分的用户从分析中剔除——是更干净的解决方案,尽管它会改变估计目标:此时你估计的是重叠区域上的ATE,这是一个比完整数据集更窄的总体。请明确记录这一选择。
AIPW在大样本下达到半参数效率界。当观测值仅有500或1,000个时,IPW校正项带来的方差膨胀可能相当可观,自助法(bootstrap)置信区间也会很宽。
在非常小的实验中,朴素回归调整可能会给出更紧凑的区间,即使其针对误设的理论保护较弱。AIPW的效率优势是一个大样本性质。
逻辑回归是一个合理的默认选择,但如果真实的选入机制涉及主效应模型无法表示的高阶交互作用,倾向性模型将以平衡性诊断无法捕捉的方式出现系统性偏差。
对干扰组件使用更灵活的模型(梯度提升、随机森林)可以在大样本下提升性能,但需要交叉拟合(cross-fitting):在留出的折叠数据上拟合倾向性和结果模型后再进行预测,这样它们的训练误差就不会泄漏到AIPW计算中并使最终估计产生偏差。交叉拟合是目标最大似然估计(TMLE)背后的核心技术框架。
本教程中从零开始的实现展示了其内在机制。你的生产环境设置需要本版本所缺少的两项能力:用于在使用灵活模型时防止过拟合偏差的交叉拟合,以及能够灵活适应数据中信号的数据自适应干扰模型。本教程中的从零实现版本如果不进行交叉拟合,将无法支撑一项严肃的观察性研究。
TMLE的Python实现可在专门的因果推断库中找到,它们各自以AIPW原则为基础并同时增加了这两项能力。TMLE直接以感兴趣的估计目标为靶向,在使用机器学习模型估计倾向性和结果组件时校正正则化偏差,并且即使在干扰模型从你所分析的同一数据中估计的情况下,也能产生有效的置信区间。
Lyft工程团队发表了一篇关于其网约车因果推断双重稳健管线的详细报告,在构建生产级系统之前值得一读(Nassiri & Chu, Lyft Engineering, 2026)。
在理论背景方面,AIPW背后的保证可追溯到Robins、Rotnitzky和Zhao(Robins et al., 1994)的研究,这之所以重要,是因为它精确地告诉你该方法的保证在哪里终止,以及你自己的建模判断从哪里开始。
与这里所展示内容最为契合的实践实施指南是加州大学伯克利分校Mark van der Laan开发的目标学习框架(van der Laan & Rose, 2011)。
本教程的配套笔记本位于github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm/tree/main/12_doubly_robust。克隆该仓库,生成合成数据集,然后打开aipw_demo.ipynb即可复现本教程中的每个代码块,包括误设情景。
你的生产环境观察性分析包含两个你希望合并为一个的近似。在你自己数据上运行第5步中的误设检验:倾向性诊断将告诉你倾向性分支承担了多少权重,而结果模型中的残差分布将告诉你回归调整分支在做多少工作。
AIPW之所以有效,是因为它正是为以下情况而设计的:两个模型都未被验证,但又都在发挥作用。如果其中一个模型成立,估计量也就能成立。
——
一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。