MathorCup大数据赛题实战:基于Transformer与运筹学的电商推荐与营销优化方案

发布时间:2026/8/22 4:07:14
MathorCup大数据赛题实战:基于Transformer与运筹学的电商推荐与营销优化方案 1. 项目概述从竞赛题目到完整解决方案的实战复盘去年带队参加了MathorCup高校数学建模挑战赛的大数据赛道选的正是B题。这个题目当时挺有意思它没有直接给一个“标准”的预测或分类问题而是聚焦在一个非常具体的业务场景上电商平台基于用户行为序列的商品推荐与营销策略优化。说白了就是给你一堆脱敏后的用户浏览、点击、加购、购买日志让你去分析用户行为模式预测其未来的购买意向并设计一套能够提升平台整体GMV商品交易总额的个性化触达策略。这题目一出来很多队伍可能就懵了感觉数据量大、序列长、目标复杂。但在我看来这正是大数据竞赛的魅力所在——它无限接近一个真实的工业级数据挖掘项目。你拿到的不是清洗好的特征表格而是最原始的日志流你要解决的不是单一的指标而是一个包含用户理解、意图预测和策略优化的系统工程。今天我就把我们从破题、建模到代码实现、结果分析的完整链条拆解一遍。无论你是想备战今年的MathorCup还是单纯对如何用数据驱动业务决策感兴趣相信这篇近万字的复盘都能给你带来不少干货。我们的最终方案在当年拿到了不错的奖项其中的一些思路和技巧即使在工业界的推荐系统项目中也依然有参考价值。2. 解题核心思路与整体方案设计面对一个复杂的竞赛题最忌讳的就是拿到数据直接闷头开干。我们花了将近一天的时间反复咀嚼赛题说明并基于数据进行了探索性分析EDA才最终确定了我们的核心解题逻辑。2.1 问题本质拆解这不是一个“单点”问题题目要求看似繁多但核心可以归纳为三个层次用户深度画像与行为模式挖掘基于历史行为序列理解每个用户的兴趣偏好、消费能力和活跃周期。短期购买意向预测预测在未来的一个时间窗口内比如接下来7天用户是否会购买以及可能购买什么品类的商品。个性化营销策略优化在有限的营销资源如优惠券额度、推送次数约束下设计一套针对不同用户的触达方案发什么券、何时发以最大化平台的整体收益。这立刻让我们意识到绝不能用一个“大模型”去端到端地解决所有问题。工业界的标准做法也是分模块、分阶段进行。因此我们的整体方案采用了“漏斗式”的管道Pipeline架构原始行为日志 - 用户画像与序列特征工程 - 购买意向预测模型 - 营销策略优化模型 - 最终策略输出每一个环节的输出都是下一个环节的输入。这样做的好处是逻辑清晰便于单独调试和优化也符合实际业务系统的迭代方式。2.2 方案选型背后的考量为什么是它们在确定了管道架构后每个模块的技术选型就成了关键。这里分享一下我们当时的决策思路特征工程部分放弃“暴力”特征拥抱序列建模最初我们尝试了手工构造大量统计特征用户历史总点击、加购转化率、最近活跃天数等等。但很快发现这些特征虽然有效但无法捕捉用户行为序列中复杂的时间依赖性和模式演变。比如一个用户连续三天深夜浏览高端数码产品其意图强度肯定远高于三天内随机点击不同类目的用户。因此我们决定引入序列模型。为什么选择Transformer的Encoder结构如BERT而非RNN/LSTM因为用户行为序列可能很长Transformer的自注意力机制能更好地建模长距离依赖并行计算效率也更高。我们使用了一个轻量化的BERT模型将每个行为商品ID、类别、行为类型转化为嵌入向量让模型自己去学习序列中的模式并取出[CLS]位置的输出作为整个序列的表示向量这是一个极其强大的特征。预测模型部分梯度提升树GBDT为主深度学习为辅有了丰富的特征包括统计特征和序列嵌入特征后预测模型我们选择了LightGBM。原因有三一是它的训练和预测速度极快对于竞赛这种时间紧迫的场景非常友好二是它对特征缺失、类型混合的情况很鲁棒三是特征重要性输出非常直观便于我们回溯分析哪些行为模式对预测贡献最大。我们将序列嵌入向量和其他特征拼接起来输入LightGBM预测用户未来一段时间内的购买概率一个0到1之间的分数。这个概率分数将成为后续策略优化的核心依据。策略优化部分将其转化为运筹学问题这是本题最精彩的部分。我们不是简单地对预测概率高的用户发券就完了。题目隐含了资源约束比如总优惠券金额有限和全局优化目标最大化GMV。这本质上是一个“预算约束下的资源分配”问题。我们将其形式化为一个整数规划问题决策变量对每个用户是否发放优惠券0或1以及发放的面额从几个固定档位中选择。目标函数最大化预估的增量GMV。增量GMV 用户购买概率 * 发放优惠券后预估的订单金额 - 不发券的预估订单金额。这里的关键是要建模优惠券对用户“购买概率”和“订单金额”的提升作用这需要基于历史数据或合理假设进行估计。约束条件总优惠券成本不超过预算每个用户至多收到一种券等。 求解这个优化问题就能得到理论上全局最优的发放策略。我们使用了Python的PuLP或ortools库来求解。注意这个“预估提升作用”是策略是否有效的关键也是最大的挑战。我们采用了相对简化的方式根据用户历史对优惠券的敏感度如有相关数据或基于用户价值分层进行假设。在真实业务中这通常需要通过A/B测试来持续校准。3. 核心模块深度解析与实操要点3.1 数据预处理与用户行为序列构建原始数据通常是按时间戳排列的日志每条记录包含user_id,item_id,category_id,behavior_type点击、收藏、加购、购买,timestamp等字段。第一步是把这些杂乱无章的日志整理成可供模型使用的格式。关键操作步骤会话切割用户行为可能发生在多天。我们以“天”为单位进行切割但更精细的做法是以“30分钟无活动”作为会话边界。对于本题按天切割在保证效果的同时更简单。序列对齐与填充每个用户的行为序列长度不一。为了输入BERT等模型需要定长处理。我们选取一个合理的最大长度如最近的50个行为不足的用特殊[PAD]标记填充超过的截断保留最近的行为因为近期行为通常更重要。行为嵌入这是重中之重。我们为item_id,category_id,behavior_type分别创建了嵌入层。item_id空间巨大直接嵌入维度爆炸且稀疏我们对其进行了哈希分桶如哈希到10万个桶。然后将这三个嵌入向量相加或拼接作为每个行为事件的初始表示。# 伪代码示意 import torch import torch.nn as nn class BehaviorEmbedder(nn.Module): def __init__(self, item_bucket_num, cat_num, behave_num, embed_dim): super().__init__() self.item_embed nn.Embedding(item_bucket_num, embed_dim) self.cat_embed nn.Embedding(cat_num, embed_dim) self.behave_embed nn.Embedding(behave_num, embed_dim) def forward(self, item_ids, cat_ids, behave_ids): item_emb self.item_embed(item_ids) # [seq_len, batch, embed_dim] cat_emb self.cat_embed(cat_ids) behave_emb self.behave_embed(behave_ids) # 简单相加作为综合行为表征 combined_emb item_emb cat_emb behave_emb return combined_emb实操心得时间特征的融入除了行为类型行为之间的时间间隔是极重要的信号。我们将相邻行为的时间差单位秒进行对数变换后作为一个独立的数值特征与行为嵌入向量拼接让模型能感知行为发生的密集或稀疏程度。全局上下文我们不仅构建了用户自身的序列还引入了“全局热门商品”、“当日热门品类”等作为序列中的辅助节点或在特征工程中作为单独特征帮助模型理解当前行为的普遍性。3.2 基于Transformer的序列特征提取器我们构建了一个简化版的BERT模型只使用其Encoder部分用于提取序列的语义表示。模型结构要点输入层上述BehaviorEmbedder的输出。位置编码由于Transformer本身没有时序概念必须加入位置编码。我们使用了可学习的位置编码而非正弦余弦公式因为可学习编码在任务特定场景下可能表现更好。Transformer Encoder层我们只使用了2-3层隐藏维度为256注意力头数为8。竞赛中模型不宜过深防止过拟合和训练过慢。池化与输出取最后一层Encoder输出在序列第一个位置通常对应[CLS]标记的向量作为整个用户行为序列的聚合表示。这个[CLS]向量是一个固定长度的稠密向量蕴含了该用户的兴趣和意图信息我们将它作为核心特征输出。训练技巧预训练任务我们采用了类似BERT的掩码语言模型MLM任务进行预训练。随机掩码序列中15%的行为让模型预测被掩码的行为类型和商品类别。这个自监督任务能让模型深入理解行为之间的逻辑关系。微调预训练完成后可以将[CLS]向量接入一个简单的分类层如预测用户是否会在次日购买用赛题提供的标注数据进行有监督微调使提取的特征更贴近最终目标。冻结与解冻在后续的LightGBM训练中我们可以选择冻结Transformer的参数仅将其作为特征提取器也可以进行端到端的联合训练但后者更复杂需要精细调参。注意训练序列模型非常消耗计算资源。务必使用GPU并注意批量大小Batch Size的设置。如果序列很长可以考虑使用梯度累积来模拟更大的批量。同时要监控训练集和验证集的损失防止过拟合。3.3 购买意向预测特征融合与模型训练这一步的目标是产出每个用户的购买概率得分。我们以LightGBM为例。特征清单示例特征类别具体特征示例说明序列嵌入特征[CLS]向量的每一维如256维从Transformer中提取的用户意图稠密表示统计特征历史总点击次数、加购次数、购买次数用户活跃度与消费能力点击到购买的转化率、加购到购买的转化率用户决策效率最近一次行为距今的天数用户近期活跃度时间特征用户活跃天数、平均每天行为次数用户粘性行为主要发生的时间段如上午、晚上用户习惯交互特征用户对特定品类/价格档位的偏好度刻画兴趣偏好用户历史购买的平均金额、最高金额消费水平模型训练关键点样本构造与标签定义根据赛题要求定义“未来一段时间内购买”为正样本。注意样本泄漏必须严格确保用于构造特征的数据都在标签时间点之前。通常需要做一个时间切分点用切分点前的数据做特征切分点后的行为定义标签。处理类别不平衡购买用户通常是少数。我们使用了LightGBM的is_unbalance参数或手动设置scale_pos_weight并在评估时重点关注AUC和精确率-召回率曲线PR-AUC因为后者在不平衡数据上更具参考性。特征重要性分析训练后查看LightGBM输出的特征重要性。我们当时发现Transformer提取的[CLS]向量中的某些维度重要性排名非常靠前甚至超过了某些手工统计特征。这证实了序列建模的有效性。import lightgbm as lgb import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score # 假设df是特征DataFramey是标签 X_train, X_val, y_train, y_val train_test_split(df, y, test_size0.2, random_state42) lgb_train lgb.Dataset(X_train, y_train) lgb_val lgb.Dataset(X_val, y_val, referencelgb_train) params { objective: binary, metric: auc, boosting_type: gbdt, num_leaves: 31, learning_rate: 0.05, feature_fraction: 0.9, bagging_fraction: 0.8, bagging_freq: 5, verbose: -1, is_unbalance: True # 处理不平衡 } gbm lgb.train(params, lgb_train, num_boost_round1000, valid_sets[lgb_val], callbacks[lgb.early_stopping(stopping_rounds50)]) # 预测概率 y_pred_prob gbm.predict(X_val, num_iterationgbm.best_iteration) print(fValidation AUC: {roc_auc_score(y_val, y_pred_prob):.4f}) # 查看特征重要性 importance_df pd.DataFrame({ feature: gbm.feature_name(), importance: gbm.feature_importance() }).sort_values(importance, ascendingFalse)4. 营销策略优化从预测到决策的临门一脚得到购买概率后故事才进行到一半。如何利用这些概率在预算限制下做出最优的营销决策是拉开差距的关键。4.1 定义优化问题数学建模我们假设有N个用户对于用户i我们有其基础购买概率p_i来自LightGBM预测以及预估的单次购买客单价v_i可用历史平均客单价或基于品类预测。我们提供K种优惠券面额为d_k成本为c_k。定义决策变量x_{ik} ∈ {0, 1}表示是否给用户i发放第k种券。核心假设发放优惠券会提升用户的购买概率和/或客单价。我们引入提升函数概率提升因子α_{ik} ≥ 1表示发放券k后用户i的购买概率变为min(1, p_i * α_{ik})。客单价提升因子β_{ik} ≥ 1表示发放券k后用户i的客单价变为v_i * β_{ik}。那么给用户i发放券k带来的预估增量GMV为ΔGMV_{ik} (p_i * α_{ik} * v_i * β_{ik}) - (p_i * v_i) p_i * v_i * (α_{ik} * β_{ik} - 1)优化目标与约束最大化 Σ_i Σ_k (ΔGMV_{ik} * x_{ik}) 约束条件 1. Σ_i Σ_k (c_k * x_{ik}) ≤ B 总预算约束 2. Σ_k x_{ik} ≤ 1, ∀i 每个用户至多得一种券 3. x_{ik} ∈ {0, 1} 整数决策4.2 提升因子的估计业务逻辑的融入这里的α和β是模型中最具“魔法”也最需要业务sense的部分。我们当时采用了分层估计的方法用户价值分层根据用户历史RFM最近购买时间、购买频次、购买金额或预测的购买概率p_i和客单价v_i将用户分为高价值、中价值、低价值等群体。券敏感性假设基于常识或有限的业务知识如历史促销活动分析进行假设。例如高价值用户对折扣券敏感度较低α较小但可能对满减券敏感提升客单价β。低价值用户对无门槛小额券敏感度很高α较大但β可能为1不提升客单价。中价值用户对多种券都可能有一定反应。参数化与调优将α和β设定为可调参数例如对不同的用户-券组合设置几组预设值。然后可以在一个历史数据集上如果有的话模拟不同参数下的总GMV选择表现最好的一组。如果没有历史数据则需要设计多套参数方案作为最终策略的多个版本进行论述。4.3 问题求解与策略输出上述优化问题是一个经典的0-1整数线性规划问题。对于数万甚至数十万用户量级直接求解可能较慢。但竞赛场景下用户数通常在万级别使用优化求解器是可行的。我们使用PuLP库调用CBC求解器或更高效的ortoolsGoogle的优化工具包来求解。# 使用PuLP的示例框架 from pulp import LpProblem, LpMaximize, LpVariable, lpSum, LpStatus, PULP_CBC_CMD # 假设数据 user_ids range(N) coupon_types range(K) B 10000 # 总预算 prob LpProblem(Coupon_Allocation, LpMaximize) # 定义决策变量 x LpVariable.dicts(x, (user_ids, coupon_types), lowBound0, upBound1, catBinary) # 设置目标函数 prob lpSum([increment_gmv[i][k] * x[i][k] for i in user_ids for k in coupon_types]) # 设置约束 # 预算约束 prob lpSum([coupon_cost[k] * x[i][k] for i in user_ids for k in coupon_types]) B # 每人至多一张券约束 for i in user_ids: prob lpSum([x[i][k] for k in coupon_types]) 1 # 求解 prob.solve(PULP_CBC_CMD(msgFalse)) print(Status:, LpStatus[prob.status]) # 输出结果 allocation_strategy [] for i in user_ids: for k in coupon_types: if x[i][k].value() 1: allocation_strategy.append({user_id: i, coupon_type: k})求解后我们就得到了一个明确的策略列表给哪些用户发放哪种优惠券。这个策略是基于全局优化得出的理论上在给定预算下能带来最大的GMV提升。5. 代码工程化与结果分析要点5.1 代码组织与复现性保障一个混乱的代码仓库是竞赛和实际项目的大忌。我们采用了模块化的组织方式project/ ├── config/ # 配置文件超参数、路径 ├── data/ # 原始数据、处理后的数据 ├── src/ │ ├── preprocess/ # 数据预处理和序列构建模块 │ ├── features/ # 特征工程模块统计特征、序列特征提取 │ ├── models/ # 模型定义Transformer, LightGBM训练脚本 │ ├── optimization/ # 策略优化求解模块 │ └── utils/ # 工具函数 ├── notebooks/ # EDA和实验性分析的Jupyter Notebook ├── train_pipeline.py # 主训练管道 └── requirements.txt # 依赖环境关键实践种子固定在所有可能引入随机性的地方数据分割、模型初始化、数据打乱设置随机种子确保结果可复现。配置化管理所有超参数、文件路径、模型结构参数都写在配置文件中避免硬编码。日志记录使用logging模块记录关键步骤的输出、模型评估指标和特征重要性便于追踪和调试。5.2 结果分析与方案阐述在竞赛论文或报告中仅仅展示最终结果是不够的必须进行深入分析证明方案的有效性和合理性。我们当时重点分析了以下几个方面模型性能对比提供了LightGBM仅使用统计特征、仅使用序列嵌入特征以及融合特征的性能对比AUC, F1-score。用表格和图表清晰展示融合特征带来的显著提升。序列模型的可解释性探索虽然Transformer是“黑盒”但我们尝试了注意力权重的可视化。对于某些典型用户我们绘制了其行为序列的注意力热力图观察模型在预测时更关注哪些历史行为例如发现模型对“加购”和“收藏”行为的注意力权重很高这为“为什么模型能预测准”提供了直观依据。策略优化结果分析发放人群画像对比了获得优惠券的用户群体与未获得群体的特征差异如平均预测概率、历史客单价、活跃度。这验证了策略是否“智能”地瞄准了高潜或高价值用户。预算使用效率计算了单位预算带来的预估GMV提升并与一些基线策略如随机发放、仅对高概率用户发放进行对比突出优化模型的价值。敏感性分析展示了当提升因子α和β在一定范围内变动时最终总GMV的变化情况说明策略的鲁棒性。方案创新点与局限性总结创新点强调了“序列建模全局优化”管道在解决此类复杂业务问题上的系统性优势提到了将时间间隔作为特征融入序列模型的具体方法。局限性坦诚说明了提升因子α和β依赖假设在真实场景中需要A/B测试校准指出模型对全新商品或突发热点事件捕捉能力可能不足。6. 常见问题与避坑指南实录在实际操作中我们踩了不少坑也总结了一些让方案更稳健的经验。6.1 数据与特征工程中的坑问题1用户行为序列长度分布极不均匀如何处理现象大部分用户只有几个行为少数用户有上千条行为。直接截断会损失信息填充过多则引入噪声。解决我们采用了分层采样的思路。对于长序列用户我们不是简单截断最后50条而是先将其切割成多个不重叠的、长度为50的子序列滑动窗口每个子序列作为一个独立的训练样本但标签相同。这相当于对长序列用户进行了数据增强。对于短序列用户则正常填充。问题2商品ID过多嵌入层参数爆炸训练缓慢且易过拟合。解决如前所述使用哈希分桶。更进一步可以结合商品类别信息先训练一个类别嵌入再在同类别商品间共享部分嵌入参数。或者使用预训练的商品向量如通过Item2Vec方法在全体数据上训练得到作为固定的嵌入输入只微调上层网络。问题3时间特征如何处理更有效踩坑最初我们只用了行为发生的绝对小时0-23效果一般。优化我们引入了更多维度行为是工作日还是周末、距离用户历史平均活跃时间的偏移量、当前行为与上一次同类型行为的时间差。特别是最后一个对于预测购买这类“周期性”或“习惯性”行为非常有效。6.2 模型训练与调参中的坑问题4Transformer模型训练不稳定损失震荡或难以收敛。检查清单梯度裁剪在训练循环中加入梯度裁剪防止梯度爆炸。学习率预热使用线性预热或余弦退火策略避免初期学习率过大。层归一化确保Transformer每一层后都使用了LayerNorm。检查数据确保输入序列中没有非法的索引如超出词表大小嵌入层初始化是否合理。降低模型复杂度先使用更少的层数如2层、更小的隐藏维度如128和更短的序列长度进行调试确保能过拟合一个小批次数据再逐步放大。问题5LightGBM模型过拟合。对策增加bagging_fraction和feature_fraction使用随机子空间和样本采样。增加min_child_samples或min_data_in_leaf让叶子节点包含更多数据。降低num_leaves这是控制模型复杂度的首要参数。使用早停法基于验证集AUC不再提升来停止迭代。6.3 策略优化中的坑问题6整数规划求解速度慢对于超大规模用户怎么办近似算法如果用户量达到百万级精确求解可能不现实。可以采用贪心算法近似求解计算每个用户-券组合的“性价比”增量GMV / 券成本按性价比从高到低排序依次发放直到预算耗尽。虽然这不是全局最优但效率极高且在实际中常作为基线或快速解决方案。分治思想先将用户按价值或概率分桶在桶内进行优化再在桶间调整预算分配。问题7预估的提升因子α, β完全不靠谱怎么办稳健性设计这是最大的不确定性来源。我们的策略是设计多套策略进行对比分析。例如策略A激进假设提升因子设得较高。策略B保守假设提升因子设得较低。策略C不考虑提升仅对高概率用户发放即α1, β1退化为选择概率最高的用户。 在报告中同时呈现不同策略下的预估结果并讨论其适用场景和风险这反而体现了思考的全面性。最后想说的是数学建模和大数据竞赛的魅力就在于将模糊的业务问题通过数据、模型和算法转化为清晰、可量化、可优化的解决方案。这个过程没有标准答案我们的方案也只是其中一种可能。重要的是掌握这种“定义问题-拆解问题-组合工具-解决问题-评估结果”的系统性思维。希望这篇超详细的复盘能帮你理清思路在下一次挑战中构建出属于你自己的、更精彩的解决方案。