ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

天猫复购预测全流程实战:特征工程与LightGBM模型优化

天猫复购预测全流程实战:特征工程与LightGBM模型优化 简介用户行为预测是电商数据分析中的核心场景而复购预测作为典型的二分类问题其难点往往不在模型本身而在于如何从原始行为日志中有效刻画用户的复购意图。针对这一挑战基于历史行为数据的特征构建与时序建模成为关键。本文从行为统计、时间间隔、行为序列等多个维度系统拆解特征工程方法并聚焦LightGBM在表格型数据上的高效训练技巧以及模型融合与时间序列验证策略的落地实践。同时针对数据泄漏、内存占用和提交结果等常见工程陷阱给出了可复用的解决方案。这套方法论不仅适用于天猫复购预测赛题也可迁移至用户流失预警、转化率预估等电商分析场景帮助从业者从数据到模型快速构建完整的预测方案。 天池这场比赛我印象很深当时花了两周多时间从baseline一步步磨到榜单上游。现在回头看天猫复购预测虽然是个老赛题但它的数据形态、特征思路和模型套路在今天很多用户行为预测场景里依然完全通用。所以我把这套完整工程的源代码和配套文档整理出来了目标是让拿到这套东西的人既能直接复现得分也能理解每一步为什么要这么做而不是只会跑通而已。这篇内容会从赛题拆解开始把特征工程、模型调优、代码组织这些环节逐个讲透中间穿插我踩过的坑和实际验证过的操作细节。适合正在打数据竞赛的同学、想转行做用户增长分析的朋友以及那些在公司里做复购预测、流失预警但总觉得特征不知道怎么构造的人。1. 先看赛题天猫复购预测到底在预测什么1.1 赛题核心逻辑与评估指标天池这个比赛的官方任务描述很简洁根据用户在天猫上的历史行为数据预测用户在某个时间窗口后是否会对同一店铺再次购买。看起来就是一个标准的二分类问题但实际做起来会发现真正难的不是模型而是怎么从原始行为日志里把“复购意图”这个东西刻画出来。我先说评估指标因为这个直接决定了方案设计方向。比赛采用AUC作为官方评估标准。AUC对样本不平衡不敏感它关心的是正负样本预测分数的相对排序而不是绝对概率值。这意味着你不需要刻意做采样平衡重点是把每个用户-店铺对的区分度做出来。我当时在代码里保留了原始样本分布只做了简单的负样本下采样用于训练加速但提交时用的是全量预测这个细节后面会细说。还有一点需要强调这个赛题是跨时间窗口预测。训练集给的是某段时间内的交互日志预测目标是用户在后续时间段内是否复购。所以整个特征构造必须严格遵循时序逻辑只能用预测时间点之前的信息绝不能用到未来数据。这也是竞赛里最容易被忽视、也是最容易作弊然后翻车的地方。1.2 数据拆解与baseline的搭建天池天猫复购赛的原始数据主要包含用户行为日志表浏览、收藏、加购、购买等行为每行数据有用户ID、商品ID、店铺ID、行为类型、时间戳等字段。看上去字段不多但数据量很大如果不做合理的聚合处理内存会直接爆炸。搭建baseline的思路很直接按用户-店铺对聚合行为统计特征比如用户对某店铺的浏览次数、购买次数、加购次数、收藏次数再算一个是否购买过的二值特征然后用LightGBM训练。操作上可以先做这些基础聚合跑出一个AUC大约在0.65左右的版本。这个分数不高但它能验证整个数据管道是否正确也能为后续特征迭代提供一个可靠的对比基准。不要一上来就堆复杂特征先把链路跑通比什么都重要。当时我写baseline时用了Pandas的groupby加agg把行为类型做成宽表之后直接merge回样本表。这里有个经验所有特征聚合都基于user_id和seller_id两个键来分组因为预测目标就是用户是否再次购买同一店铺的商品。如果你按商品维度去聚合覆盖率会很低因为一个用户通常只在同一个店铺买过少量商品但交互次数很多按店铺聚合才能把行为信号凑够。2. 特征工程高分方案的核心竞争力2.1 用户-店铺交互强度特征这套案例拿到高分靠的不是模型而是特征。我把特征体系分成三层第一层就是最基础的交互强度特征。具体包括用户对店铺的总浏览数、总收藏数、总加购数、总购买数以及各类行为占该用户全部行为的比例各类行为占该店铺全部行为的比例。这些特征回答的问题是这个用户对这个店铺的关注度到底有多高以及这个用户在店铺的所有用户里是不是属于高活跃的那批。还要构造行为转化率特征比如加购后购买率、浏览后加购率、收藏后购买率。这些比例特征比单纯的计数值更有区分度。举个例子A用户在某店铺浏览100次但只买过1次B用户浏览5次买了3次显然B的复购意愿更强只靠次数特征完全看不出这个差异。这里需要提一个实操细节统计比例特征时一定要做平滑处理否则那些只有一两次行为的用户比例会极不稳定。我用的平滑方法是分子分母同时加一个常数比如加1可以显著减少噪声。代码在案例工程的feature/behavior_ratio.py里2.2 时间间隔类特征复购预测的隐藏钥匙光看交互次数还不够时间维度是关键。这个赛题最核心的信息藏在时间间隔里。我构造了三组时间特征第一组是用户对店铺最近一次行为距离预测截止时间的间隔第二组是用户对店铺第一次行为到最近一次行为的跨度第三组是用户相邻两次行为之间的平均间隔。为什么这些特征有效因为复购行为本质上和用户的购买节奏强相关。如果一个用户每隔3天就会去某店铺逛一次那他在预测窗口内复购的概率就很高反过来如果用户三个月前活跃、最近一个月没有任何动作那基本可以判定他已经流失了。在代码实现上时间戳处理是个容易出错的点。天池给的时间戳是秒级整数直接减可以得到间隔秒数但数值量级太大在树模型里有时效果反而不好。我的做法是把间隔转成小时或天数然后做log变换压缩取值空间。这个细节在文档里专门标注了实测log变换后LightGBM的AUC能提升0.005左右别看这个数字小在高分段就是几十名的差距。2.3 行为序列与偏好特征第三层是行为序列特征。这里我是真的吃了不少苦头才把思路理顺的。所谓序列特征就是看用户行为的先后顺序而不只是总数。比如用户是“先浏览再购买”还是“先购买再浏览”这两个行为序列反映的意图完全不同。我实现了一个简单的行为转移矩阵特征统计用户从行为A到行为B的转移次数占比。比如浏览-加购转移率、加购-购买转移率、购买-浏览转移率。用Pandas的shift加分组操作就能算出来。当时跑了大概半小时换来了将近0.01的AUC提升性价比非常高。还有偏好类特征比如用户在目标店铺的购买金额占其全部店铺购买金额的比例如果有金额字段的话或者在目标店铺的行为占该用户总行为的比例。这类特征本质上是用户维度的归一化消除用户活跃度差异带来的影响让特征在用户间可比。如果你在做电商复购分析这个思路可以直接迁移不要只看用户在你店铺的行为次数还要看这些行为占他全网行为的比重这才是真正的忠诚度。2.4 特征选择与内存优化特征做完之后规模可能有几百个这时候就要做减法。我用的是LightGBM自带的重要度排序加手工筛选的组合方法。先跑一轮模型把重要度为零的特征直接删掉再用前向选择法每次增加一个特征看AUC是否明显提升如果提升小于0.001就放弃。另一个必须考虑的是内存优化。原始数据几千万行如果特征都用float64存内存很容易爆表。我的做法是在读取数据后自动把数值列降为float32或int32必要时用category类型。这一项优化让我在8G内存的笔记本上也能顺利跑完整个流程代码里专门写了个utils/reduce_memory.py函数处理这件事。还要提醒一点做特征之前一定要把训练集和测试集的数据合并起来做特征工程然后再切分。不能训练集算一份特征、测试集算一份那样很容易因为统计口径不一致导致线上分数和线下验证相差巨大。统一处理后分片保存效率最高。3. 模型训练从单模型到融合方案的演进3.1 为什么选择LightGBM作为主力模型这个项目的主力模型我选了LightGBM没有用XGBoost也没有用深度模型。原因很实在数据是典型的表格型数据特征多为稀疏计数和类别特征LightGBM基于直方图的决策树算法在这种数据上训练速度最快而且对特征缩放不敏感不需要做标准化。LightGBM的leaf-wise生长策略在样本量大的时候精度更好但也更容易过拟合所以参数上要额外注意。我最终确定的核心参数是learning_rate0.02num_leaves64min_data_in_leaf50feature_fraction0.8bagging_fraction0.8bagging_freq1。这个组合在我的验证集上表现最稳定实际跑的时候AUC在0.75左右。我在案例工程的文档里画了一个参数调优表格记录了不同参数组合对应的线下AUC方便你直接参考。比如num_leaves从31加到64AUC提升约0.008但加到127时线下反而下降说明已经过拟合了。调参不要贪心找到一个合适的区间就行。3.2 训练/验证集切分与线下评估策略竞赛最容易犯的错就是线下验证和线上评测不一致。这个赛题是时间序列预测所以不能随机切分我用的是时间切分取训练数据最后一段时间作为验证集前面所有数据作为训练集。这样可以模拟真实的预测场景也就是用过去预测未来。具体时间比例上我留出最后20%的时间段作为验证集。有个小技巧是验证集和训练集在用户ID上尽可能没有重叠否则会高估模型效果。什么意思呢如果同一个用户在训练集和验证集都出现模型就见过这个用户的行为模式预测就会偏乐观。所以切分时要按用户分组切而不是按行随机切。案例代码里提供了sklearn的TimeSeriesSplit和自定义的用户分组切分两种方式跑出来的验证指标会有明显差异读者可以自己实验感受一下。我实测下来按用户分组的切分方式与线上成绩更接近误差控制在0.003以内。3.3 多模型融合的细节单棵LightGBM能到0.75左右但要冲击更高分数需要融合。我当时做了三个模型的融合LightGBM、XGBoost、CatBoost。三个模型各自训练然后用逻辑回归作为stacking的meta模型或者简单地对预测得分做加权平均。权重的确定方式我推荐用爬山法先固定两个模型等权调整第三个模型权重然后遍历所有模型找最优组合。我最终确定的权重是LGB占0.5XGB占0.3CatBoost占0.2。这个组合在线下AUC比单模型高0.01左右。融合时有个坑不同模型的预测分数分布不同直接平均会被量级大的模型带偏。所以融合前最好先对每个模型的预测分数做rank归一化也就是把分数转成百分位数再融合。我在代码里封装了这个函数实测能再提升一点AUC而且更加稳健。4. 工程化源代码结构与文档组织4.1 代码模块划分照着组织就能跑通这份案例工程我做了一个标准的数据竞赛项目结构每个文件都有明确的职责边界。整体分成config、data、features、models、ensembles、utils六个目录根目录放run_all.sh一键运行脚本和README.md。config目录放所有超参数和路径配置用YAML格式维护。data目录放数据处理脚本原始数据、中间特征、提交结果都有独立的子目录。features目录按特征类型拆分脚本比如behavior.py、time_feature.py、sequence.py每个脚本只负责生成一类的特征输出到统一的parquet格式。models目录放单模型训练脚本ensembles目录放模型融合脚本utils目录放公共功能函数。这样拆分的好处是当你需要加一个特征时只需要在features目录新增一个脚本改一下配置文件不需要动其他代码。这套结构让我在比赛后期迭代特征时效率非常高不会因为改一处代码导致其他功能崩掉。4.2 关键代码模块的实现技巧我选几个代码里最有价值的模块讲一下实现思路。特征聚合的核心代码是def build_behavior_features(df): df df.copy() df[is_buy] (df[action_type] buy).astype(int) df[is_cart] (df[action_type] cart).astype(int) df[is_fav] (df[action_type] fav).astype(int) df[is_pv] (df[action_type] pv).astype(int) agg_dict { is_buy: [sum, mean], is_cart: [sum, mean], is_fav: [sum, mean], is_pv: [sum, mean] } user_seller_feat df.groupby([user_id, seller_id]).agg(agg_dict).reset_index() user_seller_feat.columns [_.join(col) if col[1] else col[0] for col in user_seller_feat.columns] return user_seller_feat这段代码的优势是run起来快而且逻辑一目了然。因为天池这个赛事的特征就是围绕user和seller做多维聚合把所有行为类型一次性统计出来。如果后续要加一个行为类型只需要在agg_dict里加一行。时间特征的计算是用Pandas向量化操作的df[time_gap_days] (df[last_behavior_time] - df[first_behavior_time]).dt.days df[time_gap_log] np.log1p(df[time_gap_days]) df[recency_days] (reference_time - df[last_behavior_time]).dt.days df[recency_log] np.log1p(df[recency_days])这里有个重要的经验时间类型必须先转成datetime再做差值运算直接用秒数减出来的数值虽然在大小关系上正确但日期含义不明显而且数值尺度太大对模型反而有副作用。在案例文档里我还加了一个模块流程图。注意不是用Mermaid而是用文字描述加缩进的方式表示数据从原始日志到聚合特征、再到模型训练的流向方便新手理解整个pipeline。4.3 文档说明让别人能照着跑也让自己三个月后还能看懂这份文档说明花了我不少精力但很值得。我按面向对象组织环境配置说明、数据格式说明、特征说明表、模型调参记录、复现步骤、常见错误排查表。这里重点说一下特征说明表。我把每个特征都写清楚了特征名、含义、计算逻辑、数据类型、是否平滑处理。这样做的好处有两个一是比赛隔了三个月再回头看还能知道当时为什么这样设计二是如果要把方法迁移到其他数据集只需要对照说明表替换字段名即可。文档里我还记录了我的调参手记虽然有些参数已经被淘汰了但保留调参过程比直接给最终参数更有价值。比如我当时记录了从learning_rate0.1降到0.02对AUC的影响以及不同迭代轮数下的模型表现。新人看这个能学到调参的思路而不只是收获一堆数字。4.4 目录结构清单我把完整的目录结构在README里展示如下方便你复制到自己的项目里或者对齐代码和文档的位置。. ├── README.md # 项目总览与快速开始 ├── config/ │ └── config.yaml # 全局配置路径、参数、开关 ├── data/ │ ├── raw/ # 原始数据按天分区存放 │ ├── interim/ # 中间处理结果 │ ├── processed/ # 特征工程后的数据 │ └── submissions/ # 模型预测结果 ├── features/ │ ├── __init__.py │ ├── behavior.py # 行为统计特征 │ ├── time_feature.py # 时间间隔特征 │ ├── sequence.py # 行为序列特征 │ ├── ratio_feature.py # 转化率/平滑比例特征 │ └── build_all.py # 全量特征构建脚本 ├── models/ │ ├── train_lgb.py │ ├── train_xgb.py │ └── train_cat.py ├── ensembles/ │ └── blend.py # 模型融合与权重搜索 └── utils/ ├── reduce_memory.py # 内存压缩 ├── time_split.py # 时序交叉验证切分 ├── metrics.py # 评估指标 └── logger.py # 实验日志这套结构不只为这个赛题我后来在公司里搭的很多数据分析项目也沿用了类似的分层结构。它很适合需要同时维护代码、数据和模型实验的场景。5. 实操中踩过的坑与问题排查5.1 数据泄漏一个特征毁掉整个模型数据泄漏是这类时序预测比赛最致命的问题。有一次我在构造特征时顺手做了全量数据的统计比如把整个训练集用户在所有时间的平均购买次数作为特征线下AUC直接到0.85但线上分数一塌糊涂因为测试集的数据分布根本不同这个特征等于是在预测历史规律。处理原则很简单任何特征的计算范围都不能包含预测时间点之后的数据。实现时我在config里设置了一个feature_end_time变量所有时间特征都以这个时间为边界只有早于这个时间的日志才参与统计。案例代码里专门写了一个时间过滤器从源头避免这类问题。5.2 内存爆炸与运行时间过长天池这个赛题的数据量直接读进来再merge特征普通笔记本很容易卡死。我踩过最大的坑是在做one-hot编码时把一个高基数的类别ID直接展开成几千列内存瞬间爆掉。正确的做法是用LightGBM原生支持的类别特征只需要在训练时把对应列名传给categorical_feature参数即可不需要人为编码。这样既节省内存也能减少训练时间。代码里我加了自动识别类别特征并传入模型的配置推荐你也这样处理。5.3 提交结果格式错误不要笑这个坑栽过的人非常多。提交格式要求是user_id、seller_id、prob三列但概率列究竟是复购概率还是非复购概率官方文档说得很模糊。我第一次提交时直接把模型输出原样交上去了结果线上分数只有0.5后来才发现需要确认正样本的定义。所以我在代码里特意加了一步提交前自动检查预测均值是否在合理区间如果均值小于0.05或者大于0.6就弹警告。因为复购本身就是小概率事件预测均值一般应该在0.1到0.3之间偏离太远大概率是正负样本搞反了。5.4 特征重要性分析最后补充一个很有用的技巧。我在调优过程中会定期查看模型的特征重要性这个习惯帮我发现了不少特征构造中的bug。比如有一个特征显示重要度为0我开始以为它没用后来排查发现是构造时分组键写错了所有样本的值都一样所以模型自然用不上。修正之后这个特征的重要度直接排进前20。所以当你看到某个理论上应该很重要的特征重要性为0不要急着删特征先回去看看代码逻辑是不是有问题。这种事我在比赛和工作中遇到过不止一次。6. 一些真实体会打这个比赛最深的感受是特征工程不是数据越多越好而是要有逻辑。每一个特征都要能回答一个问题比如“这个用户最近还活跃吗”“这个用户对这个店铺的依赖程度有多高”“这个用户过去的行为路径是什么样的”。如果你的特征能对着业务解释清楚那这个特征大概率是有效的如果连你自己都说不清这个特征在刻画什么那它很可能就是噪声。这套源代码和文档能带给你什么我认为是可以直接复现的结果以及一套从数据处理到模型融合的完整方法论。你可以在它的基础上换特征、换模型、换思路但核心的数据处理流程和验证策略是经过验证的可以直接取用。如果你正在准备数据竞赛或者工作中正在做复购预测相关的需求拿这套方案做底子会比从零开始省下大量时间。最后再分享一个小技巧实验记录要比写代码更重要。我每次调参都会记录模型参数、特征列表、线下分数、提交分数比赛结束后翻这些记录能非常清晰地看到方案的进化路径。这个习惯让我受益到现在也建议你从一开始就养成。本文还有配套的精品资源点击获取
返回列表