paper2skills Playbook

DML Cohort 因果效应 - 群体异质性 HTE 估计

Skill-DML-Cohort-Causal-Effect · 01-因果推断

causalexperimentpricing客服与VOC定价与利润WF-C 客服分诊WF-F 动态定价WF-G Listing内容优化WF-H 复购增长
年化 ROI1500-2500 万元
实现难度⭐⭐⭐⭐☆
业务优先级⭐⭐⭐⭐☆
业务视角
适用角色增长负责人 / CMO · 数据分析师 · 广告优化师
适用平台Amazon · TikTok Shop · Meta Ads · DTC 独立站
什么情况下用广告预算花了,但不确定哪个渠道真的带来新客;做了大促,不知道销量增长是促销效果还是季节规律
成功是什么样的能区分「真实增量」和「自然购买」,砍掉虚假归因渠道后同等预算 ROI 提升 20-40%
业务痛点
钱花出去了不知道有没有用各渠道报告都说自己贡献最大怎么向老板证明这笔钱值得花

1. 解决的问题

平台对所有新妈妈用户统一发放"新生儿满减券",ROI 整体回归到 1.2-1.5x,猜测某些群体响应强、某些群体弱,但不知如何切分 - 数据要求:用户注册时填写宝宝生日 + 高维行为日志(2000 维:RFM、品类偏好、渠道、地理) - DML 配置: - 第一阶段:XGBoost 拟合 $E[Y|X]$,LightGBM 拟合 $E[D|X]$ - PCA 降维至 10 维,K-mea

2. 核心算法逻辑

电商场景中"促销/拉新/订阅"等干预对不同用户群体(cohort)效应差异巨大,但传统 A/B 难以分群估计。DML(Double Machine Learning)通过双稳健残差化消除高维混杂,Neyman 正交性保证 ML 估计偏差对因果参数为二阶小量,PCA+Kmeans cohort 特征化给出每个客户的个体 CATE。

3. 业务应用场景

- 业务问题:平台对所有新妈妈用户统一发放"新生儿满减券",ROI 整体回归到 1.2-1.5x,猜测某些群体响应强、某些群体弱,但不知如何切分 - 数据要求:用户注册时填写宝宝生日 + 高维行为日志(2000 维:RFM、品类偏好、渠道、地理) - DML 配置: - 第一阶段:XGBoost 拟合 $E[Y|X]$,LightGBM 拟合 $E[D|X]$ - PCA 降维至 10 维,K-means K=5 群体(囤货型/品牌敏感型/价格敏感型/全品类型/跨境首购型) - 输出每个 cohort 的 CATE β_k - 业务价值:识别"0-3 月龄高客单价用户响应最强 (CATE 75

- 业务问题:评估"首单立减"对不同孕/育阶段用户 12 月 LTV 的因果效应,以确定是按月龄分层投放还是统一投放 - 数据要求:用户月龄分段 + 12 月消费 LTV + 高维控制变量 - BGATE 配置(扩展):平衡协变量分布消除"用户质量差异",分离纯月龄效应 - 业务价值:发现 7-12 月龄用户 LTV CATE 最高(刚需密集期),集中投放该群体使首单优惠 ROI 提升 40-60%;年化拉新成本节省 800-1200 万元

4. 输入数据要求

请查看原始代码模板获取输入规格。

5. 输出结果

请查看原始代码模板获取输出规格。

6. 业务价值 / ROI

  • 易处:EconML 开源 + sklearn,工程实现成熟
  • 难处:需要严格保证 SUTVA、unconfoundedness,业务团队需要因果推断 sense
  • 难处:亿级数据需要 Spark 分布式实现(EconML 单机可跑千万级)

7. 代码模板

代码块数量:1 · 路径:未检测到

"""
DML Cohort CATE 最小骨架
论文 arXiv:2409.02332 (Amazon, ECML PKDD 2023)
基于 EconML (开源) + sklearn
"""
from __future__ import annotations
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor, GradientBoostingClassifier
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans

try:
    from econml.dml import LinearDML
    HAS_ECONML = True
except ImportError:
    HAS_ECONML = False


def simulate_baby_ecom_data(n: int = 5000, seed: int = 42) -> tuple:
    rng = np.random.default_rng(seed)
    X = rng.standard_normal((n, 50))
    baby_age_months = rng.integers(0, 24, n)
    X[:, 0] = baby_age_months / 24.0

    propensity = 1.0 / (1.0 + np.exp(-0.3 * X[:, 0] - 0.5 * X[:, 1]))
    D = (rng.random(n) < propensity).astype(int)

    true_cate = 50.0 + 30.0 * (1 - baby_age_months / 24.0)
    Y = 200.0 + true_cate * D + 20.0 * X[:, 0] + rng.standard_normal(n) * 50.0
    return X, D, Y, baby_age_months, true_cate


def build_cohort_features(X: np.ndarray, n_components: int = 10, n_clusters: int = 5) -> np.ndarray:
    pca = PCA(n_components=n_components).fit_transform(X)
    kmeans = KMeans(n_clusters=n_clusters, random_state=0, n_init=10).fit(pca)
    dists = kmeans.transform(pca)
    psi = 1.0 / (dists + 1e-8)
    psi = psi / psi.sum(axis=1, keepdims=True)
    return psi


def fit_dml_cohort_cate(X: np.ndarray, D: np.ndarray, Y: np.ndarray, psi: np.ndarray):
    if not HAS_ECONML:
        raise ImportError("Install econml: pip install econml")
    model = LinearDML(
        model_y=GradientBoostingRegressor(n_estimators=100),
        model_t=GradientBoostingClassifier(n_estimators=100),
        featurizer=None,
        cv=3,
        random_state=42,
    )
    model.fit(Y, D, X=psi, W=X)
    return model


def main() -> None:
    X, D, Y, ages, true_cate = simulate_baby_ecom_data()
    psi = build_cohort_features(X)
    print(f"Cohort feature shape: {psi.shape}")

8. 论文来源

  • 2401.08290
  • 2409.02332