简介这份资源是2025年五一数学建模竞赛C题的完整参赛作品面向备战数学建模竞赛的学生、从事社交媒体分析与推荐系统优化的科研人员及工程师。内容围绕用户行为预测与内容推荐展开融合LSTM、Nadam优化算法、XGBoost与CNN-XGBoost等模型依次求解博主新增关注数预测、用户关注行为二分类、在线状态与互动关系预测以及引入时间段维度的互动时段预测四个问题并附完整论文与Python代码实现。资源包共1个PDF文件约2.96MB涵盖赛题分析、模型构建、结果评价与改进讨论便于读者理解建模思路并复现实验。目前已有240人学习下载适合希望系统掌握时间序列预测、分类模型与推荐系统优化方法的读者参考借鉴。1. 五一赛C题社交媒体用户分析从赛题到可复现方案五一数学建模竞赛的C题这几年有个明显趋势题目越来越像真实业务里的用户增长分析需求而不是纯粹的数学题。2025年这道社交媒体平台用户分析题核心诉求通常落在三件事上用户行为预测、用户分群、以及平台指标的影响因素归因。很多队伍拿到题第一反应是套模型结果论文写出来自己都不信——因为数据清洗没做透特征工程全靠拍脑袋模型评估只报一个准确率就交差。这篇笔记不讲空泛的建模套路而是把这道题从读数据到出论文结论的完整链路拆开。涉及的技术栈是Python为主模型侧会用到LSTM做时间序列预测、XGBoost做回归和分类、CNN做文本或序列特征提取优化器用Nadam。适合两类人正在准备数模竞赛、需要一套能直接改的代码框架的以及做社交媒体用户分析、想看看这套组合拳在真实数据上边界的。下面按「数据怎么进 → 特征怎么造 → 模型怎么选 → 结果怎么验」的顺序推。2. 数据读取与用户行为特征构造先把脏数据摁住2.1 社交媒体数据的典型脏法和你该先看的几列社交媒体平台导出的用户行为数据常见字段包括用户ID、发帖时间、互动类型点赞/评论/转发、内容长度、粉丝数、是否认证等。这类数据脏得很有规律时间戳格式不统一、互动类型有中英文混写、用户ID存在重复但属性冲突、大量缺失值集中在粉丝数和认证字段。拿到数据先别急着df.describe()先做三件事看时间列的取值范围和格式分布、看分类列的unique值、看每个用户ID出现的频次分布。频次分布能直接告诉你数据是「用户-事件」长表还是「用户」宽表这决定了后面聚合的粒度。import pandas as pd import numpy as np df pd.read_csv(social_media.csv, encodingutf-8) # 时间列格式探查看前20个非空值的原始形态 print(df[timestamp].dropna().head(20).tolist()) # 分类列的唯一值判断有没有中英文混写 for col in [interaction_type, verified]: if col in df.columns: print(col, df[col].unique()[:15]) # 用户频次分布判断是长表还是宽表 user_freq df[user_id].value_counts() print(user_freq.describe()) print(单次出现用户占比:, (user_freq 1).mean())这段代码的逻辑是先摸清数据形态再动手。timestamp那行如果打印出来格式五花八门后面就必须写统一解析函数不能直接pd.to_datetime一把梭。interaction_type的unique值如果出现「点赞/like/赞」三种写法说明需要映射表归一。user_freq的describe能看出数据是每个用户多条记录还是基本一人一条单次出现用户占比超过70%意味着用户侧特征会非常稀疏后面做用户分群时要特别小心。参数上encoding建议先试utf-8报错就换gbk或utf-8-sig这是中文数据最常见的坑。读取大文件时加low_memoryFalse避免类型推断警告。2.2 时间窗口聚合与互动率特征社交媒体分析里最有信息量的特征往往不是原始字段而是聚合出来的比率和趋势。常见做法是按用户维度聚合出发帖频率、平均互动量、互动率互动数/粉丝数、活跃时间段分布、内容长度均值。如果题目涉及时间预测还要按天或按小时做时间窗口聚合。# 统一时间格式 df[timestamp] pd.to_datetime(df[timestamp], errorscoerce) df df.dropna(subset[timestamp]) # 互动类型归一 type_map {点赞: like, 赞: like, like: like, 评论: comment, comment: comment, 转发: share, share: share} df[interaction_type] df[interaction_type].map(type_map) # 按用户聚合核心特征 user_feat df.groupby(user_id).agg( post_count(timestamp, count), active_days(timestamp, lambda x: x.dt.date.nunique()), avg_content_len(content_length, mean), first_seen(timestamp, min), last_seen(timestamp, max) ).reset_index() # 互动率需要先按用户统计互动总量再除以粉丝数 interact df[df[interaction_type].notna()].groupby(user_id).size().rename(interact_total) user_feat user_feat.merge(interact, onuser_id, howleft) user_feat[interact_total] user_feat[interact_total].fillna(0) fans df.groupby(user_id)[fans_count].max().rename(fans) user_feat user_feat.merge(fans, onuser_id, howleft) user_feat[interact_rate] user_feat[interact_total] / (user_feat[fans] 1) # 活跃跨度 user_feat[active_span] (user_feat[last_seen] - user_feat[first_seen]).dt.days聚合逻辑的关键在于fans 1这个操作避免除零。active_days用nunique而不是count是因为同一天多次发帖只算一天活跃这个特征在用户分群时比总发帖数更能区分「高频低质」和「稳定输出」两类用户。active_span为0说明用户只在一天内出现过这类用户在做留存分析时通常要单独处理。注意如果数据里粉丝数本身是随时间变化的用max聚合会引入偏差更严谨的做法是取用户最后一次记录时的粉丝数或者按时间排序后取最后一个非空值。2.3 缺失值处理与异常值截断社交媒体数据的缺失值不能无脑填均值。粉丝数缺失往往意味着用户是新号或隐私设置填0比填均值更合理。内容长度缺失可以填中位数但如果缺失比例超过30%这个特征本身就该考虑弃用。异常值方面粉丝数和互动量通常极度右偏直接标准化会被极端值带偏。常见做法是先做分位数截断再取对数。# 缺失值粉丝数填0内容长度填中位数 user_feat[fans] user_feat[fans].fillna(0) user_feat[avg_content_len] user_feat[avg_content_len].fillna( user_feat[avg_content_len].median() ) # 异常值截断按99分位截断后再log1p for col in [fans, interact_total, post_count]: upper user_feat[col].quantile(0.99) user_feat[col] user_feat[col].clip(upperupper) user_feat[col _log] np.log1p(user_feat[col])clip到99分位是数模里性价比最高的异常值处理方式比IQR温和比不处理稳。log1p而不是log是为了处理0值粉丝数为0的用户取log会变成负无穷。这两个变换做完后面XGBoost和LSTM的输入分布会正常很多。3. XGBoost与LSTM的选型分工别拿一个模型硬套所有问题3.1 什么目标用XGBoost什么目标用LSTM这道题通常有两类子问题一类是「预测某个用户下个月是否流失」或「预测互动量」另一类是「预测平台未来N天的活跃用户数」。前者是截面数据上的分类或回归后者是时间序列预测。很多队伍两类问题都上LSTM结果截面数据上LSTM表现还不如逻辑回归因为LSTM吃的是序列依赖截面数据没有时序结构硬套就是杀鸡用牛刀。XGBoost在截面特征上的优势是自动处理特征交互、对缺失值鲁棒、训练快、特征重要性可解释。LSTM的优势是捕捉时间依赖和长期模式。分工原则很简单有明确时间顺序且预测目标依赖历史序列的用LSTM否则优先XGBoost。import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score, mean_squared_error # 分类任务用户是否高活跃互动率高于中位数 user_feat[is_active] (user_feat[interact_rate] user_feat[interact_rate].median()).astype(int) feature_cols [post_count_log, active_days, avg_content_len, fans_log, interact_total_log, active_span] X user_feat[feature_cols].fillna(0) y user_feat[is_active] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) clf xgb.XGBClassifier( n_estimators300, max_depth5, learning_rate0.05, subsample0.8, colsample_bytree0.8, eval_metricauc, random_state42 ) clf.fit(X_train, y_train) pred clf.predict_proba(X_test)[:, 1] print(AUC:, roc_auc_score(y_test, pred))XGBoost的参数里max_depth5是截面数据上的稳妥起点超过7在小数据集上几乎必过拟合。learning_rate0.05配合n_estimators300是常见的慢学习组合比0.1配100更稳。subsample和colsample_bytree都设0.8是为了引入随机性抗过拟合。stratifyy保证训练测试集类别比例一致分类任务里这个不能省。3.2 LSTM时间序列预测的数据窗口构造如果题目要求预测平台未来活跃用户数就需要把数据整理成时间序列再用滑动窗口构造监督学习样本。LSTM的输入是(样本数, 时间步长, 特征数)这个三维结构是新手最容易搞错的地方。import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset # 按天聚合平台活跃用户数 daily df.groupby(df[timestamp].dt.date).agg( active_users(user_id, nunique), total_interact(interaction_type, count) ).reset_index() daily[date] pd.to_datetime(daily[date]) daily daily.sort_values(date).reset_index(dropTrue) # 滑动窗口构造样本 def make_windows(data, window7, horizon1): X, y [], [] for i in range(len(data) - window - horizon 1): X.append(data[i:iwindow]) y.append(data[iwindow:iwindowhorizon, 0]) # 预测active_users return np.array(X), np.array(y) values daily[[active_users, total_interact]].values.astype(np.float32) # 标准化 mean, std values.mean(axis0), values.std(axis0) 1e-8 values_norm (values - mean) / std X, y make_windows(values_norm, window7, horizon1) X_tensor torch.tensor(X) y_tensor torch.tensor(y) dataset TensorDataset(X_tensor, y_tensor) loader DataLoader(dataset, batch_size16, shuffleTrue)window7表示用过去7天预测下1天这个值要根据数据长度调数据少于60天时window设3到5更合适。标准化用全局mean/std在时间序列里其实有数据泄漏风险更严谨的做法是用训练集的统计量但数模场景下数据量小全局标准化通常也能接受论文里说明即可。3.3 LSTM模型定义与Nadam优化器配置class LSTMPredictor(nn.Module): def __init__(self, input_size2, hidden_size32, num_layers1): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) out out[:, -1, :] # 取最后一个时间步 return self.fc(out) model LSTMPredictor(input_size2, hidden_size32, num_layers1) optimizer torch.optim.Nadam(model.parameters(), lr1e-3) criterion nn.MSELoss() for epoch in range(100): model.train() total_loss 0 for xb, yb in loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() total_loss loss.item() if (epoch 1) % 20 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.4f})hidden_size32在小数据集上是合理起点超过64基本没必要。num_layers1是因为数据量小两层LSTM在小样本上容易过拟合。Nadam相比Adam的优势在于它把Nesterov动量融进了自适应学习率在序列预测任务上收敛通常略快但差异不大lr1e-3是标准起点。out[:, -1, :]取最后一个时间步的输出这是做单步预测的标准做法如果要预测多步这里要改成取多个时间步或加decoder结构。4. CNN做文本特征提取把用户内容变成可用向量4.1 什么场景下CNN比TF-IDF更值得用社交媒体数据里如果有用户发帖文本传统做法是TF-IDF加朴素贝叶斯或SVM。CNN做文本分类的优势在于能捕捉局部n-gram特征和词序信息在文本较长、语义依赖较强的场景下比TF-IDF好。但如果文本平均长度不到10个词CNN的卷积核基本滑不动这时候TF-IDF反而更实在。判断标准文本平均长度超过20个词、且分类任务依赖短语搭配比如判断帖子是否含情绪倾向用CNN否则先用TF-IDF跑baseline。import torch.nn.functional as F class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim64, num_filters32, filter_sizes(2, 3, 4), num_classes2): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.convs nn.ModuleList([ nn.Conv2d(1, num_filters, (fs, embed_dim)) for fs in filter_sizes ]) self.dropout nn.Dropout(0.3) self.fc nn.Linear(num_filters * len(filter_sizes), num_classes) def forward(self, x): x self.embedding(x) # (B, L, E) x x.unsqueeze(1) # (B, 1, L, E) x [F.relu(conv(x)).squeeze(3) for conv in self.convs] x [F.max_pool1d(c, c.size(2)).squeeze(2) for c in x] x torch.cat(x, dim1) x self.dropout(x) return self.fc(x)filter_sizes(2,3,4)对应二元、三元、四元短语特征这是TextCNN的经典配置。num_filters32每个尺寸的卷积核数量小数据集上32够用。padding_idx0让padding不参与梯度更新。F.max_pool1d做全局最大池化把变长文本压成固定维度。这个结构在文本分类任务上通常比LSTM快且效果不差但前提是文本长度足够。4.2 词表构建与文本预处理的最小流程from collections import Counter import re def tokenize(text): text re.sub(r[^\w\s], , str(text).lower()) return text.split() # 构建词表 all_tokens [] for text in df[content].dropna(): all_tokens.extend(tokenize(text)) counter Counter(all_tokens) vocab {word: idx 1 for idx, (word, _) in enumerate(counter.most_common(5000))} # 0留给padding vocab[unk] len(vocab) 1 def encode(text, max_len50): tokens tokenize(text)[:max_len] ids [vocab.get(t, vocab[unk]) for t in tokens] ids [0] * (max_len - len(ids)) return ids df[content_ids] df[content].fillna().apply(encode)词表限制在5000是因为社交媒体文本词汇量大但长尾严重低频词对分类贡献极小。max_len50截断超过50个词的帖子信息密度已经很低。unk处理未登录词避免测试时遇到训练集没见过的词直接报错。这套流程跑完content_ids列就可以直接喂给TextCNN。5. 避坑与排查数模代码里最容易翻车的五个地方5.1 时间戳解析后NaT静默丢弃导致样本量骤减现象pd.to_datetime加errorscoerce后dropna发现数据少了一半。原因原始时间列格式混杂比如「2025/5/1」和「2025-05-01 12:00:00」混在一起coerce把解析失败的都变成NaT。解决先抽样看格式分布写自定义解析函数按格式分支处理或者用formatmixedpandas 2.0让pandas自动推断。5.2 XGBoost特征重要性全集中在某一个特征上现象训练完一看feature_importance某个特征占了80%权重。原因通常是该特征存在数据泄漏比如用「互动总量」预测「是否高活跃」而高活跃的定义本身就来自互动率。解决检查特征和目标之间是否存在计算关系有则剔除。另一个可能是特征量纲差异过大树模型虽然对量纲不敏感但极端偏态分布会让分裂点集中。5.3 LSTM训练loss不降或震荡现象loss在前几个epoch就卡住不动或者上下大幅震荡。原因学习率过大、输入未标准化、或者window设置导致样本间高度重叠。解决先把lr降到1e-4试确认输入做了标准化检查make_windows里步长是否为1——步长为1时相邻样本几乎一样batch内梯度方向太一致可以改成步长2或3增加样本多样性。5.4 CNN文本分类准确率停在50%左右现象二分类任务准确率一直在0.5附近跟随机猜一样。原因最常见的是标签和文本没对齐比如content_ids和label来自不同排序的DataFramemerge时索引错位。解决在encode之前先reset_index(dropTrue)确保文本和标签在同一行。另一个原因是词表太小导致大量unk把most_common的阈值调大。5.5 论文里模型评估只报准确率现象论文评审反馈「评估不充分」。原因分类任务只报accuracy回归任务只报MSE。解决分类任务至少报accuracy、precision、recall、F1、AUC回归任务报MSE、MAE、R²。时间序列预测还要画预测值和真实值的对比曲线这个图在论文里比任何数字都有说服力。6. 论文结论怎么从模型输出里长出来数模论文的结论部分最容易写成「模型A的AUC是0.85模型B的MSE是0.03」这种数字罗列。评审想看到的是这些数字说明了什么业务含义平台方看了能做什么决策。我一般的做法是从三个地方挖结论。第一XGBoost的特征重要性排序直接对应「哪些用户行为指标对活跃度影响最大」这个可以写成平台运营建议。第二LSTM的预测曲线和真实曲线的偏差段偏差大的时间段往往对应特殊事件节假日、平台活动这个可以写成「模型在异常事件期间预测能力下降建议引入事件特征」。第三用户分群结果如果做了聚类每个群的画像描述就是现成的用户运营策略。# 从XGBoost特征重要性生成结论素材 importance pd.DataFrame({ feature: feature_cols, importance: clf.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance) # 从LSTM预测结果找偏差段 model.eval() with torch.no_grad(): pred_all model(X_tensor).numpy().flatten() true_all y_tensor.numpy().flatten() residual np.abs(pred_all - true_all) top_error_idx np.argsort(residual)[-5:] print(预测偏差最大的时间点索引:, top_error_idx)特征重要性表直接放进论文当表格比文字描述有力。偏差最大的时间点索引对应到具体日期后去原始数据里查那几天发生了什么这就是论文里「模型局限性分析」的素材。这套流程走下来论文的结论部分就不是空话每一条都有模型输出撑着。最后一个习惯所有随机过程固定random_state包括train_test_split、XGBoost、torch的manual_seed。数模评审如果复现你的代码发现结果对不上信任度直接归零。我吃过这个亏现在每个脚本开头必写这几行。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
热缩冷胀隔圈:负热膨胀材料在光学装调中的精准补偿应用 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 1:20:01
数据结构期中卷答案精讲:从概念到代码的自查清单 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 1:19:55
电容位置决定EMC成败:高频去耦的物理本质与布局法则 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 1:19:55
设备无法获取IP问题处理过程:从DHCP到静态绑定的排障实录 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 2:35:46
RK3588部署YOLOv5s实战:环境搭建与模型转换全流程指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 2:35:46
醴陵智能锁哪家售后稳妥 在醴陵买智能锁,售后常踩的4个坑,第3个很多人装完才后悔
在醴陵选智能锁,多数人先看价格和功能,真正等出问题才发现售后才是麻烦。门店多、牌子杂,售后稳不稳,往往装完才见分晓。
第一个坑:以为… · 2026/9/26 2:35:46
QT QTextEdit 自动滚动到底部怎么关?TaoToken 配置骨架与验证清单 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 2:35:46
Yao Agent MCP 集成实战:为智能助手接入外部工具、资源与跨服务器并发调用 Agent 框架后端低代码RAG 【免费下载链接】yao ✨ All your agents and workspaces in one place, on every device you own. Track tasks on a board, accessible from desktop, mobile, browser, or API. Self-hosted. 项目地址: https://gitcode.com/gh_mirrors/… · 2026/9/26 2:35:40
深度学习基础|第R1周 RNN-心脏病预测 第RI周:RNN-心脏病预测 🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊编译器:jupyterlab
一、前期准备 1. 数据导入 2. 检查数据 二、数据预处理
1. 划分训练集和测试集 2. 标准化 … · 2026/9/26 2:35:40
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46