
简介这是一份面向计算机、人工智能及相关专业学生与开发者的多模态虚假新闻检测完整项目源码采用Python实现融合文本与图像双通道特征进行识别曾在相关比赛中获得第一名。项目以BERT等自然语言处理技术提取文本特征结合卷积神经网络处理图像信息并设计了一套有效的多模态融合方法提升检测准确率适合作为课程设计、毕业设计、竞赛复现或项目初期立项的参考方案。压缩包共41个文件约400KB包含16个py源码文件、5个txt说明与配置、4个md文档、3个sh脚本及tsv、json、checkpoint、ipynb等训练与评估相关文件覆盖模型定义、特征提取、训练与预测全流程。目前已有391人学习关注。代码均经测试运行成功附文档说明与依赖清单可帮助读者快速理解多模态检测的整体架构、数据组织方式与融合思路并在此基础上进行二次开发与实验对比。1. 多模态虚假新闻检测源码包文本图像双通道到底怎么跑起来社交平台上一条带图的“突发消息”往往比纯文字跑得更快。纯文本模型看到“某地突发”四个字只能判断措辞像不像新闻但配图若是张移花接木的旧照文本侧根本无从察觉。这个基于 Python 的虚假新闻检测多模态识别源码包解决的正是“文本像真的、图像是假的”这类跨模态不一致问题——它把 BERT 文本特征和 CNN 图像特征拼在一起做二分类据项目说明在相关比赛中拿到过第一名。包里除了训练脚本还塞进了bert-final.py、lgb_cat_blend_lb9546.py、predict_test.py以及一份model.zip预训练权重属于那种“下载完能直接对着 README 跑”的课程设计级资源。适合正在做毕设、课程设计或者想找一个多模态融合 baseline 的在校生和初级算法工程师。2. 拆包先看目录哪些文件是主干哪些是 BERT 官方遗留2.1 从文件清单反推项目结构拿到压缩包先别急着pip install把目录树打印出来看一遍能省掉后面一半的报错。这个包的结构其实分三层最外层是项目自己的代码中间夹着一份完整的 BERT 官方仓库最里面是model.zip权重。# 解压后先看两层目录不要直接递归到底 unzip 基于Python的虚假新闻检测多模态识别源码文档说明.zip -d fake_news_det cd fake_news_det find . -maxdepth 2 -type f | sort执行完你会看到类似这样的分布文件/目录归属作用bert-final.py项目主干多模态模型定义与训练入口lgb_cat_blend_lb9546.py项目主干LightGBM CatBoost 融合LB 0.9546 的来源predict_test.py项目主干生成测试集预测结果extract_features.py项目主干文本/图像特征抽取model.zip/model/权重预训练 BERT 与图像分支权重bert-master/第三方BERT 官方代码含run_classifier.py、modeling.pycatboost_info/训练产物CatBoost 训练日志可删可留req.sh/requirements.txt环境依赖安装脚本逻辑说明find -maxdepth 2只展开两层是因为bert-master/里面还有几十个文件全列出来会淹没主干。参数上-type f过滤掉目录sort让输出稳定方便你截图存档。2.2 分清“能改的”和“别动的”bert-master/是 Google 官方 BERT 仓库的拷贝里面的modeling.py、optimization.py、tokenization.py是底层实现。项目自己的bert-final.py会import这些模块。常见做法是只改项目根目录下的脚本bert-master/里的文件除非你要换 BERT 变体否则一行都别碰。我见过有人为了“优化”去改modeling.py里的 attention结果预训练权重加载不上排查了一整晚。model.zip需要先解压到model/目录里面通常包含tf_bert_model、checkpoint、bert_config.json、vocab.txt四件套。bert_config.json决定隐藏层维度、注意力头数vocab.txt决定分词表——这两个文件必须和权重配套换一个都会导致shape mismatch。提示解压model.zip时确认目标路径和脚本里--bert_model参数指向一致路径差一级是最常见的“权重加载失败”原因。3. 环境与依赖req.sh 里藏着的版本约束3.1 依赖安装的正确顺序requirements.txt和req.sh同时存在说明作者踩过依赖顺序的坑。多模态项目同时依赖 TensorFlowBERT 侧和 PyTorch 或 CatBoost融合侧版本冲突是家常便饭。# 建议用独立虚拟环境Python 3.7 是 BERT 官方代码的舒适区 python -m venv venv_fnd source venv_fnd/bin/activate # Windows 用 venv_fnd\Scripts\activate # 先装底层再装项目依赖 pip install --upgrade pip pip install tensorflow1.15.0 pip install -r requirements.txt逻辑说明tensorflow1.15.0是 BERT 官方run_classifier.py时代的标配用 TF2.x 直接跑bert-master/里的脚本大概率报tf.contrib找不到。参数上如果你机器只有 CPU把tensorflow换成tensorflow-cpu同版本即可别装tensorflow-gpu又没配 CUDA启动时会卡在Could not load dynamic library。req.sh里通常还包含catboost、lightgbm、scikit-learn、pandas、pillow。图像分支如果用的是预训练 CNN还会依赖torchvision或keras.applications。装完用下面这行验证python -c import tensorflow as tf; import catboost; import lightgbm; print(tf.__version__)能打印出版本号环境这关就算过了。3.2 数据目录该长什么样源码包里没有附带完整数据集比赛数据通常有版权但sample_text.txt给了文本格式样例。你需要自己按下面结构组织数据data/ ├── train/ │ ├── text.csv # 列id, text, label │ └── images/ # 文件名与 text.csv 的 id 对应 ├── val/ │ ├── text.csv │ └── images/ └── test/ ├── text.csv # 无 label 列 └── images/extract_features.py一般会按id去images/下找同名图片。常见做法是图片统一 resize 到 224×224格式统一成 jpg避免 PNG 透明通道导致三通道读取失败。这一步不做后面predict_test.py会在读图环节抛NoneType错误。4. 文本图像双通道BERT 与 CNN 特征怎么拼、怎么训4.1 文本侧BERT 特征抽取bert-final.py的核心逻辑是加载预训练 BERT取[CLS]位置的输出作为整句表示。下面是从项目脚本里提炼出的关键片段# bert-final.py 文本分支核心逻辑节选重构 import tensorflow as tf from bert_master import modeling, tokenization # 1. 加载配置与权重 bert_config modeling.BertConfig.from_json_file(model/bert_config.json) tokenizer tokenization.FullTokenizer(vocab_filemodel/vocab.txt) # 2. 构建输入input_ids / input_mask / segment_ids input_ids tf.placeholder(tf.int32, [None, 128], nameinput_ids) input_mask tf.placeholder(tf.int32, [None, 128], nameinput_mask) segment_ids tf.placeholder(tf.int32, [None, 128], namesegment_ids) # 3. BERT 前向取 pooled_output 作为句向量 model modeling.BertModel( configbert_config, is_trainingTrue, input_idsinput_ids, input_maskinput_mask, token_type_idssegment_ids, use_one_hot_embeddingsFalse ) text_feat model.get_pooled_output() # shape: [batch, 768]逻辑说明max_seq_length128是比赛场景的常见折中长文本截断、短文本补齐。get_pooled_output()返回的是[CLS]经过一层 tanh 后的向量维度等于bert_config.hidden_sizebase 版是 768。参数上is_trainingTrue时 BERT 内部会启用 dropout推理阶段记得切到False否则predict_test.py的结果会有随机波动。4.2 图像侧CNN 特征抽取图像分支在extract_features.py里通常用 ResNet 或 VGG 的卷积部分做特征提取去掉最后的分类头# extract_features.py 图像分支节选重构 from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input from tensorflow.keras.preprocessing import image import numpy as np # include_topFalse 去掉 ImageNet 分类头只要卷积特征 cnn ResNet50(weightsimagenet, include_topFalse, poolingavg) def extract_image_feat(img_path): img image.load_img(img_path, target_size(224, 224)) x image.img_to_array(img) x preprocess_input(x) # 归一化到 ResNet 期望的分布 x np.expand_dims(x, axis0) feat cnn.predict(x) # shape: [1, 2048] return feat逻辑说明poolingavg把卷积输出做全局平均池化得到 2048 维向量和文本的 768 维拼起来就是 2816 维。preprocess_input不能省它做的是通道均值减法省掉会让图像特征分布和预训练时不一致融合层学不动。4.3 融合层拼接之后接什么两个特征拿到手融合方式决定了模型上限。项目里用的是最直接也最稳的拼接 全连接# 多模态融合节选重构 text_feat tf.layers.dropout(text_feat, rate0.3, trainingis_training) image_feat tf.layers.dropout(image_feat, rate0.3, trainingis_training) # 拼接[batch, 768] [batch, 2048] - [batch, 2816] concat tf.concat([text_feat, image_feat], axis-1) # 两层全连接做非线性融合 fc1 tf.layers.dense(concat, 512, activationtf.nn.relu) fc1 tf.layers.dropout(fc1, rate0.3, trainingis_training) logits tf.layers.dense(fc1, 2) # 二分类真/假逻辑说明dropout放在拼接前和全连接后是为了防止文本分支或图像分支单独过拟合——多模态数据里两个模态的信息量往往不对等不加 dropout 模型会偏向信息量大的那个模态。fc1的 512 是经验值显存紧张可以降到 256但别低于 128否则融合能力退化。4.4 训练与融合提分lgb_cat_blend 在做什么lgb_cat_blend_lb9546.py是另一个独立流程它不直接训深度模型而是把 BERT 和 CNN 抽出的特征喂给 LightGBM 和 CatBoost再做加权融合。文件名里的lb9546就是这套融合在排行榜上的分数。# 典型执行顺序 python extract_features.py --data_dir data/ --out_dir feats/ python lgb_cat_blend_lb9546.py --feat_dir feats/ --submit predict_test.csv逻辑说明extract_features.py先把训练集和测试集的特征落盘成 npy 或 csvlgb_cat_blend_lb9546.py再读这些特征做树模型训练。树模型对特征尺度不敏感所以这一步不需要额外归一化。融合权重一般按验证集 AUC 搜索常见做法是 LightGBM 占 0.6、CatBoost 占 0.4具体看你的验证集表现调。5. 避坑与排查跑不起来时先看这几条5.1 权重加载报 shape mismatch现象启动bert-final.py时抛Assign requires shapes of both tensors to match。 原因bert_config.json里的hidden_size或num_hidden_layers和model.zip里的 checkpoint 不配套或者你误用了 base 配置去加载 large 权重。 解决打开bert_config.json确认hidden_size是 768base还是 1024large和 checkpoint 文件名里的标识对齐。不确定就重新解压model.zip别混用。5.2 图像读取返回 None现象extract_features.py跑到某张图时AttributeError: NoneType object has no attribute shape。 原因text.csv里的 id 在images/下找不到对应文件或者图片是 CMYK 模式、损坏文件。 解决先跑一遍完整性检查把缺失和损坏的图片列出来from PIL import Image import os, pandas as pd df pd.read_csv(data/train/text.csv) missing [i for i in df[id] if not os.path.exists(fdata/train/images/{i}.jpg)] print(缺失图片数:, len(missing))对损坏图片统一转成 RGB 再存一遍Image.open(p).convert(RGB)能解决大部分通道问题。5.3 显存不够batch 调不下去现象训练几个 step 后OOM when allocating tensor。 原因BERT base ResNet50 同时驻留显存batch_size32在 8G 卡上基本跑不动。 解决把batch_size降到 8 或 16同时把max_seq_length从 128 降到 64。梯度累积可以补偿小 batch 的稳定性但项目脚本里没内置需要自己加。5.4 预测结果每次不一样现象predict_test.py跑两次提交文件里的概率值有微小差异。 原因推理时is_training没切成Falsedropout 仍在生效。 解决在预测脚本里显式传is_trainingFalse或者用tf.keras.backend.set_learning_phase(0)。这个坑很隐蔽因为差异小到不影响准确率但会让复现结果对不上。5.5 catboost_info 目录报权限错误现象lgb_cat_blend_lb9546.py启动时报无法写入catboost_info/。 原因catboost_info/是从压缩包里带出来的训练产物解压后权限可能只读。 解决直接删掉整个catboost_info/目录CatBoost 会自动重建。这个目录本来就是日志删了不影响任何结果。6. 从能跑到好用把融合分数再往上推一点跑通只是起点。这套源码的 baseline 已经不错但比赛场景下还有几个提分点值得试。第一是文本侧换bert-large或者领域预训练权重bert_config.json和vocab.txt同步替换hidden_size从 768 变 1024融合层输入维度记得改。第二是图像侧别只用 ResNet50 的 avg pooling把中间层特征也抽出来做多尺度拼接常见做法是block4和block5各取一次全局池化再 concat维度翻倍但信息更全。第三是融合策略。项目里的lgb_cat_blend_lb9546.py用的是树模型 stacking你可以再加一层逻辑回归做元学习器把 LightGBM、CatBoost、深度模型三路输出当特征。验证集上做 5 折每折的 out-of-fold 预测拿来训元学习器这是 Kaggle 上最稳的提分套路。# 三路融合的元学习器示意 from sklearn.linear_model import LogisticRegression import numpy as np # oof_lgb / oof_cat / oof_dnn 都是 5 折 out-of-fold 预测 X_meta np.column_stack([oof_lgb, oof_cat, oof_dnn]) meta LogisticRegression(C1.0, max_iter1000) meta.fit(X_meta, y_train) print(融合后验证 AUC:, roc_auc_score(y_val, meta.predict_proba(X_val_meta)[:, 1]))逻辑说明C1.0是正则强度元学习器输入维度低不用太强的正则。max_iter调到 1000 是为了避免 lbfgs 不收敛的警告。这一步的前提是每一路模型的 oof 预测都严格按折生成否则元学习器会过拟合。最后说个验证习惯每次改完融合权重别只看整体准确率把假新闻类的召回单独打出来。虚假新闻检测里漏判的代价远大于误判classification_report里label1的 recall 才是真正要盯的指标。我早期只盯 accuracy模型把大部分样本判成真新闻也能到 0.9上线才发现假新闻一条没抓到。从那以后我每次调完参都强制走一遍混淆矩阵确认假新闻召回没掉才提交。希望帮到你。本文还有配套的精品资源点击获取