以下内容由大模型生成,如有错误恳求指正


1. 定义与公式

1.1 定义与计算方式

AUUC(Uplift Curve下的面积)
  • 计算公式
    f(k)=(YkTNkT−YkCNkC)⋅(NkT+NkC) f(k) = \left( \frac{Y_k^T}{N_k^T} - \frac{Y_k^C}{N_k^C} \right) \cdot (N_k^T + N_k^C) f(k)=(NkTYkTNkCYkC)(NkT+NkC)
    其中:

    • YkTY_k^TYkTYkCY_k^CYkC:在Top-k样本中,处理组(Treatment)和对照组(Control)的响应数(如购买数)。
    • NkTN_k^TNkTNkCN_k^CNkC:在Top-k样本中,处理组和对照组的总样本数。
    • f(k)f(k)f(k) 表示Top-k样本中干预带来的提升(ATE)。
  • AUUC的计算
    对所有样本按Uplift值降序排序后,累加每个k对应的f(k)f(k)f(k),即:
    AUUC=∑k=1nf(k) AUUC = \sum_{k=1}^{n} f(k) AUUC=k=1nf(k)

  • 特点

    • 直接反映干预组和对照组在Top-k样本中的响应差异,并乘以样本总数,强调绝对提升量
Qini Coefficient(Qini Curve下的面积)
  • 计算公式
    g(k)=YkT−YkC⋅NkTNkC g(k) = Y_k^T - \frac{Y_k^C \cdot N_k^T}{N_k^C} g(k)=YkTNkCYkCNkT

    • g(k)g(k)g(k) 表示Top-k样本中,处理组的实际响应数减去对照组按比例调整后的响应数。
  • Qini Coefficient的计算
    累加每个k对应的g(k)g(k)g(k),即:
    Qini=∑k=1ng(k) Qini = \sum_{k=1}^{n} g(k) Qini=k=1ng(k)

  • 特点

    • 通过缩放对照组的响应数,消除样本数量不平衡的影响。

由计算公式可得:
f(k)=g(k)NkT⋅(NkT+NkC) f(k) = \frac{g(k)}{N_k^T} \cdot (N_k^T + N_k^C) f(k)=NkTg(k)(NkT+NkC)

1.2 曲线形态与面积意义

  • 关键区别
    • AUUC的曲线横轴是样本数量(kkk),纵轴是绝对提升量(f(k)f(k)f(k));
    • Qini曲线的横轴是样本比例,纵轴是调整后的相对提升量(g(k)g(k)g(k))。

1.3 应用场景

AUUC适用场*:处理组和对照组样本数量相近。
Qini Coefficient适用场景:处理组和对照组样本数量不均衡。

示例说明:
- AUUC:若模型能精准识别Top 20%的用户,则AUUC面积较大。
- Qini Coefficient:若模型在Top 20%用户中,干预组的响应数远高于对照组,Qini面积仍会较大。


2. 代码实现AUUC

2.1 Python实现版

def calculate_auuc_with_curve(y_true, uplift_score, treatment):
    """
    计算 AUUC 及其曲线上的每个点
    :param y_true: array-like, 用户是否转化,如 [0, 1, 0, 1]
    :param uplift_score: array-like, 模型预测的uplift分数
    :param treatment: array-like, 是否为处理组(1表示处理组,0表示对照组)
    :return: auuc_value: float, 使用梯形法则计算的AUUC值;
             curve_values: list, 每个划分点的AUUC值(包括第0行);
             indexes: list, 每个分档的下标(从0到n-1)
    """
    df = np.array(list(zip(y_true, uplift_score, treatment)))
    # 按uplift分数从高到低排序
    df_sorted = df[df[:, 1].argsort()[::-1]]
    cum_treatment_response = 0
    cum_control_response = 0
    cum_treatment_count = 0
    cum_control_count = 0
    curve_values = [0.0]  # 初始第0行值为0
    indexes = [0]         # 第0行对应索引0

    for i in range(len(df_sorted)):
        label, score, treat = df_sorted[i]
        
        if treat == 1:
            cum_treatment_response += label
            cum_treatment_count += 1
        else:
            cum_control_response += label
            cum_control_count += 1
        
        if cum_treatment_count > 0 and cum_control_count > 0:
            avg_treat = cum_treatment_response / cum_treatment_count
            avg_control = cum_control_response / cum_control_count
            delta = avg_treat - avg_control
        elif cum_treatment_count > 0:
            delta = cum_treatment_response / cum_treatment_count
        else:
            delta = -cum_control_response / cum_control_count
        
        total_samples = cum_treatment_count + cum_control_count
        current_auuc = delta * total_samples
        curve_values.append(current_auuc)
        indexes.append(i + 1)  # 索引从1开始对应后续绘图点
    # 使用梯形法则求面积
    auuc_value = 0
    for i in range(1, len(curve_values)):
        base = 1  # 每次增加一个样本单位
        height_avg = (curve_values[i - 1] + curve_values[i]) / 2
        auuc_value += base * height_avg
    
    return auuc_value, curve_values, indexes

2.2 开源代码scikit-uplift版

from sklift.metrics import uplift_curve, uplift_auc_score
from sklift.viz import plot_uplift_curve

# 计算auuc曲线的每个点
cnts, gains = uplift_curve(df['y_true'], df['uplift_pred'], df['treatment'])

# 与perfect曲线对比的分值(例如perfect是100分,模型预测值是85分即0.85)
score = uplift_auc_score(df['y_true'], df['uplift_pred'], df['treatment'])

# 画图,包含3条曲线:模型预测、perfect、random
plot_uplift_curve(
    df['y_true'], df['uplift_pred'], df['treatment'],
    perfect=True, name='mode_pred', #ax=ax
);

从uplift_curve到uplift_auc_score:

from sklift.metrics import uplift_curve, uplift_auc_score, perfect_uplift_curve
from sklift.viz import plot_uplift_curve
from sklearn.metrics import auc

cnt, gains = uplift_curve(df['y_true'], df['uplift_pred'], df['treatment'])
cnt_p, perfect_gains = perfect_uplift_curve(df['y_true'], df['treatment'])
cnt_rand = [0, cnt[-1]]
gains_rand = [0, gains[-1]]

rand_auc = auc(cnt_rand, gains_rand)
pred_auc = auc(cnt, gains)
perfect_auc = auc(cnt_p, perfect_gains)
score2 = (pred_auc - rand_auc) / (perfect_auc - rand_auc)  # score2 与 score 相等

2.2 开源代码causalml版

from causalml.metrics import auuc_score
outcome_col="y_true"
treatment_col="treatment"
score = auuc_score(df1, outcome_col, treatment_col)
score
# uplift_pred    0.767181
# Random         0.470098
# dtype: float64

这个auuc score是如何计算的:

from causalml.metrics import auuc_score, get_cumgain, get_cumlift
outcome_col="y_true"
treatment_col="treatment"
treatment_effect_col="tau"
normalize=True
random_seed=42

df1 = df[['y_true', 'treatment', 'uplift_pred']].copy()
lift = get_cumlift(df1, outcome_col, treatment_col, treatment_effect_col, random_seed)  # 计算每个分位点的增益率
gains = lift.mul(lift.index.values, axis=0)  # 乘数量得到增益值

if normalize:
    gains2 = gains.div(np.abs(gains.iloc[-1, :]), axis=1)

gains2.sum() / len(gains2)  # 等于上面的score

与scikit-uplift的异同:

  • 相同:uplift_curve计算得到的gains与上面的gains结果是相同的(略有小差别,两种方法对T和C缺失的处理不太一样,对整体影响不大)
  • 不同:
    • sklift中uplift_auc_score:增加了与baseline和perfect的对比,并计算出一个相对分值
    • causalml:
      • 有两处归一化:gains除以gains[-1],gains[-1]是用所有样本算的一个整体增益;gains2.sum() / len(gains2),相当于对gains2求了个均值
      • 增加了一个random对比对象:源码中是随机生成了10组random的uplift_pred,计算完增益后求了个均值
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐