主题
第5章 购买意图预测模型
5.1 为什么需要意图预测
在决定"发不发券"之前,我们首先需要回答一个基础问题:这个用户有多大可能完成首单?
购买意图模型(Intent Model)就是对这个问题的量化回答。它预测的是:
即给定用户特征
意图预测 vs Uplift 预测
意图模型回答"用户会不会买",Uplift 模型回答"发券能不能让不买的人买"。两者是互补关系:
- 意图模型帮助我们理解用户(高意图 vs 低意图)
- Uplift 模型帮助我们决定干预(发券 vs 不发券)
在实际系统中,意图分数常作为 Uplift 模型的输入特征之一,也用于兜底策略的优先级排序。
5.2 模型设计
问题定义
| 要素 | 定义 |
|---|---|
| 任务类型 | 二分类 |
| 标签 | 用户在注册后 7 天内是否完成首单(0/1) |
| 特征 | 第 4 章的浏览特征 + 用户画像特征 |
| 评估指标 | AUC、LogLoss |
为什么选择 LightGBM
对于这类表格数据 + 特征工程的场景,GBDT(梯度提升树)是工业界的首选:
- 天然处理非线性:不需要手动构造交叉特征
- 对缺失值鲁棒:LightGBM 内置缺失值处理
- 训练速度快:相比 XGBoost 快 2-5 倍
- 内存效率高:直方图算法大幅降低内存占用
什么时候考虑深度学习?
当你有序列行为数据(如用户点击流的时间序列)或多模态特征(图片 + 文本 + 数值)时,DIN、DIEN 等深度模型会更有优势。对于本项目的静态聚合特征,树模型足够。
5.3 实现详解
模型类结构
python
# src/models/intent_model.py
class IntentModel:
"""基于 LightGBM 的用户购买意图二分类模型."""
def __init__(self, params: LGBMParams | None = None) -> None:
self._params = params or default_lgbm_params
self._model: lgb.LGBMClassifier | None = None
self._feature_names: list[str] = []设计要点:
- 参数外部化:通过
LGBMParamsdataclass 传入,方便统一管理和调参 - 延迟初始化:
_model初始为None,训练后才赋值,预测时会检查
训练流程
python
# src/models/intent_model.py
def train(self, X: pd.DataFrame, y: pd.Series) -> dict:
"""训练模型,内部 80/20 split + early stopping."""
X_train, X_val, y_train, y_val = train_test_split(
X, y, test_size=0.2, random_state=self._params.random_state, stratify=y,
)
self._feature_names = list(X.columns)
p = self._params.to_dict()
p["objective"] = "binary"
self._model = lgb.LGBMClassifier(**p)
self._model.fit(
X_train,
y_train,
eval_set=[(X_val, y_val)],
callbacks=[lgb.early_stopping(50, verbose=False), lgb.log_evaluation(-1)],
)
y_pred = self._model.predict_proba(X_val)[:, 1]
return {
"auc": float(roc_auc_score(y_val, y_pred)),
"logloss": float(log_loss(y_val, y_pred)),
}几个关键设计决策:
① stratify 分层抽样
python
train_test_split(..., stratify=y)新用户首单转化率通常只有 5%-15%,这是一个类别不平衡问题。stratify=y 确保训练集和验证集中正样本比例一致,避免验证集中正样本过少导致评估不稳定。
② Early Stopping
python
callbacks=[lgb.early_stopping(50, verbose=False)]设置 n_estimators=500 作为上界,在验证集上连续 50 轮无提升时自动停止。这是防止过拟合的最重要手段——比调正则化参数更有效。
③ 返回验证指标
训练方法直接返回 AUC 和 LogLoss,调用方不需要再额外写评估代码:
- AUC:衡量排序能力,"高意图用户是否排在前面"
- LogLoss:衡量概率校准,"预测 0.8 的用户是否真的有 80% 转化"
推理与特征重要度
python
# src/models/intent_model.py
def predict(self, X: pd.DataFrame) -> np.ndarray:
"""返回首单转化概率 (shape (n,))."""
self._check_fitted()
return self._model.predict_proba(X)[:, 1]
def feature_importance(self) -> pd.DataFrame:
"""返回按重要度降序排列的特征重要度表."""
self._check_fitted()
imp = self._model.feature_importances_
df = pd.DataFrame({"feature": self._feature_names, "importance": imp})
return df.sort_values("importance", ascending=False).reset_index(drop=True)feature_importance() 返回的是 LightGBM 内置的 split 重要度(特征被用于分裂的次数)。在实际项目中,建议同时关注:
| 重要度类型 | 含义 | 适用场景 |
|---|---|---|
| split | 特征被用作分裂的次数 | 发现"常用"特征 |
| gain | 特征带来的信息增益总量 | 发现"关键"特征 |
| SHAP | 每个样本的特征贡献 | 模型可解释性报告 |
模型持久化
python
# src/models/intent_model.py
def save(self, path: str) -> None:
self._check_fitted()
joblib.dump({"model": self._model, "feature_names": self._feature_names}, path)
@classmethod
def load(cls, path: str) -> IntentModel:
data = joblib.load(path)
obj = cls()
obj._model = data["model"]
obj._feature_names = data["feature_names"]
return obj使用 joblib 序列化,同时保存 feature_names。这是为了在加载后能验证输入特征与训练时一致——特征漂移是线上事故的常见原因。
5.4 LightGBM 参数解读
我们在 config.py 中定义了统一的 LightGBM 参数:
python
# src/config.py
@dataclass
class LGBMParams:
objective: str = "binary"
n_estimators: int = 500
learning_rate: float = 0.05
num_leaves: int = 63
feature_fraction: float = 0.8
bagging_fraction: float = 0.8
bagging_freq: int = 5
min_child_samples: int = 50
reg_alpha: float = 0.1
reg_lambda: float = 0.1
verbose: int = -1
random_state: int = 42| 参数 | 值 | 作用 |
|---|---|---|
learning_rate | 0.05 | 步长较小,配合 early stopping 效果最佳 |
num_leaves | 63 | 控制单棵树复杂度, |
feature_fraction | 0.8 | 每棵树随机选 80% 特征,降低过拟合 |
bagging_fraction | 0.8 | 每棵树随机选 80% 样本 |
min_child_samples | 50 | 叶子节点最少 50 个样本,防止在噪声上过拟合 |
reg_alpha / reg_lambda | 0.1 | L1/L2 正则化 |
调参建议
工业实践中,最值得调的参数按优先级排序:
learning_rate+n_estimators(配合 early stopping)num_leaves+min_child_samples(控制树复杂度)feature_fraction+bagging_fraction(随机性)
不要花太多时间在正则化参数上,early stopping 已经是最好的正则化。
5.5 本章小结
本章实现了购买意图预测模型,核心要点:
- 问题定义:二分类任务,预测新用户 7 天内首单概率
- 模型选择:LightGBM 二分类,适合表格特征场景
- 训练策略:stratify 分层 + early stopping 50 轮,兼顾样本不平衡和过拟合
- 评估指标:AUC(排序能力)+ LogLoss(概率校准)
- 工程实践:参数外部化、特征名保存、joblib 持久化
意图模型是整个系统的"基础视力"——它让我们看到每个用户的转化可能性。但知道"谁会买"还不够,我们更需要知道"谁因为券才买"。下一章的 Uplift 模型将回答这个关键问题。
思考题
- 如果正样本比例只有 3%,你会如何调整训练策略?(提示:考虑
scale_pos_weight、过采样 SMOTE、或调整阈值) - Early stopping 的 patience 设为 50 是否合理?太大或太小分别有什么风险?
- 意图模型的预测结果如何与 Uplift 模型配合使用?两个模型的预测结果是否需要校准?