资讯动态

多任务图神经网络谣言检测系统实战

发布时间:2026/10/29 14:29:40 来源:尧图企业网站定制
简介本资源是一套面向本科毕业设计与机器学习课程实践的多任务谣言检测系统完整实现聚焦社交网络谣言识别与立场判断双重任务适用于具备Python基础和PyTorch/GNN入门经验的学习者。项目融合图卷积神经网络GCN与注意力机制复现并改进MSA-BiGCN、BERT-BiGCN等主流模型在PHEME与SemEval-2017 Task 8数据集上完成端到端训练与评估。压缩包共74个文件18.63MB含25个核心Python模块如BiGCN.py、MSABiGCN.py、train.py、12个Jupyter Notebook含训练/测试/可视化脚本、15个JSON格式配置与结果数据、17个TXT文本含requirements与日志说明及4份Markdown文档结构清晰、模块解耦支持快速复现实验与对比baseline性能。已有251人学习下载提供可本地编译运行的全链路代码、标注数据集及助教审定的实验方案评审得分达95分以上适合课程大作业、科研入门与GNN实战进阶。1. 毕业设计真能跑通的多任务谣言检测系统不是玩具模型是带完整数据流、可复现训练推理闭环的图神经网络实战包你是不是也经历过——毕设选题时看到“图卷积注意力谣言检测”这种组合心里一热搜了一堆论文结果下载的 GitHub 项目要么缺数据集、要么环境报错十七个、要么 train.py 里硬编码了绝对路径改完又崩这个资源不是那种“理论漂亮、落地翻车”的半成品。它是一套经过本地实测、评审95分、含 PHEME SemEval2017-Task8 双数据集、支持 stance detection 和 rumor classification 两个子任务同步训练的完整工程。核心是 MSA-BiGCNMulti-Source Attention Bi-directional GCN和 BERT-BiGCN 两套主干全部用 PyTorch 实现不依赖任何黑匣子框架。适合本科毕设、课程大作业或想快速上手图神经网络在社交文本建模中应用的初学者——它不教你从零推导 GCN 矩阵乘法但每一步数据怎么构图、注意力权重怎么可视化、多任务 loss 怎么加权都写在 .py 文件的注释里。如果你正卡在“图怎么建”“邻接矩阵怎么算”“为什么 stance 和 rumor 一起训反而效果掉点”这些真实痛点上这份源码就是你调试到凌晨三点后还能跑出 valid_f10.73 的那个后悔药。2. 从原始微博/帖子到图结构输入数据预处理链路拆解与 PHEME/SemEval 数据集适配细节2.1 PHEME 数据集结构解析为什么不能直接pd.read_csv就开训PHEME 是谣言检测领域最常用的公开数据集之一但它不是规整的 CSV 表格而是按事件event组织的 JSON 树状结构。每个事件包含 root post原始爆料、response posts转发/评论、以及人工标注的 stance支持/反对/中立和 rumor labeltrue/false/unverified。本项目在datasets/PHEME/下提供了已清洗的版本但关键在于rumorDataset.py中的build_graph()方法——它不把整条传播链当序列处理而是构建有向传播图root node → response nodes边权重由时间差归一化后计算越早回复权重越高。注意原始 PHEME 的 timestamp 字段是字符串格式data.py里用了datetime.strptime(x, %Y-%m-%d %H:%M:%S)解析若你替换数据需校验格式否则AttributeError: str object has no attribute timestamp会直接中断 DataLoader。# rumorDataset.py 第 127 行附近MSA-BiGCN 版本 def build_graph(self, event_id): # 获取该事件所有 post 的 id、text、timestamp、parent_id posts self.event_posts[event_id] # list of dict # 构建节点特征BERT embedding handcrafted features (e.g., user verified, post length) node_features [] for p in posts: bert_emb self.bert_model(p[text])[0][0].cpu().numpy() # [768] hand_feat np.array([ int(p.get(user_verified, False)), len(p[text]), p.get(retweet_count, 0), (datetime.now() - datetime.strptime(p[timestamp], %Y-%m-%d %H:%M:%S)).days ]) node_features.append(np.concatenate([bert_emb, hand_feat])) # [772] # 构建邻接矩阵仅 parent→child 连边无 self-loop n len(posts) adj np.zeros((n, n)) id_to_idx {p[id]: i for i, p in enumerate(posts)} for i, p in enumerate(posts): if p[parent_id] and p[parent_id] in id_to_idx: j id_to_idx[p[parent_id]] adj[j][i] 1.0 # 有向边parent → child return torch.tensor(node_features, dtypetorch.float), torch.tensor(adj, dtypetorch.float)提示node_features拼接了 BERT 向量和 4 维手工特征这是本项目提升鲁棒性的关键设计。很多初学者只用文本 embedding但在谣言场景下“用户是否认证”“转发数”这些元信息对 stance 判断贡献显著——实验表明去掉 hand_feat 后 stance F1 下降 4.2%。2.2 SemEval2017-Task8 数据集适配如何把 flat table 转成 graphSemEval2017-Task8 是 flat 结构每行一个 tweet但本项目通过semeval2017-8-test.ipynb提供了基于 reply-to 关系重建传播树的脚本。核心逻辑在getbash.py它读取train.csv中的target原帖 ID和tweet_id用networkx.DiGraph()构建有向图再用nx.dfs_tree(G, sourcetarget_id)提取子图。注意原始 SemEval 数据中存在target为空或tweet_id target的情况getbash.py第 89 行做了过滤——漏掉这步会导致图构建失败报NodeNotFound。2.3 多任务标签对齐stance 和 rumor label 如何共存于同一图这是本项目最易被忽略的设计点。PHEME 中每个 post 有 stance label但 rumor label 是 per-event 的。rumorDataset.py的__getitem__返回的是(graph, stance_labels, rumor_label)三元组其中stance_labels: 长度为图节点数的 tensor值 ∈ {0,1,2}against, favor, nonerumor_label: scalar值 ∈ {0,1,2}false, true, unverified训练时train.py用nn.CrossEntropyLoss()分别计算 stance loss 和 rumor loss再加权求和total_loss 0.6 * stance_loss 0.4 * rumor_loss权重 0.6/0.4 来自trainOnlyStance.py和trainOnlyRumor.py的单任务 baseline 对比实验——强行 0.5/0.5 会导致 rumor task 收敛变慢。3. MSA-BiGCN 与 BERT-BiGCN 双主干实现注意力机制嵌入位置与 GCN 层叠策略详解3.1 MSA-BiGCN多源注意力如何作用于双向图卷积MSA-BiGCNMulti-Source Attention BiGCN是本项目的创新主干其核心不是简单堆叠 GCN 层而是在每层 GCN 后插入 Multi-Head Self-AttentionMHSA模块且 MHSA 的 Q/K/V 来源不同Q 来自当前层 GCN 输出K/V 来自上一层 GCN 输出形成跨层注意力ABGCN.py第 42 行定义了MultiSourceAttention类关键参数num_heads4实验发现 4 头会过拟合2 头捕捉不足dropout0.1训练时启用推理时关闭biasTrue保留偏置项对小样本 PHEME 更稳定# ABGCN.py 第 68 行 class MultiSourceAttention(nn.Module): def __init__(self, embed_dim, num_heads, dropout0.1, biasTrue): super().__init__() self.embed_dim embed_dim self.num_heads num_heads self.head_dim embed_dim // num_heads self.scaling self.head_dim ** -0.5 self.q_proj nn.Linear(embed_dim, embed_dim, biasbias) # Q from current layer self.kv_proj nn.Linear(embed_dim, 2*embed_dim, biasbias) # K/V from previous layer self.out_proj nn.Linear(embed_dim, embed_dim, biasbias) self.dropout_module nn.Dropout(dropout) def forward(self, query, key_value): # query: [B, N, D], key_value: [B, N, D] bsz, tgt_len, embed_dim query.size() src_len key_value.size(1) q self.q_proj(query) * self.scaling # [B, N, D] k, v self.kv_proj(key_value).chunk(2, dim-1) # [B, N, D] each q q.view(bsz, tgt_len, self.num_heads, self.head_dim).transpose(1, 2) # [B, H, N, d] k k.view(bsz, src_len, self.num_heads, self.head_dim).transpose(1, 2) v v.view(bsz, src_len, self.num_heads, self.head_dim).transpose(1, 2) attn_weights torch.matmul(q, k.transpose(-2, -1)) # [B, H, N, N] attn_weights F.softmax(attn_weights, dim-1) attn_probs self.dropout_module(attn_weights) attn_output torch.matmul(attn_probs, v) # [B, H, N, d] attn_output attn_output.transpose(1, 2).contiguous().view(bsz, tgt_len, embed_dim) return self.out_proj(attn_output)注意key_value参数必须是上一层 GCN 的输出而非原始输入。MSABiGCN.py的forward方法中第 102 行明确调用attn_out self.attn_layers[i](gcn_out, prev_gcn_out)prev_gcn_out是循环中缓存的上一轮输出。若误传query自身模型会退化为普通 MHSA多源特性失效。3.2 BERT-BiGCN为什么用 BERT 初始化比随机初始化提升 11.3% F1BERT-BiGCN版本见BertBiGCN.py将transformers.BertModel作为 encoder 替代原始的 LSTM。关键改动输入文本经BertTokenizer截断至max_length128使用bert_model(input_ids)[0]取最后一层所有 token embedding非 [CLS]对每个 post取[CLS]embedding 作为节点初始特征非平均池化实验证明用bert-base-uncased初始化后trainOnlyRumor.py在 PHEME 上的 macro-F1 从 0.621 → 0.734。原因在于 BERT 捕捉了“谣言话术”如“速转”“震惊”的深层语义模式而 LSTM 仅靠字符级统计难以建模。3.3 BiGCN 层叠策略为什么只用 2 层 GCN更多层反而性能下降ABGCN.py和BiGCN.py均采用2 层 GCN ReLU Dropout结构self.gc1 GraphConvolution(nfeat, nhid, dropoutdropout, actF.relu) self.gc2 GraphConvolution(nhid, nclass, dropoutdropout, actlambda x: x)实验对比见result.ipynb显示3 层 GCN 在 PHEME 上 valid loss 波动增大test F1 下降 2.1%。根本原因是社交传播图稀疏性高平均度 3深层 GCN 导致 over-smoothing——节点特征趋同区分度丧失。本项目通过在 GCN 后加 MHSAMSA-BiGCN或用 BERT 提供强初始特征BERT-BiGCN来缓解而非堆叠层数。4. 训练与评估全流程从 requirements.txt 环境搭建到 result.ipynb 可视化分析4.1 环境配置避坑requirements-version.txt 为何比 requirements.txt 更可靠项目提供两个依赖文件requirements.txt仅列库名如torch1.13.1requirements-version.txt含精确版本wheel URL如torch1.13.1cu117 -f https://download.pytorch.org/whl/torch_stable.html必须用requirements-version.txt因为PHEME 数据加载依赖networkx2.6.3新版 networkx 3.x 移除了dfs_tree的source参数检查逻辑导致getbash.py报错transformers4.21.0与torch1.13.1兼容升级到 4.30 会触发KeyError: past_key_values因模型输出结构变更正确命令conda create -n rumor-env python3.8 conda activate rumor-env pip install --upgrade pip pip install -r requirements-version.txt # 注意不是 requirements.txt4.2 单任务 baseline 训练如何验证你的环境真的 OK不要一上来就跑train.py多任务。先验证单任务python trainOnlyStance.py --dataset PHEME --model MSA-BiGCN --lr 0.001 --epochs 50 python trainOnlyRumor.py --dataset PHEME --model BERT-BiGCN --lr 0.0001 --epochs 30成功标志trainOnlyStance.py输出Best Stance F1: 0.712PHEMEtrainOnlyRumor.py输出Best Rumor F1: 0.734PHEME若卡在OSError: Unable to open file检查datasets/PHEME/是否存在且权限可读若报CUDA out of memory在train.py第 21 行添加torch.cuda.empty_cache()并减小--batch-size 8默认 16。4.3 多任务联合训练loss 加权与 early stopping 的实操设置train.py支持多任务训练关键参数--alpha 0.6stance loss 权重默认 0.6--patience 7valid loss 连续 7 epoch 不下降则 stop--save-dir ./checkpoints/msa-pheme/模型保存路径运行命令python train.py \ --dataset PHEME \ --model MSA-BiGCN \ --alpha 0.6 \ --lr 0.001 \ --batch-size 8 \ --epochs 100 \ --patience 7 \ --save-dir ./checkpoints/msa-pheme/注意--alpha必须与trainOnlyStance.py/trainOnlyRumor.py的单任务 F1 对齐。result.ipynb中的Alpha Sweep表格显示当 alpha0.6 时加权 F1 最高0.721alpha0.5 时 rumor task 拖累整体。4.4 结果可视化plot.ipynb 里的三个必看图表plot.ipynb不只是画 loss 曲线它包含Stance Confusion Matrix验证模型是否混淆 “against” 和 “none”常见错误Rumor Label Distribution per Event检查是否存在 event-level bias如某事件 90% 标为 falseAttention Weight Heatmap用semeval2017-8-test.ipynb导出 attention weights可视化 root→reply 的权重分布——优质模型应给早期反驳性 reply 更高权重5. 避坑指南6 个血泪经验总结——那些让毕设答辩前夜崩溃的隐藏雷区5.1 现象train.py运行到第 3 epoch 报RuntimeError: CUDA error: device-side assert triggered原因rumorDataset.py中 stance label 编码错误。PHEME 的 stance 原始标签是字符串support/deny/comment但__getitem__里stance_map {support:0, deny:1, comment:2}若漏掉queryPHEME 有少量 query 类型索引会越界。解决检查rumorDataset.py第 201 行stance_map是否覆盖全部标签补充query:3并同步修改nclass_stance4。5.2 现象getbash.py执行后datasets/semeval2017-task8/graphs/为空原因SemEval2017-Task8 原始数据中train.csv的target列存在空值或NaNgetbash.py的pd.read_csv默认将空字符串读为NaN导致nx.dfs_tree(G, sourcetarget_id)失败。解决在getbash.py第 32 行后添加df df.dropna(subset[target])并确保target列类型为 stringdf[target] df[target].astype(str)。5.3 现象plot.ipynb运行plt.show()报ModuleNotFoundError: No module named matplotlib原因requirements-version.txt未包含matplotlib因作者本地已装但plot.ipynb依赖它。解决手动执行pip install matplotlib3.5.3与requirements-version.txt中其他库版本兼容。5.4 现象codetest.ipynb中model.eval()后torch.no_grad()下仍显存暴涨原因ABGCN.py的forward方法中self.attn_layers[i]的输出未 detach梯度计算链未切断。解决在codetest.ipynb的 inference cell 中显式调用with torch.no_grad():包裹整个前向过程并在model()后加.cpu().detach().numpy()。5.5 现象semeval2017-8-test.ipynb导出的 attention heatmap 全是 0原因MSABiGCN.py的forward方法中attn_weights在 softmax 后未做clamp(min1e-6)极小值被截断为 0。解决在ABGCN.py的forward方法末尾attn_weights F.softmax(attn_weights, dim-1)后添加attn_weights torch.clamp(attn_weights, min1e-6)。5.6 现象trainOnlyRumor.py在 SemEval 上 F1 仅 0.42远低于 PHEME 的 0.73原因SemEval2017-Task8 的train.csv包含 test set 的部分数据官方 leak直接训练会过拟合。解决严格按semeval2017-8-test.ipynb的说明用train_dev_split.py项目未提供需自行编写将原始 train.csv 按 8:2 划分 train/dev禁用官方 test set 参与训练。6. 进阶技巧用result.ipynb做 error analysis定位模型在哪类 post 上持续翻车6.1 构建 post-level 错误日志不只是看 overall F1result.ipynb的Error Analysis部分我通常会补一段代码把每个 misclassified post 的原始 text、pred label、true label、attention weights 导出为 CSV# 在 result.ipynb 末尾新增 cell import pandas as pd error_records [] for i, (pred, true) in enumerate(zip(all_preds, all_labels)): if pred ! true: # 获取第 i 个 sample 的原始文本需提前在 train.py 中保存 text_list text text_list[i] if i len(text_list) else N/A error_records.append({ post_id: i, text: text[:100] ... if len(text) 100 else text, true_label: true.item() if hasattr(true, item) else true, pred_label: pred.item() if hasattr(pred, item) else pred, attention_weights: attention_weights[i].cpu().numpy().tolist() if attention_weights in locals() else [] }) pd.DataFrame(error_records).to_csv(error_analysis.csv, indexFalse, encodingutf-8-sig)运行后生成error_analysis.csv用 Excel 筛选true_label0 pred_label1把 false 当 true你会发现87% 的此类错误 post 都含感叹号“”或“速转”字样——模型把情绪强烈等同于谣言而非基于事实逻辑。这提示你需要在hand_feat中加入标点密度特征。6.2 可视化 attention weight 的物理意义验证“多源”是否真起作用semeval2017-8-test.ipynb提供了visualize_attention()函数但默认只画 heatmap。我习惯加一行代码计算每个节点的 attention entropy# 修改 visualize_attention 函数内 entropy -torch.sum(weights * torch.log(weights 1e-8), dim-1) # [N] print(fAttention entropy per node: {entropy.tolist()}) # entropy 越低表示模型越聚焦于少数邻居健康entropy 1.5 说明注意力分散可能学偏实测发现当entropy.mean() 1.8时rumor F1 下降 5%。此时我会检查rumorDataset.py的邻接矩阵构建逻辑——是否错误地加入了无 reply 关系的边。6.3 用codetest.ipynb做快速原型验证3 分钟测试新想法别动train.py所有新想法如换 pooling 方式、加 dropout rate都在codetest.ipynb验证Step 1加载预训练 checkpointtorch.load(./checkpoints/msa-pheme/best_model.pth)Step 2修改 model 的某层如model.gc1.dropout nn.Dropout(0.3)Step 3用 1 个 batch 数据前向检查 output shape 和 loss 是否合理这样避免每次改代码都等 2 小时训练。从那以后我每次加新模块都强制走一遍codetest.ipynb的 mini-batch 流程再提交到正式训练——这省下的 17 个小时够我把毕设 PPT 做成答辩全场最稳的那一个。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑