☰
营销增益模型实战:从原理到落地,精准识别“因营销而买”的用户
2026/10/5 1:04:33 网站建设 项目流程

做过营销投放的朋友应该都有过这种体验:一批优惠券发出去,打开率看着还行,可一算ROI,心里就发凉。明明提前用响应率模型筛过人群,为什么预算还是浪费了大半?问题很可能不是模型不准,而是从一开始就搞错了优化目标——你在预测“谁会买”,但真正该预测的是“谁因为你的这次营销才买”。这两个问题之间的差值,就是智能营销增益模型(uplift model)要解决的。这篇内容本来是配合PPT和视频讲的,我把它整理成一份能直接照着做的文字版,适合营销数据分析师、增长团队和做策略的算法同学,无论是从零了解还是准备落地,都能找到有用的部分。

1. 为什么传统响应模型会烧光预算,而增益模型不会

很多团队用的“用户响应模型”,本质是在估计 P(购买|用户特征),也就是“这个用户有多大概率会买”。这个指标本身没错,但它天然混杂了一部分“你不打扰他,他也会买”的用户。营销动作真正能创造的价值,应该是“因为触达而多出来的那部分购买概率”,我们一般叫增量(uplift)。

1.1 你需要的不是“谁可能买”,而是“谁被我打动才买”

举个最直观的例子。你运营一个电商小程序,准备给用户发一张10元优惠券。有三个人:

  • A用户:最近一周加购了三次,就算你不发券,今晚大概率也会下单。
  • B用户:已经半个月没打开小程序,看到优惠券后才回来买了东西。
  • C用户:上次领券后不仅没买,还因为短信骚扰把小程序消息屏蔽了。

如果按传统响应模型打分,A的购买概率最高,会被优先发券;B的购买概率低,可能被漏掉;C偶尔也会被高概率误判。但从营销视角看,A的增量几乎为0,不发券也照样买,发券只是白白送钱。B才是真正应该被营销触达的人,因为他“本不会买,因为这张券才买”。C更极端,属于负增量人群,触达反而伤害关系。

增益模型做的就是把“原本会买”和“因为营销才会买”这两件事分开。它的输出不是购买概率,而是每个用户的增量分数:Δ(x) = P(购买|触达, x) - P(购买|未触达, x)。这个分数高的人,才值得把预算砸上去。

1.2 增益模型里的四类用户画像

在做增益模型分析时,我习惯把用户放进下面这张四象限表:

用户类型触达后购买未触达购买增量营销策略
忠实顾客是是约等于0不需要发权益,正常运营维护即可
营销敏感型是否显著大于0核心目标人群,优先投放
无动于衷型否否约等于0不要浪费预算,减少打扰
反感抵触型否是小于0紧急避让,停止触达或换渠道

这个表看着简单,但它是整个增益模型落地的底层逻辑。传统模型把所有“触达后会买”的用户都当成目标,增益模型却要求你把“不发也会买”的人挑出来踢出投放池。这也是很多团队做了响应模型后ROI提不上去的原因——发券转化率看着高,其中相当一部分是自然转化,营销预算并没有带来额外收益。

1.3 增益模型真正解决的三个业务场景

增益模型不是只能用在“要不要发券”上。我落地过程中遇到过三类典型需求,都适合用增益模型做:

一是权益金额决策。同一个用户,究竟是给他发5元、10元还是20元,才能用最小的成本换来最大增量?这种问题本质上是在比较不同处理强度下的CATE(条件平均处理效应),增益模型比简单的分桶测试更精细。

二是触达频次控制。用户收到1条短信和收到3条短信,购买概率的差异可能很小,但投诉和退订率差别很大。用增益模型可以找出来那些“催一催就买”但“催多了就跑”的人,动态控制频次。

三是沉睡用户唤醒。很多团队做流失预警,把高流失风险用户拉出来做召回,但召回之后转化效果一直一般。原因在于部分高流失用户本来就已经决定不买了,怎么触达都没用;而另一部分低活跃但高敏感的用户,才是召回ROI的来源。增益模型能帮你把这两类人从同一批流失名单里拆开。

2. 增益模型的核心原理:增量从哪来

既然要预测“因为营销才产生的购买概率差”,首先得直面一个统计学上的老大难问题:反事实缺失。

2.1 反事实缺口的解决办法

一个用户在同一时间只能处于一种状态:要么被触达,要么没被触达。你永远看不到同一个用户在“被触达”和“未被触达”两种情况下同时发生的结果。我们只能定义个体因果效应:

个体增量 = Y_i(触达) - Y_i(未触达)

但在真实数据里,这两个Y_i永远只出现一个,另一个就是反事实。单个用户的增量不可观测,那怎么办?答案是用随机实验加分组均值来逼近。

如果我们把用户随机分成两组,一组触发营销活动(Treatment组,简称T组),另一组不触发(Control组,简称C组),那么对于某一类特征完全相同的人群x,就可以用两组购买率的差来代替个体增量:

CATE(x) = E[Y | T=1, X=x] - E[Y | T=0, X=x]

这个公式就是整个增益模型的基石。它不需要看到反事实,只需要保证两组用户在统计意义上除了“是否触达”之外没有系统性差别。这也解释了一个我经常强调的点:增益模型不是随便拿历史数据就能算的,必须有设计良好的实验数据支撑,否则得到的分数量级和排序都会失真。

2.2 S-Learner:把是否触达当特征

拿到实验数据后,第一种最直观的建模方式是S-Learner。做法很简单:把是否触达T也作为一个普通特征放进模型,和用户特征x一起训练一个分类器,估计P(Y|X, T)。

预测时,对每个用户做两次预测:

  • 第一次令T=1,得到 f(x, 1)
  • 第二次令T=0,得到 f(x, 0)

两者之差就是该用户的增量估计。这个方法的优点是实现成本极低,可以直接套用现有的XGBoost、 LightGBM或逻辑回归;缺点是模型很“懒”,如果特征X和T之间的交互效应比较弱,模型倾向直接忽略T,导致所有用户的增量估计都差不多。实际使用时要多观察增量分数的分布,如果方差很小,说明模型没有捕捉到异质性。

2.3 T-Learner:治疗组和对照组分开建模

T-Learner是另一种常见方案,它不把T当特征,而是把样本拆成两份:

  • 用Treatment组单独训练模型 f_1(x),预测P(Y|T=1,X)
  • 用Control组单独训练模型 f_0(x),预测P(Y|T=0,X)

增量 = f_1(x) - f_0(x)。

好处是两个组各自的特征模式拟合得更充分,尤其当营销对某类用户作用差异很大时,T-Learner通常比S-Learner更灵敏。坏处也明显:两组样本量被拆半,如果其中一组数据量本身就少,模型方差会放大,增量分数可能忽高忽低。我在项目里如果Treatment组样本不足几千条,一般不会直接用T-Learner。

2.4 X-Learner:兼顾两组差异的进阶方案

X-Learner是很多工业级项目里更稳的选择。它的思路分几步:

先用Treatment组和Control组分别训练两个基础模型,也就是上面说的T-Learner。

然后交叉计算残差:

  • 在Treatment组里,计算 d_1 = y - f_0(x),表示“实际购买概率”减去“对照组预测购买概率”;
  • 在Control组里,计算 d_0 = f_1(x) - y,表示“治疗组预测购买概率”减去“实际购买概率”。

再用这两组残差分别训练两个残差模型 g_1(x) 和 g_0(x),最终把两个残差模型的输出按一定权重(可以用倾向得分或样本占比)加权,得到最终增量。

这个方法对Treatment和Control样本量不均衡的情况更稳健,因为它先利用全量数据学了一遍购买规律,再用残差修正增量方向。缺点是需要训练多个模型,工程上稍重一点,但换来的是更稳定的增量排序效果,值得。

3. 从零实操:模拟数据跑通增益模型全流程

讲完原理,我带你把整个流程跑一遍。这里用模拟数据生成,目的是先把建模、评估的链路打通,换到真实数据时只需要替换数据源和特征。

3.1 模拟一组带真实增量规律的用户数据

先说明:真实数据里我们永远不知道每个用户真实的增量是多少,但模拟数据可以设置“上帝视角”,方便你验证模型方向是否找对。

import numpy as np import pandas as pd np.random.seed(2025) n = 10000 # 用户基础特征 age = np.random.randint(18, 65, n) city_level = np.random.choice([1, 2, 3], size=n, p=[0.4, 0.35, 0.25]) consume_freq = np.random.exponential(4, n).clip(0, 30) # 月消费频次 active_days = np.random.randint(0, 30, n) # 最近30天活跃天数 # 基础转化率:由特征决定,模拟“不触达也会买”的部分 base_prob = 1 / (1 + np.exp(-(0.05 * age - 0.3 * city_level + 0.15 * consume_freq + 0.1 * active_days - 5))) # 增量:触达后额外提升的概率,这里特意做成“高活跃低增量、中活跃高增量”的非线性结构 uplift_true = 0.25 * (active_days < 10).astype(int) \ + 0.08 * ((active_days >= 10) & (active_days < 20)).astype(int) \ - 0.02 * (active_days >= 20).astype(int) # 随机分流:一半触达,一半不触达 treat = np.random.binomial(1, 0.5, n) prob = (base_prob + uplift_true * treat).clip(0, 1) buy = np.random.binomial(1, prob, n) df = pd.DataFrame({ "age": age, "city_level": city_level, "consume_freq": consume_freq, "active_days": active_days, "treat": treat, "buy": buy })

这套数据里,低活跃用户对营销最敏感,高活跃用户天然转化率高但对营销增量小甚至为负。真实场景里这种结构很常见:最活跃的老顾客往往是“不发也会买”的人。

3.2 用S-Learner和T-Learner分别建模

建模前先做特征处理。这里特征不多,我简单归一化一下,然后切分训练集和测试集。真实项目中,特征工程会更重,比如加入优惠券面额、历史响应率、渠道偏好等。

from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier features = ["age", "city_level", "consume_freq", "active_days"] X = df[features].copy() X_scaled = StandardScaler().fit_transform(X) # 全量训练集/测试集 X_train, X_test, y_train, y_test, t_train, t_test = train_test_split( X_scaled, df["buy"], df["treat"], test_size=0.3, random_state=42)

S-Learner实现:

# 将是否触达作为特征加入 X_train_s = np.column_stack([X_train, t_train]) X_test_s = np.column_stack([X_test, t_test]) model_s = RandomForestClassifier(n_estimators=200, min_samples_leaf=50, random_state=42) model_s.fit(X_train_s, y_train) # 分别预测触达和未触达 X_test_treat = np.column_stack([X_test, np.ones(len(X_test))]) X_test_control = np.column_stack([X_test, np.zeros(len(X_test))]) pred_treat = model_s.predict_proba(X_test_treat)[:, 1] pred_control = model_s.predict_proba(X_test_control)[:, 1] uplift_s = pred_treat - pred_control

T-Learner实现:

model_t1 = RandomForestClassifier(n_estimators=200, min_samples_leaf=50, random_state=42) model_t0 = RandomForestClassifier(n_estimators=200, min_samples_leaf=50, random_state=42) model_t1.fit(X_train[t_train == 1], y_train[t_train == 1]) model_t0.fit(X_train[t_train == 0], y_train[t_train == 0]) pred_t1 = model_t1.predict_proba(X_test)[:, 1] pred_t0 = model_t0.predict_proba(X_test)[:, 1] uplift_t = pred_t1 - pred_t0

这里我特意把两个模型的超参设置成一致,这样比较时能控制变量。实际项目中,建议针对每个子模型单独调参,但也要防止过拟合。

3.3 用Qini曲线和AUUC评估模型好坏

很多同学第一次做增益模型,会习惯性用AUC评估,这是不对的。AUC衡量的是分类能力,而增益模型要的是增量排序能力:把增量最高的人优先排到前面。因此业界更常用Qini曲线和AUUC(Area Under Uplift Curve)。

def qini_curve(df_score, score_col="score", t_col="treat", y_col="buy"): df_score = df_score.sort_values(score_col, ascending=False).reset_index(drop=True) n = len(df_score) n_treat_total = df_score[t_col].sum() n_control_total = n - n_treat_total cum_ratio = np.arange(1, n + 1) / n cum_treat_y = (df_score[y_col] * df_score[t_col]).cumsum() cum_control_y = (df_score[y_col] * (1 - df_score[t_col])).cumsum() # 按触达组规模做等比缩放,避免分组不均匀导致曲线失真 if n_treat_total > 0 and n_control_total > 0: scaled_control = cum_control_y * (n_treat_total / n_control_total) else: scaled_control = cum_control_y qini = cum_treat_y - scaled_control return cum_ratio, qini # 造一个评估数据框 eval_df = pd.DataFrame({ "score": uplift_t, "treat": t_test, "buy": y_test }).reset_index(drop=True) ratio, qini_t = qini_curve(eval_df) # 随机模型的期望Qini是一条过原点的直线 random_qini = qini_t.iloc[-1] * ratio # AUUC可以近似用梯形法则计算 from numpy import trapz auuc_t = trapz(qini_t, ratio) auuc_random = trapz(random_qini, ratio) print("AUUC(T-Learner):", auuc_t) print("AUUC(Random):", auuc_random)

Qini曲线的含义很简单:横轴是按模型预测增量从高到低排序后累计触达的人群比例,纵轴是累计净增量。曲线越高,说明模型越能把真正有增量的人排到前面。随机模型的曲线理论上是一条从原点出发的直线,模型如果比随机还差,那就要回头检查特征和实验设计了。

3.4 模拟结果怎么看

我从这个模拟数据里跑完的典型结果是:T-Learner的AUUC高于随机基线,而且曲线前段抬升快,说明低活跃用户被优先找出来了。S-Learner也有提升,但增量分数整体比较平缓,排序能力弱一些。这符合我前面说的理论预期:当影响量在特征上存在明显交互结构时,T-Learner更容易捕捉到。

不过这不代表T-Learner永远更好。真实项目中,Treatment组样本量经常只有对照组的三分之一甚至更少,此时T-Learner的方差会很大,X-Learner反而更实用。建议你至少跑S和T两种做对比,如果工程时间充裕,把X-Learner也加上,多数情况下它的排序稳定性最好。

4. 落地避坑清单:实验设计与阈值计算

模型只是增益模型落地的一半,另一半在实验设计和决策策略上。这一块踩坑最多,我按遇到频率从高到低整理一下。

4.1 实验设计决定模型上限

增益模型的训练数据必须来自随机实验,至少也要来自近似随机的分流。很多团队直接拿“上月发券用户”和“上月未发券用户”的历史数据建模,这会产生严重的选择偏差——当时为什么发券、为什么不发券,背后往往有运营策略,这些策略本身就和用户特征相关。结果模型学出来的“增量”里混进了策略偏好,分数排序也是错的。

正确做法是至少提前一到两周做小流量随机分流:把目标用户群随机切成Treatment组和Control组,Treatment组正常触达营销,Control组不触达或使用兜底策略。等到数据回收后,先做AA检验确认两组基础转化率没有显著差异,再用实验数据训练模型。记住,实验设计是增益模型的地基,地基歪了,后面用什么高级模型都补不回来。

另外要特别注意分流单位。如果营销是以用户为维度触发的,就用user_id做哈希分流;如果是以设备、家庭或账号为维度,分流口径要与之对齐,避免同一用户被拆分造成数据污染。

4.2 离线评估通过不代表线上一定有效

Qini曲线和AUUC只是排序指标,它证明的是“模型可以把高增量用户排到前面”,并不直接等于“按这个分数投放就能赚钱”。最稳妥的做法是离线建模后用一轮新的小流量AB实验验证,让模型选出来的高分用户和随机用户同时投放,对比真实增量与估计量是否一致。如果线上增量明显低于离线估计,优先怀疑特征时间窗口问题。

我见过一个项目,离线Qini提升非常漂亮,线上却几乎无效。排查到最后,发现训练特征里包含了“活动页面是否点击”这类变量,而点击行为本来就是营销触达后的结果,属于典型的数据穿越。用户还没收到触达,你不可能提前知道他会不会点活动页。这类后视特征会严重抬高模型离线表现,落地时却完全用不上。

4.3 决策阈值怎么定:从模型分到ROI

拿到每个用户的增量分数后,下一个问题就是:增量多高才值得触达?这不能拍脑袋,要从经济账倒推。

假设一次短信触达的成本是0.3元,客单价200元,毛利率30%,那么每笔成交带来的毛利是60元。模型预测某用户的增量是5%,意思是“触达比不触达多出5个百分点的购买概率”,期望收益就是60元 × 5% = 3元。减去触达成本0.3元,净期望收益2.7元,值得投放。

反过来,如果模型预测增量只有0.3%,期望收益是60元 × 0.3% = 0.18元,已经低于0.3元触达成本,亏本,应该放弃。

更一般化的阈值公式是:

临界增量 = 触达成本 / 单用户边际贡献

其中单用户边际贡献 = 客单价 × 毛利率。这是静态版本,还没有考虑券面成本、退换货、长期价值。如果你发的是优惠券,成本要把券面核销费用也算进去;如果衡量长期价值,就用CLV替代单次毛利。这个公式的好处是,它把模型分数翻译成了老板能直接看懂的预算语言,也方便你按不同渠道、不同权益设定不同阈值。

4.4 常见问题速查表

常见问题可能原因排查方向
增量分数几乎全部集中在0附近用户间异质性弱,或Treatment样本量太小检查实验样本量,分群体重新建模
离线Qini很高,线上增量对不上特征穿越或用了触达后变量审查特征生成时间点,剔除“未来信息”
Treatment组和Control组特征分布差异大随机分流失败或存在策略干预重跑AA检验,检查分流代码逻辑
模型把大量高活跃用户排在前面模型学到的是响应而不是增量比较S-Learner和T-Learner结果,做残差分析
同一批用户反复被营销,效果递减缺少频次控制和疲劳度特征将最近N次触达次数、响应历史加入模型

5. 从模型分数到预算分配的实战经验

最后一个部分,我说说增量模型上线后怎么用。很多人以为模型上线就是“打个分、取top20%用户发券”,其实真正的落地是一个持续循环的系统。

5.1 标准落地流程:圈人-打分-排序-配额-投放-回流评估

环节上,我习惯按下面六步来推进:

一是圈人。先圈定本次活动的候选人群,比如“近90天有登录但未下单用户”,或者“所有可触达且未退订用户”。圈人时要把不可触达、黑名单、敏感人群提前过滤干净。

二是打分。用训练好的增益模型给每个候选用户算出增量分数。这里要注意分数校准,不同模型输出的增量分数分布不一样,最好按分位点排序,而不是直接拿原始分值跨渠道比较。

三是排序和配额。结合ROI阈值公式计算临界增量,再结合渠道预算做配额。预算有限时优先命中增量分最高的用户,至少保证整体期望收益为正。

四是投放。按最终名单执行营销触达。注意控制频控规则,不要因为模型打分高就对同一用户在不同渠道反复轰炸,这会加速疲劳。

五是回流数据。投放结束后,回收展示、点击、转化、客单价、售后等完整数据。这一步常常被忽略,但没有回流数据,下一轮实验设计和模型迭代就无从谈起。

六是评估与迭代。重新计算实际增量,观测是否达到预期,把投放结果作为下一轮训练数据的一部分。增益模型是典型的“数据飞轮”产品,每一轮投放都在为了让下一轮更准。

5.2 结合CLV做长期决策

增量模型天然是短期转化视角,它关心“这次触达有没有多带来一单”。但营销决策还要考虑长期价值,尤其是召回老客、做会员运营时,今天这单可能不赚钱,却把用户唤醒了,后面持续复购。所以我在实际业务里不太建议单一使用增益模型做所有决策,更适合把它和CLV模型组合使用:增益模型负责判断“触达敏感度”,CLV负责判断“长期价值”,两张分组合成分层策略,比如对高CLV高增量的用户给高价值权益,对低CLV高增量的用户给低面额权益,对高CLV低增量的用户干脆不打扰,避免无谓成本。

5.3 团队落地需要什么条件

最后聊聊团队落地增益模型需要具备的东西。模型算法本身门槛并不高,哪怕用sklearn的随机森林也能跑出不错的效果,真正的门槛在三件事上:第一是实验文化,业务方愿意在活动里腾出一部分人群做对照,不会因为短期KPI压力把对照组也偷偷投放掉;第二是数据埋点,触达记录、转化记录、用户特征要能对上,尤其是用户级粒度,不然无法建模;第三是迭代耐心,增益模型第一轮上线往往提升不明显,因为它需要真实投放反馈来修正,至少要跑两三轮才能看出效果。

如果你准备从零开始,我建议不要一上来就追求复杂模型,先用随机森林或XGBoost实现S-Learner和T-Learner,把数据链路、评估口径、阈值管理跑通,再逐步换X-Learner以及更进阶的因果推断方法。这个路线看起来慢,但踩坑最少。

最后分享一个我实际用下来最深的体会:增益模型做得好不好,一半在建模,一半在业务方愿不愿意相信“不触达也是一种策略”。以前大家比的是谁圈的人多、发券覆盖面广,现在比的是谁能在正确的用户身上花刚好的钱。能把“不打扰”和“不浪费”落实到系统里,增量模型的价值才算真正发挥出来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询