
简介这是一份面向计算机相关专业学生与项目实战学习者的课程设计资料围绕Python与MLP多层感知机实现互联网虚假新闻检测器适合正在准备期末大作业、课程设计或需要机器学习实战练习的人群可帮助理解文本预处理、特征提取、模型训练与预测评估的完整流程。压缩包共21个文件约92.72MB包含4个py源码文件、4个xml配置、3个model模型文件、2个pkl序列化数据、2个csv数据集、2个txt说明及md、docx文档等覆盖数据预处理、模型拟合、参数优化与预测推理等模块并附有项目报告文档。目前已有104人学习下载。资源提供可运行的完整源码与文档说明读者可据此复现虚假新闻检测实验参考模型训练与调参思路并借助报告文档梳理项目结构适合作为课程设计模板或二次开发的基础。1. 从一份大作业说起Python MLP 虚假新闻检测器到底能跑出什么期末周前后总有人拿着一个压缩包来找我名字大概就是「基于 Python 和 MLP 实现的互联网虚假新闻检测器源码 文档说明」。问的问题也高度一致这东西能不能直接跑MLP 是不是太浅了做出来会不会被老师一眼看穿文档说明里到底该写什么才不算凑字数先把结论摆出来这个题目的价值不在模型有多深而在于它是一条完整的、可解释的文本二分类流水线。虚假新闻检测本质上是把新闻标题或正文映射成「真 / 假」两个标签MLP多层感知机在这里承担的是分类头角色前面必须接文本向量化。它适合三类人刚学完 Python 想找一个能写进简历的完整项目的人、需要交课程大作业但不想只调库的人、以及想搞清楚「文本分类从原始数据到预测结果中间到底发生了什么」的入门者。下面我按自己带学生复现这套东西的顺序把选型、代码、参数和翻车点讲清楚。2. 数据与特征虚假新闻检测器的输入到底长什么样2.1 先搞清楚任务形态再决定 MLP 的输入维度虚假新闻检测在工程上通常被建模成有监督的二分类问题。数据集一般是一个 CSV 或 TSV至少两列文本列title 或 text和标签列label取值 0/1 或 fake/real。MLP 本身不认识字符串所以中间必须有一个向量化层把每条新闻压成一个固定长度的数值向量。常见做法有两种。第一种是 TF-IDF把词袋加权后取 top-N 个特征得到一个稀疏但维度可控的向量比如 5000 维。第二种是词嵌入平均用预训练词向量把每个词映射成 100 或 300 维再对整句取平均。大作业场景下我更推荐 TF-IDF原因是它不依赖外部词向量文件离线可跑而且特征权重可解释——你能直接看出哪些词把样本推向了「假」。这里有个容易被忽略的点MLP 的输入维度等于特征维度不是句子长度。很多人第一次写的时候把 padding 后的序列直接塞进全连接层结果维度对不上报错信息还特别绕。记住全连接层要的是「一条样本一个向量」不是「一条样本一个矩阵」。2.2 用 TfidfVectorizer 做特征参数怎么设下面这段是数据加载加特征化的最小可跑代码我一般会先把它单独跑通确认特征矩阵形状对了再往下接模型。import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split # 读取数据假设文件是 utf-8 编码列名为 text 和 label df pd.read_csv(news.csv, encodingutf-8) df df.dropna(subset[text, label]) # 丢掉空文本和空标签 df[text] df[text].astype(str) # 标签统一成 0/1兼容 fake/real 这类字符串标签 if df[label].dtype object: df[label] df[label].map({fake: 0, real: 1}) # TF-IDF最多 5000 个词忽略出现在超过 80% 文档里的词 vectorizer TfidfVectorizer( max_features5000, ngram_range(1, 2), # 同时统计单词和二元词组 min_df2, # 至少出现在 2 篇文档里才保留 max_df0.8, # 出现在 80% 以上文档的词视为停用词 sublinear_tfTrue # 用 1log(tf) 抑制高频词 ) X vectorizer.fit_transform(df[text]).toarray() y df[label].values X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(X_train.shape, X_test.shape)逻辑说明fit_transform只在训练集上做拟合测试集必须用同一个 vectorizer 的transform否则特征空间不一致这是数据泄漏的经典来源。参数上max_features5000是精度和内存的折中文本量在几万条以内够用ngram_range(1,2)能捕捉「not true」这类否定搭配对虚假新闻识别有实际收益min_df2过滤只出现一次的词降低噪声sublinear_tfTrue让高频词不至于主导权重。提示如果数据量超过十万条.toarray()会把稀疏矩阵转成稠密矩阵内存可能直接爆掉。这种情况要么调小max_features要么改用稀疏输入配合支持稀疏的模型。2.3 标签分布和文本长度先看再训在接模型之前我习惯先做两件事看标签比例、看文本长度分布。虚假新闻数据集经常是不平衡的真假比例可能是 7:3 甚至更极端。如果不处理模型只要全预测多数类就能拿到 70% 准确率看起来很美实际毫无用处。文本长度也要看。如果大部分样本只有几十个字符TF-IDF 的 5000 维里会有大量零MLP 容易过拟合如果文本很长截断策略就要提前定好。这两步不写进最终代码但决定了你后面参数怎么调。3. 用 PyTorch 搭一个能收敛的 MLP 分类器3.1 网络结构两层隐藏层就够了MLP 在文本分类里不需要堆很深。我的经验是两层隐藏层、每层 256 或 128 个神经元配合 Dropout效果和三层差不多但训练更快、更不容易过拟合。结构大致是输入 5000 维 → 全连接 256 → ReLU → Dropout 0.3 → 全连接 128 → ReLU → Dropout 0.3 → 输出 2 类。import torch import torch.nn as nn class FakeNewsMLP(nn.Module): def __init__(self, input_dim, hidden1256, hidden2128, num_classes2, dropout0.3): super().__init__() self.net nn.Sequential( nn.Linear(input_dim, hidden1), nn.ReLU(), nn.Dropout(dropout), nn.Linear(hidden1, hidden2), nn.ReLU(), nn.Dropout(dropout), nn.Linear(hidden2, num_classes) ) def forward(self, x): return self.net(x) model FakeNewsMLP(input_dimX_train.shape[1]) print(model)逻辑说明input_dim必须等于 TF-IDF 的特征数写死会出问题所以从X_train.shape[1]取。Dropout 放在每个 ReLU 之后训练时随机丢弃一部分神经元推理时自动关闭。输出层不加 Softmax因为 PyTorch 的CrossEntropyLoss内部已经包含了 log-softmax重复加会导致数值不稳定。参数上hidden1和hidden2是主要调节对象。如果训练集只有几千条建议降到 128 和 64如果数据量在十万级以上可以升到 512 和 256。dropout在 0.2 到 0.5 之间调过拟合严重就往上加。3.2 训练循环学习率、batch size 和早停训练部分我一般用 Adam学习率 1e-3batch size 64。这三个值在文本分类里算是比较稳的起点。from torch.utils.data import TensorDataset, DataLoader # 转成 tensor train_ds TensorDataset( torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.long) ) test_ds TensorDataset( torch.tensor(X_test, dtypetorch.float32), torch.tensor(y_test, dtypetorch.long) ) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) test_loader DataLoader(test_ds, batch_size64) device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) best_acc 0.0 patience, wait 3, 0 for epoch in range(30): model.train() for xb, yb in train_loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() optimizer.step() # 每个 epoch 结束在测试集上评估 model.eval() correct total 0 with torch.no_grad(): for xb, yb in test_loader: xb, yb xb.to(device), yb.to(device) pred model(xb).argmax(dim1) correct (pred yb).sum().item() total yb.size(0) acc correct / total print(fepoch {epoch1}, acc {acc:.4f}) # 早停连续 3 个 epoch 没提升就停 if acc best_acc: best_acc acc wait 0 torch.save(model.state_dict(), best_mlp.pt) else: wait 1 if wait patience: print(early stop) break逻辑说明weight_decay1e-4是 L2 正则配合 Dropout 一起压制过拟合。早停的patience3表示连续三轮测试准确率没刷新就停避免无效训练。保存的是state_dict而不是整个模型加载时更灵活。参数上学习率如果 loss 震荡明显就降到 5e-4batch size 受显存限制CPU 训练可以降到 32。如果发现训练准确率一直上不去先检查特征矩阵是不是全零或者标签是不是映射错了这两个是新手最常见的翻车点。3.3 不平衡数据的处理加权损失前面提到标签可能不平衡。最省事的做法是在CrossEntropyLoss里传weight给少数类更高的权重。import numpy as np class_counts np.bincount(y_train) weights 1.0 / class_counts weights weights / weights.sum() * len(class_counts) criterion nn.CrossEntropyLoss( weighttorch.tensor(weights, dtypetorch.float32).to(device) )逻辑说明np.bincount统计每类样本数取倒数后归一化少数类权重自然更大。这样模型在优化时对少数类的错误惩罚更重召回率会明显改善。注意权重张量要放到和模型同一个 device 上否则会报设备不匹配。4. 评估与调参准确率之外你该看什么4.1 混淆矩阵和 F1比单一准确率靠谱虚假新闻检测里把假新闻判成真漏报和把真新闻判成假误报的代价不一样。只看准确率会掩盖问题必须看混淆矩阵和 F1。from sklearn.metrics import classification_report, confusion_matrix model.eval() all_preds [] with torch.no_grad(): for xb, yb in test_loader: xb xb.to(device) pred model(xb).argmax(dim1).cpu().numpy() all_preds.extend(pred) print(confusion_matrix(y_test, all_preds)) print(classification_report(y_test, all_preds, digits4))逻辑说明classification_report会输出每类的 precision、recall、F1。如果假新闻类的 recall 明显低于真新闻类说明模型偏向多数类需要回头调损失权重或者做重采样。混淆矩阵能直观看出错误集中在哪个方向。4.2 关键超参数的影响顺序调参不要一把抓按影响从大到小来。我的经验顺序是特征维度max_features 学习率 隐藏层大小 Dropout batch size。特征维度决定了信息上限5000 和 10000 的差距往往比换优化器更明显。学习率决定能不能收敛1e-3 不行就试 5e-4 和 2e-3。隐藏层大小和 Dropout 是过拟合的主要调节旋钮放在后面调。参数常用范围调大后的影响调小后的影响max_features3000–20000信息多易过拟合内存涨信息少可能欠拟合学习率5e-4–2e-3收敛快可能震荡收敛慢可能卡住hidden1128–512表达强易过拟合表达弱欠拟合dropout0.2–0.5正则强可能欠拟合正则弱易过拟合batch_size32–128训练稳收敛慢收敛快噪声大4.3 交叉验证小数据集别只切一次如果数据量在几千条级别单次 train_test_split 的结果波动会很大。用 5 折交叉验证看平均 F1比单次准确率可信得多。from sklearn.model_selection import StratifiedKFold skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)): # 每折重新拟合 vectorizer避免泄漏 vec TfidfVectorizer(max_features5000, ngram_range(1, 2), min_df2) X_tr vec.fit_transform(df[text].iloc[tr_idx]).toarray() X_va vec.transform(df[text].iloc[va_idx]).toarray() # 后面接训练循环记录每折 F1 print(ffold {fold}, train {X_tr.shape}, val {X_va.shape})逻辑说明每折都要重新fitvectorizer只在训练折上拟合验证折只做transform。这是交叉验证里最容易出错的地方很多人图省事在全部数据上拟合一次结果验证分数虚高。5. 避坑与排查这份源码最容易翻车的五个地方5.1 报错「mat1 and mat2 shapes cannot be multiplied」现象模型 forward 时报维度不匹配提示类似mat1 and mat2 shapes cannot be multiplied (64x300 and 5000x256)。原因input_dim和实际特征维度不一致。常见于先定义了模型后来又改了max_features或者测试集用了不同的 vectorizer。解决在建模前打印X_train.shape[1]用它初始化FakeNewsMLP。测试集必须复用训练集的 vectorizer不要重新fit。5.2 准确率一直卡在 50% 左右现象训练 loss 不降准确率在 0.5 附近徘徊。原因标签映射错了比如fake/real没转成 0/1或者转的时候映射反了导致标签和特征对不上也可能是特征矩阵全零。解决打印y_train[:20]和X_train[:1]确认。检查df[label].unique()确保只有两个取值且已转成整数。5.3 训练集准确率 99%测试集只有 60%现象训练集表现极好测试集拉胯典型过拟合。原因特征维度太高、模型太大、Dropout 太小或者数据量本身太少。解决先把max_features降到 3000hidden1降到 128dropout提到 0.5加weight_decay。如果还不行考虑做数据增强或换更简单的模型。5.4 中文文本没分词TF-IDF 全是单字现象特征里全是单个汉字模型效果一般。原因TfidfVectorizer默认按空格切词中文没有空格整句被当成一个 token 或者按字符切。解决中文场景要么先分词再传入要么把token_pattern改成按字符匹配。分词可以用 jieba处理后再拼成空格分隔的字符串。import jieba df[text] df[text].apply(lambda s: .join(jieba.cut(s)))逻辑说明jieba 把中文切成词再用空格连接TfidfVectorizer就能正常按词统计。这一步对中文虚假新闻检测的效果提升很明显不做的话特征基本没有语义。5.5 保存模型后加载预测结果不对现象训练时准确率 85%加载best_mlp.pt后预测全错。原因加载时模型结构参数和保存时不一致比如隐藏层大小改了或者忘了model.eval()Dropout 还在起作用。解决加载前用相同参数实例化模型load_state_dict后再eval()。推理时用torch.no_grad()包住避免不必要的计算图。6. 把检测器做成能演示的东西文档说明与推理脚本大作业和真实项目的区别在于前者需要「可展示」。文档说明不是凑字数它要回答三个问题数据从哪来、模型怎么训、结果怎么复现。我一般会在文档里放一张流程图文字描述即可、一份参数表、一段推理示例。推理脚本要独立于训练脚本输入一条新闻文本输出预测标签和置信度。import torch import jieba from sklearn.feature_extraction.text import TfidfVectorizer def predict(text, model, vectorizer, device): model.eval() # 中文先分词和训练时保持一致 tokens .join(jieba.cut(text)) vec vectorizer.transform([tokens]).toarray() tensor torch.tensor(vec, dtypetorch.float32).to(device) with torch.no_grad(): logits model(tensor) prob torch.softmax(logits, dim1) pred prob.argmax(dim1).item() label 真实 if pred 1 else 虚假 return label, prob[0][pred].item() # 使用示例 text 某地发生离奇事件官方尚未回应 label, conf predict(text, model, vectorizer, device) print(f预测{label}置信度{conf:.4f})逻辑说明推理时的预处理必须和训练时完全一致分词方式、vectorizer 参数、标签映射都不能变。softmax把 logits 转成概率置信度低于 0.6 的样本建议人工复核这在真实场景里比强行给一个标签更负责任。文档说明里我还会写清楚环境依赖Python 3.8 以上、PyTorch、scikit-learn、jieba、pandas。版本不写死但会注明「在 Python 3.8 PyTorch 1.x 环境下验证通过」。这样别人复现时遇到问题能有个对照。最后说一个我自己的习惯每次改完参数我都会把当次的配置、准确率、F1 记在一个experiments.md里哪怕只是随手几行。带过几届学生之后我发现真正拉开差距的不是模型多复杂而是有没有留下可追溯的记录。这份虚假新闻检测器的大作业把 MLP 跑通只是起点能说清楚每个参数为什么这么设、每个错误为什么这么修才是它值得写进简历的原因。希望帮到你。本文还有配套的精品资源点击获取