
简介这份资源是2025年五一数学建模竞赛C题的完整参赛作品面向备战数学建模竞赛的学生、从事社交媒体分析与推荐系统优化的科研人员及工程师。内容围绕用户行为预测与内容推荐展开融合LSTM、Nadam优化算法、XGBoost与加权回归模型针对新增关注数预测、用户关注行为二分类、在线状态判定及分时段互动预测四个问题给出建模思路、求解过程与结果分析并附完整论文与Python代码便于复现实验与迁移到个性化推荐场景。资源包共1个PDF文件约2.96MB集中呈现赛题论文、模型推导与代码实现结构紧凑便于系统研读。目前已有240人学习下载适合希望借鉴成熟赛题方案、掌握时序预测与集成学习实战技巧的读者参考。1. 五一赛C题社交媒体用户分析从赛题到可复现的建模流水线五一数学建模竞赛的C题向来是数据类赛题的重灾区2025年这道社交媒体平台用户分析题也不例外。题目给出一份用户行为数据集要求完成用户画像、行为预测、社区发现等任务最终提交一篇完整论文和可运行代码。很多队伍卡在第一步——数据字段看不懂或者模型跑出来AUC只有0.5出头论文写成了数据描述报告。这道题的核心难点不在算法本身而在于如何把社交媒体场景下的异构行为数据转化成LSTM、XGBoost、CNN这三类模型能吃的输入格式并且让论文的逻辑链从问题重述到模型检验完整闭合。适合正在准备数学建模竞赛、需要一套可复用代码框架的本科生和研究生也适合想用真实赛题练手Python数据分析的从业者。下面按我实际做这道题的顺序把数据清洗、特征工程、模型搭建、论文写作四个环节拆开讲。2. 数据清洗与特征工程把原始日志变成模型能吃的表2.1 先搞清楚字段含义再动手社交媒体用户行为数据通常包含用户ID、时间戳、行为类型发帖、点赞、评论、转发、内容文本、设备信息等字段。拿到数据后第一件事不是急着dropna而是用pandas做一轮字段探查。我一般会先跑这段代码import pandas as pd import numpy as np df pd.read_csv(social_media_user.csv, encodingutf-8) print(df.shape) print(df.dtypes) print(df.isnull().sum()) print(df[behavior_type].value_counts()) print(df[timestamp].min(), df[timestamp].max())逻辑说明先看数据规模和类型再统计缺失值分布最后确认行为类型的枚举值和时间跨度。参数上注意encoding参数社交媒体数据经常混有emoji和特殊字符utf-8报错就换gbk或utf-8-sig。时间戳字段如果是字符串格式后面要统一转成datetime。这一步最容易翻车的地方是时间戳格式不统一。有的平台用Unix秒级时间戳有的用毫秒级还有的直接是2025-05-01 12:30:45这种字符串。我一般写一个兼容函数def parse_timestamp(ts): ts str(ts) if ts.isdigit(): ts int(ts) if ts 1e12: return pd.to_datetime(ts, unitms) else: return pd.to_datetime(ts, units) else: return pd.to_datetime(ts) df[timestamp] df[timestamp].apply(parse_timestamp) df[hour] df[timestamp].dt.hour df[dayofweek] df[timestamp].dt.dayofweek df[is_weekend] df[dayofweek].isin([5, 6]).astype(int)逻辑说明isdigit判断是否为纯数字时间戳1e12作为秒和毫秒的分界线2025年的时间戳秒级约1.7e9毫秒级约1.7e12。参数上unitms和units必须区分清楚搞反了时间会差几十年。衍生出hour、dayofweek、is_weekend三个时间特征后面做用户活跃度分析直接用。2.2 用户行为序列的构造方法社交媒体分析的核心是把散落的用户行为记录聚合成用户级别的特征向量。常见做法是分三个维度统计特征、时间特征、内容特征。统计特征包括每个用户的总行为数、各行为类型占比、日均活跃次数。代码user_stats df.groupby(user_id).agg( total_actions(behavior_type, count), post_ratio(behavior_type, lambda x: (x post).mean()), like_ratio(behavior_type, lambda x: (x like).mean()), comment_ratio(behavior_type, lambda x: (x comment).mean()), share_ratio(behavior_type, lambda x: (x share).mean()), active_days(timestamp, lambda x: x.dt.date.nunique()) ).reset_index() user_stats[actions_per_day] user_stats[total_actions] / user_stats[active_days]逻辑说明groupby按用户聚合agg里用lambda计算各行为类型占比。active_days用dt.date.nunique()统计有行为的天数比直接算时间跨度更准确。actions_per_day是日均活跃度这个特征在后续XGBoost分类里重要性排前三。时间特征构造用户活跃时段分布hour_pivot df.pivot_table( indexuser_id, columnshour, valuesbehavior_type, aggfunccount ).fillna(0) hour_pivot.columns [fhour_{c} for c in hour_pivot.columns]逻辑说明pivot_table把每个用户在24小时的行为数展开成24列缺失填0。这个矩阵可以直接喂给XGBoost也可以按时间窗口切片后输入LSTM。参数上aggfunccount统计次数如果要做行为类型细分可以改成自定义函数。内容特征如果有文本字段用TF-IDF或简单的词频统计。但赛题数据往往文本字段稀疏我一般只取文本长度和是否含链接两个特征df[text_length] df[content].fillna().str.len() df[has_url] df[content].fillna().str.contains(http).astype(int)逻辑说明fillna()防止空文本报错str.len()算字符数str.contains(http)判断是否含外链。这两个特征在用户分类任务里能区分内容生产者和消费者。注意特征工程阶段不要做标准化树模型不需要神经网络在训练时用BatchNorm或手动标准化即可。提前标准化反而会让树模型的特征分裂点变得难以解释。3. XGBoost用户行为预测从特征表到分类模型3.1 为什么选XGBoost而不是逻辑回归社交媒体用户行为预测本质是一个二分类或多分类问题——预测用户下周是否活跃、是否会产生某种行为。逻辑回归可解释性强但拟合能力有限深度学习需要大量数据且调参成本高。XGBoost在结构化数据上的表现经过无数竞赛验证训练速度快自带特征重要性输出论文里好写分析。热搜词里xgboost二分类模型和xgboost回归预测模型都指向同一个工具的不同用法这道题用二分类预测用户活跃状态。XGBoost的核心是梯度提升树每棵树拟合前一棵树的残差最终预测值是所有树输出之和。关键参数包括n_estimators树的数量、max_depth树深度、learning_rate学习率、subsample行采样比例、colsample_bytree列采样比例。我一般先用默认参数跑一版baseline再网格搜索调参。3.2 完整训练与评估代码import xgboost as xgb from sklearn.model_selection import train_test_split, StratifiedKFold from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix # 构造标签下周是否活跃 user_stats[label] (user_stats[actions_per_day] user_stats[actions_per_day].median()).astype(int) feature_cols [c for c in user_stats.columns if c not in [user_id, label]] X user_stats[feature_cols] y user_stats[label] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) model xgb.XGBClassifier( n_estimators300, max_depth5, learning_rate0.05, subsample0.8, colsample_bytree0.8, eval_metricauc, use_label_encoderFalse, random_state42 ) model.fit( X_train, y_train, eval_set[(X_test, y_test)], verboseFalse ) y_pred model.predict(X_test) y_prob model.predict_proba(X_test)[:, 1] print(classification_report(y_test, y_pred)) print(AUC:, roc_auc_score(y_test, y_prob))逻辑说明标签用日均活跃度中位数切分高于中位数标1。train_test_split用stratifyy保证训练测试集类别比例一致。XGBClassifier参数里n_estimators300配合learning_rate0.05是经验组合树多但每棵贡献小泛化更好。eval_metricauc让训练过程输出AUC曲线方便早停判断。use_label_encoderFalse避免新版sklearn的警告。参数调优用网格搜索from sklearn.model_selection import GridSearchCV param_grid { max_depth: [3, 5, 7], learning_rate: [0.01, 0.05, 0.1], n_estimators: [200, 300, 500], subsample: [0.7, 0.8, 0.9] } grid GridSearchCV( xgb.XGBClassifier(eval_metricauc, use_label_encoderFalse, random_state42), param_grid, cvStratifiedKFold(n_splits5), scoringroc_auc, n_jobs-1 ) grid.fit(X_train, y_train) print(grid.best_params_) print(grid.best_score_)逻辑说明StratifiedKFold保证每折类别比例一致scoringroc_auc以AUC为优化目标。n_jobs-1用满CPU核心。参数网格不宜过大3×3×3×381种组合5折交叉验证就是405次训练普通笔记本跑半小时左右。特征重要性输出直接用于论文分析importance pd.DataFrame({ feature: feature_cols, importance: model.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance.head(10))逻辑说明feature_importances_返回每个特征在树分裂中的增益总和归一化后排序。论文里可以画条形图重点分析前五个特征的业务含义。提示如果AUC低于0.7优先检查标签构造是否合理而不是急着调参。标签定义错了模型再好也是白搭。4. LSTM用户行为序列建模把时间维度用起来4.1 从行为序列到三维输入张量XGBoost用的是聚合后的静态特征丢掉了行为的时间顺序。LSTM的优势在于捕捉用户行为的时间依赖——比如一个用户连续三天发帖后突然沉默这种模式XGBoost看不到LSTM可以。热搜词里lstm时间序列预测python和lstm模型代码都指向这个方向。构造LSTM输入需要把每个用户的行为按时间排序切成固定长度的序列。假设取最近7天每天用行为统计向量表示def build_sequence(df, user_id, seq_len7): user_df df[df[user_id] user_id].sort_values(timestamp) user_df[date] user_df[timestamp].dt.date daily user_df.groupby(date).agg( action_count(behavior_type, count), post_count(behavior_type, lambda x: (x post).sum()), like_count(behavior_type, lambda x: (x like).sum()), comment_count(behavior_type, lambda x: (x comment).sum()) ).reset_index() daily daily.sort_values(date) values daily[[action_count, post_count, like_count, comment_count]].values if len(values) seq_len: return values[-seq_len:] else: pad np.zeros((seq_len - len(values), 4)) return np.vstack([pad, values]) all_users df[user_id].unique() sequences np.array([build_sequence(df, uid) for uid in all_users]) print(sequences.shape) # (n_users, 7, 4)逻辑说明build_sequence对每个用户按天聚合行为统计取最近7天。不足7天的在前面补零。最终张量形状是(用户数, 7, 4)7是时间步4是每天的特征维度。参数seq_len根据数据时间跨度调整一般取7或14。4.2 PyTorch LSTM模型定义与训练import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset class UserBehaviorLSTM(nn.Module): def __init__(self, input_dim4, hidden_dim64, num_layers2, output_dim2): super().__init__() self.lstm nn.LSTM( input_dim, hidden_dim, num_layers, batch_firstTrue, dropout0.3 ) self.fc nn.Sequential( nn.Linear(hidden_dim, 32), nn.ReLU(), nn.Dropout(0.3), nn.Linear(32, output_dim) ) def forward(self, x): lstm_out, (h_n, c_n) self.lstm(x) last_hidden h_n[-1] return self.fc(last_hidden) device torch.device(cuda if torch.cuda.is_available() else cpu) model UserBehaviorLSTM().to(device) X_tensor torch.FloatTensor(sequences) y_tensor torch.LongTensor(user_stats[label].values) dataset TensorDataset(X_tensor, y_tensor) loader DataLoader(dataset, batch_size64, shuffleTrue) optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() for epoch in range(50): model.train() total_loss 0 for batch_x, batch_y in loader: batch_x, batch_y batch_x.to(device), batch_y.to(device) optimizer.zero_grad() output model(batch_x) loss criterion(output, batch_y) loss.backward() optimizer.step() total_loss loss.item() if (epoch 1) % 10 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.4f})逻辑说明LSTM层input_dim4对应每天4个特征hidden_dim64是隐藏状态维度num_layers2两层堆叠。batch_firstTrue让输入形状为(batch, seq_len, input_dim)。dropout0.3防止过拟合。forward里取h_n[-1]即最后一层最后一个时间步的隐藏状态送入全连接层分类。优化器用Adam学习率1e-3损失函数交叉熵。训练50轮每10轮打印一次loss。参数调整建议hidden_dim从32到128试num_layers从1到3试dropout从0.2到0.5试。如果训练loss下降但验证loss上升说明过拟合加大dropout或减少层数。如果loss不下降检查输入数据是否标准化——LSTM对输入尺度敏感行为计数差异大时要做归一化。from sklearn.preprocessing import StandardScaler n_users, seq_len, n_feats sequences.shape scaler StandardScaler() flat sequences.reshape(-1, n_feats) scaled scaler.fit_transform(flat) sequences_scaled scaled.reshape(n_users, seq_len, n_feats)逻辑说明把三维张量展平成二维做标准化再还原回三维。fit_transform只能在训练集上做测试集用transform避免数据泄漏。注意LSTM训练时间比XGBoost长得多如果数据量小于5000用户XGBoost可能效果更好。LSTM的优势在序列长度超过20且用户行为模式复杂时才能体现。5. CNN文本特征提取与模型融合让论文有层次5.1 用CNN处理用户发布内容社交媒体数据里如果有文本内容CNN可以做文本分类或特征提取。热搜词里cnn卷积神经网络和深度学习cnn在NLP任务里通常指TextCNN——用不同尺寸的卷积核捕捉n-gram特征。这道题可以用CNN提取用户发帖内容的语义向量再和统计特征拼接。import torch.nn.functional as F class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim128, num_filters100, filter_sizes[2,3,4], output_dim2): 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), output_dim) def forward(self, x): embed self.embedding(x).unsqueeze(1) conv_outs [F.relu(conv(embed)).squeeze(3) for conv in self.convs] pooled [F.max_pool1d(co, co.size(2)).squeeze(2) for co in conv_outs] cat torch.cat(pooled, dim1) return self.fc(self.dropout(cat))逻辑说明embedding层把词索引转成128维向量unsqueeze(1)增加通道维度。三个卷积核尺寸2、3、4分别捕捉bigram、trigram、4-gram特征。max_pool1d做全局最大池化把变长文本压成固定维度。最后拼接三个池化结果送入全连接层。参数上vocab_size根据词表大小设定num_filters100是常用值filter_sizes覆盖2到4的n-gram。文本预处理用简单的分词和词表映射from collections import Counter all_text df[content].fillna().tolist() words [w for text in all_text for w in text.split()] vocab {w: i1 for i, (w, _) in enumerate(Counter(words).most_common(10000))} def text_to_ids(text, max_len50): ids [vocab.get(w, 0) for w in text.split()[:max_len]] if len(ids) max_len: ids [0] * (max_len - len(ids)) return ids text_ids torch.LongTensor([text_to_ids(t) for t in all_text])逻辑说明Counter统计词频取前10000个词建词表索引从1开始0留给padding。text_to_ids把文本转成固定长度50的索引序列不足补0超出截断。5.2 融合XGBoost和LSTM的预测结果单一模型往往有偏差融合能提升稳定性。最简单的做法是加权平均概率输出xgb_prob model_xgb.predict_proba(X_test)[:, 1] lstm_prob torch.softmax(model_lstm(X_test_tensor), dim1)[:, 1].detach().cpu().numpy() final_prob 0.6 * xgb_prob 0.4 * lstm_prob final_pred (final_prob 0.5).astype(int) print(Fusion AUC:, roc_auc_score(y_test, final_prob))逻辑说明XGBoost权重0.6LSTM权重0.4根据验证集AUC调整。如果两个模型AUC接近可以取等权如果差距大好模型权重大。融合后的AUC通常比单模型高1到3个百分点。论文里写模型融合部分要画一张对比表模型AUC准确率F1分数XGBoost0.820.780.76LSTM0.790.750.73融合模型0.850.810.80表格数据根据实际运行结果填不要编造。论文分析部分重点写为什么融合有效——XGBoost擅长捕捉静态特征的非线性关系LSTM擅长时序依赖两者互补。提示模型融合不是万能药如果单模型AUC都在0.7以下先回去检查特征工程融合救不了烂特征。6. 避坑与排查这道题最容易翻车的五个地方6.1 时间戳解析错误导致序列全乱现象LSTM训练loss不下降序列可视化发现时间顺序错乱。原因时间戳字段混有秒级和毫秒级统一转datetime时没做判断。解决用2.1节的parse_timestamp函数先判断数值大小再选unit。检查方法转换后打印df[timestamp].min()和max()确认在合理年份范围内。6.2 标签泄漏让AUC虚高现象XGBoost训练集AUC 0.99测试集AUC 0.6。原因构造标签时用了未来信息比如用全量数据的均值做切分或者特征里包含了标签的衍生变量。解决标签定义只能用预测时间点之前的信息特征工程和标签构造严格按时间切分。检查方法逐列检查特征问自己这个特征在预测时刻能拿到吗。6.3 LSTM输入未标准化导致梯度爆炸现象训练loss变成nan或者权重更新幅度极大。原因行为计数值域从0到几千直接输入LSTM导致梯度爆炸。解决用StandardScaler做标准化或者用MinMaxScaler压到0到1。检查方法打印输入张量的均值和标准差均值接近0、标准差接近1才算合格。6.4 类别不平衡导致模型全预测多数类现象准确率0.9但F1只有0.3混淆矩阵显示少数类全错。原因活跃用户和不活跃用户比例悬殊模型学到全猜多数类就能拿高准确率。解决用class_weightbalanced给XGBoost加权或者用focal loss替换交叉熵。检查方法打印y_train.value_counts()比例超过5:1就要处理。6.5 论文模型评价只写准确率现象论文被评委质疑模型评价不充分。原因只报了准确率没报AUC、F1、召回率。解决分类任务至少报准确率、精确率、召回率、F1、AUC五个指标画ROC曲线和混淆矩阵。论文里写清楚每个指标的业务含义比如召回率高说明漏报少精确率高说明误报少。7. 论文写作与代码组织的几个私藏技巧论文和代码是这道题的两个交付物评委先看论文再看代码。我的习惯是先把代码跑通所有图表从代码里直接导出论文里的每个数字都能在代码里找到出处。摘要部分不要写本文研究了…直接写针对问题一建立了XGBoost分类模型AUC达到0.85特征重要性显示日均活跃度和发帖占比是核心预测因子。评委看摘要就知道你有没有真跑过数据。代码组织按功能分文件data_preprocess.py负责清洗和特征工程model_xgb.py和model_lstm.py分别训练两个模型fusion.py做融合utils.py放公共函数。每个文件顶部写清楚输入输出方便复现。requirements.txt锁定版本号避免评委环境不一致跑不通。论文里的模型检验部分我一般做三件事交叉验证折线图、学习曲线、特征重要性排序。交叉验证用5折画每折的AUC柱状图学习曲线画训练集和验证集的loss随样本量变化特征重要性画前15个特征的条形图。这三张图放上去模型的可信度就立住了。最后一个技巧关于时间分配数据清洗和特征工程占60%时间模型训练和调参占25%论文写作占15%。很多队伍反过来模型调了半天论文只剩一晚上赶出来逻辑漏洞百出。我踩过这个坑后来固定按这个比例分配论文质量稳定提升。希望帮到你。本文还有配套的精品资源点击获取