← 返回蜂巢洞察

在大型语言模型应用中,当你的两个模型都出现错误时,如何通过双重鲁棒性估计方法来进行产品测试与优化?

六个月前,你们推出的这款人工智能产品开始采用“用户主动选择参与”的模式。你们进行了倾向性分析,并考虑了用户的参与程度以及查询的准确性等因素,最终发现任务完成率确实提高了8个百分点。这一数据被纳入了季度业务报告中,大家都对此感到满意。 然而,严谨的数据科学家难免会提出一些棘手的问题:你们有多确定这个倾向性模型已经考虑到了所有可能影响结果的因素?如果遗漏了某些因素,那么逻辑回归模型得出的选择概率就会不准确;又或者,如果结果回归模型的设定本身就有误——因为任务完成率与查询准确性之间的关系并非线性的,线性模型根本无法准确反映这一关系——那又会怎样呢? 你们有两个模型,但并不确定哪个是正确的,而这两个模

六个月前,你们推出的这款人工智能产品开始采用“用户主动选择参与”的模式。你们进行了倾向性分析,并考虑了用户的参与程度以及查询的准确性等因素,最终发现任务完成率确实提高了8个百分点。这一数据被纳入了季度业务报告中,大家都对此感到满意。

然而,严谨的数据科学家难免会提出一些棘手的问题:你们有多确定这个倾向性模型已经考虑到了所有可能影响结果的因素?如果遗漏了某些因素,那么逻辑回归模型得出的选择概率就会不准确;又或者,如果结果回归模型的设定本身就有误——因为任务完成率与查询准确性之间的关系并非线性的,线性模型根本无法准确反映这一关系——那又会怎样呢?

你们有两个模型,但并不确定哪个是正确的,而这两个模型都对于最终的分析结果起着关键作用。

默认情况下,采用“用户主动选择参与”模式的产品都会遇到这样的问题。在那些没有进行随机化的基于大语言模型的实验中,你们会得到那些选择参与的用户与那些没有选择参与的用户的结果数据。

问题在于,这些分组是用户自己选择的。因此,任何你构建出来的用于分析因果关系的模型,其实都只是对未知真相的一种近似。

如果倾向性模型有误,那么仅依靠倾向性加权进行数据分析也是无效的;同样,如果结果回归模型的设定有误,单纯的回归分析也会得出错误的结果。每种方法都把所有希望寄托在某个模型能够被正确设定的前提上。

而双重稳健估计方法,尤其是增强型逆概率加权(AIPW)估计器,则采取了不同的策略。它将倾向性模型和结果模型结合在一起,形成一个统一的估计器。只有当这两个模型同时出错时,AIPW估计器的效果才会受到影响。

这种可靠性来自于该估计器所依据的半参数效率理论——这一数学特性被内置于其计算机制中。可以将其理解为一种用于保证因果分析结果可靠性的“冗余设计”机制,它依靠的是与分布式系统中那种在单个节点出现故障时仍能保持系统正常运行的容错逻辑。

在本教程中,你们将使用scikit-learn从零开始实现AIPW估计器,还会添加自助法置信区间,并通过故意破坏其中一个模型来验证这种双重稳健性。对于那些在进行数据实验时发现每个模型都只是近似值的数据科学家来说,这一框架能够确保你们的分析结果具有可靠性。

本教程中的每一个代码示例都可以在github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm/tree/main/12_doubly_robust/处的配套笔记本中完整运行。该笔记本文件的名称为aipw_demo.ipynb

目录

  • 为什么没有一个模型能赢得你的信任

  • 双重稳健估计方法究竟能起到什么作用

  • 先决条件

  • 准备工作

    为什么这两种模型都无法赢得你的信任

    要想让倾向得分方法取得成功,有一个关键条件:必须构建一个能够准确反映所有混淆因素的倾向模型。而要进行回归分析,同样需要满足一个前提:必须拥有一个能够正确描述协变量与结果之间关系的结果模型。在实践中,这两个条件都十分重要,但人们往往很难判断自己是否满足了这些要求。

    在大型语言模型的自愿参与分析中,倾向模型会存在三种具体的问题。首先,事件日志中的各项数据其实属于用户做出自愿参与决定之后的行为数据。用户的查询置信度得分反映的只是模型在接收到该查询时所做的评估,而用户选择是否自愿参与的真正动机则完全不在你的测量范围之内。

    其次,逻辑回归无法自动捕捉非线性关系。如果企业套餐中的高级用户与个人套餐中的高级用户在选择自愿参与方面的比例存在显著差异,那么仅使用主要效应逻辑模型就会完全忽略这一差异。第三,那些未被测量的混淆因素在模型中根本无法被考虑进去。例如,如果经常阅读你公司技术博客的用户选择自愿参与的比例远高于其他用户,而你却缺乏这一数据作为依据,那么无论你的倾向模型调整得多么精确,它都会给出错误的判断结果。

    结果模型的失败原因则有所不同。在大型语言模型系统中,任务的完成情况取决于查询的复杂性,而这种复杂性往往带有很大的不确定性;它还取决于所使用的模型版本,而这些信息可能并没有被你作为协变量纳入模型中;此外,用户是否处于使用了自定义系统提示的企业工作环境中,也会影响任务完成结果——而这些因素很可能根本不会出现在你的数据日志中。如果对这些协变量进行线性回归分析,那么模型的功能形式很可能会出错,而且偏差的方向也是不可预测的。

    实际操作中遇到的问题是,你无法通过任何测试来确证这两种模型中的哪一种是正确的。平衡诊断方法可以评估倾向模型的质量,但它们的作用仅限于此,并不能检测出那些未被测量的混淆因素;残差图虽然能说明结果模型与观测数据之间的匹配程度,但却无法揭示你的协变量中究竟遗漏了哪些信息。即使你对这两种模型进行了改进,也仍然无法确定是否真正解决了根本问题。这其实比听起来要困难得多。

    任何基于倾向性的因果分析都必须满足三个基本假设。只有当这三个假设都成立时,AIPW或其他任何估计方法才能给出有效的因果结论。

    1. 无混淆性(也称为“强忽略性”):所有那些同时影响用户是否选择自愿参与以及任务完成情况的变量都必须被测量并纳入模型中。

    2. 重叠性:每个用户都有属于实验组或对照组的概率,不存在某个子群体肯定会选择自愿参与或不会选择的情况。

    3. 单一处理效应性:每个用户的潜在结果都不会受到其他用户是否接受处理的影响,并且处理方式也只有一种。AIPW放宽了模型必须严格满足这些假设的要求,但这些假设本身并不会因此消失。它们仍然必须在数据中得到验证,任何方法上的巧妙设计都无法改变这一事实。

    双重稳健估计的实际作用

    AIPW能够提供单一模型方法所无法实现的数学保障:只要倾向性模型或结果模型中的任意一个被正确设定,估计结果就不会出现偏差。只要至少有一个模型是正确的,这种估计方法就能产生可靠的结果;只有当这两个模型同时出错时,估计结果才会失效。

    AIPW估计量关注的是所有倾向得分有重叠的用户群体的平均处理效应,这与倾向匹配所关注的“接受处理者的平均处理效应”是不同的。通过将倾向值范围限定在[0.01, 0.99]之间,可以有效缩小样本范围,只保留倾向得分有足够重叠的用户群体,但此时估计量仍然代表的是该特定范围内的平均处理效应。

    计算公式如下:

    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校正就能弥补这些预测误差。

    反过来想,如果倾向性模型的设定是正确的,IPW校正项本身就已经能够产生无偏的估计结果,此时结果模型只需要起到减少方差的作用即可。因此,只要其中一个模型是正确的,估计方法就能够正常运行;只有当两个模型同时出错时,估计结果才会失效。

    图1(AIPW的双模型结构):AIPW采用双模型结构设计,倾向性模型和结果模型各自提供冗余的保护机制。只要其中任何一个模型设定正确,估计结果就不会出现偏差。

    这种特性被称为“双重稳健性”,它在实际应用中具有非常重要的意义。当两个模型都被正确设定且满足相关规则条件时,AIPW会逐渐趋近于半参数效率的上限——在大规模样本中,它能够像任何常规估计方法一样从数据中提取尽可能多的统计信息。在实际应用中,这种效率提升意味着可以在不增加数据收集量的情况下获得更精确的置信区间。

    先决条件

    您需要使用 Python 3.11 或更高版本,同时需要对 pandas 和 scikit-learn 有基本的了解,并掌握回归分析及逆概率加权的相关概念。

    请安装本教程所需的软件包:

    pip install numpy pandas scikit-learn scipy

    预期输出结果:

    成功安装了 numpy、pandas、scikit-learn 和 scipy

    这四个软件包就是您所需要依赖的全部内容。scikit-learn提供了逻辑回归和线性回归模型;scipy则用于图表脚本中的核密度估计功能。您不需要任何专门用于因果推断的库,因为 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

    预期输出结果:

    搭建实验环境
    

    该数据集模拟了一个 SaaS 产品环境,在这种环境中,用户可以选择是否使用由更强大的模型支持的高级功能。共有 50,000 名用户,他们选择使用高级功能的比例因参与程度不同而有所差异:重度用户中有 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:+.4f}")
    print(f"实验组用户数量:{T.sum()}, 对照组用户数量:{(1-T).sum()}")
    

    预期输出结果:

    步骤1:建立倾向性模型
    

    倾向性得分是指根据用户的可观察特征预测出的该用户选择参与调查的概率。对于这个数据集来说,使用逻辑回归分析用户的参与程度和查询的置信度是一个合适的起点。

    from sklearn.linear_model import LogisticRegression
    
    # 构建协变量矩阵
    X_df = pd.get_dummies(
        df[["engagement_tier", "query_confidence"],
        drop_first=True
    ).astype(float)
    X = X_df.values
    
    # 建立倾向性模型
    ps_model = LogisticRegression(max_iter=1000, C=1.0)
    ps_model.fit(X, T)
    
    e_hat = ps_model.predict_proba(X)[:, 1]
    
    # 为了保证数值的稳定性,对极端值进行裁剪
    e_hat = np.clip(e_hat, 0.01, 0.99)
    
    print(f"倾向性得分的范围:{e_hat.min():.3f} 到 {e_hat.max():.3f}")
    print(f"处理组的平均倾向性得分:{e_hat[T == 1].mean():.3f}")
    print(f>对照组的平均倾向性得分:{e_hat[T == 0].mean():.3f}")
    

    预期输出:

    倾向性得分的范围:0.114 到 0.675
    处理组的平均倾向性得分:0.401
    对照组的平均倾向性得分:0.220
    

    你需要将用户的参与程度这一分类变量进行独热编码,同时保持查询置信度这一连续变量的数值不变,然后使用逻辑回归模型来预测用户是否选择参与调查。

    predict_proba方法会返回每个用户属于第一类的概率,这个概率就是他们的倾向性得分。将倾向性得分的值裁剪到[0.01, 0.99]的范围内,是为了避免在计算AIPW公式时出现除以零的错误——因为当倾向性得分接近边界值时,确实容易发生这种错误。

    通过合理性检查可以确认:处理组的平均倾向性得分(0.401)确实高于对照组(0.220),这一结果与预期的情况相符——毕竟高参与度的用户更有可能被纳入处理组,而模型也确实会给他们赋予更高的倾向性概率。

    倾向性得分的范围为0.114到0.675,这一事实证明了“重叠假设”是成立的:没有用户的倾向性得分接近0或1,因此每个用户都有可能性属于任意一组。

    在开始使用任何估计方法之前,先进行倾向性得分范围的检查是非常重要的。如果范围像0.114到0.675这样较窄,那就说明重叠假设是成立的;而如果数值接近0或1,那就说明存在违规情况。

    图2(倾向性得分重叠图):在这个包含50,000名用户的合成数据集上,处理组(选择了参与调查的13,451名用户)和对照组(没有选择参与调查的36,549名用户)的分布在整个倾向性得分范围内都有重叠部分,这一事实证明了“正性假设”是成立的。下图显示了按参与程度划分的处理组和对照组的用户数量。” height=

    步骤2:拟合结果模型

    这些结果模型会分别预测实验组与对照组的任务完成情况。

    你需要进行两次独立的回归分析:第一次仅使用实验组的数据进行训练,第二次则仅使用对照组的数据;之后再利用这两种模型,在每种假设的处理分配情况下,预测数据集中每个用户的最终结果。

    from sklearn.linear_model import LinearRegression
    
    # 为实验组用户拟合结果模型
    m1_model = LinearRegression()
    m1_model.fit(X[T == 1], Y[T == 1])
    
    # 为对照组用户拟合结果模型
    m0_model = LinearRegression()
    m0_model.fit(X[T == 0], Y[T == 0])
    
    # 预测所有用户的反事实结果
    m1_hat = m1_model.predict(X)   # 如果所有用户都接受实验处理,他们的预期结果
    m0_hat = m0_model.predict(X)   # 如果所有用户都处于对照组,他们的预期结果
    
    # 回归调整估计值(仅基于结果模型,不考虑倾向得分)
    ate_regression = (m1_hat - m0_hat).mean()
    print(f"回归调整后的平均处理效应:{ate_regression:+.4f}")
    

    预期输出:

    回归调整后的平均处理效应:+0.0847
    

    你首先对实验组用户进行线性回归分析,以了解这些协变量与该组结果之间的关系;然后对对照组用户也进行类似的回归分析。之后,你可以利用这些模型预测数据集中每个用户在接受实验处理或处于对照组时的预期结果(分别为m1_hatm0_hat)。 回归调整估计值实际上是对这些预测差异的平均值,其值为+0.0847,这个数值比简单的+0.2106要接近真实值+0.08得多。

    在这里,回归调整发挥了重要作用——它利用结果模型来填补每个用户缺失的反事实数据。而0.0847与0.0800之间的差距,则反映了结果模型本身存在的局限性,而这正是AIPW方法中倾向得分校正所需要弥补的地方。

    步骤3:将结果整合到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]:
        """
        增强逆概率加权(AIPW)估计器。
    
        参数
        ----------
        Y       : 数组类型数据,实际观察结果
        T       : 数组类型数据,二进制处理标识符
        e_hat   : 数组类型数据,倾向得分估计值
        m1_hat  : 数组类型数据,实验组预期结果
        m0_hat  : 数组类型数据,对照组预期结果
    
        返回值
        -------
        float : 平均处理效应估计值
        numpy.ndarray: 倾向得分数组
    
        """
        # 对实验组观测数据进行IPW校正
        ipw_treated = T * (Y - m1_hat) / e_hat
    
        # 对对照组观测数据进行IPW校正
        ipw_control = (1 - T) * (Y - m0_hat) / (1 - e_hat)
    
        # 计算每个观测值的AIPW影响函数
        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_aipw:+.4f}")
    print(f>简单平均处理效应:{naive_ate:+.4f}")
    print(f>仅基于回归分析的平均处理效应:{ate_regression:+.4f}")
    print(f>真实值:+0.0800")
    

    预期输出:

    AIPW平均处理效应:+0.0847
    朴素平均处理效应:+0.2106
    仅使用回归模型的平均处理效应:+0.0847
    真实值:+0.0800
    

    该函数会计算每个观测值的AIPW影响值。第一项m1_hat - m0_hat代表回归调整部分;第二项T * (Y - m1_hat) / e_hat用于对接受处理的用户进行IPW校正:它取这些用户的实际结果与模型预测值之间的差异,然后根据其倾向概率的倒数对其进行加权调整。由于那些被认为不太可能选择参与实验的用户在处理组中的比例较低,因此会给予他们较大的权重以进行补偿;第三项则用于对对照组用户进行对称校正。

    将每个观测值的影响值平均后,就可以得到AIPW的估计值。在这个数据集上,该估计值为+0.0847,与仅使用回归模型得到的结果相同。当两个模型的设定都正确时,就会出现这种情况:两种方法的计算结果一致,且都接近真实值+0.08,同时也都远高于朴素估计值+0.2106。此外,该函数还会返回phi_obs,即你在第5步进行模型设定错误检测时所需要的每个观测值的影响值。

    步骤4:自助法置信区间

    如果没有置信区间,那么仅有的点估计值就是不完整的。最稳妥的处理方法是采用非参数自助法:通过有放回的方式重新抽样数据,然后从头开始重新拟合所有模型,并计算每次重采样后估计值分布的百分位数。

    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]:
        """
        使用自助法计算AIPW平均处理效应,并给出95%置信区间。
    
        在每次重采样时,都会从头开始重新拟合倾向概率模型、结果预测模型以及AIPW计算公式。
        """
        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):
            # 有放回地重新抽样
            idx = rng.integers(0, n, size=n)
            X_b, T_b, Y_b = X_all[idx], T_all[idx], Y_all[idx]
    
            # 重新拟合倾向概率模型
            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)
    
            # 重新拟合结果预测模型
            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平均处理效应
            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 = nppercentile(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_aipw:+.4f}")
    print(f"95%自助法置信区间:[{{ci_lo:+.4f}}, {ci_high:+.4f}]")
    print(f>自助法标准差:{boot_dist.std():.4f}")
    

    预期输出:

    AIPW ATE:+0.0847
    95% 自举置信区间:[+0.0744, +0.0952]
    自举标准差:0.0053

    你通过重复抽样50,000条记录来获取500个自举样本。在每次重采样时,你需要从头开始重新构建倾向性模型和结果模型,并根据这些新数据计算AIPW值。

    在每次重采样时都重新构建所有模型是至关重要的:如果你只对固定模型中的残差进行重抽样,就会低估由于模型估计误差所导致的变异程度。

    95%置信区间为[+0.0744, +0.0952],这个区间完全包含了真实值+0.0800,并且将错误的估计值+0.2106远远排除在外。自举标准差为0.0053,因此你的估计值通常会存在大约半个百分点的抽样误差。

    步骤5:通过故意设定错误模型来验证双重稳健性

    在实际应用中,双重稳健性这一特性依然成立。你可以通过在自己的数据集上逐一故意设置错误的模型,然后观察AIPW值是否仍然保持稳定,从而对其进行实证验证。

    情景1:倾向性模型错误,结果模型正确

    将所有用户的倾向性得分都替换为常数0.3。这样一来,无论用户的参与程度或查询的可靠性如何,他们都会获得相同的权重,这种设定显然属于故意设置错误的倾向性模型。在这种情况下,仅使用IPW进行计算就会得出错误的结果;而由于结果模型是正确设定的,AIPW值应该不会受到影响。

    # 情景1:所有用户的倾向性得分均为0.3
    e_wrong = np.full(len(df), 0.3)
    
    # 使用错误的倾向性得分计算IPW
    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
    ate_aipw_wrong_ps, _ = calculate_aipw_ate(Y, T, e_wrong, m1_hat, m0_hat)
    
    print("=== 情景1:所有用户的倾向性得分均为0.3 ===")
    print(f"使用错误的倾向性得分计算的IPW:{ate_ipw_wrong:+.4f} (结果应该是错误的)")
    print(f"经过回归调整后的值:{ate_regression:+.4f} (应该约为0.085)")
    print(f"使用错误的倾向性得分和正确的结果模型计算的AIPW:{ate_aipw_wrong_ps:+.4f} (结果应该仍然约为0.085)")
    print(f"真实值:+0.0800")
    

    预期输出:

    === 情景1:所有用户的倾向性得分均为0.3 ===
    使用错误的倾向性得分计算的IPW:+0.2106 (结果应该是错误的)
    经过回归调整后的值:+0.0847 (应该约为0.085)
    使用错误的倾向性得分和正确的结果模型计算的AIPW:+0.0847 (结果应该仍然约为0.085)
    真实值:+0.0800
    <你可以将每个用户的“平坦倾向值”替换为0.3,然后计算两项数值。首先,使用仅包含这个错误倾向值的模型来计算纯粹的IPW值:由于该模型会忽略用户的参与程度等级,对所有用户进行平均加权处理,因此计算结果为+0.2106;这种做法显然没有纠正用户选择行为所体现出的规律性。>

    其次,即使使用了正确的倾向性评分,但如果结果模型设定错误,估计值仍然会保持为+0.0847。结果模型中的各项参数会继续影响这一估计值,而IPW校正方法则会引入一些噪声,但这些噪声在样本整体中会相互抵消。当其中一个因素出现异常时,另一个因素仍能保证估计值的稳定性。

    情景2:结果模型错误,倾向性模型正确

    现在,我们保持倾向性评分的估算值不变,但将两个结果模型都替换为常数。对于所有用户,我们将m1_hat = m0_hat = 0.5设为固定值,这意味着所有人完成任务的概率都被预测为50%。仅进行回归调整的话,其结果应该会变为零;而由于倾向性模型设定正确,AIPW的值也应该不会受到影响。

    # 情景2:结果模型均为常数(所有用户的m1 = m0 = 0.5)
    m1_wrong = np.full(len(df), 0.5)
    m0_wrong = np.full(len(df), 0.5)
    
    # 使用错误的结果模型进行回归调整
    ate_regression_wrong = (m1_wrong - m0_wrong).mean()
    
    # 使用正确的倾向性评分进行纯IPW计算(用于对比)
    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计算
    ate_aipw_wrong_out, _ = calculate_aipw_ate(Y, T, e_hat, m1_wrong, m0_wrong)
    
    print("=== 情景2:结果模型均为常数(所有用户的m1 = m0 = 0.5) ===")
    print(f"使用错误的结果模型进行回归调整:{ate_regression_wrong:+.4f} (应为0.0)")
    print(f"使用正确的倾向性评分进行IPW计算:{ate_ipw_correct:+.4f} (应为约0.085)")
    print(f"使用错误的结果模型进行AIPW计算:{ate_aipw_wrong_out:+.4f} (也应约为0.085)")
    print(f"真实值:+0.0800")
    

    预期结果:

    === 情景2:结果模型均为常数(所有用户的m1 = m0 = 0.5) ===
    使用错误的结果模型进行回归调整:+0.0000  (应为0.0)
    使用正确的倾向性评分进行IPW计算:+0.0851  (应为约0.085)
    使用错误的结果模型进行AIPW计算:+0.0849  (也应约为0.085)
    真实值:+0.0800
    

    当结果模型被设定为0.5时,回归调整项m1_hat - m0_hat的值会变为零,这个估计值完全没有任何实际意义;而使用正确的倾向性模型进行纯IPW计算后,其结果仍然是+0.0851。即使结果模型设定错误,但只要倾向性模型正确,AIPW的计算结果也会是+0.0849,因为IPW校正项会承担所有的权重作用,残差Y - 0.5会通过逆倾向性评分得到正确的加权处理,最终得出正确的结果。结果模型设定错误只会增加数据的方差,但估计值本身的稳定性不会受到影响。

    通过这两种情景的测试,你可以对自己所做的分析进行验证,这种验证方法可以将“双重稳健性”这一理论概念转化为一个具体的数字,从而让持怀疑态度的人也能理解其合理性。

    当双重稳健估计失败时

    AIPW为防止模型设定错误提供了一层保护机制,在呈现研究结果之前,有一些重要的限制因素需要加以注意。

    两种模型同时存在设定错误

    双重稳健性的保障适用于至少有一种模型是正确的情形。如果倾向性模型遗漏了某个关键混杂因素,而结果模型也没有准确反映真实的函数关系,那么AIPW就会受到其中错误程度较小的那个模型的影响,从而产生偏差。

    一个不容忽视的现实是:AIPW会原封不动地保留那些未被测量的混杂因素。它允许你犯一次“错误”,但这个“错误”的次数最多只能是一次。

    极端的倾向性得分会导致方差增大

    由于有些用户的倾向性得分接近0或1,因此在AIPW公式中用于进行校正的项的值会变得非常大。例如,如果某用户的倾向性得分为e_hat = 0.02,那么相应的校正系数就会变为Y / 0.02 = 50 * Y;如果该用户的研究结果属于异常情况,这种校正因素就可能会严重影响整个估计结果的准确性。

    将用户的倾向性得分限制在[0.01, 0.99]的范围内确实能提供一定的保护作用,但这种做法效果有限。更有效的解决办法是剔除那些倾向性得分极端的用户,不过这样做会改变最终估计的对象范围——你实际上是在对重叠部分的数据进行估计,而这个样本规模要比整个数据集小。因此,在采用这种方法时必须明确说明这一选择的原因。

    有限样本下的方差会超出渐近理论的预测值

    在样本量较大的情况下,AIPW确实能够达到半参数效率的上限。然而当观察值数量仅为500或1,000个时,由于IPW校正项的存在,方差可能会显著增加,从而导致自助法构建的置信区间变得很宽。

    在样本量非常小的实验中,即使AIPW在理论上对模型设定错误具有较弱的防护能力,采用简单的回归调整方法也可能得到更精确的置信区间。不过,AIPW的效率优势主要体现在大样本情况下。

    选择适合两个组分的模型仍然需要判断

    逻辑回归通常是一个合理的默认选择,但如果真正的选择机制涉及到高阶交互作用,而主效应模型无法体现这些交互作用,那么倾向性模型就会系统性地出现错误,而这些错误是常规的诊断方法无法发现的。

    对于那些会带来干扰的组分,使用更加灵活的建模方法(如梯度提升算法或随机森林模型)确实可以在大样本情况下提高估计的准确性,但这种方法需要通过交叉验证来进行调整:在预测之前,先使用一部分数据来训练倾向性模型和结果模型,这样就可以避免它们的训练误差影响到AIPW的计算结果,从而避免产生偏差。交叉验证也是目标最大似然估计方法的基础。

    策略性的应用方法

    本教程中从零开始实现的示例展示了相关原理。不过,在实际应用中,你的研究环境还需要另外两个要素,而这些要素当前这个版本是缺乏的:首先是需要进行交叉适配,这样才能避免在使用灵活模型时出现过拟合现象;其次,还需要使用能够根据数据中的信号特征进行调整的数据自适应模型。如果不进行交叉适配,本教程中提供的从零开始实现的方案是无法帮助你完成任何复杂的观测研究的。在专门的因果推断库中,可以找到基于Python实现的TMLE方法。这些实现都遵循AIPW原则,并对其进行了进一步优化。TMLE直接针对感兴趣的估计量进行计算;当使用机器学习模型来处理倾向性和结果变量时,它还能有效纠正正则化偏差;此外,即使那些干扰性模型也是利用你正在分析的同一数据估算出来的,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方法之所以能够发挥作用,正是因为它正是为这种既没有对任何一个模型进行过验证、同时又同时使用这两种模型的情况而设计的。如果其中一个模型是正确的,那么估计结果也会是准确的。

相关文章

技术实践

如何构建用于确保在高峰时段能够正常运行的自动化工作负载模型

如果你曾经花费两天时间从APM工具中提取数据,只是为了回答“在我的负载测试中应该使用多少个虚拟用户?”这个问题,那么这个教程非常适合你。 通过学习这个教程,你将了解到如何在不到五分钟的时间内,直接从生产环境中的实时数据中计算出工作负载模型中所需的各个数值,而无需进行任何估算。 我们的应用场景: 每年,在黑色星期五之前的几周,性能工程师和运维人员都会面临同样的问题:有人需要为高峰期的负载测试建立工作负载模型,而且时间非常紧迫。 通常的做法是登录APM工具,导出CSV文件,在电子表格中对数据进行处理,然后根据这些数据对用户行为进行推测。这个过程需要花费数天的时间,还需要多个人参与,但最终得到的结果

阅读全文
技术实践

用于估算人工智能提示设计效果的产品实验反事实方法

想象一下,你的团队在两周前将A提示在全球范围内推广使用。由于时间紧迫且对效果充满信心,因此在没有进行任何A/B测试、也没有设置测试对照组或保留用户群的情况下,这一更新就应用到了100%的用户身上。 虽然各项完成率看起来都很稳定,但深夜时一位同事从测试环境中提交了一个新的提示方案,这就引发了一个重要的问题:这个替代方案是否才是更合适的上线选择呢? 现在你陷入了这些已记录的数据所构成的困境之中。这个问题看似无解,但实际上并非如此。产品团队会通过前瞻性实验来观察如果推出某个功能会发生什么结果;而反事实估算则能帮助我们了解:如果选择了其他方案,最终会得到什么样的结果。 对于那些负责处理大语言模型产品日

阅读全文
技术实践

如何使用MONAI在超声数据上训练肿瘤分割模型

大多数分割教程都是从选择一个模型开始,将图像输入该模型中,然后调整超参数直到相关指标得到改善。但这种方法忽略了通常最为关键的一步:理解数据本身。 在本教程中,我们首先会对数据集进行详细分析,随后会根据这些分析结果来决定MONAI分割流程中的每一个设计细节。 我们将涵盖以下内容: 本教程适合谁? 关于数据集 什么是MONAI,为什么使用它? 什么是Dice评分? 第1部分——建模前的数据分析 类别平衡对分割结果的影响 患者数量对数据划分的影响 第2部分——构建分割流程 单一配置对象 按患者分组的数据划分方式 由快照自动选择的转换操作 模型、损失函数与评估指标 结果解读 预测结果可视化 失败模式比

阅读全文
技术实践

人工智能评估工程:从零开始构建一款可用于生产环境的大型语言模型评估平台【完整使用手册】

一个令人印象深刻的演示与一个值得信赖的系统之间的差距,其实是通过各种评估来衡量的。 我想先讲一个目前正在数百个工程团队中发生的真实案例。 有一个团队为法律研究开发了一个RAG应用程序。他们用40个精心挑选的问题对该程序进行了测试,结果看起来很不错,于是便向合作方展示了这个系统。合作方对它印象深刻,随后便决定将其正式投入使用。 然而在系统投入生产三周后,一名法律助理发现其中一个答案错误地引用了某项法规。工程团队查看了相关数据,发现“准确性得分”为0.91,这个数值看起来是正常的;他们还检查了答案的相关性,结果也符合标准。 但他们忽略了一个重要的指标:即“上下文完整性”。这个指标用于判断系统是否检

阅读全文