利用工具变量进行产品实验:在Python环境中消除干扰因素,从而帮助大型语言模型做出更合理的路由决策
对于那些负责管理多模型路由系统的数据科学专家和产品经理来说,使用传统的回归分析方法来评估模型质量其实存在根本性的缺陷。无论你是否意识到这一点,你实际上都在进行因果推断实验;而你的路由规则正在潜移默化地影响你对系统性能的评估结果。 以这样一个路由系统为例:它会根据查询的置信度阈值,将请求要么转发给高级模型,要么转发给速度更快、成本更低的替代模型。如果查询的置信度低于某个阈值,就会选择高级模型进行处理;而高于该阈值的查询,则会使用低成本模型。如果你分析相关日志,并对路由决策结果进行回归分析,可能会发现使用高级模型处理请求确实能让系统性能提升14个百分点。基于这一数据,你的基础设施团队或许会提议将所
配套笔记本:本文中提到的每一个代码示例,都可以在github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm/tree/main/11_instrumental_variables/处的配套仓库中的iv_demo.ipynb文件中完整运行。
目录
为什么路由机制会干扰回归分析的结果
路由系统会根据某些相关性因素来决定将查询请求分配到哪种服务类型。那些置信度较低、字符长度较长或意图表达较为复杂的查询请求,会被优先分配到高级服务;而那些简短明了、完全符合低级服务处理能力的查询请求,则会被分配到低级服务。这种相关性正是路由机制的核心所在。
问题在于,那些影响路由决策的因素同样也会影响到你真正关心的分析结果。
对于复杂的查询请求来说,无论使用哪种模型来处理它们,完成这些任务的难度都会增加。当你构建模型公式task_completed ~ routed_to_premium + controls时,controls这个变量就可以用来涵盖那些可观察到的影响因素,比如查询长度、用户的使用等级以及你记录的其他相关数据。
然而,那些无法被观测到的因素仍然会潜藏在routed_to_premium这个系数中,从而对分析结果产生干扰。这种干扰可能会导致估计值偏低(因为复杂的查询请求较少被分配到高级服务,从而使高级服务的效果看起来不如实际那样好),或者导致估计值偏高,具体取决于这些干扰因素的方向。
在本教程使用的合成数据集中,OLS模型的估计结果显示效应为+3.3个百分点,而实际上真正的因果效应应该是+6个百分点。这种2.7个百分点的偏差完全是由那些无法被观测到的查询复杂度因素所导致的。
回归分析的结果看起来很可靠,p值也具有显著性,标准OLS分析的输出中也没有任何迹象表明存在问题。正是这种隐蔽性使得这种故障模式极其危险:在标准的回归诊断工具中,根本无法发现它的存在。
2SLS方法正是为了解决这类问题而设计的。在这种情况下,我们需要一个与查询质量无关的、能够引起路由变化的外部因素,而限流机制所引发的回退机制正好满足了这一需求。
图1:工具变量的因果结构。这个工具变量(Z = 限流回退机制)满足了相关性要求(Z能够影响路由决策)、排他性要求(Z与结果路径没有直接的关联),同时也满足独立性要求(Z与未观测到的查询复杂度无关)。虚线红色箭头表示那些会导致OLS分析出现偏差的混杂因素路径。
什么是工具变量
工具变量是一种能够影响内生变量(即路由决策),但却不会通过任何其他直接途径影响到结果变量(任务完成情况)的变量。
一个有效的工具变量需要满足四个假设条件。
相关性
工具变量必须真正能够影响内生变量。例如,如果某个限流回退机制会在15%的高优先级查询请求时被触发,那么这一机制就必然会影响到这些查询是否会被分配到高优先级处理路径上。
这个假设是可以进行检验的:可以通过第一阶段的F统计量来进行验证。传统的判断标准是F值大于10,这一标准是由Staiger和Stock在1997年提出的,这意味着在最坏的情况下,使用2SLS方法得到的估计结果与使用OLS方法得到的结果相比,其偏差幅度最多为10%。
不过,Andrews、Stock和Sun在2019年的研究指出,在样本量较小或存在多个工具变量的情况下,这一标准可能过于宽松。因此,在进行实际分析时,如果回退数据有限,应该将F值大于10作为最低标准,并通过进一步的敏感性检验来确认结果的可靠性。如果F值低于10,那么这个工具变量显然就不具备有效性,由此得出的估计结果也是不可信的。
排他性要求
工具变量必须仅通过影响路由决策这一途径来影响最终结果。限流回退机制只是通过改变处理查询的模型来完成任务,并不会产生任何其他直接的关联路径。
这个假设需要基于合理的业务逻辑来进行推理,仅仅依靠数据是无法验证其正确性的。例如,由整体基础设施负载所触发的回退机制,与用户的具体请求内容或任务的难度无关。
独立性
工具变量必须与所有可能的混杂因素保持独立。限流事件的触发原因仅仅是整体的API流量情况,而与任何单个查询的详细信息都无关。因此,某个特定查询是否会导致限流回退机制被触发,这一概率与查询的复杂度、用户的等级或任何其他混杂因素都没有关联。这个假设同样需要通过逻辑推理来证明其合理性。
单调性
该工具必须使所有受影响的单元朝同一方向发生变化。对于速率限制机制下的回退方案,每个受影响的查询都会从高级模式切换到廉价模式,但不会有任何查询因回退而从廉价模式切换回高级模式。这一规定排除了某些特殊情况的发生,并且是确保“LATE解释”成立所必需的。
当以上四个条件都得到满足时,2SLS方法就能仅利用该工具所引发的路由变化,来估算路由对任务完成率的影响。
先决条件
您需要准备以下内容:
Python 3.11或更高版本
熟练掌握pandas库及statsmodels中的OLS分析方法
对线性回归有一定了解(2SLS实际上是由两个OLS回归步骤串联而成的)
请安装本教程所需的软件包:
pip install numpy pandas statsmodels scipy
这些软件包正是本教程中会用到的。其中statsmodels提供了OLS分析功能及相应的公式计算接口,而scipy则用于自助法步骤中的统计计算。
请克隆配套代码仓库以获取合成数据集:
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
通过克隆这个代码仓库并使用固定的随机种子重新生成那个包含50,000个用户的合成数据集,您就能确保自己的实验结果与本文中预期的输出一致。
设置实验环境
本教程会在共享数据集中添加三个模拟变量,从而在代码中构建出完整的工具变量因果分析模型:
rate_limit_fallback:这个变量代表了“工具变量Z”。它的取值遵循纯伯努利分布(概率为0.15),且与任何查询特征都无关。routed_to_premium_actual:这是内生处理变量“D”。路由选择的结果同时受到query_confidence(可观测变量)和query_complexity(不可观测变量)的影响,因此使用OLS分析会存在偏差。task_completed_iv:这是因变量“Y”。我们根据工具变量因果模型重新模拟了这个变量的值,并设置了+6个百分点的路由优化效果,这样就能验证估计结果是否与真实情况一致。
需要说明的是,在实际的生产环境中,速率限制回退事件的数据通常来自API网关日志,而用户的遥测数据中并不包含这些信息。因此,在实际操作中需要将这两个数据源结合起来才能构建出真正的工具变量。
这里的模拟设置保留了真实工具变量的本质特征:它会因为基础设施方面的原因而被触发,且与查询质量无关;同时,也不需要依赖生产环境中的网关日志来进行分析。
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
np.random.seed(42)
df = pd.read_csv("data/synthetic_llm_logs.csv")
rng = np.random.default_rng(99)
n = len(df)
# 未观测到的混杂因素:复杂查询会使得用户更倾向于选择高级服务,而且这种选择的频率也更低
query_complexity = rng.normal(0, 1, n)
# 内生性路由机制:这一机制取决于“查询置信度”(可观测变量)以及“查询复杂性”(未观测变量);这就是所谓的混杂结构
log_odds = -2.0 + 4.0 * (1.0 - df["query_confidence"]) + 0.6 * query_complexity
premium_prob = 1.0 / (1.0 + np.exp(-log_odds))
df["routed_to_premium_iv"] = rng.binomial(1, premium_prob).astype(int)
# 作为工具变量的随机变量:服从纯伯努利分布,概率为0.15,且与所有查询特征无关
df["rate_limit_fallback"] = rng.binomial(1, 0.15, n)
# 实际的路由结果:如果用户原本就打算选择高级服务,就会实际获得该服务;除非“速率限制回退机制”起到了干扰作用
df["routed_to_premium_actual"] = (
df["routed_to_premium_iv"] * (1 - df["rate_limit_fallback"])
).astype(int)
# 结果变量:根据已知的因果关系计算得出,其中高级服务的选择会使得完成任务的概率增加0.06
engagement_base = np.where(df.engagement_tier == "heavy", 0.70,
npWHERE(df.engagement_tier == "medium", 0.55, 0.35))
completion_prob = np.clip(
engagement_base
+ 0.06 * df["routed_to_premium_actual"] # 真实的因果效应
- 0.04 * query_complexity # 未观测到的混杂因素
+ rng.normal(0, 0.02, n),
0.01, 0.99
)
df["task_completed_iv"] = rng.binomial(1, completion_prob).astype(int)
# 将“参与等级”转换为虚拟变量
df = pd.get_dummies(df, columns=["engagement_tier"], drop_first=True)
tier_dummies = [c for c in df.columns if c.startswith("engagement_tier_")]
covariate_str = " + ".join(["query_confidence"] + tier_dummies)
print(f"‘速率限制回退机制’的适用概率:{df.rate_limit_fallback.mean():.3f}")
print(f"实际选择高级服务的概率:{df.routed_to_premium_actual.mean():.3f}")
print(f"当‘速率限制回退机制’未启用时,平均置信度为:{df[df_rate_limit_fallback==1].query_confidence.mean():.3f}")
print(f"当‘速率限制回退机制’启用时,平均置信度为:{df[df.rate_limit_fallback==0].query_confidence.mean():.3f}")
预期输出:
限流回退率:0.151
高级路由率(实际值):0.271
当回退设置为1时的平均置信度:0.716
当回退设置为0时的平均置信度:0.715
在上述代码中,回退设置为1和回退设置为0两组之间的平均置信度数值几乎相同,这一事实证明了该指标与可观测的路由信号无关。这就是你可以用来检验任何拟议指标是否满足独立性假设的方法。query_complexity这个指标在本次模拟中是可以获取的,但在实际应用中是无法被观察到的;回归分析也不会使用这个指标。
步骤1:简单线性回归(存在偏差的基准值计算)
首先进行标准回归分析,就可以得到一个未考虑混杂因素影响时所得到的“有偏差的基准值”。大多数工程团队在报告这一数值时,并没有意识到它在数学上其实是存在缺陷的。
ols_formula = f"task_completed_iv ~ routed_to_premium_actual + {covariate_str}"
ols_model = smf.ols(ols_formula, data=df).fit(cov_type="HC3")
olscoef = ols_model.params["routed_to_premium_actual"]
ols_se = ols_model.bse["routed_to_premium_actual"]
ols_pval = ols_model.pvalues["routed_to_premium_actual"]
print(f"简单线性回归估计的高级路由效应:{ols_coef:+.4f}")
print(f>HC3标准误差:{ols_se:.4f}")
print(f>p值:{ols_pval:.4f}")
预期输出:
简单线性回归估计的高级路由效应:+0.0327
HC3标准误差:0.0050
p值:0.0000
实际情况是这样的:简单线性回归分析得出的结果是+3.3个百分点;而真正的因果效应应该是+6.0个百分点。之所以会出现2.7个百分点的偏差,是因为那些较复杂的查询被优先分配到了高级路由路径上,而这些查询完成的概率较低,从而导致了这种负向的混杂效应。p值看起来似乎很显著,标准误差也看起来比较精确……但这些输出数据并不能说明这个估计结果是错误的。
请记住这一点:在第二步中使用的2SLS分析方法会揭示这一偏差的具体数值。
步骤2:从头开始进行的二阶段最小二乘法分析
二阶段最小二乘法通过分离出由限流回退机制所导致的外生性路由变化,仅利用这些变化来估计真正的因果效应,从而纠正了之前的偏差。
阶段1:根据仪器指标及协变量来预测路由结果
stage1_formula = f"routed_to_premium_actual ~ rate_limit_fallback + {covariate_str}"
stage1 = smf.ols(stage1_formula, data=df).fit(cov_type="HC3")
print(f"阶段1分析中仪器指标的系数:{stage1.params['rate_limit_fallback']:+.4f}")
print(f>p值:{stage1.pvalues['rate_limit_fallback']:.4f}")
df["rtp_hat"] = stage1.fittedvalues
预期输出:
第一阶段的工具变量系数:-0.3190
p值: 0.0000
在这段代码中,你将内生性的路由变量与工具变量以及你在第二阶段会使用的那些协变量进行回归分析。
拟合得到的值rtp_hat包含两个部分:工具变量所能解释的外生性变化,以及协变量所能解释的外生性变化。
内生性成分(即与未观测到的查询复杂性相关的变化)会残留在残差中,因此不会出现在rtp_hat中。rate_limit_fallback的负系数证实了相关性假设:当备用路径被启用时,使用高级路由的概率会下降大约32个百分点。
第二阶段:对预测路由结果进行回归分析。
stage2_formula = f"task_completed_iv ~ rtp_hat + {covariate_str}"
stage2 = smf.ols.stage2_formula, data=df).fit(cov_type="HC3")
tslscoef = stage2.params["rtp_hat"]
tsls_se = stage2.bse["rtp_hat"]
print(f"2SLS估计值: {tsls_coef:+.4f}")
print(f"第二阶段的标准误差(低估值):{tsls_se:.4f}")
预期输出:
2SLS估计值: +0.0599
第二阶段的标准误差(低估值): 0.0188
具体来说,将routed_to_premium_actual替换为rtp_hat后,路由变化中的内生性部分就被去除了。第二阶段的系数(+0.0599)就是高级路由对任务完成率影响的2SLS估计值,这个数值与真实值+0.06几乎完全一致。
关于标准误差需要特别注意的一点是:手动进行2SLS分析得到的第二阶段标准误差往往会偏小。而使用OLS方法时,rtp_hat会被视为一个已知且固定的回归变量,但实际上这个系数是在第一阶段根据数据计算得出的。这种估计误差会引入额外的方差成分,而这些成分在第二阶段的残差分析中是无法被体现出来的。
无论你向哪些利益相关者报告分析结果,都应使用linearmodels.IV2SLS函数(详见“下一步该做什么”部分),因为这个函数能够计算出正确的标准误差值。
对比OLS与2SLS的分析结果:
print(f"OLS估计值(有偏差): {olscoef:+.4f}")
print(f"2SLS估计值(工具变量法):{tsls_coef:+.4f}")
print(f>实际的高级路由效应: +0.0600")
print(f>OLS的偏差值: {olscoef - 0.06:+.4f}")
预期输出:
OLS估计值(有偏差): +0.0327
2SLS估计值(工具变量法):+0.0599
实际的高级路由效应: +0.0600
OLS的偏差值: -0.0273
从结果来看,OLS方法的估计值比真实值低了2.7个百分点,相当于低估了45%;而2SLS方法则将这一误差缩小到了0.01个百分点以内。这种差异的方向也与造成偏差的机制是一致的:未观测到的查询复杂性会导致复杂请求被优先路由到高级路径,从而降低这些请求的完成率,进而使得OLS方法的系数偏低。
图2:在包含50,000个用户的合成数据集上得出的基于数据分析的结果。左图:不同回退策略对路由选择率的影响证实了第一阶段的分析结果:当回退策略为0时,39.1%的查询会使用高级路由方式;而当回退策略为1时,这一比例降至0%(即完全替代高级路由方式)。右图:普通最小二乘法的置信区间(红色)完全忽略了实际上存在的+0.06个百分点的效果,而2SLS方法的置信区间(绿色)则涵盖了这一效应。2SLS方法之所以会产生较宽的置信区间,是因为它仅依赖于该工具变量自身的外生变化,因此这种估计方法会受到方差的影响。
步骤3:弱工具变量的诊断
如果一个工具变量对研究结果的影响微乎其微,那么它就被视为一个“弱工具变量”。使用弱工具变量进行2SLS估计时,得到的估计值会具有较大的方差,在样本量较小时这些估计值往往会趋近于普通最小二乘法的估计结果,这样一来,这种估计方法就失去了其原有的意义。判断一个工具变量是否属于弱工具变量的标准方法是计算第一阶段的F统计量。
stage1_restricted = smf.ols(
f"routed_to_premium_actual ~ {covariate_str}", data=df
).fit()
f_stat, f_pval, _ = stage1.compare_f_test(stage1_restricted)
print(f"第一阶段的F统计量(工具变量):{f_stat:.2f}")
print(f"p值: {f_pval:.4f}")
if f_stat > 10:
print("该工具变量是强工具变量(F值大于10),因此使用2SLS方法得到的估计结果是可靠的。")
elif f_stat > 4:
print("该工具变量属于弱工具变量(4 < F值 < 10),在解读分析结果时需要谨慎。」)
else:
print("该工具变量是弱工具变量(F值小于4),因此使用2SLS方法得到的估计结果是不可靠的。")
print(f"\n工具变量的第一阶段系数为:"
f"{stage1.params['rate_limit_fallback']:+.4f}")
预期输出结果:
第一阶段的F统计量(工具变量):3780.94
p值: 0.0000
该工具变量是强工具变量(F值大于10),因此使用2SLS方法得到的估计结果是可靠的。
工具变量的第一阶段系数为:-0.3190
在上面的代码中,我们通过F检验将包含该工具变量的完整第一阶段模型与不包含该工具变量的限制模型进行了对比。F值为3780,这一数值远远高于Staiger-Stock给出的经验法则所推荐的临界值。在包含50,000个观测样本的数据集上,当回退率为15%时,第一阶段的效应会被准确地估计出来。
如果在实际的生产数据集上,回退率较低,或者数据集的规模较小,那么F统计量的数值也会相应降低。如果得到的F值小于10,那么就需要寻找一个更强的工具变量,或者增加更多的回退数据,然后再得出结论。
工具变量的强度与排除约束的有效性之间存在一种权衡关系,这一点值得特别指出。虽然可以通过提高回退率来增强工具变量的效果,但如果回退率过高,从而影响到用户的体验,那么回退机制就会直接通过影响用户的满意度和重试行为来影响任务的完成情况,这样一来就违背了排除约束的要求。我们的目标当然是找到一个既能满足相关性要求,又能满足排除约束要求的强工具变量。
内生性方向检验:
gap = olscoef - tsls_coef
print(f"OLS与2SLS的差距:{gap:+.4f}")
if abs(gap) > 0.005:
print("这一差距表明OLS存在内生性偏差。")
else:
print("差距很小:OLS和2SLS的结果基本一致。")
print("如需进行正式的Hausman内生性检验,请使用linearmodels IV2SLS。")
预期输出:
OLS与2SLS的差距:-0.0272
这一差距表明OLS存在内生性偏差。
如需进行正式的Hausman内生性检验,请使用linearmodels IV2SLS。
其含义是:OLS与2SLS之间的差距可用于判断是否存在内生性问题。如果这一差距为2.7个百分点,那就说明该变量确实与一些未被观测到的混淆因素存在关联,而OLS方法在计算结果时吸收了这些混淆因素的影响。
若要进行严格意义上的Hausman检验(即那种在原假设成立的情况下其卡方统计量具有已知分布的检验),请使用`linearmodels.IV2SLS`内置的检验功能。上述方向检验仅是一种快速的诊断工具而已。
步骤4:LATE才是你真正关心的指标
2SLS方法能够估算出“局部平均处理效应”(LATE),这种效应也被称为“遵从者群体的平均因果效应”(CACE)。LATE仅适用于那些在工具变量发挥作用后其处理方式确实发生改变的样本——也就是说,仅适用于那些“遵从规定”的查询对象。而速率限制机制所导致的处理结果变化仅会影响那些符合高级服务资格但不得不使用低级服务的查询对象,因此LATE这一指标也专门针对这类子群体进行估算。
compliers_mask = df["rate_limit_fallback"] == 1
complier_count = compliers_mask.sum()
complier_pct = complier_count / n * 100
print(f"遵从规则的对象数量约为:{compler_count:,},占所有查询对象的{complierpct:.1f}%")
print(f"\n遵从规则的对象的平均置信度为:{df[compliers_mask]['query_confidence'].mean():.3f}")
print(f"不遵从规则的对象的平均置信度为:{df[~compliers_mask]['query_confidence'].mean():.3f}")
print(f"\n2SLS方法估算的LATE值为:{tslscoef:+.4f}")
print("这就是那些因速率限制机制而被重新路由到的查询对象所享有的高级服务带来的因果效应,但这一结论并不适用于数据集中的所有查询对象。")
预期输出:
遵从规则的对象数量约为:7,575个,占所有查询对象的15.2%
遵从规则的对象的平均置信度为:0.716
不遵从规则的对象的平均置信度为:0.715
2SLS方法估算的LATE值为:+0.0599
这就是那些因速率限制机制而被重新路由到的查询对象所享有的高级服务带来的因果效应,但这一结论并不适用于数据集中的所有查询对象。
在这段代码中,被认定为“遵从规则”的对象共有7,575个,它们都是那些因为速率限制机制而被迫从高级服务渠道转向低级服务渠道的查询对象。这些对象的平均置信度为0.716,与那些不遵守规则的查询对象的平均置信度0.715几乎相同,这一事实进一步证明了速率限制机制的实施是独立于各类查询对象的特定特征的。
当“遵从规则”的对象能够代表所有可观测特征相同的查询对象时,LATE往往就能较为准确地反映“平均处理效应”(ATE)的实际数值。
可观测性的代表性符合最低诊断标准。正式的“晚期处理效应与最终处理效应一致”这一要求,要么意味着所有样本单位的处理效应必须相同,要么意味着总体中每个样本单位都拥有有效的测量工具。如果不同类型的查询在处理效果上存在差异(例如,高级路由方式对复杂查询的帮助远大于简单查询),那么即使合规组的平均置信度与非合规组相似,晚期处理效应也与最终处理效应也会出现显著偏差。
对于战略性的容量规划来说,这正是你所需要的指标。当你考虑是增加高级模型的容量还是调整速率限制时,你实际上是在探讨当前那些受到基础设施限制的查询需求。2SLS方法能够直接回答这个问题。
步骤5:自助法置信区间
如步骤2中所解释的,手动运用2SLS方法计算得出的第二阶段标准误差会偏小。而通过自助法计算置信区间,则无需推导复杂的分析方差公式,就能获得可靠的不确定性估计值。自助法会对整个两阶段分析过程进行重新抽样,从而同时捕捉到两个阶段的抽样变异。
rng_boot = np.random.default_rng(7)
olsBoot, tslsBoot = [], []
for _ in range(500):
samp = df.sample(len(df), replace=True,
random_state=int(rng.boot.integers(1_000_000_000)))
# OLS自助法
ols_b = smf.ols(
f"task_completed_iv ~ routed_to_premium_actual + {covariate_str}",
data=samp
).fit()
olsBoot.append(ols_b.params["routed_to_premium_actual"])
# 2SLS自助法(两阶段合并)
s1b = smf.ols(
f"routed_to_premium_actual ~ rate_limit_fallback + {covariate_str}",
data=samp
).fit()
samp = samp.copy()
samp["rtp_hat"] = s1b.fittedvalues
s2b = smf.ols(
f"task_completed_iv ~ rtp_hat + {covariate_str},
data=samp
).fit()
tslsBoot.append(s2b.params["rtp_hat"])
ols_ci = (np.percentile(ols_boot, 2.5), nppercentile(ols.boot, 97.5))
tsls_ci = (np_percentile(tsls-boot, 2.5), nppercentile(tsls/boot, 97.5))
true_eff = 0.0600
print(f"OLS 95%置信区间: [{ols_ci[0]:+.4f}, {ols_ci[1]:+.4f}]")
print(f"2SLS 95%置信区间: [{tsls_ci[0]:+.4f}, {tsls_ci[1]:+.4f}]")
print(f"真实值: +{true_eff:.4f}")
print(f"OLS置信区间是否包含真实值: {ols_ci[0] <= true_eff <= ols_ci[1]}")
print(f"2SLS置信区间是否包含真实值: {tsls_ci[0] <= true_eff <= tsls_ci[1]}")
预期输出:
OLS 95%置信区间: [+0.0227, +0.0426]
2SLS 95%置信区间: [+0.0247, +0.0969]
真实值: +0.0600
OLS置信区间是否包含真实值: False
2SLS置信区间是否包含真实值: True
在这段代码中,OLS的95%置信区间([+0.023, +0.043])完全偏离了真实的+0.06效应。该区间内的所有数值都低于真实值,因此OLS的估计结果显然是错误的。而2SLS的95%置信区间([+0.025, +0.097])则包含了真实值。由于2SLS方法需要考虑更多的变异因素,其置信区间自然会比OLS的方法更宽,这也体现了在估计工具变量时,精确度与有效性之间存在权衡关系:为了获得更准确的结果,就必须牺牲一定的精确度。
在每次迭代中,自助法都会对这两个阶段进行重新抽样,因此这种不确定性能够正确反映这种两阶段结构。当通过手动方法得出2SLS结果并进行报告时,应使用自助法构建的置信区间,因为它们比第二阶段的参数标准误差更为可靠。
当工具变量失效时
与倾向得分分析或回归不连续性分析相比,工具变量分析的失败模式更为隐蔽,因为在这四种假设中,有两条是无法仅通过数据来检验的。
弱工具变量
如果第一阶段的F值低于10,那就说明存在严重的识别问题。弱工具变量会导致2SLS估计量的方差增大,并且在有限样本情况下其估计结果会趋向于OLS的结果,从而看似采用了更为复杂的方法,但实际上只是重复了有偏的基线结果。在解读任何工具变量分析的结果之前,都必须先检查F值。
如果F值低于10,就需要寻找更合适的工具变量,或者在对估计结果进行报告时明确说明该工具变量的弱性。在这个例子中,所使用的工具变量是强工具变量(F值为3780),因为当对50,000次查询应用15%的回退率时,会导致7,500次以上的路由变化发生。
违反排除限制规定
如果限速回退机制通过除路由决策之外的其他途径影响了任务的完成过程,那么这种排除限制规定就被违反了。
存在两种可能的违规情况:第一种是,在高流量时段,回退事件会集中发生,而此时用户也更有可能正在执行复杂的批处理任务,因此这些工具变量实际上与查询的复杂程度存在相关性;第二种是,当用户收到回退提示后,他们会因为响应质量下降而直接终止当前的会话,从而形成了从“Z”到“Y”的直接关联路径。
这两种情况都会违反排除限制规定,但同时也会影响分析结果的相关性。这些违规情况是无法通过数据来检验的,必须依靠对系统运行机制的了解才能发现它们。
LATE估计与ATE估计之间的混淆
如果遵守规则的用户属于非典型群体,那么使用LATE估计结果来支持某种广泛的路由策略调整是错误的。因为如果限速回退机制主要影响复杂查询(由于复杂查询需要更长的处理时间,因此在会话进行到中途时更容易遇到速度限制),那么LATE估计实际上反映的只是这种特定子群体中高级路由机制所带来的因果效应。
但如果将LATE估计结果当作ATE估计结果来报告,就会高估了为所有查询提供高级路由服务所带来的好处。第4步中列出的遵守规则用户与不遵守规则用户的特征对比表就可以用来判断这一点:如果这两组用户在可观测变量上的表现相似,那么LATE估计确实可以作为一种可靠的ATE估计方法。
违反规定者与单调性假设
要正确理解LATE估计的结果,就必须满足单调性假设,即工具变量必须使所有受影响的单元朝同一个方向发生变化。对于限速回退机制而言,这一假设几乎总是成立的,因为回退操作肯定会降低相关查询获得高级路由服务的概率。
然而,如果存在某种补偿机制(比如某些查询会触发后续查询的优先处理),那么就会出现违反单调性假设的情况。在信任LATE估计结果之前,必须先验证这种机制是否存在以及其作用方向是否一致。
下一步该怎么做
本教程中介绍的2SLS实现方法虽然能够清晰地展示其计算机制,但得出的标准误差并不准确。因此,对于任何你需要向相关方报告的结果,或需要纳入已发表的分析报告中的数据,都应使用linearmodels.IV2SLS函数:
# 用于生成标准误差正确的2SLS模型
# pip install linearmodels
from linearmodels.iv import IV2SLS
exog_vars = ["query_confidence"] + tier_dummies
iv_model = IV2SLS.from_formula(
f"task_completed_iv ~ 1 + {' + '.join(exog_vars)} "
f"[routed_to_premium_actual ~ rate_limit_fallback]",
data=df
).fit(cov_type="robust")
print(iv_model.summary)
具体实现过程如下:linearmodels会计算出能够反映两阶段结构特征的准确2SLS方差,进行必要的第一阶段诊断分析,并提供正式的Hausman内生性检验。该函数使用[D ~ Z]的语法格式来区分内生变量和工具变量。
完整的实现代码(包括自举置信区间以及图2中的可视化结果)可以在以下链接处的笔记本中找到:github.com/RudrenduPaul/product-experimentation-causal-inference-genai-llm/tree/main/11_instrumental_variables/。你可以克隆这个仓库,生成合成数据集,然后运行iv_demo.ipynb文件来逐行测试所有的代码。
最后需要说明的一点是:在什么情况下应该使用工具变量进行分析?如果你的系统支持强制路由随机化机制(即无论查询的置信度如何,都会随机将部分请求分配到高级服务路径上),那么进行标准的A/B测试会更为简单,而且也能得到更准确的ATE估计值。
只有当随机化操作无法实现时,工具变量才才是真正适用的解决方案。例如,当路由规则已经被内置于生产系统中,当你不能容忍故意使用次优的路由策略时,或者当你需要利用历史观测数据进行分析时,工具变量就显得非常有用。如果可能的话,还是应该优先进行真正的实验来获取最准确的结果。
对于任何经过优化的路由系统而言,混淆因素都是其固有的存在。传统的回归分析会将模型质量与查询的难度这两项因素合并为一个系数,从而在需要区分它们时无法得到准确的信息。
速率限制机制为过滤掉基础设施层面带来的干扰、从而更准确地分析路由效果提供了理想的工具。这种方法能够帮助你的团队获得一个可靠的因果估计结果,从而了解你的模型架构究竟是如何创造商业价值的。
相关文章
播客:Scott Jenson谈论桌面操作系统的演变、以本地体验为核心的设计理念以及“主动式用户体验”设计思路
在这一集中,以参与Macintosh、Google Maps和Chrome等项目的设计而闻名的资深UX设计师Scott Jenson,分析了桌面操作系统的长期发展停滞现象,以及当前以移动设备和云计算为中心的发展模式所存在的局限性。 作者:Scott Jenson
阅读全文
如何判断你的搜索控制台中的展示数据是人为操作产生的,还是由机器自动生成的
您的Search Console报告显示,在90天的时间里,某个页面在谷歌搜索结果的第一页获得了3,068次展示机会,但在同一时期内,该页面并没有获得任何点击量。 通常人们会认为这个页面存在点击率低的问题,因此就会修改标题、优化元描述,然后等待效果出现。但实际上这种分析很可能是错误的,据此采取行动只会浪费精力。因为根本没有人会进行这样的搜索操作,所以那些数据也就没有任何实际意义。 本教程将教您如何区分网站所获得的两种类型的展示机会。您可以使用自己的Search Console数据运行一个简单的脚本,根据搜索排名将这些展示机会分为不同的类别,读取查询日志以分析机器生成的搜索请求信息,并将可疑页面
阅读全文
演讲主题:克劳德能够自我修复吗?利用大型语言模型进行事件响应
人类可靠性工程师亚历克斯·帕尔库埃分享了关于如何利用大语言模型来应对现实世界中的突发事件的相关经验。他解释了为什么人工智能在分析日志和追踪数据时能够发挥“超人般”的作用,为何在根本原因分析过程中,它仍然难以区分因果关系与相关性,以及工程领域的领导者该如何将人工智能融入到应急响应的工作流程中,同时避免削弱人类的专业能力。 作者:亚历克斯·帕尔库埃
阅读全文
如何使用Python和Neo4j构建知识图谱【完整指南】
你所处理的大部分数据实际上都反映了各种关系:一个客户属于某个账户,某次事件会影响某种服务,而一名工程师负责维护某个代码库。你把所有这些信息存储在表格中,长期以来,这种存储方式一直运行得非常顺利。 然而,有时会有人提出这样的问题: 哪些工程师最近了解过昨晚那次事件所影响的服务情况? 这类问题很容易理解,但编写相应的SQL查询却相当困难。通常需要使用四到五次连接操作,每次连接都会生成一个比最终结果范围更广的中间数据集,而大部分这些中间数据最终都会被丢弃。随着表格规模的扩大,查询速度会变得越来越慢,而且每次查看这个查询语句时,都很难理解其具体逻辑。 为了解决这类问题,人们才创造了图数据库。 在这本手
阅读全文