ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PyTorch新闻文本分类实战:从TextCNN到LSTM的完整源码解析

2026/10/8 5:28:54 拓冰建站 浏览量
PyTorch新闻文本分类实战:从TextCNN到LSTM的完整源码解析 简介这是一份基于PyTorch实现新闻数据集文本分类的实战源代码适合正在学习自然语言处理与深度学习的中高级开发者。资源围绕端到端训练流程展开内容涵盖文本预处理、数据集迭代器、模型搭建、训练与评估函数以及可参考的代码组织结构能够帮助读者理解卷积神经网络等模型在文本分类中的实际应用。压缩包共15个文件容量约15.6MB以7个Python脚本为主体另含4个txt说明文件、2个npz数据文件、1个Markdown文档和1个pkl模型文件目录按模型库、工具函数等模块划分便于快速定位和复用。目前已有2591人学习下载配套代码贴近工程实践从数据加载到模型评估均给出可运行实现尤其适合希望结合PyTorch实训项目巩固NLP技能的读者参考。1. 为什么拿 PyTorch 做新闻文本分类一个能完整跑通的源码包做新闻标题分类这个任务很多人第一步就想上 BERT结果显存先炸了调参调得怀疑人生。实际在一线落地时用 PyTorch 搭一个文本卷积网络或者多层循环网络在新闻标题这类短文本上照样能拿到可用的准确率而且单卡 CPU 都能在半小时内跑完一个 epoch。这套 python 深度学习实战源码就是干这个的它把 PyTorch 环境下的新闻数据集文本分类整条链路——分词、数据集迭代、模型构建、训练评估——都拆成了可直接复现的文件尤其适合刚啃完 PyTorch 基础教程、想做第一个 NLP 实战项目又不想从零造轮子的人。2. 先读懂源码包再动手五个核心模块如何协作2.1 目录结构入口文件与数据仓的布局解压News-dataset-text-classification-in-practice-master.zip之后第一件事不是急着跑主要.py而是先把目录结构摸清楚。这套源码的文件命名是中文的对新手友好但如果你平时看惯了英文命名反而容易懵。我一般会先执行一次tree命令看全貌tree /d /f News-dataset-text-classification-in-practice-master命令跑完你会看到大约下面这样的结构News-dataset-text-classification-in-practice-master/ ├── 主要.py ├── 工具函数.py ├── 训练与评估函数.py ├── 数据集迭代器类.py ├── __init__.py ├── 数据仓/ ├── 模型库/ │ ├── 文本卷积神经网络.py │ └── 文本重叠神经网络.py ├── 清华中文文本分类工具包/ └── README.md说说每个文件在我理解里的分工。主要.py是整个项目的入口负责把数据加载、模型初始化、训练循环串起来你只需要改改路径和几个超参数就能跑工具函数.py承担的是文本清洗、分词、标签映射这类脏活数据集迭代器类.py负责把原始文本变成 PyTorch 能吃的 batch训练与评估函数.py封装了交叉熵损失、优化器、验证集评估逻辑模型库/底下两个文件分别实现了卷积和循环两个方向的分类网络。这里要特别提一下清华中文文本分类工具包这个文件夹。它本身是一个参考实现常见做法是把它当作第三方依赖引进主要.py用来做分词预处理或者词表构建。如果你只是跑通主线流程其实用不到里面所有文件但 README 建议你先把它能 import 通因为工具函数.py里大概率有一行from 清华中文文本分类工具包 import ...这步断了后面全断。2.2 工具函数.py分词、去停用词、类别映射这个文件是整套源码里最容易被跳过、但最值得读的一个。实际跑项目时真正影响最终准确率的往往不是模型结构而是你喂给模型的文本干不干净。工具函数.py里常见操作是用 jieba 做中文分词、去掉停用词、把原始标签比如体育、财经、娱乐映射成整数 id最后返回一个可以被模型直接消费的样本列表。我按这个文件的职责写了一个等价版本方便你对着接口理解# 工具函数.py 的核心职责示意 import jieba import re STOPWORDS {的, 了, 是, 在, 和, 有, 与, 中} LABEL_MAP {体育: 0, 财经: 1, 娱乐: 2, 科技: 3, 健康: 4} def clean_text(text: str) - str: 去掉网页标签和特殊符号只保留中文、英文、数字 text re.sub(r.*?, , text) text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9], , text) return text.strip() def tokenize(text: str) - list[str]: jieba 分词 去停用词 words jieba.lcut(clean_text(text)) return [w for w in words if w and w not in STOPWORDS] def encode_label(label: str) - int: 把中文标签编码成数字训练时交给交叉熵损失 return LABEL_MAP[label]clean_text里那个正则[^\u4e00-\u9fa5a-zA-Z0-9]是关键它把新闻原始数据里的全角空格、换行符、括号统一洗掉。我见过不少人在这个正则上翻车把所有数字也过滤了导致年份数字特征全部消失标题分类准确率直接掉两三个点。tokenize的返回值是分词后的 list而不是拼接好的字符串。这个细节很重要因为后面数据集迭代器类.py要做词表映射输入如果是字符串还得二次split白白增加出错概率。encode_label之所以单独抽一个函数是因为新闻数据集标签经常是中文而 PyTorch 的CrossEntropyLoss只接受整数 target这一步不做训练循环里必报错。2.3 数据集迭代器类.py为什么不直接用 torchtexttorchtext 确实能加载新闻数据集但版本坑太多0.9 和 0.12 之间的 API 完全不兼容网上搜到的代码大多跑不起来。这套源码自己实现了一个数据集迭代器类思路更直观读文件 → 构建词表 → 按 batch 返回 tensor。我用等价代码还原一下它的核心逻辑# 数据集迭代器类.py 的核心示意 import torch from torch.utils.data import Dataset, DataLoader from 工具函数 import tokenize, encode_label class NewsDataset(Dataset): def __init__(self, file_path: str, vocab: dict, max_len: int 64): self.samples [] self.vocab vocab self.max_len max_len with open(file_path, r, encodingutf-8) as f: for line in f: label, text line.strip().split(\t, 1) words tokenize(text)[:max_len] ids [vocab.get(w, 1) for w in words] # 1 是 UNK self.samples.append((ids, encode_label(label))) def __len__(self): return len(self.samples) def __getitem__(self, idx): ids, label self.samples[idx] return torch.tensor(ids, dtypetorch.long), torch.tensor(label, dtypetorch.long) def build_vocab(file_path: str, min_freq: int 1) - dict: from collections import Counter counter Counter() with open(file_path, r, encodingutf-8) as f: for line in f: _, text line.strip().split(\t, 1) counter.update(tokenize(text)) return {w: i 2 for w, i in counter.items() if i min_freq}build_vocab里我把词表索引从 2 开始0 留给 PAD1 留给 UNK。这是文本分类任务里非常容易漏的细节如果你词表里没有 UNK测试集里一旦出现训练集没见过的新词vocab.get(w, 1)就会默认取到 1这也是为什么编号 1 必须预留。max_len的默认值我经验上取 64新闻标题基本都在这个长度内残留的尾部信息切掉对准确率影响很小。NewsDataset继承自torch.utils.data.Dataset这样后续可以直接用DataLoader的num_workers参数做多进程加载。main.py里常见调用是DataLoader(train_set, batch_size64, shuffleTrue, num_workers2)shuffle必须开否则模型会在每个 epoch 内看到完全相同顺序的样本收敛速度会明显变慢。2.4 环境准备PyTorch 与 torchtext 的版本对齐源码能在你的机器跑起来环境要过三关。第一关是 Python 版本这套代码适合 Python 3.8 到 3.10第二关是 PyTorch 安装建议直接用 conda 建独立环境别往 base 环境里塞第三关是 torchtext 版本这套源码里如果直接 import 了torchtext.data那它大概率是基于 torchtext 0.12 或更早版本写的新版 torchtext 0.16 把legacy目录改成了必须手动指定。我一般这样搭建conda create -n news_cls python3.9 conda activate news_cls pip install torch1.13.1cpu torchtext0.12.0 --extra-index-url https://download.pytorch.org/whl/cpu pip install jieba pandas numpy scikit-learn这里刻意锁了torchtext0.12.0而不是最新版因为 0.13 之后torchtext.data.Field被移到了torchtext.legacy.data如果不熟悉新版 API跑源码第一行就报ModuleNotFoundError。如果你拿到的是新版 torchtext改法是在源码所有from torchtext.data import ...前加上import torchtext前缀或者批量替换成from torchtext.legacy import datascikit-learn一定要装之后生成混淆矩阵和分类报告都依赖它。装完可以用一段极短代码自检环境是否通顺import torch import torchtext print(torch.__version__) print(torchtext.__version__)能打印出版本号不代表能跑但至少说明 import 链是通的。下一步再去主要.py里确认一下数据仓里的数据文件名和代码里读的路径是否一致比如代码写的是数据仓/新闻数据.csv实际文件却是数据仓/新闻标题分类数据.csv那就先改名对齐省得启动后报FileNotFoundError再回头查。3. 模型构建文本卷积网络与文本重叠网络的选型逻辑3.1 文本卷积神经网络用卷积核抓取局部 n-gram 特征新闻标题分类和句子级情感分类很像核心判别信息往往集中在一两个关键词或短语上比如标题里出现“世界杯”“夺冠”基本可以往体育方向猜。文本卷积神经网络就是冲着这个特点设计的多个不同宽度的卷积核在 embedding 序列上滑动相当于同时捕获 3-gram、4-gram、5-gram 的特征然后再做全局最大池化把最重要的特征抽出来。源码里模型库/文本卷积神经网络.py的结构等价实现如下import torch.nn as nn class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim128, num_filters256, kernel_sizes(3, 4, 5), num_classes5): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.convs nn.ModuleList([ nn.Conv1d(embed_dim, num_filters, k, paddingk // 2) for k in kernel_sizes ]) self.dropout nn.Dropout(0.5) self.fc nn.Linear(num_filters * len(kernel_sizes), num_classes) def forward(self, x): # x: (batch, seq_len) emb self.embedding(x) # (batch, seq_len, embed_dim) emb emb.transpose(1, 2) # Conv1d 需要 channel 在第二维 pooled [] for conv in self.convs: out conv(emb) # (batch, num_filters, seq_len) out out.max(dim2).values # 全局最大池化 pooled.append(out) out torch.cat(pooled, dim1) out self.dropout(out) return self.fc(out)padding_idx0是必须传的参数它让 PAD 位置在 embedding 后全是零向量卷积核滑到 padding 区域不会产生有效特征。kernel_sizes(3, 4, 5)的含义是三组卷积核宽度分别为 3、4、5每组拿 256 个 filter拼接之后全连接层输入维度是256 * 3 768。这几个数字是文本 CNN 的经典配置不需要一开始就魔改。一个我反复提醒自己别踩的坑nn.Conv1d的输入是(batch, channels, length)而nn.Embedding输出是(batch, length, embed_dim)中间必须transpose(1, 2)。漏掉这行会直接报维度不匹配的错误而且报错信息不会提示你是转置问题看起来像通道数爆炸。3.2 文本重叠神经网络多层循环网络的堆叠逻辑源码里的另一个模型文件叫文本重叠神经网络.py从命名习惯看这里的“重叠”指的是多层循环网络在时间步上的堆叠。每层 RNN 的隐藏状态作为下一层的输入逐层传递这样模型就能在新闻标题这种短文本里捕捉到比单层 LSTM 更长距离的依赖关系。相比 CNN 只抓局部词组合循环网络的记忆能力对“先抑后扬”这种转折型标题更友好。用 PyTorch 实现一个双层 LSTM 分类器等价代码如下import torch.nn as nn class TextRNN(nn.Module): def __init__(self, vocab_size, embed_dim128, hidden_size128, num_layers2, num_classes5): super().__init__() self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) self.lstm nn.LSTM(embed_dim, hidden_size, num_layersnum_layers, batch_firstTrue, dropout0.3, bidirectionalTrue) self.dropout nn.Dropout(0.5) self.fc nn.Linear(hidden_size * 2, num_classes) def forward(self, x): emb self.embedding(x) out, (h_n, c_n) self.lstm(emb) # out: (batch, seq, 2 * hidden) out out[:, -1, :] # 取最后一个时间步 out self.dropout(out) return self.fc(out)bidirectionalTrue在这里非常关键。新闻标题的判别词也许出现在句首也许出现在句尾单向 LSTM 只能记住从左往右的信息双向结构让每个位置的向量同时包含前后语境。全连接层的输入维度写成hidden_size * 2就因为双向会把两个方向的隐层拼接。num_layers2就是前面说的“重叠”数。增加到 3 层或 4 层确实能提升模型表达能力但新闻标题本身只有几十个 token数据量不够反而容易过拟合一块 GPU 上训练时间也成倍增长。我的习惯是先从 2 层起步在验证集准确率不再上涨时再加层而不是一开始就堆到 4 层。3.3 词嵌入与预训练先随机初始化再谈提精度源码里 embedding 层默认是随机初始化的PyTorch 会按均匀分布生成初始向量。随机初始化的意思是每个词的向量在训练刚开始没有任何语义信息纯粹靠梯度回传慢慢调整。这在新闻分类这种数据量充足的任务上够用但如果你想榨取更高准确率可以加载预训练词向量。torchtext 0.12 里加载 GloVe 是这样写的from torchtext.vocab import GloVe vectors GloVe(name6B, dim300) embedding_matrix vectors.get_vecs_by_tokens(vocab.itos, lower_case_backupTrue) model.embedding.weight.data.copy_(embedding_matrix)看起来很简单但有三个现实坑。第一个是下载慢GloVe 6B 大概 800MB国内网络经常断第二个是词表对齐问题如果新闻数据集里的词在 GloVe 里不存在get_vecs_by_tokens会生成随机向量这部分词的比例一旦超过 5%预训练收益就基本被抵消第三个是lower_case_backupTrue一定要开中文新闻里夹杂的英文专名如 iPhone、AI 会因此命中大小写变体。我的建议是第一版先把随机 embedding 的模型跑通确认整个 pipeline 没问题再考虑上预训练向量。很多新手一上来就加载 GloVe结果因为词表对齐问题浪费一整天最后发现随机初始化的模型只比它低一个点完全不值得。4. 训练与评估从迭代器到损失曲线的落地细节4.1 训练与评估函数单轮循环怎么写才不浪费步数训练与评估函数.py封装了两个核心函数一个负责跑训练一个负责跑验证。训练函数内部的写法决定了你能不能及时发现问题——如果训练时 loss 直接变成nan第一轮就必须停下排查而不是等 10 个 epoch 跑完再看。等价实现如下import torch from sklearn.metrics import accuracy_score def train_one_epoch(model, iterator, optimizer, criterion): model.train() total_loss, total_correct, total_num 0, 0, 0 for batch in iterator: inputs, labels batch optimizer.zero_grad() logits model(inputs) loss criterion(logits, labels) loss.backward() optimizer.step() total_loss loss.item() * labels.size(0) preds logits.argmax(dim1) total_correct (preds labels).sum().item() total_num labels.size(0) return total_loss / total_num, total_correct / total_num def evaluate(model, iterator, criterion): model.eval() total_loss, total_correct, total_num 0, 0, 0 with torch.no_grad(): for batch in iterator: inputs, labels batch logits model(inputs) loss criterion(logits, labels) total_loss loss.item() * labels.size(0) preds logits.argmax(dim1) total_correct (preds labels).sum().item() total_num labels.size(0) return total_loss / total_num, total_correct / total_num注意几个细节。optimizer.zero_grad()必须放在每次 forward 之前放在 backward 之后会导致梯度累积两次loss 曲线震荡。计算total_loss时我乘以labels.size(0)这样可以消除最后一个 batch 样本数不满导致的权重偏差。evaluate里torch.no_grad()绝对不能省它会关掉自动求导图让显存占用显著下降验证速度也快很多。model.train()和model.eval()是两个新手最容易忽略的开关。Dropout层在 train 模式下会随机失活部分神经元在 eval 模式下会恢复成完整网络。如果验证时忘记切回 eval 模式dropout 仍然杀神经元验证准确率会比实际值低 25 个百分点。4.2 超参数设多少epoch、batch size、学习率的踩坑组合源码 README 里一般会给一组默认超参数但不同新闻数据集规模差很多直接套用很容易出问题。我按常见的中文新闻标题分类任务整理了一张参数表你可以作为起点再微调超参数建议值调整方向batch_size64显存不够降到 32太小则收敛慢learning_rate0.001用 Adam 时 0.001 起步不稳定则降到 0.0001epochs20设早停 patience3连续 3 轮不涨就断embed_dim128数据量大于 10 万条可升到 256hidden_size128文本更长时升到 256num_layers2不要轻易上 3 层dropout0.5明显过拟合时加到 0.7learning_rate0.001配合 Adam 是最稳的起点。Adam 自带自适应步长不像是 SGD 那样需要精细调整 momentum 和 weight decay。但 Adam 不代表学习率可以随便设0.01 起步在文本分类这种任务上经常会导致 loss 先降后炸。如果发现训练 loss 前几个 step 出现nan第一步就是把学习率除以 10大概率能救回来。epochs和早停要一起说。20 个 epoch 在新闻分类任务上通常够用但如果你只看训练 loss你会发现它在 20 轮内一直下降——那是训练集过拟合的假象。正确做法是每个 epoch 结束都调用evaluate算验证准确率连续 3 轮不涨就提前终止省掉后面跑圈的时间。4.3 过拟合与正则化dropout 与 L2 的用法新闻数据集如果只有几千条TextCNN 这类模型很容易过拟合。判断标准很直观训练准确率接近 100%验证准确率却停在某个值上不来。源码里已经内置了nn.Dropout(0.5)但 0.5 不一定是你的最优值。数据量越小dropout 可以开得越大0.6、0.7 都不过分。除了 dropoutL2 正则化是另一个常用手段。PyTorch 里给 Adam 加 L2 不需要额外写代码直接在优化器里设weight_decayoptimizer torch.optim.Adam( model.parameters(), lr0.001, weight_decay1e-5 )weight_decay1e-5的含义是在每次参数更新时额外扣掉一小部分权重让网络倾向于学小权重从而降低对个别特征的过度依赖。经验值是 1e-5 到 1e-4 之间太大反而会导致模型欠拟合loss 压不下去。还有一个正则化技巧是限制max_len。新闻标题超过 64 个 token 的少把max_len从 128 砍到 64相当于让模型只看头部信息减少尾部 padding 和噪声对梯度的干扰。这个改动往往比调 dropout 更立竿见影。5. 避坑手册四个高频问题和排查记录5.1 现象torchtext 版本不对Field 直接报错换了一台机器重新搭环境代码在原来的环境里跑得好好的换到新机器import torchtext.data直接抛ModuleNotFoundError: No module named torchtext.data。看一眼版本发现是 torchtext 0.16而自己原来用的是 0.12。原因非常简单torchtext 在 0.13 开始把旧 API 整体移入legacy不再默认暴露。解决办法有两个一是锁版本重新安装pip install torchtext0.12.0二是把所有from torchtext.data import ...批量改成from torchtext.legacy.data import ...。我建议直接锁版本因为legacy路径在部分新版里又发生了二次调整锁版本省心。5.2 现象中文分词泛化差模型准确率上不去用同样的模型结构训练英文新闻数据集准确率有 85%中文新闻只有 70% 出头。看了预测结果发现很多类别错在“词语边界”上比如“中国女排”被切成“中国”“女排”倒还好但“夺冠”被切成了“夺”“冠”就完全丢失了原词义。原因出在分词器的词表覆盖不足jieba 默认词典对新闻领域专有名词、人名、赛事名覆盖本来就有限。解决思路是加载 jieba 自定义词典把新闻数据里的高频专名加进去同时也可以对tokenize函数做一次极端验证把jieba.cut(中国女排夺冠)的结果打印出来人工看切分是否理想不理想的词加进userdict.txt然后在代码里加载jieba.load_userdict(userdict.txt)。5.3 现象loss 降了验证集准确率一直停在随机水平训练 loss 从 1.8 一路降到 0.4看起来模型在学但验证集准确率始终在 20% 附近跟随机猜差不多。这个现象我遇到几次后发现根因多半在数据泄露和标签错位二选一。先检查build_vocab的读文件逻辑如果验证集也参与构建词表不算严重泄露但如果训练集和验证集样本在按行切分时没有打乱文本连续片段会同时出现在两个集合里模型记忆了文本位置而不是语义。解决办法是把数据按train_test_split(data, test_size0.2, random_state42)打乱后再切分。另一个隐蔽问题是encode_label的映射表和训练用的数据顺序不一致比如标签科技在训练集里是 3验证集里变成 4准确率直接掉到随机水平。排查方法是打印前 10 个label和preds的数值肉眼核对映射是否一致。5.4 现象CUDA 内存不足batch 一加大就崩GPU 显存 6Gbatch_size 设 64 跑得好好的改成 128 直接报CUDA out of memory。原因很直接显存占用和 batch_size 成正比而文本 CNN 在 embedding 矩阵大、序列长的情况下中间激活值会占掉大量显存。解决路径按优先级排先降 batch_size 到 32别贪再把max_len从 128 调到 64这一步能省一半激活显存最后检查模型是否把model.eval()阶段的数据也传到 GPU 并且开启了梯度验证阶段记得加with torch.no_grad()。还有一个容易忽略的技巧embedding 层的梯度只在训练时需要验证时如果显存依然不够可以把inputs和labels在拼 batch 前就用to(device)不要在collate_fn里一次性转整个数据集。5.5 现象GPU 训练结果复现不稳定同一个脚本、同一份数据第一轮跑准确率 82%第二轮变成 81.2%还能看出波动但如果差异到 3 个百分点以上说明哪里不对。最常见原因是 PyTorch 的卷积和 LSTM 在 GPU 上使用 cuDNN 的确定性算法默认是不开启的。解决办法是在训练脚本启动前固定所有随机源import random import numpy as np import torch def seed_everything(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True torch.backends.cudnn.benchmark Falsecudnn.deterministic True是关键它让 cuDNN 选择相同的卷积算法代价是速度慢 5%10%。复现结果比那点速度更重要。如果你在验证复现性记得每次跑之前调用一次seed_everything()否则光设 seed 无效。6. 用混淆矩阵和真实新闻标题做最后的验证6.1 混淆矩阵的可视化脚本训练结束时看到的准确率只是整体数字新闻分类任务还需要知道到底哪些类别容易互相混淆。比如财经和科技可能因为标题里都出现“AI”“金融科技”这类词而分不开。source 里已经导入了 scikit-learn生成混淆矩阵的脚本可以这么写from sklearn.metrics import confusion_matrix, classification_report import matplotlib.pyplot as plt def plot_confusion(y_true, y_pred, labels): cm confusion_matrix(y_true, y_pred) fig, ax plt.subplots(figsize(8, 6)) im ax.imshow(cm, cmapBlues) ax.set_xticks(range(len(labels))) ax.set_yticks(range(len(labels))) ax.set_xticklabels(labels, rotation45) ax.set_yticklabels(labels) for i in range(len(labels)): for j in range(len(labels)): ax.text(j, i, cm[i, j], hacenter, vacenter) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)classification_report会给出每个类别的精确率、召回率、F1 值比整体准确率信息量大得多。我通常在训练脚本末尾把这两样全打印出来然后挑几个被分错的具体文本打印在控制台自己人对一眼确认是数据问题还是模型问题。6.2 保存与恢复部署时加载模型模型验证完了才到真正的落点保存和恢复。新手往往只保存model.state_dict()结果换环境后词表没同步加载模型后预测全乱。标准姿势是把词表和模型参数打包成一份 checkpointtorch.save({ model_state_dict: model.state_dict(), vocab: vocab, max_len: 64, label_map: LABEL_MAP, }, news_cls.pt) # 恢复预测 ckpt torch.load(news_cls.pt, map_locationcpu) model.load_state_dict(ckpt[model_state_dict]) model.eval()我这里把vocab一起存进去了。因为预测时要用同样的词表把新文本转成 id如果只存权重部署时词表对不上模型等于白训。恢复后model.eval()必须调这个写在 4.1 里说过再一次强调。6.3 一个小习惯只用 100 条数据把流程跑通再全量训练我的习惯是把训练与评估函数.py里的训练集临时截取前 100 条用 10 个 epoch 跑一轮完整流程确认 loss 能下降、准确率能变化、保存逻辑能工作再把截断代码去掉跑全量数据。这套源码包我拿来时也是先这么验证的避免直接在 10 万条新闻上跑 3 小时才发现某处变量没对齐。从那以后我每拿到一个文本分类资源都会强制先走一遍这个 100 条数据的冒烟流程再全量训。希望帮到你。本文还有配套的精品资源点击获取