简介这是一份基于Python的虚假新闻检测多模态识别完整源码与配套文档面向高校毕业设计、期末大作业及课程设计场景适合有一定Python基础、希望快速落地深度学习项目的学生。项目包含文本与图像等多模态特征处理、模型训练与预测脚本并配有清晰注释部署门槛低运行稳定。压缩包共39个文件以16个py源码文件为主辅以md/txt文档说明、sh部署脚本、json配置及tsv数据文件等整体仅353KB轻量易用。已有498人学习下载足见其参考价值。通过项目可掌握多模态虚假新闻检测的完整链路包括数据预处理、模型调优、集成与结果对比等实操经验同时获得可直接运行或二次开发的高分项目模板能够有效支撑答辩演示与功能扩展。1. 多模态虚假新闻检测为何要把文本和图像作为一个整体来建模一条带有“某地发生爆炸”文字的推文配图却来自三年前的普通仓库照片人工核实需要一分钟纯文本模型大概率会误判为真因为文字本身没有任何语法错误。把文本和图像分开建模的检测系统在真实社交媒体场景下同样会漏掉这类样本原因在于假新闻的误导性往往藏在“跨模态”的矛盾里单看文字可信单看图像也正常放在一起才暴露问题。这篇拆解的是一个基于 Python 的虚假新闻检测多模态识别项目完整的源代码和文档说明通常被用作 Python 期末大作业或毕业设计。核心工作不是简单地把文本模型和图像模型的结果做加权平均而是构建一个能同时读取文本语义和视觉语义的双通道网络在融合层学习二者之间的相互作用。对于正在选型或者准备复现这套系统的开发者这篇文章会讲清楚为什么需要双通道、PyTorch 里如何实现、训练时有哪些直接影响精度的坑以及最后怎样把模型解释和部署优化写进课程设计的验收报告里。2. 双通道编码与注意力融合多模态识别系统的整体模块设计2.1 为什么文本和图像要分开编码而不是直接拼接输入多模态识别的第一个设计决策是决定在哪个抽象层次上让文本和图像发生交互。常见做法是在模型底部就把两个模态的数据作为不同通道输入然后在高层特征上进行融合而不是把原始字符串和像素直接拼在一起。原因很简单文本和图像的数据分布完全不同文本是离散符号图像是连续像素强行拼接会让梯度更新时两个分支互相干扰。一个合格的多模态虚假新闻检测系统通常会保留两个独立的编码器分支。文本分支负责提取语义特征比如事件主体、情感倾向、关键词之间的依赖关系图像分支负责提取视觉特征比如画面中是否存在经过篡改的痕迹、主体是否与文本描述一致。两个分支的输入独立预处理输出是固定维度的向量再进入融合模块。这样做的优点是每一路特征提取都能复用成熟的预训练模型不需要从头训练。2.2 文本分支选型TF-IDF 与预训练语言模型的取舍文本特征提取有两条路。一条是传统统计特征将新闻正文做 TF-IDF 向量化再输入全连接层或浅层分类器。另一条是使用预训练语言模型把句子 token 化后直接输入 BERT、RoBERTa 这类模型取[CLS]向量作为句子表示。从系统工程的角度看TF-IDF 更适合短时间内能跑完的课程设计因为不需要下载大模型权重CPU 上也能跑。但如果面对的新闻数据里存在大量隐晦表达和文本与图像的语义错位TF-IDF 的 n-gram 特征很难捕捉深层依赖。这个项目如果是作为毕业设计来展示我更建议文本分支选择预训练模型。# 文本分支使用 BERT 编码新闻文本 from transformers import AutoTokenizer, AutoModel import torch tokenizer AutoTokenizer.from_pretrained(bert-base-chinese) bert_model AutoModel.from_pretrained(bert-base-chinese) def encode_text(texts, max_len128): # 统一填充到固定长度方便组成 batch encoded tokenizer( texts, truncationTrue, paddingmax_length, max_lengthmax_len, return_tensorspt, ) with torch.no_grad(): outputs bert_model(**encoded) # 取 [CLS] 位置的向量作为整句语义表征 return outputs.last_hidden_state[:, 0, :]这里max_len128是为了限制显存占用如果新闻正文较长可以扩大到 256 或者改用truncationTrue截断。[CLS]向量是 BERT 中专门为分类任务设计的表示包含了整句话的聚合语义很多多模态模型都直接把它当作文本模态的全局特征。2.3 图像特征提取与多模态融合策略对比图像分支的主流选择是 ResNet 或 EfficientNet。考虑到预训练权重的易得性ResNet50 是稳定且够用的选项。实际使用时去掉最后的全连接分类层只取卷积网络平均池化后的 2048 维特征向量。融合层的设计直接决定多模态识别的最终效果。下面这张表对比了三种常见策略融合策略实现方式优势劣势典型应用场景特征拼接文本向量和图像向量直接torch.cat实现简单训练稳定无法建模模态间的交互快速基线模型加权求和两个分支输出学习到的权重系数再相加参数少不易过拟合权重是标量表达能力有限特征都比较稳定的场景注意力融合计算文本特征对图像特征的注意力权重或反之能捕捉跨模态相关性实现复杂训练时间更长文本和图像内容相关性强的样本在这个虚假新闻检测项目里注意力融合更适合。原因在于很多造假样本是“文本说一件事图像显示另一件事”这种矛盾恰恰需要文本向量与图像向量逐维计算相关性而非简单的加法。# 注意力融合层文本特征作为 query图像特征作为 key/value class CrossAttentionFusion(nn.Module): def __init__(self, text_dim768, image_dim2048, fusion_dim512): super().__init__() self.text_proj nn.Linear(text_dim, fusion_dim) self.image_proj nn.Linear(image_dim, fusion_dim) self.attn nn.MultiheadAttention(embed_dimfusion_dim, num_heads4, batch_firstTrue) self.classifier nn.Linear(fusion_dim, 2) def forward(self, text_feat, image_feat): # text_feat: (batch, 768), image_feat: (batch, 2048) t self.text_proj(text_feat).unsqueeze(1) # (batch, 1, fusion_dim) i self.image_proj(image_feat).unsqueeze(1) # (batch, 1, fusion_dim) attn_out, _ self.attn(t, i, i) attn_out attn_out.squeeze(1) return self.classifier(attn_out)num_heads4是一个折中选择头数太少无法表达多种关联头数太多会增加计算量。unsqueeze(1)是把特征变成序列长度为 1 的输入以满足MultiheadAttention对三维输入的格式要求。这套交叉注意力结构的实际效果是让模型关注图像中与文本语义最匹配的局部特征从而学习到跨模态的“矛盾”信号。3. PyTorch 实现从数据管道到多模态分类器的完整代码拆解3.1 数据读取与预处理这类项目的数据集通常是一个 CSV 文件每条样本包含text列、image_path列和label列其中标签 1 表示虚假新闻0 表示真实新闻。预处理阶段要保证文本 token 化和图像缩放一致。# 数据集类设计 from torch.utils.data import Dataset from PIL import Image class FakeNewsDataset(Dataset): def __init__(self, df, tokenizer, image_transform): self.data df.reset_index(dropTrue) self.tokenizer tokenizer self.image_transform image_transform def __len__(self): return len(self.data) def __getitem__(self, idx): row self.data.iloc[idx] # 文本编码 text self.tokenizer( row[text], truncationTrue, paddingmax_length, max_length128, return_tensorspt, ) input_ids text[input_ids].squeeze(0) attention_mask text[attention_mask].squeeze(0) # 图像读取与 transform image Image.open(row[image_path]).convert(RGB) image_tensor self.image_transform(image) label torch.tensor(row[label], dtypetorch.long) return { input_ids: input_ids, attention_mask: attention_mask, image: image_tensor, label: label, }这里paddingmax_length是为了让每个 batch 的形状完全一致避免后续在模型内部做动态 pad。注意图像读取时强制convert(RGB)如果数据集里混有灰度图或 PNG 透明通道这一步可以避免通道数不一致导致报错。image_transform建议统一为Resize((224, 224))ToTensor()Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225])与 ImageNet 预训练权重对齐。3.2 多模态模型结构定义整体的模型结构是两个分支并行最后接入注意力融合分类器。为了避免训练开始时 BERT 和 ResNet 的预训练权重被破坏需要设置不同的学习率或冻结策略。# 完整多模态网络 import torch.nn as nn class MultiModalFakeNewsDetector(nn.Module): def __init__(self, text_encoder, image_encoder, fusion_layer): super().__init__() self.text_encoder text_encoder self.image_encoder image_encoder self.fusion_layer fusion_layer def forward(self, input_ids, attention_mask, image): # 文本分支 text_outputs self.text_encoder( input_idsinput_ids, attention_maskattention_mask, return_dictTrue, ) text_feat text_outputs.last_hidden_state[:, 0, :] # 图像分支 image_feat self.image_encoder(image) # ResNet50 的输出是 (batch, 1000)需要把最后一层全连接抽掉 if isinstance(image_feat, tuple): image_feat image_feat[0] if image_feat.dim() 2 and image_feat.shape[1] 1000: # 使用 ImageNet 预训练的 ResNet 时需要这样处理 image_feat image_feat.unsqueeze(1) return self.fusion_layer(text_feat, image_feat)这段代码里return_dictTrue是transformers库的标准用法返回一个包含last_hidden_state的对象。image_feat可能因为 ResNet 的nn.Linear层而输出 1000 维分类结果这里通过形状判断并保留特征维度实际工程中更推荐直接在构建 ResNet 时用torchvision.models.resnet50(pretrainedTrue)然后删除model.fc nn.Identity()。3.3 训练循环和超参数设置训练多模态模型和训练普通分类器的差异不大但有两处要特别注意一是 BERT 分支的学习率通常要比图像分支小二是两个分支的前向计算必须同步解锁梯度否则容易训练不动。# 训练主循环 from transformers import AdamW from torch.utils.data import DataLoader def train_model(model, train_loader, epochs5): # 分组设置学习率BERT 部分 2e-5图像部分 1e-4 text_params list(model.text_encoder.parameters()) image_params list(model.image_encoder.parameters()) fusion_params list(model.fusion_layer.parameters()) optimizer AdamW([ {params: text_params, lr: 2e-5}, {params: image_params, lr: 1e-4}, {params: fusion_params, lr: 1e-4}, ]) criterion nn.CrossEntropyLoss() for epoch in range(epochs): model.train() total_loss 0 for batch in train_loader: input_ids batch[input_ids] attention_mask batch[attention_mask] image batch[image] labels batch[label] logits model(input_ids, attention_mask, image) loss criterion(logits, labels) optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch 1}, Loss: {total_loss / len(train_loader):.4f})2e-5和1e-4是微调预训练模型时的经典设置前者是 BERT 论文中微调任务推荐的上限后者是 ResNet 微调时的经验值。如果训练过程中损失不下降可以检查数据加载部分是否把 label 处理成了 float很多踩坑问题都出在CrossEntropyLoss要求 label 是long类型。超参数推荐值说明batch_size16显存不够时优先降到 8epochs5-10BERT 微调不宜太多容易过拟合max_len128新闻文本短文本够用text_lr2e-5预训练 BERT 使用image_lr1e-4预训练 ResNet 使用optimizerAdamW带权重衰减适配 Transformer4. 训练与评估多模态模型的超参数调整和异常排查4.1 数据不平衡与分层采样虚假新闻检测的真实数据集往往存在明显的标签不均衡真实新闻比虚假新闻多。如果直接按原始比例切分训练集模型会倾向于把所有样本预测为多数类导致 F1 值虚高但实际泛化能力差。常见的做法是在sklearn中使用train_test_split时开启stratify参数。from sklearn.model_selection import train_test_split df pd.read_csv(processed_news.csv) # 按标签分层采样保证训练集和测试集中的正负样本比例一致 train_df, val_df train_test_split( df, test_size0.2, random_state42, stratifydf[label], )stratifydf[label]会按照标签比例重新分配样本避免测试集里全是真实新闻。对于图像数据还要检查image_path是否都能正确读取。有些失效样本在训练时才触发FileNotFoundError建议在数据读取环节加一个异常捕获将坏样本过滤掉否则训练会中途崩溃。4.2 评估指标准确率不等于一切多模态模型的评估不能只盯着准确率虚假新闻检测更关心在“预测为假”的样本中有多少是真正的假新闻也就是精确率以及所有假新闻中有多少被找出来了也就是召回率。两者冲突时F1 分数是更均衡的指标。from sklearn.metrics import classification_report def evaluate_model(model, val_loader, device): model.eval() all_preds [] all_labels [] with torch.no_grad(): for batch in val_loader: input_ids batch[input_ids].to(device) attention_mask batch[attention_mask].to(device) image batch[image].to(device) logits model(input_ids, attention_mask, image) preds torch.argmax(logits, dim1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(batch[label].numpy()) print(classification_report(all_labels, all_preds, target_names[真实, 虚假]))classification_report会输出每一类的精确率、召回率和 F1以及微平均和宏平均。对于课程设计来说宏平均 F1 更能体现模型在两个类别上的综合表现如果只写准确率评审老师很容易看出问题。4.3 常见踩坑损失不降、过拟合和图像分支不学习多模态训练中的第一类问题是损失不降。除了学习率设置错误外最常见的原因是 BERT 的attention_mask没有传入文本编码器导致 padding 部分也被参与计算引入了大量无意义信息。第二类是过拟合。BERT 参数量大而多模态数据集往往只有几万条训练 5 轮以上就会在验证集上开始退步。解决方法是给文本分支加 dropout或者在训练过程中加入早停机制。第三类是图像分支不学习。很多复现者会发现训练后图像分支的权重几乎没有变化这是因为默认只微调 BERT图像分支的requires_grad被全局设置成了False。排查时可以直接打印参数更新前后的数值差# 检查图像分支参数是否更新 image_backbone model.image_encoder loss.backward() for name, param in image_backbone.named_parameters(): if param.grad is not None: grad_norm param.grad.norm().item() if grad_norm 1e-10: print(f参数 {name} 的梯度极低可能存在梯度消失)现象可能原因排查方向loss 在 epoch 1 就不变学习率过大或 label 类型错误检查 label 是否为 long验证集 F1 先升后降模型过拟合减少 epoch 或增强图像数据图像分支参数不更新全模型冻结时漏了图像分支检查requires_grad设置5. 从课程设计到部署可解释性分析与一个推理加速技巧5.1 用注意力权重解释模型预测多模态模型的课程设计答辩中最能拉开差距的部分是“可解释性”。交叉注意力层输出中的注意力权重可以直接告诉我们模型是根据图像的哪一部分和文本的哪个词做出判断的。将注意力权重可视化能直观展示模型捕捉到的跨模态矛盾。# 提取注意力权重用于可视化 model.eval() with torch.no_grad(): t model.fusion_layer.text_proj(text_feat).unsqueeze(1) i model.fusion_layer.image_proj(image_feat).unsqueeze(1) attn_output, attn_weights model.fusion_layer.attn(t, i, i) # attn_weights 形状为 (batch, head, target_len, source_len) # 这里关注的是文本 query 对图像 key 的注意力分布attn_weights的最后一维代表图像特征的空间位置ResNet 输出的特征图是 7×7因此可以还原到原图的不同区域。这样在论文或项目文档中就能画出一张“模型认为该图左上区域与文本描述最不匹配”的热力图。5.2 把模型导出为 ONNX提升推理速度如果课程设计要求交付可运行的 demo建议在完成训练后将多模态模型导出为 ONNX 格式这样在 CPU 上也能获得接近原生的推理速度并且可以避开不同设备上 PyTorch 版本差异带来的部署问题。dummy_text { input_ids: torch.ones(1, 128, dtypetorch.long), attention_mask: torch.ones(1, 128, dtypetorch.long), } dummy_image torch.randn(1, 3, 224, 224) torch.onnx.export( model, (dummy_text[input_ids], dummy_text[attention_mask], dummy_image), multimodal_fakenews.onnx, input_names[input_ids, attention_mask, image], output_names[logits], dynamic_axes{ input_ids: {0: batch_size}, attention_mask: {0: batch_size}, image: {0: batch_size}, }, opset_version11, )动态轴设置保证了导出后的模型能接受任意 batch 大小不至于在服务端推理时被迫 padding 到训练时的固定尺寸。ONNX 导出后可以用onnxruntime直接加载并推理也可以配合 OpenVINO 做进一步的 CPU 加速。5.3 一套能稳定复现的文件组织方式最后分享一个用得上的项目文件组织逻辑避免本地能跑、换个目录就报错的尴尬。文件结构尽量分成data/、models/、train.py、predict.py和requirements.txt所有路径都在入口脚本里使用相对路径定位。多模态项目最容易复现失败的原因是 BERT 权重下载慢建议把预训练模型先下载到本地目录再通过AutoModel.from_pretrained(./local_bert)加载这样交作业时即使对方电脑没有外网也能直接跑通。这个细节虽然没有提升任何指标但对期末作业和毕业设计的验收体验影响很大。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
工业3D打印定量铺粉器设计与优化实践 1. 项目背景与核心需求在工业级3D打印设备中,铺粉环节的精度直接影响成型件的尺寸精度和表面质量。传统铺粉器存在粉末利用率低、铺粉厚度不均等问题,特别是在打印高精度金属零件时,0.05mm的厚度偏差就可能导致层间结合强度下降30%。这个项目… · 2026/9/23 1:14:10
基于文本的MBTI人格预测:从TF-IDF到FastAPI服务部署 简介:这份资源是一套基于机器学习的MBTI人格预测系统完整项目,面向具备一定Python基础、希望将机器学习落地到文本与行为分析场景的开发者与学习者。项目围绕个体语言和行为模式展开,涵盖数据清洗、特征分析处理、人格预测模型构建、参数调优… · 2026/9/23 1:14:03
项目管理资源管理思维导图与实战技巧 1. 项目资源管理全景图在项目管理领域,资源管理就像乐高积木的底板,所有其他知识领域都要在这个基础上搭建。我做了15年项目经理,见过太多因为资源管理不善导致项目崩盘的案例。最近整理了一套可视化思维导图,把PMBOK第六版第13章… · 2026/9/23 1:14:03
Word折线图怎么做?3步搞定性能优化的源码解析 Word折线图怎么做?3步搞定性能优化的源码解析 官方文档里关于图表生成的章节动辄几百页,新手打开一看就头大,根本抓不住重点。想快速掌握 word折线图怎么做 且保证渲染性能,光看界面操作远远不够,必须深入到底层逻辑。… · 2026/9/23 2:59:15
新手避坑指南:有些路只能一个人走,搞懂证书注销别硬扛 新手避坑指南:有些路只能一个人走,搞懂证书注销别硬扛 学会语法却不知怎么搭项目?别急,先看看这个更隐蔽的坑。很多开发者在独立接手业务系统时,卡在“有些路只能一个人走”的尴尬境地,尤其是涉及电子证书查询、变更与注销流程时,往往因为没人带,踩了… · 2026/9/23 2:59:08
数制转换计算器源码解析:API 突变后的重构实战 数制转换计算器源码解析:API 突变后的重构实战 版本升级后 API 全变了,你手里的数制转换计算器代码直接报错,是不是瞬间头皮发麻?别慌,这种“断崖式”变更在开源库迭代中太常见了。今天咱们不背文档,直接上手做 源码解析… · 2026/9/23 2:58:50
户外蓝牙音箱选购指南:IP67、续航与音质如何权衡 上个月露营,半夜下了一场雨,帐篷里外都湿漉漉的,同行朋友顺手把音箱放在帐篷门口,雨水直接打在网面上。他回头跟我说了句“没事,这音箱IP67”,然后继续切歌。那一刻我突然意识到,户外蓝牙音箱这… · 2026/9/23 2:58:44
户外蓝牙音箱怎么选?防水等级、续航与蓝牙稳定性的硬核选购指南 户外蓝牙音箱这些年是真的火,露营、徒步、骑行、海边聚会,几乎成了标配。但我在帮朋友挑音箱、自己也折腾过好几台之后发现,大多数人买户外音箱还是只看“响不响”和“好不好看”,对防水等级、续航标定、蓝牙稳定性这些真正决定体… · 2026/9/23 2:58:44
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29