简介这是一份2025年五一数学建模竞赛C题的完整参赛作品内容聚焦社交媒体平台用户行为预测与内容推荐优化。作者基于2024年7月中旬的用户与博主互动数据系统求解四个子问题用LSTM模型预测博主新增关注数用LSTM结合FocalLoss预测用户次日关注行为用XGBoost分类用户在线状态并通过回归模型预测互动关系再引入时间段维度依靠CNN-XGBoost模型完成分时段在线与互动预测。压缩包共1个PDF文件大小约2.96MB收录完整论文与Python代码实现涵盖问题分析、建模过程、求解结果、模型评价与改进讨论可帮助读者理解LSTM、Nadam优化、XGBoost等算法在社交场景中的实际应用。资源已有242人学习下载适合对机器学习、数据挖掘、社交媒体分析感兴趣的科研人员和从业者尤其适合推荐系统优化方向的工程师作为参赛备战或项目复现的实战参考。1. 先看清这套资源一个四问递进的用户行为预测建模包拿到这份五一赛C题资源时我第一反应不是看模型而是看它把「用户行为预测」这件事拆到了什么粒度。整套内容围绕 2024 年 7 月 11 日至 20 日的用户与博主互动数据展开用 LSTM、XGBoost、CNN-XGBoost 三种模型组合依次回答四个问题各博主 21 日新增关注数、用户在 22 日的新关注行为、用户在 21 日在线与互动情况、用户在 23 日按时段的在线与互动。配合完整论文和 Python 代码适合正在做推荐系统、用户行为分析、时序建模的从业者也适合想找一套完整数模复现路径的同学。它不是泛泛的算法讲解而是把「数据怎么整理、模型怎么组、参数怎么设、结果表怎么填」都串起来的落地包。2. 先把数据读懂四层递进的问题设计与特征向量构造2.1 数据构成用户-博主互动序列的四种行为维度这套题的核心数据是「用户与博主在某一天发生的互动行为」原始表里每一行可以理解为一个用户对一个博主在当天的行为记录。论文把行为压缩成四个维度观看次数、点赞次数、评论次数、关注次数。也就是说某个用户 U 在时间 t 对某个博主 B 的互动可以用一个四维向量表示。X [x1, x2, x3, x4]其中 x1 是观看次数x2 是点赞次数x3 是评论次数x4 是关注次数。多个日期拼接起来就形成一个用户-博主对的时间序列。问题一和问题二都建立在「把用户行为看成时间序列」这个前提上区别在于问题一按博主聚合预测新增关注总数问题二按用户个体预测是否产生关注行为。做数据预处理时我一般习惯先按「用户ID 博主ID 日期」做分组聚合再展开成宽表或序列因为原始数据里同一个用户同一天可能对同一个博主有多条记录不聚合直接喂模型会把时序搞乱。值得注意的一点是关注行为在四个维度里是特殊的。观看、点赞、评论可以反复发生但关注对一个用户-博主对来说基本是 0 到 1 的跳变。所以问题一预测「博主新增关注数」本质上是在预测大量用户-博主对里 x4 从 0 变 1 的事件总和问题二预测「用户新关注哪个博主」就是预测 x4 是否从 0 变 1。这也是为什么问题二需要 FocalLoss 来处理样本不平衡——关注事件在整体行为里占比非常低。2.2 四个问题如何递进博主粒度到时段粒度四个问题不是并列关系而是层层细化。我用一个表把它们的关系理清楚问题预测目标粒度核心模型关键输出问题一各博主 2024-07-21 新增关注数博主级LSTM NadamTop5 博主及预测关注数问题二用户在 2024-07-22 新关注了谁用户级LSTM FocalLoss Softmax指定用户新关注博主 ID问题三用户在 2024-07-21 是否在线、与谁互动用户-博主级XGBoost 时间衰减回归在线状态 互动 Top3 博主问题四用户 2024-07-23 在线时段及互动用户-博主-时段级CNN XGBoost指定时段的互动博主从表中可以看出问题一关心的是内容侧的产出效果哪个博主吸粉能力强问题二开始转向用户侧的个性化预测问题三和问题四则引入了「在线状态」这个中间变量把预测从「会不会互动」深化到「什么时间互动」。这种递进关系在读论文时容易忽略但对复现代码很关键——因为每个问题的标签构造方式都不一样如果你用问题一的思路去构造问题三的标签特征和预测目标会完全对不上。2.3 问题三/四的特征工程细节活跃天数、互动数与时间衰减问题三的在线状态预测论文构造了四个统计特征这部分我强烈建议直接照抄近 10 天活跃天数对用户每天是否活跃是否有任何互动行为做二值化后求和日均互动数近 10 天总互动数除以 10活跃时间早晚偏好用户每天首次互动时间的平均值活跃时间集中度用户每天活跃时间的标准差前两个特征好理解第三个和第四个其实是作息习惯的量化。首次互动时间早说明用户可能是晨间活跃型活跃时间标准差小说明行为规律稳定。这四个特征组合起来XGBoost 能很快学会「作息规律且互动频繁的用户更可能在线」这样的规则。问题四把时间维度进一步打碎成 24 个 1 小时时段每个时段提取三个特征该时段是否活跃、该时段点赞评论关注次数之和、当天总互动数。这个设计比直接展平成 72 维特征24 时段 × 3 特征更合理因为它既保留了时段局部信息又没丢掉天级别的整体强度。CNN 在这里的作用是捕捉相邻时段的联动模式比如「下午 14 点到 16 点连续活跃」这类局部时间模式。3. LSTM 与 Nadam新增关注数预测与关注行为二分类的实操参数3.1 LSTM 门控机制在行为序列上的工作方式LSTM 在用户行为预测里为什么比普通 RNN 稳核心在于三个门和一个记忆单元。输入门决定当前时刻的新信息有多少写进记忆遗忘门决定过去的记忆保留多少输出门决定当前记忆输出多少。用行为序列来理解用户在 7 月 11 日点赞了某个博主这个信息进入记忆单元7 月 15 日又观看了一次输入门会把这次观看的信息叠加进去遗忘门则在衡量「7 月 11 日的点赞对 7 月 21 日是否关注还有多大影响」。对新增关注数预测来说LSTM 的最后一个隐藏状态包含了整个 10 天序列的压缩信息再接一个线性层映射到预测值。论文里的做法是y_pred W_h * h_T b_hh_T 是 LSTM 最后时刻的隐藏状态W_h 是全连接层权重b_h 是偏置。这里有个容易踩的坑LSTM 默认输出的是整个序列每个时间步的隐藏状态取最后一个时间步的隐藏状态需要显式操作。用 PyTorch 时很多人直接把lstm_out整个张量丢进全连接层维度对不上报错后才意识到要取outputs[-1]。3.2 用 Nadam 替代 Adam收敛速度与参数初始化Nadam 是 Adam 加上 Nesterov 动量的变体。Adam 已经能自适应调整每个参数的学习率Nadam 在此基础上让参数更新时考虑到「下一步的梯度方向」类似 Nesterov 加速的直觉。论文里给出的初始化参数是一组可以直接复用的配置参数值说明beta10.9一阶矩衰减率beta20.999二阶矩衰减率学习率0.001初始学习率epsilon1e-8数值稳定项实际训练时我建议学习率不要直接设成固定值而是配合ReduceLROnPlateau做动态调整。LSTM 训练前期损失下降快后期容易在局部震荡学习率衰减到 0.0003 左右往往能再压一截损失。另外Nadam 的偏差修正在训练前几步尤其重要前几个 batch 的一阶矩和二阶矩估计偏差很大不做修正梯度更新幅度会失真导致损失先涨后跌。3.3 FocalLoss 解决关注样本稀疏alpha 与 gamma 设置问题二的难点在于关注行为是极少数事件。假设一万个用户-博主对里只有几十个发生了关注普通交叉熵损失下模型只要全部预测「不关注」就能拿到很低的总损失但这对任务毫无意义。FocalLoss 的思路是给难分类样本更大的梯度权重论文里的公式是L -alpha * (1 - P)^gamma * y * log(P)alpha 是类别权重论文里正类取 0.25gamma 是聚焦因子取 2.0。gamma 越大模型越关注那些预测概率接近 0.5 的难样本——对一个真实关注行为如果模型只给出 0.3 的预测概率(1 - 0.3)^2 0.49这个系数会把损失放大如果模型已经给出 0.9 的预测概率系数只有 0.01损失自然压低。alpha 取 0.25 意味着对正样本的关注度默认不那么激进配合 gamma 的放大效果互相制衡。3.4 问题一与问题二的 PyTorch 可复现代码骨架这里给一段可以直接改数据路径就跑通的问题一 PyTorch 骨架代码输入是每个博主的每日四维行为向量输出是新增关注数预测import torch import torch.nn as nn class LSTMRegressor(nn.Module): def __init__(self, input_size4, hidden_size32, num_layers1): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue ) self.fc nn.Linear(hidden_size, 1) def forward(self, x): # x: [batch_size, seq_len, 4] out, (h_n, c_n) self.lstm(x) # 取最后一个时间步的隐藏状态 last_hidden h_n[-1] # [batch_size, hidden_size] return self.fc(last_hidden).squeeze(-1) model LSTMRegressor() optimizer torch.optim.Nadam(model.parameters(), lr0.001, betas(0.9, 0.999), eps1e-8) criterion nn.MSELoss() # 训练循环示意 for epoch in range(200): optimizer.zero_grad() pred model(sequence_data) # sequence_data: [B, T, 4] loss criterion(pred, target_follow) # target_follow: [B] loss.backward() optimizer.step()逻辑说明这里把每个博主的历史行为拆成[B, T, 4]的序列张量T 是历史天数4 对应观看、点赞、评论、关注。LSTM 每个时间步吃一个四维向量经过隐藏层后用最后一个隐藏状态接全连接层输出一个标量作为新增关注数预测值。参数说明hidden_size设 32 足够处理 10 天级别的短序列设置过大反而容易过拟合num_layers用 1 层即可多层 LSTM 在这里属于过度设计。Nadam 优化器如果当前环境版本不支持可以用AdamW加momentum配合替代效果差异不大。问题二只要把MSELoss换成FocalLoss自定义实现并把最后的全连接层输出改成 2 类 Softmax 即可。问题二的 FocalLoss 实现也很简短import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alpha0.25, gamma2.0): super().__init__() self.alpha alpha self.gamma gamma def forward(self, pred, target): # pred: 模型输出概率, target: 0/1 标签 probs torch.clamp(pred, min1e-7, max1 - 1e-7) loss -self.alpha * (1 - probs) ** self.gamma * target * torch.log(probs) \ - (1 - self.alpha) * probs ** self.gamma * (1 - target) * torch.log(1 - probs) return loss.mean()这里alpha权重和gamma聚焦因子的组合需要简单实验一次如果预测结果里正样本召回率偏低把alpha从 0.25 提到 0.5 试试代价是误报会增多。这个调参过程在论文里是静态结论复现时要按自己的数据分布重新校准。4. XGBoost 与 CNN-XGBoost在线状态与时段互动预测的关键写法4.1 在线状态四特征活跃天数、日均互动、活跃偏好与集中度问题三的在线状态预测是标准的二分类任务但特征构造比模型选择更影响上限。论文把用户 7 月 11 日至 20 日共 10 天的行为压缩成四个统计量活跃天数、日均互动数、平均首次互动时间、活跃时间标准差。前两个是强度指标后两个是作息指标组合起来覆盖了「爱不爱用」和「什么时候用」两个维度。用代码表达就是import pandas as pd import numpy as np # df_daily: uid, date, is_active, total_interact, first_active_hour, active_hour_std feat df_daily.groupby(uid).agg( active_days(is_active, sum), avg_interact(total_interact, mean), avg_first_hour(first_active_hour, mean), hour_std(active_hour_std, mean) ).reset_index() # 归一化后喂给模型 from sklearn.preprocessing import StandardScaler scaler StandardScaler() feat_scaled scaler.fit_transform(feat.iloc[:, 1:])逻辑说明active_days是近 10 天里有多少天产生过互动取值范围 0 到 10注意这里只算天数不算次数避免和avg_interact信息冗余。avg_first_hour是把每天首次互动时间取平均凌晨型用户和傍晚型用户在这一项上会自然区分开。hour_std越小说明活跃时段越固定这类用户对推荐系统的响应更可预测。参数说明XGBoost常规参数我一般用learning_rate0.1, max_depth4, n_estimators200, subsample0.8, colsample_bytree0.8。深度 4 对 4 个特征的表格数据足够深度过大容易在 10 天级别的样本上过拟合。subsample和colsample_bytree取 0.8 是为了给每棵树制造一点随机性让集成更稳。测试时如果训练集准确率接近 100% 而验证集明显下降优先把learning_rate调低到 0.05 并把树的数量提上去。4.2 XGBoost 目标函数与正则化为什么用对数损失XGBoost 的目标函数由两部分组成损失函数和正则化项。L(theta) sum(l(y_i, y_pred_i)) sum(Omega(f_k)) Omega(f_k) gamma * T 0.5 * lambda * ||w||^2前半部分是经验损失衡量预测与真实的差距后半部分是正则化控制树的复杂度。T 是叶子节点数w 是叶子权重gamma 和 lambda 是超参数。在线状态预测是二分类问题论文用对数损失-y * log(y_pred) - (1 - y) * log(1 - y_pred)。对数损失对置信度敏感比 0/1 准确率提供更平滑的梯度模型能利用「预测概率 0.7 和 0.9」之间的差异来优化。实际复现时如果用XGBClassifier直接在参数里设objectivebinary:logistic就行默认 eval_metric 是 logloss这个设置和论文是对应的。4.3 CNN-XGBoost 把 24×3 时段特征变成预测结果问题四的特征是 24 个时段每个时段 3 个特征整体形状是[24, 3]。如果能把这 24 行理解成一张「时间 × 行为」的二维图CNN 就可以像处理图像一样提取局部时间模式——这是 CNN-XGBoost 混合模型的动机。# 输入形状: [batch, 24, 3] # Conv1d 在时间维度上滑动窗口捕捉相邻时段的活跃联动论文用卷积层提取局部特征后接 ReLU 激活再做最大池化降维展平后把特征向量交给 XGBoost 做分类或回归。这里的实现顺序值得注意CNN 不是直接输出预测结果而是充当「特征提取器」XGBoost 负责最后的分类或回归决策。在代码里可以用nn.Conv1d实现import torch.nn as nn class CNNFeatureExtractor(nn.Module): def __init__(self): super().__init__() self.conv1 nn.Conv1d(in_channels3, out_channels16, kernel_size3, padding1) self.relu nn.ReLU() self.pool nn.MaxPool1d(kernel_size2, stride2) self.flatten nn.Flatten() def forward(self, x): # x: [batch, 3, 24]把特征维放前面便于 Conv1d x self.relu(self.conv1(x)) # [batch, 16, 24] x self.pool(x) # [batch, 16, 12] return self.flatten(x) # [batch, 192]逻辑说明in_channels3对应三个时段特征kernel_size3表示每次看相邻 3 个时段卷积核在 24 个时段上滑动提取「连续 3 小时活跃」「傍晚集中互动」这类局部模式。池化窗口为 2把 24 个时段压缩到 12 个位置保留最大响应值相当于只留住每个局部窗口里最显著的活跃信号。参数说明输出通道如果设 32 或 64在这个数据量下很容易过拟合论文数据只有 10 天左右行为记录16 通道已经够用。展平后的 192 维特征直接喂 XGBoost注意用StandardScaler对特征做标准化否则 CNN 输出的特征尺度会直接影响 XGBoost 的树分裂阈值判断。4.4 时间衰减因子越近的互动权重越高互动预测部分论文引入了时间衰减因子来给用户对博主的兴趣度加权D sum(gamma^(10-k) * (点赞 评论 关注)), k 1..10gamma 取 0.8意味着 7 月 20 日那次互动的权重是 0.8^1 0.87 月 11 日的权重只有 0.8^10 ≈ 0.107。这个设计很符合行为规律用户昨天点赞过的博主比十天前点赞过的博主更可能产生新的互动。这里还有个值得注意的细节加权公式里故意忽略了观看次数。理由是观看行为的互动意图太弱用户刷到视频随手划过不代表真的感兴趣硬加进去反而会稀释点赞评论的权重信号。复现时如果发现互动预测列表里总是混入一些「观看很多但几乎不点赞」的博主大概率就是衰减因子没设对或者把观看行为也算进去了。5. 复现避坑数据穿越、样本失衡与 CNN 过拟合的常见问题5.1 避坑一时间穿越——预测日数据混入训练集现象模型在训练集上表现很好但预测 7 月 21 日结果时博主 B15 的新增关注数异常偏高事后排查发现 7 月 21 日当天的部分行为数据被误当成了训练输入。原因原始附件以天为单位存储如果你在合并数据时用了全局筛选而不是按日期条件切分很容易把目标日期的数据带进历史序列。尤其做特征工程时用groupby聚合容易忽略日期过滤条件。解决在数据加载环节就强制分层。按「7 月 11 日至 20 日」划分训练数据「7 月 21 日」单独作为标签数据预测「7 月 22 日」时同样严格排除 21 日之后的所有信息。我在代码里习惯用train_cutoff和pred_date两个变量统一控制切分点避免各处硬编码日期。5.2 避坑二关注行为样本稀疏导致全预测为 0现象问题二的模型训练完预测结果里所有用户的新关注博主都是空概率全部低于 0.5模型等于完全退化成「永远预测不关注」。原因关注行为在全部互动里占比极低普通交叉熵损失下模型发现全部预测为 0 的总损失反而更小梯度信号被大量负样本淹没。这是典型的类别不平衡问题。解决换 FocalLoss 并把正类权重调起来。alpha0.25, gamma2.0是论文里的配置但如果你数据集里关注行为占比比原题还低把 alpha 提到 0.5 甚至 0.7同时把预测阈值从 0.5 降到 0.35 左右看 Precision-Recall 曲线找平衡点。每调一次都要重新检查正样本召回率别只看准确率。5.3 避坑三用户-博主对缺失导致特征全零现象有些用户对某些博主完全没有历史互动记录特征向量全零模型照样给出一个预测值但结果明显不合理——预测某用户会关注一个他从没听说过的博主。原因LSTM 序列和 XGBoost 特征都要求「用户-博主对」存在数据而原始数据的结构是稀疏的不是所有用户和所有博主都有交集。缺失部分直接填 0 会让模型把「没有记录」和「真实零互动」混为一谈。解决问题二和问题三的互动预测建议先构建完整的「用户 × 博主」笛卡尔积在缺失对的位置填入 0 并加一个 mask 特征标记是否缺失或者干脆只保留有历史交集的用户-博主对。原题预测 7 月 22 日关注行为时候选博主理论上只从用户历史互动过的博主里选这个先验能大幅减少误报。5.4 避坑四CNN-XGBoost 在数据量不足时比不过纯 XGBoost现象问题四的 CNN-XGBoost 在验证集上准确率反而不如直接只用 XGBoost而且 CNN 部分训练时损失震荡剧烈。原因用户时段行为数据量小CNN 是参数较多的模型需要足够样本才能学到稳定的卷积核。10 天 × 24 时段的样本规模对 CNN 来说偏少很容易过拟合或欠拟合。解决先用纯 XGBoost 跑一遍拿到基线再上 CNN 特征提取器。如果 CNN 不能明显提升至少可以退回到「24 个时段的原始三特征 时段聚合统计」直接进 XGBoost。CNN 部分加上 Dropout 0.3 并限制训练轮数特征提取器的输出维度控制在 128 以内能有效缓解过拟合。实践中问题四这种粒度下把「该时段是否活跃」作为 XGBoost 的单独特征往往比 CNN 卷积更直接有效。6. 验证技巧用离线回测与 TopK 命中率检验整套预测管线6.1 回测窗口怎么切整套预测的核心假设是「用 7 月 11 日至 20 日预测 21 日」这个切分是题目给定的但你复现时不能只验证这一个切点。我的习惯是把窗口整体前移用 7 月 11 日至 19 日做训练20 日做验证跑通后再用原始切分做最终预测。前移一天的优势在于20 日的真实数据在附件里是完整的可以直接算预测误差而 21 日的真实值本身依赖人填表判断。# 伪代码两次切窗对比 for cutoff in [2024-07-19, 2024-07-20]: train load_data(2024-07-11, cutoff) test load_data(cutoff 1 day) model train_model(train) metrics evaluate(model, test)除了整体切窗对 LSTM 这种需要序列输入的模型还要检查序列长度和标签的对应关系。标签是「截止 20 日之后新增的关注数」序列输入不能包含 20 日之后的任何信息否则就是时间穿越——这个问题我在第五节已经踩过一遍。6.2 TopK 命中率与排序一致性对于「找出新增关注数最高的 5 位博主」这类问题MSE 只能告诉你预测值和真实值的平均差距但无法说明排序对不对。实际意义更强的指标是 TopK 命中率预测 Top5 里有多少真的落在真实 Top5 中。命中数 |预测Top5 ∩ 真实Top5| 命中率 命中数 / 5如果命中率超过 0.6说明模型排序能力是可用的如果只有 0.2 甚至更低即使 MSE 数值很好看结论也不能直接用。除了精确命中还可以看 Spearman 秩相关系数衡量整体排序的一致性。这套题目的四个问题本质上都在做排序——博主按新增关注数排序、用户按互动概率排序、时段按互动量排序——所以排序指标比回归误差更能反映模型价值。论文里给出的 Top5 结果复现时可以重点拿这五个 ID 做校验。6.3 热力图辅助观察论文里给博主每日关注数画了热力图这在复现时是很好的调试工具。把预测值按博主 × 日期画成热力图能直观发现两类问题一是某些博主预测值周期性高得离谱多半是 LSTM 记住了某个日期的异常峰值二是所有博主预测值都趋于均值说明模型退化成了求解平均值的回归器。热力图配合 TopK 命中率一起用一个看图看趋势一个看数看结果比单看 loss 曲线更能判断模型是否真的学会了行为模式。从那以后我每次做这类时间序列预测任务都强制自己走一遍「先切窗回测、再算 TopK、最后画热力图」的流程三关都过了才敢提交结果。这套资源里的论文和代码值得反复对照着看尤其是四个问题的特征构造方式换一个数据集也照样能用。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?