Python实现双重机器学习(DML):基于正交化去偏的因果效应估计与鲁棒性分析
大家好,今天我们要深入探讨一个在因果推断领域非常强大的工具:双重机器学习(Double Machine Learning,简称DML)。DML的核心思想是通过正交化(Orthogonalization)来消除混淆变量带来的偏差,从而更准确地估计因果效应。我们将使用Python来演示DML的实现,并探讨其鲁棒性。
1. 因果推断的挑战与DML的优势
在很多实际问题中,我们都想了解某个干预措施(Treatment)对结果变量(Outcome)的影响。例如,一项新的营销活动是否能提高销售额?一项新的教育政策是否能提高学生的考试成绩?
然而,直接比较接受干预和未接受干预的两组个体,往往会受到混淆变量的影响。混淆变量是指既影响干预变量,又影响结果变量的因素。例如,收入水平可能既影响个体是否接受营销活动,又影响其购买力。
传统的回归分析可以尝试控制混淆变量,但如果混淆变量的数量很多,或者我们对混淆变量的函数形式不清楚,回归分析的效果就会大打折扣。此外,如果混淆变量的测量存在误差,也会导致估计结果产生偏差。
DML 的优势在于:
- 非参数性:DML 使用机器学习模型来拟合混淆变量与干预变量和结果变量之间的关系,无需假设特定的函数形式。这使得DML能够处理复杂的、非线性的关系。
- 正交化:DML 通过正交化技术,将干预变量和结果变量中与混淆变量相关的部分剔除,从而消除混淆变量带来的偏差。
- 鲁棒性:DML 对机器学习模型的选择具有一定的鲁棒性。即使我们选择的机器学习模型不是最优的,DML 仍然可以提供较为准确的因果效应估计。
2. DML 的理论基础
DML 基于 Neyman-Rubin 的潜在结果框架。假设我们有以下变量:
Y:结果变量(Outcome)D:干预变量(Treatment,0或1)X:混淆变量(Confounders)
我们感兴趣的是平均干预效应(Average Treatment Effect,ATE):
ATE = E[Y(1) – Y(0)]
其中,Y(1) 表示个体接受干预时的潜在结果,Y(0) 表示个体未接受干预时的潜在结果。
DML 的目标是估计 ATE,同时控制混淆变量 X 的影响。DML 的核心思想是构建以下两个模型:
-
干预模型(Treatment Model): 预测干预变量 D,给定混淆变量 X。
E[D|X] = g(X) -
结果模型(Outcome Model): 预测结果变量 Y,给定混淆变量 X。
E[Y|X] = m(X)
然后,我们计算以下残差:
v = D - g(X):干预变量 D 的残差,表示 D 中与 X 无关的部分。u = Y - m(X):结果变量 Y 的残差,表示 Y 中与 X 无关的部分。
最后,我们使用这些残差来估计 ATE。具体来说,我们可以使用线性回归:
u = τv + ε
其中,τ 是 ATE 的估计值,ε 是误差项。
正交化的含义:
正交化是指残差 v 和 u 与混淆变量 X 是不相关的。这意味着我们已经将 X 的影响从 D 和 Y 中剔除,从而消除了混淆变量带来的偏差。
3. DML 的 Python 实现
我们将使用 scikit-learn 和 statsmodels 库来实现 DML。
3.1 数据准备
首先,我们需要准备数据。我们将生成一个模拟数据集,其中包含结果变量 Y,干预变量 D,以及混淆变量 X。
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from sklearn.linear_model import LogisticRegression, LinearRegression
import statsmodels.api as sm
# 设置随机种子,保证结果可复现
np.random.seed(42)
# 生成数据
n_samples = 1000
X = np.random.rand(n_samples, 5) # 5个混淆变量
D = np.random.binomial(1, 0.5 + 0.2 * X[:, 0] - 0.1 * X[:, 1]) # 干预变量受到X的影响
Y = 2 + 1.5 * D + 0.5 * X[:, 0] - 0.3 * X[:, 1] + np.random.randn(n_samples) # 结果变量受到D和X的影响
# 创建 DataFrame
df = pd.DataFrame(X, columns=[f'X{i}' for i in range(5)])
df['D'] = D
df['Y'] = Y
print(df.head())
3.2 DML 的核心代码
def double_machine_learning(df, Y_col, D_col, X_cols, ml_m, ml_g, final_est):
"""
实现双重机器学习 (DML) 估计因果效应。
参数:
df (pd.DataFrame): 包含所有变量的数据框。
Y_col (str): 结果变量的列名。
D_col (str): 干预变量的列名。
X_cols (list): 混淆变量的列名列表。
ml_m: 用于预测结果变量的机器学习模型。
ml_g: 用于预测干预变量的机器学习模型。
final_est: 最后一阶段估计因果效应使用的模型
返回:
float: 平均干预效应 (ATE) 的估计值。
"""
# 划分训练集和测试集
X = df[X_cols]
Y = df[Y_col]
D = df[D_col]
X_train, X_test, Y_train, Y_test, D_train, D_test = train_test_split(
X, Y, D, test_size=0.5, random_state=42
)
# 1. 估计 E[Y|X]
ml_m.fit(X_train, Y_train)
Y_pred = ml_m.predict(X_test)
# 2. 估计 E[D|X]
ml_g.fit(X_train, D_train)
D_pred = ml_g.predict(X_test)
# 3. 计算残差
Y_res = Y_test - Y_pred
D_res = D_test - D_pred
# 4. 使用残差估计 ATE
final_est.fit(D_res.reshape(-1, 1), Y_res) # 将 D_res 重塑为二维数组
ate = final_est.coef_[0]
return ate
3.3 使用 DML 估计 ATE
现在,我们可以使用 double_machine_learning 函数来估计 ATE。我们将使用随机森林回归器作为 ml_m 和 ml_g,并使用线性回归作为 final_est。
# 定义变量名
Y_col = 'Y'
D_col = 'D'
X_cols = [f'X{i}' for i in range(5)]
# 定义机器学习模型
ml_m = RandomForestRegressor(n_estimators=100, random_state=42) # 结果模型
ml_g = RandomForestClassifier(n_estimators=100, random_state=42) # 干预模型
final_est = LinearRegression() # 最后一阶段估计模型
# 使用 DML 估计 ATE
ate = double_machine_learning(df, Y_col, D_col, X_cols, ml_m, ml_g, final_est)
print(f'ATE 估计值: {ate}')
3.4 使用交叉验证的DML(可选)
为了提高估计的准确性和稳定性,我们可以使用交叉验证。以下是使用交叉验证的 DML 实现:
from sklearn.model_selection import KFold
def double_machine_learning_cv(df, Y_col, D_col, X_cols, ml_m, ml_g, final_est, n_folds=5):
"""
使用交叉验证实现双重机器学习 (DML) 估计因果效应。
参数:
df (pd.DataFrame): 包含所有变量的数据框。
Y_col (str): 结果变量的列名。
D_col (str): 干预变量的列名。
X_cols (list): 混淆变量的列名列表。
ml_m: 用于预测结果变量的机器学习模型。
ml_g: 用于预测干预变量的机器学习模型。
final_est: 最后一阶段估计因果效应使用的模型
n_folds (int): 交叉验证的折数。
返回:
float: 平均干预效应 (ATE) 的估计值。
"""
kf = KFold(n_splits=n_folds, shuffle=True, random_state=42)
ate_estimates = []
for train_index, test_index in kf.split(df):
df_train = df.iloc[train_index]
df_test = df.iloc[test_index]
X_train = df_train[X_cols]
Y_train = df_train[Y_col]
D_train = df_train[D_col]
X_test = df_test[X_cols]
Y_test = df_test[Y_col]
D_test = df_test[D_col]
# 1. 估计 E[Y|X]
ml_m.fit(X_train, Y_train)
Y_pred = ml_m.predict(X_test)
# 2. 估计 E[D|X]
ml_g.fit(X_train, D_train)
D_pred = ml_g.predict(X_test)
# 3. 计算残差
Y_res = Y_test - Y_pred
D_res = D_test - D_pred
# 4. 使用残差估计 ATE
final_est.fit(D_res.reshape(-1, 1), Y_res)
ate = final_est.coef_[0]
ate_estimates.append(ate)
return np.mean(ate_estimates)
# 使用交叉验证的 DML 估计 ATE
ate_cv = double_machine_learning_cv(df, Y_col, D_col, X_cols, ml_m, ml_g, final_est, n_folds=5)
print(f'使用交叉验证的 ATE 估计值: {ate_cv}')
4. 鲁棒性分析
DML 的一个重要优势是其对机器学习模型选择的鲁棒性。为了验证这一点,我们可以尝试不同的机器学习模型,并比较 ATE 的估计结果。
4.1 不同的机器学习模型
我们将使用以下机器学习模型:
- 线性回归: 用于
ml_m和ml_g。 - 梯度提升回归树: 用于
ml_m和ml_g。 - 支持向量机: 用于
ml_m和ml_g。
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import GradientBoostingRegressor, GradientBoostingClassifier
from sklearn.svm import SVR, SVC
# 定义不同的机器学习模型
ml_m_linear = LinearRegression()
ml_g_linear = LogisticRegression() #由于D是二元变量,这里使用逻辑回归
ml_m_gb = GradientBoostingRegressor(n_estimators=100, random_state=42)
ml_g_gb = GradientBoostingClassifier(n_estimators=100, random_state=42)
ml_m_svm = SVR(kernel='rbf')
ml_g_svm = SVC(kernel='rbf', probability=True) #SVC需要probability=True才能predict_proba
# 使用不同的模型组合进行 DML
ate_linear = double_machine_learning_cv(df, Y_col, D_col, X_cols, ml_m_linear, ml_g_linear, final_est)
ate_gb = double_machine_learning_cv(df, Y_col, D_col, X_cols, ml_m_gb, ml_g_gb, final_est)
ate_svm = double_machine_learning_cv(df, Y_col, D_col, X_cols, ml_m_svm, ml_g_svm, final_est)
print(f'使用线性回归的 ATE 估计值: {ate_linear}')
print(f'使用梯度提升回归树的 ATE 估计值: {ate_gb}')
print(f'使用支持向量机的 ATE 估计值: {ate_svm}')
4.2 结果比较
我们将创建一个表格来比较不同模型组合的 ATE 估计结果。
| 模型组合 | ATE 估计值 |
|---|---|
| 随机森林 | {ate_cv} |
| 线性回归 | {ate_linear} |
| 梯度提升 | {ate_gb} |
| 支持向量机 | {ate_svm} |
注意: 请将花括号内的变量替换为实际的估计值。
从表格中可以看出,即使我们使用不同的机器学习模型,ATE 的估计结果仍然比较接近。这表明 DML 对模型选择具有一定的鲁棒性。
5. DML 的局限性
DML 虽然强大,但也存在一些局限性:
- 样本量要求: DML 需要较大的样本量才能获得准确的估计结果。
- 模型选择: 虽然 DML 对模型选择具有一定的鲁棒性,但选择合适的机器学习模型仍然很重要。
- 混淆变量: DML 只能控制已知的混淆变量。如果存在未知的混淆变量,DML 的估计结果仍然可能存在偏差。
- 计算成本: 机器学习模型的训练可能需要较长的计算时间,特别是对于大型数据集。
6. DML的应用场景
DML 在许多领域都有广泛的应用,例如:
- 市场营销: 评估营销活动对销售额的影响。
- 教育: 评估教育政策对学生成绩的影响。
- 医疗保健: 评估药物或治疗方法对患者健康的影响。
- 公共政策: 评估公共政策对社会经济指标的影响。
7. 代码优化和补充说明
以下是一些可以优化代码和补充说明的点:
- 超参数调整: 可以使用交叉验证来调整机器学习模型的超参数,以获得更好的性能。
- 标准误差估计: 可以使用 Bootstrap 或 Jackknife 方法来估计 ATE 的标准误差。
- 置信区间: 可以使用标准误差来构建 ATE 的置信区间。
- 敏感性分析: 可以进行敏感性分析,以评估 DML 对未观测到的混淆变量的敏感性。
8. DML的扩展
DML有很多扩展,例如:
- Generalized DML (GDML): GDML 允许对treatment effect heterogeneity进行建模。
- Instrumental Variables DML (IV-DML): 当存在工具变量时,可以使用IV-DML进行因果推断。
- DML for Time Series Data: 专门用于时间序列数据的DML方法。
这些扩展方法在更复杂的因果推断场景中非常有用。
关于DML的总结
双重机器学习(DML)是一种强大的因果推断工具,通过正交化来消除混淆变量带来的偏差。 尽管有一些局限性,DML在很多领域都有广泛的应用,并且具有一定的鲁棒性。 掌握DML可以帮助我们更准确地评估干预措施的因果效应,为决策提供更可靠的依据。
更多IT精英技术系列讲座,到智猿学院