简介这份源码包面向计算机、数学、电子信息等专业课程设计与毕业设计场景提供一套基于机器学习、深度学习和预训练模型的虚假新闻检测完整实现针对中文微信消息标题进行真假二分类。代码按传统机器学习、神经网络、预训练模型三条路线清晰组织涵盖数据加载、文本预处理、词袋模型与词频-逆文档频率特征工程、模型训练与评估等完整环节并输出准确率、召回率、F1值与AUC指标便于横向对比不同算法效果。资源共76个文件压缩包约163KB以45个脚本为主体辅以开发环境配置、模型运行记录、说明文档及交互式笔记目录结构清晰可直接在Python科学计算环境与深度学习框架下运行方便按模块查阅与二次开发。特别适合作为课程设计、期末大作业与毕业设计参考资料目前已有1138人学习下载。对希望快速搭建新闻检测基线、开展算法对比实验或在此基础扩展功能的开发者而言是一份能看懂代码即可上手使用的实用参考资料。1. 虚假新闻检测源码包为什么说它不是“跑通就完事”的项目这是一个把机器学习、深度学习和BERT模型实操合在同一个代码库里的虚假新闻检测项目。打开源码包你会看到TF-IDF逻辑回归基线、Word2Vec加LSTM的深度学习路线以及基于BERT的预训练模型三套完整实现适合毕业设计、课程设计也适合想通过实际项目学习机器学习实战的从业者。很多人下载后直接跑train.py看到loss下降就以为大功告成但这恰恰是把它当成黑匣子的开始。虚假新闻检测的难点在于数据不平衡、长文本截断、验证集指标虚高以及阈值怎么选。这篇笔记会把源码拆成能复现的步骤先看三条特征路线怎么协作再跑通训练然后逐条列出我踩过的坑最后落到如何把模型导出成可调用的分类接口。2. 项目拆解三条特征路线怎么协作代码结构与数据划分先理顺2.1 TF-IDF负责兜底Word2Vec负责对照BERT负责上限一个完整的虚假新闻检测项目不会只放一个BERT模型。常见做法是同时保留三套方案TF-IDF加逻辑回归、Word2Vec加LSTM、BERT加分类头。它们各有用途。TF-IDF是性价比最高的兜底方案数据量小的时候也能得到一个可用的0.85左右F1Word2Vec加LSTM是深度学习的经典对照实验用来证明“换了大模型之后提升到底来自哪里”BERT则是这次项目的重点它借助大规模预训练语料里学到的语义知识把分类上限抬起来。TF-IDF特征通常是这样的from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression tfidf TfidfVectorizer(max_features5000, ngram_range(1, 2)) X_train tfidf.fit_transform(train_texts) X_val tfidf.transform(val_texts) model LogisticRegression(C1.0, max_iter500) model.fit(X_train, train_labels) val_acc model.score(X_val, val_labels) print(fTF-IDF LR acc: {val_acc:.4f})train_texts是归一化后的原始文本列表train_labels是0、1标签。fit_transform只允许用在训练集上验证集和测试集只能用transform否则TF-IDF会从验证集里学到词频统计造成数据泄漏。max_features5000是一个比较常用的起始值对中文新闻来说去掉低频词和超高频词之后前5000个特征已经能覆盖绝大多数可判别信息。ngram_range(1,2)同时保留单词和二元词组能抓住“不是真的”这类带否定词的局部结构。C1.0是逻辑回归的逆正则化强度C越小越依赖正则在特征维度明显大于样本量时先试C1.0附近的值即可。Word2Vec和LSTM这条路线在源码里一般会单独定义成一个模型文件。它的输入是词索引序列而不是TF-IDF稀疏矩阵。下面是一个典型的PyTorch实现import torch import torch.nn as nn class LSTMClassifier(nn.Module): def __init__(self, vocab_size, embed_dim128, hidden_dim64): super().__init__() # padding_idx0 让填充位置不参与梯度更新 self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.lstm nn.LSTM(embed_dim, hidden_dim, batch_firstTrue, bidirectionalTrue) self.classifier nn.Linear(hidden_dim * 2, 2) def forward(self, input_ids): emb self.embedding(input_ids) output, _ self.lstm(emb) # 双向LSTM把正反两个方向的最后状态拼接更好地捕捉上下文 last_state output[:, -1, :] return self.classifier(last_state)vocab_size是词表大小embed_dim控制每个词的稠密向量维度hidden_dim是LSTM隐藏层大小。双向LSTM的output最后一个时间步包含两个方向的拼接信息所以分类层的输入维度是hidden_dim * 2。对深度学习入门者来说这条LSTM路线是理解Embedding层和序列建模最清晰的载体这个项目把它保留下来是一组很有说服力的对照设计。初学深度学习时最容易在这里犯的错是忘记Embedding层直接把token id当成浮点特征丢进LSTM这样模型几乎学不到东西。BERT这条路线在源码里通常是对transformers封装一层from transformers import BertForSequenceClassification class BertClassifier(BertForSequenceClassification): def forward(self, input_ids, attention_mask, token_type_idsNone): outputs super().forward( input_idsinput_ids, attention_maskattention_mask, token_type_idstoken_type_ids, return_dictFalse ) logits outputs[0] return logitsreturn_dictFalse表示让模型返回tuple而不是字典这样和旧版脚本兼容不至于在训练循环里到处改字段名。token_type_ids对单句分类没有帮助传None即可。如果你在源码包里看到类似class BERTWithLSTM这样的写法也不要惊讶有些人会把BERT输出再接一层LSTM但这会显著增加参数量对短文本收益有限最常见到的还是直接取CLS向量加全连接分类头。2.2 数据集划分先分布再切分train/val/test一个都不能少虚假新闻数据集很容易踩到类别不平衡的坑。有些公开新闻数据集里真实新闻可能占80%假新闻只占20%如果不做分层抽样随机划分后某个验证集批次里甚至可能完全没有假新闻样本。所以源码里的prepare脚本通常会用train_test_split并指定stratify参数。import pandas as pd from sklearn.model_selection import train_test_split data pd.read_csv(data/news.tsv, sep\t) data[label] data[label].astype(int) train_val, test train_test_split( data, test_size0.2, stratifydata[label], random_state42 ) train, val train_test_split( train_val, test_size0.25, stratifytrain_val[label], random_state42 ) print(len(train), len(val), len(test))第一个train_test_split从全量里分出20%作为测试集第二个从剩余80%里再分出25%作为验证集最终训练集占60%。stratify会让标签比例在每一次划分后保持和原始数据一致。random_state固定为42这是一个习惯性数字换成任意整数都行关键是固定否则你在两个机器上跑出来的划分结果完全不同。如果源码包里已经有现成的划分文件我建议直接沿用它的划分不要自己重新分——测试集的对比基准一旦变了和论文或者报告里的数字就没法对齐。划分和清洗的顺序是这里最容易出错的地方。必须先划分再做文本清洗和特征统计。清洗过程中如果用了全量数据的语料统计比如去停用词前先统计词频、或者TF-IDF在全量上fit那测试集就以某种形式参与了训练线上效果会明显低于离线测试。2.3 代码结构导读先看目录再找train.py拿到源码包不要急着双击train.py。先花五分钟看目录理解每个模块的归属。这个项目的目录结构长得比较规矩的是这样文件/目录职责data/原始新闻数据、预处理脚本features/TF-IDF与Word2Vec特征构建models/LSTMClassifier、BertClassifier定义train.py训练入口统一数据加载和评估predict.py单条和批量预测入口config/超参数与路径配置requirements.txt第三方依赖清单这个结构的优点是模型定义和训练逻辑解耦。要改BERT的dropout比例只需要去models/里改要改学习率去config里改。如果源码里把所有内容都堆在train.py里后续调试成本会高很多因为你每做一次实验都要从几十个参数里找到要改的那一个。requirements.txt里通常包含pandas、scikit-learn、torch、transformers、datasets、tqdm。其中transformers的版本对API影响最大比如新版本里Trainer不再支持部分旧参数。安装时建议用虚拟环境隔离python -m venv venv source venv/bin/activate # Windows系统venv\Scripts\activate pip install --upgrade pip pip install -r requirements.txtvenv不是形式主义。torch和transformers的依赖树很复杂直接装进系统Python过不了几个月就会碰到“这个项目要transformers 4.x、另一个要transformers 5.x”的版本冲突。虚拟环境删除重建都方便算是深度学习环境里最值得养成的一个日常习惯。装完验证一下基础导入python -c import torch, transformers, sklearn; print(ok)输出ok后再进入具体的训练环节。导入报错时不要急着改项目代码先检查包的版本和源码里调用的API是否匹配绝大多数情况下是依赖版本不一致。3. 环境、预处理与训练把参数说明白训练日志不再像黑匣子3.1 先确认GPU可用再调整batch_size在Windows笔记本和云服务器上跑这个项目体验完全不一样。BERT-base有大约110M参数PyTorch默认不开启梯度检查点时序列长度128、batch_size32的显存占用通常超过8GB。如果你的显卡只有6GB显存训练时直接OOM的可能性很高。所以拿到源码后第一步不是改模型而是确认当前机器的算力。nvidia-smi这个命令能看到GPU型号、驱动版本和当前显存占用。如果机器上根本没有NVIDIA GPU也不必马上放弃CPU模式下依然能完成训练只是速度慢很多几万条样本、3个epoch在6核CPU上可能要跑数小时而一张1060级别的显卡能把时间压缩到几十分钟。对课程设计和毕业设计来说时间成本是必须纳入计划的因素。PyTorch是否真的用上了GPU用下面这行确认python -c import torch; print(torch.cuda.is_available())如果输出False原因大概率不在代码而是PyTorch安装时的CUDA版本和显卡驱动不匹配。比如你安装了CPU版torch或者安装了针对CUDA 12的包而驱动只支持CUDA 11都会出现这种情况。这个问题排查起来很枯燥但很常见建议先从PyTorch官方安装命令重新装一遍而不是急着改项目代码。依赖安装走基础流程即可pip install -r requirements.txt如果卡在某个包的下载上可以换成国内镜像源。装好后检查transformers和torch版本观察源码里from_pretrained的调用方式是否和当前API兼容这是最容易出现“导入不报错但一跑就报错”的地方。3.2 预处理原始文本到BERT输入四个参数要一次调对BERT的输入是input_ids、attention_mask、token_type_ids三个张量。很多第一次做BERT模型实操的人会把“分词”理解成用jieba切词然后直接传给模型。这是错的。BERT的tokenizer用的是subword分词一个词可能被切成多个片段所以必须用预训练模型对应的tokenizer来处理文本。from transformers import BertTokenizer tokenizer BertTokenizer.from_pretrained(bert-base-uncased) enc tokenizer( train_texts.tolist(), max_length128, paddingmax_length, truncationTrue, return_tensorspt, ) input_ids enc[input_ids] attention_mask enc[attention_mask]max_length、padding、truncation三个参数缺一不可。max_length是最终长度超过的部分被截断不足的部分补0paddingmax_length是让batch里每条样本都补到同一个长度这样能拼成一个张量truncationTrue是让超出部分从尾部切掉。如果漏了paddingbatch中不同样本长度不一致DataLoader拼接时直接报错。如果漏了truncation超长样本会让max_length失效甚至触发tokenizer内部报错。max_length取多少不要拍脑袋。我一般先跑一段统计lengths [len(tokenizer.encode(t)) for t in train_texts] lengths.sort() p90 lengths[int(len(lengths) * 0.9)] print(90分位长度:, p90)如果90分位长度是175那max_length设成192会比128保留更多信息又比512省很多显存。对新闻类长文本来说直接设置256是一个比较常见的折中但如果数据大多只有几十个token128就够用。这个细节直接决定模型在长文上的表现设太短假新闻的关键证据被截断设太长训练速度和显存双双付出代价。处理完成后一般会生成一个TensorDataset供DataLoader使用from torch.utils.data import TensorDataset train_dataset TensorDataset( input_ids, attention_mask, torch.tensor(train_labels.tolist(), dtypetorch.long) )TensorDataset要求所有张量第一维大小一致所以input_ids和attention_mask必须都已经padding到max_length。这里的dtypetorch.long不要省标签是整数类别最后一个维度在计算CrossEntropyLoss时必须有long类型否则会报类型错误。3.3 训练参数学习率、epochs、weight_decay和checkpoint的选择BERT微调是一个极其依赖超参数经验的过程。transformers对BERT微调给出的官方参考是learning_rate 2e-5到5e-5epochs 3到5batch_size 8到32。这个范围是很多人用真金白银的显卡试出来的不是论文里的装饰。如果你的训练日志里loss一路下降但验证集F1不动先把学习率往下调再看是否过拟合如果loss根本不降先检查数据预处理部分有没有把标签或文本搞反。以源码里常见训练命令为例python train.py \ --data-path data/news.tsv \ --model bert-base-uncased \ --max-length 128 \ --batch-size 16 \ --epochs 3 \ --lr 2e-5 \ --weight-decay 0.01 \ --output-dir output/batch_size16是一个很多机器都能跑的档位。显存不够就降成8显存有余想加速可以升到32但要注意学习率是否需要跟着调整。增大batch_size时梯度更新次数变少有时需要同步放大学习率减小batch_size时则相反。这是训练实验中最常见的调节逻辑。weight_decay是深度学习L2正则化在PyTorch中的落地方式和AdamW配合时取值0.01是比较标准的起点。它的作用是在更新权重时乘上一个略小于1的衰减系数让权重不至于长得过大。很多人把它理解成“防过拟合神器”实际上它对BERT的作用更多是稳定训练过拟合问题还要靠早停和更多数据解决。训练过程中源码一般会在每个epoch结束保存一个checkpoint目录里面包含pytorch_model.bin、config.json、tokenizer相关文件。这里有一个很常见的坑最后一个epoch的loss最低但验证集F1不一定最高因为模型在训练集尾部拟合过度。正确的做法是把每个epoch的验证集F1都记下来最后选F1最高的那一个而不是选epoch数最大的那一个。记录指标时不要只记accuracy。建议同时记录每个类别的precision、recall、F1和验证集AUC。这类项目的评价指标可以做成一张表指标说明重点关注Accuracy总体正确率样本均衡时参考Precision预测为假新闻中真实假新闻的比例误报代价高时Recall假新闻中被正确找出的比例漏报代价高时F1Precision与Recall的调和均值最常用的综合指标AUC排序能力阈值未定前评估train.py里一般已经有输出日志的代码。看日志时如果训练集F1已经到0.98验证集F1只有0.86说明模型过拟合如果训练集F1和验证集F1都在0.95以下说明数据量或特征表达能力不够。过拟合时优先加数据增强、降低模型复杂度、增大weight_decay欠拟合时优先检查特征输入是否正确而不是急着加网络层数。注意判断模型能不能用不要只看train loss。训练loss下降但验证集F1不动通常意味着过拟合训练loss和验证loss都高才是没收敛或数据有问题。4. 避坑指南五个最容易翻车的细节与修复方法4.1 现象训练完所有预测都是“真新闻”Accuracy却高达92%我第一次拿到这个项目时看到训练结束后的准确率接近93%心里很满意直到去打印混淆矩阵才发现所有测试样本都被预测成“真新闻”。原因是数据集里假新闻只占7%左右模型什么都不做就能达到93%的正确率它根本不需要学习任何文本特征。解决这个问题第一步是切换评价指标。Accuracy在类别极度不平衡时没有参考价值要用F1和AUC。第二步是给损失函数加权让模型在预测错误类别时付出更高代价。在PyTorch里最常见的做法是把BCEWithLogitsLoss或CrossEntropyLoss中的class_weight参数设置成与样本数量成反比。如果源码里没有这个参数手动乘一个权重矩陈也不复杂。第三步是重采样把少数类复制几份或合成新样本但重采样要谨慎对BERT微调来说直接复制文本容易让模型过拟合到重复样本上。from sklearn.metrics import confusion_matrix print(confusion_matrix(y_true, y_pred))如果第一类那一列全为0说明模型压根没有预测出任何一条假新闻问题基本可以锁定在数据不平衡上。4.2 现象验证集F1比训练集还高感觉“白捡了一个好模型”如果验证集指标好到不真实通常不是模型强而是数据泄漏。泄漏的高发点有两个一是划分前就在全量数据上做过停用词统计、TF-IDF拟合或长度过滤二是预处理脚本里用了全量数据的统计信息去清洗每条文本验证集文本被“看过”。这种情况下验证集不再是对未来数据的模拟指标自然虚高。解决方法是把“划分”放到最前面所有统计操作都只在训练集上fit验证集和测试集只做transform。如果把源码里的预处理流程按时间顺序画出来划分应该在清洗的第一行而不是最后一行。检查时可以故意把验证集随机打乱后再预测如果F1没有明显下降说明验证集信息可能已经被模型学到需要重新做数据流程。4.3 现象max_length512显存直接OOMBERT的注意力计算量随序列长度呈平方增长把max_length设满512等于让所有样本都按照最长规格计算。很多新闻文本其实只有一两百个token没必要喂512。出现OOM时大家本能地想到去减小batch_size但更合理的做法是先看文本长度分布。for max_len in [128, 192, 256]: covered sum( 1 for t in train_texts if len(tokenizer.encode(t)) max_len ) / len(train_texts) print(max_len, covered)如果192已经覆盖了95%的样本那么max_length设成192或256就足够同时可以把batch_size提回去。注意truncationTrue截断的是超长部分的尾巴。新闻文本的重要信息有时不在结尾而在前半部分所以尾部截断对这类任务的伤害通常比头部截断小。4.4 现象同一个脚本跑两次结果差了半个点这不算玄学而是随机性。PyTorch的DataLoader shuffle、GPU并行归约、参数初始化都会引入随机性。如果你只设置random.seed而没有固定torch的随机种子跑出来的结果依然会浮动。解决方法是统一固定三套随机种子并关掉cudnn的不可确定性算法import random import numpy as np import torch def set_seed(seed): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) set_seed(42) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark Falsecudnn.benchmarkFalse会牺牲一点训练速度换来确定性。如果是为了复现论文里的数字这一步不能省。如果只是做探索性实验关掉benchmark可能不划算可以只固定seed。另外DataLoader内部的shuffle也需要随机源可以单独指定generatorg torch.Generator() g.manual_seed(42) loader DataLoader( train_dataset, batch_size16, shuffleTrue, generatorg, )4.5 现象直接model(这是一条新闻)结果报错transformers的BertForSequenceClassification不接受原始字符串输入。它第一层是Embedding输入必须是整数token id。这也是最常见的新手报错TypeError或者维度不匹配。解决方式只有一个所有文本在进入模型前必须先经过tokenizer。而且要注意tokenizer的输出是一个字典要用**inputs展开或者显式传input_ids和attention_mask。写推理脚本时先打印一次tokenizer的结果确认input_ids是torch.long类型的张量再进行模型调用enc tokenizer(这是一条测试新闻, return_tensorspt) print(enc[input_ids].dtype) # torch.int64 with torch.no_grad(): logits model(**enc).logits这个小习惯能省下大量查错时间。你不需要把模型的内部结构全部看懂但一定要知道它的输入长什么样。5. 部署与推理从PyTorch checkpoint到可调用的分类接口5.1 单条推理与批处理先model.eval()再谈效率训练结束后predict.py的职责是加载checkpoint把新闻文本转成模型输入最后输出每个类别的概率。有一个细节最容易忽略model.load_state_dict后模型仍处于训练模式训练模式下dropout会随机丢弃节点导致同一段文本每次输出不同概率。所以推理前强制加一行model.eval()。from transformers import BertTokenizer, BertForSequenceClassification import torch model_path output/checkpoint-1850 # 选验证集F1最高的那个 tokenizer BertTokenizer.from_pretrained(model_path) model BertForSequenceClassification.from_pretrained(model_path) model.eval() text 官方刚刚发布了关于这件事的最新通报请以权威信息为准。 enc tokenizer( text, max_length128, paddingmax_length, truncationTrue, return_tensorspt, ) with torch.no_grad(): logits model(**enc).logits probs torch.softmax(logits, dim1) print(ffake prob: {probs[0][1].item():.4f})probs的形状是batch_size × 2两个类别分别对应“真新闻”和“假新闻”具体哪个是0、哪个是1由训练集标签映射决定。调用模型前打印一次label2id是避免最后输出反的最直接手段。with torch.no_grad()不能省推理过程如果创建计算图内存占用会成倍上升即便只预测一条也要用。如果要在接口里做批量预测Tokenizer可以一次性接收一个列表texts [新闻一, 新闻二, 新闻三] enc tokenizer( texts, max_length128, paddingmax_length, truncationTrue, return_tensorspt, ) with torch.no_grad(): logits model(**enc).logits probs torch.softmax(logits, dim1)批量大小和训练时保持一致即可。三条样本的实际长度不同但都被pad到128。深度学习模型对padding的位置天然不敏感因为它不知道哪些位置是真实tokenattention_mask在训练时已经告诉模型哪些位置要做注意力计算所以padding位不会参与更新。5.2 阈值调节0.5只是起点不是唯一答案机器学习模型的输出是概率而“假新闻”这个判断本身需要阈值。默认阈值0.5在类别比例和人工标注概率都比较均衡时没问题但在虚假新闻场景下漏报假新闻的代价通常高于误报真新闻于是阈值应该往低方向调让更多低置信度样本进入“疑似”队列。调阈值前先拿到验证集上每一条样本属于“假新闻”的概率from sklearn.metrics import precision_recall_curve precision, recall, thresholds precision_recall_curve( y_val, val_probs[:, 1] ) # 找出第一个 recall 达到 0.85 的阈值 for p, r, t in zip(precision, recall, thresholds): if r 0.85: print(fthreshold: {t:.4f}, precision: {p:.4f}, recall: {r:.4f}) break这个操作实质上是把模型当成排序器而不是分类器。阈值越低recall越高precision越低。在接口设计上可以把阈值作为一个外部参数而不是写死在代码里这样业务方可以根据运营反馈动态调整。“置信度低于0.3不下结论转人工审核”这类策略就是基于这种动态阈值实现的。5.3 导出ONNX或包一层HTTP接口如果要做深度学习模型部署比较省事的路线是先用ONNX导出再用ONNX Runtime加载。这样推理端不需要torch和transformers整个服务依赖会轻很多。import torch model.eval() dummy_input tokenizer( [用于导出ONNX的占位文本], max_length128, paddingmax_length, truncationTrue, return_tensorspt, ) torch.onnx.export( model, (dummy_input[input_ids], dummy_input[attention_mask]), bert_news.onnx, input_names[input_ids, attention_mask], output_names[logits], dynamic_axes{ input_ids: {0: batch}, attention_mask: {0: batch}, }, opset_version14, )export接收的第二个参数必须是模型forward的输入这里传的是input_ids和attention_mask两个张量和模型签名一致。dynamic_axes把batch维设置成动态这样导出的模型可以接受任意batch大小。opset_version14在大多数部署端都能兼容版本太低会缺一些算子太高可能导致旧Runtime无法加载。导出后ONNX Runtime的推理代码只依赖numpy不再依赖transformers。但tokenizer仍然需要在前端Python服务里保留这是很多人在部署时容易漏掉的部分。如果训练时的文本清洗和部署时的文本清洗不一致再好的模型权重都会打折扣。我习惯把清洗逻辑、tokenizer初始化、模型加载三件事放进同一个模块避免两套代码各写一份。部署完成后拿训练时见过的测试集样本过一遍接口比较logits和PyTorch原始输出的差异。ONNX Runtime的浮点结果与PyTorch可能在小数点后几位不同这是正常的但类别和相对顺序不能变。如果输出完全偏离优先检查input_ids是否被tokenizer改变尤其是中文场景特殊标记和编码方式不一致很容易造成这种问题。注意ONNX导出只解决了模型部分tokenizer仍然是独立的。生产环境下通常会把tokenizer和预处理逻辑也一并固化成服务代码否则训练端和推理端的文本清洗方式一旦不一致模型效果会立刻缩水。6. 进阶技巧留住检查点、调阈值、看误判三个习惯做出差异化如果你的目标是毕业设计答辩或者把它写进简历那么这个源码包里最值得展示的不是“我跑通了”而是“我知道模型在哪里会失效”。围绕这一点有三个技巧性价比极高。第一把所有epoch的checkpoint都留住不要只留最后一个。训练过程中在验证集F1最高的那个点往往出现在倒数第几个epoch而不是最后一个epoch。保留全部checkpoint你随时可以回头加载那个最好的权重做进一步分析。损失函数在训练集上一路下降验证集指标却像过山车这是过拟合的征兆你不需要觉得模型“有问题”只需要选好checkpoint。第二把验证集的预测结果导出到csv逐条看误判样本。这是最值得养成的习惯。导出后看三类样本假新闻被预测成真新闻、真新闻被预测成假新闻、模型输出高置信度但预测错误的样本。前两类决定阈值怎么调第三类决定模型结构要不要改、特征要不要补。曾经我在测试集上一轮轮调参F1终于到了0.91后来换了一组真实的用户投稿数据F1直接掉到0.74那种感觉就像吃了后悔药。后悔没有用真正有用的是把误判样本当成下一次实验的说明书。第三做一个“最短复现流程”每次训练都走同一条路。我现在的固定顺序是先固定随机种子再划分数据再统计文本长度分布再定max_length然后训练最后用验证集P-R曲线选阈值。每一步之间不穿插额外操作。这个过程看起来朴素但可以避免80%的无效反复因为每个结果都能追溯到每一步的设定。如果你正在找一份能把机器学习、深度学习、BERT三条路线都串起来的项目源码这份包算是不错的参照。它不完美但足够让你在复现和改造中理解文本分类的完整链条。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?