新闻详情

新闻详情

首页 / 资讯中心 / 详情

PyTorch实战:新闻文本分类从数据加载到TextCNN模型训练完整指南

发布时间:2026/9/28 19:12:31来源:尧图网络
PyTorch实战:新闻文本分类从数据加载到TextCNN模型训练完整指南
简介这份资源面向具备一定Python基础、希望进入自然语言处理领域的开发者与学习者提供一套基于PyTorch的新闻数据集文本分类完整实战源码。项目围绕端到端文本分类流程展开涵盖数据加载与清洗、分词与停用词处理、词嵌入使用以及CNN、LSTM、GRU等模型的构建思路并涉及损失函数、优化器选择、过拟合应对与模型保存等关键环节帮助读者理解深度学习在NLP任务中的落地方式。资源包共15个文件以7个py源码文件为核心辅以4个txt说明、2个npz数据文件、1个md文档和1个pkl模型文件压缩包约15.6MB目录中可见工具函数、训练与评估函数、数据集迭代器类及文本卷积神经网络、文本重叠神经网络等模块结构清晰便于按功能查阅。目前已有2590人学习下载适合作为课程设计、项目练手或NLP入门进阶的参考实现。1. 新闻文本分类为什么成了 PyTorch 入门的试金石手里有一份新闻数据集想用 PyTorch 跑一个文本分类模型这件事听起来简单实际动手时却卡住不少人。新闻文本分类是 NLP 里最经典的落地任务之一输入一段新闻标题或正文输出它属于体育、财经、科技还是娱乐。它不像图像分类那样有现成的 ImageNet 和预训练权重可以拿来就用也不像大模型微调那样动辄需要几十 G 显存。它刚好卡在一个舒服的位置——数据量可控、模型结构清晰、训练时间以分钟计但里面藏着分词、词表构建、变长序列处理、类别不均衡这些真实工程问题。你如果能把新闻文本分类这条链路完整走通一遍后面换任何数据集、换任何模型结构套路都是通的。这篇文章面向的是已经装好 PyTorch、想找一个完整项目练手的人也面向那些跑过 demo 但一换数据就翻车的人。我会从数据加载讲到模型训练和推理把每个环节的参数含义和常见坑点都摊开说。2. 新闻数据集文本分类的完整链路拆解2.1 从原始新闻文本到模型可读的张量新闻数据集通常以 CSV 或 JSON 格式提供每行包含文本字段和标签字段。原始文本是字符串模型只认数字张量中间需要经过分词、构建词表、数值化、padding 四个步骤。分词环节中文新闻常用 jieba 做切词英文新闻按空格和标点切分即可。构建词表时统计训练集中所有词的出现频率保留频率最高的前 N 个词其余映射为unk。数值化就是把每个词替换成词表中的索引 ID。padding 是因为新闻长度不一一个 batch 内必须对齐长度短的补 0长的截断。这里有一个容易忽略的点词表必须只在训练集上构建验证集和测试集用训练集的词表做映射。如果全量数据一起构建词表验证集的信息就泄漏到了训练过程中评估结果会虚高。我一般会先把数据按 8:1:1 切分然后在训练集上build_vocab再把同一个词表对象传给验证集和测试集的 Dataset。import jieba from collections import Counter def build_vocab(texts, max_size20000, min_freq2): 在训练集上构建词表返回 word2idx 和 idx2word counter Counter() for text in texts: tokens jieba.lcut(text) counter.update(tokens) # 按频率排序过滤低频词 words [w for w, c in counter.most_common(max_size) if c min_freq] # 保留特殊 token word2idx {pad: 0, unk: 1} for w in words: word2idx[w] len(word2idx) idx2word {i: w for w, i in word2idx.items()} return word2idx, idx2wordmax_size控制词表上限新闻数据集一般 2 万到 5 万足够覆盖大部分高频词。min_freq2过滤只出现一次的词这些词对模型几乎没有贡献反而增加参数量。pad固定为 0 是因为后面Embedding层要设置padding_idx0让 padding 位置不参与梯度更新。2.2 Dataset 和 DataLoader 的工程化写法PyTorch 的Dataset是一个抽象类你需要实现__len__和__getitem__两个方法。新闻文本分类的 Dataset 要做的事情很明确读一条文本分词查词表转成 ID 列表返回张量和标签。但这里有个性能陷阱——如果每次__getitem__都调用 jieba 分词训练时 DataLoader 的多个 worker 会反复做重复计算。更好的做法是在初始化阶段就把所有文本预分词并转成 ID 列表存成一个 list__getitem__只做索引取值和 padding。import torch from torch.utils.data import Dataset, DataLoader class NewsDataset(Dataset): def __init__(self, texts, labels, word2idx, max_len128): self.labels labels self.max_len max_len self.word2idx word2idx # 预分词并转 ID避免训练时重复计算 self.encoded [] for text in texts: tokens jieba.lcut(text)[:max_len] ids [word2idx.get(t, word2idx[unk]) for t in tokens] self.encoded.append(ids) def __len__(self): return len(self.labels) def __getitem__(self, idx): ids self.encoded[idx] # padding 到 max_len if len(ids) self.max_len: ids ids [0] * (self.max_len - len(ids)) return torch.tensor(ids, dtypetorch.long), torch.tensor(self.labels[idx], dtypetorch.long)max_len128对新闻标题足够如果是正文分类可以设到 256 或 512。预分词的做法在数据量十万级时内存占用大约几百 MB完全可以接受。DataLoader的num_workers设为 4 或 8 能明显加快数据加载但 Windows 下有时会有多进程问题设成 0 最稳。2.3 TextCNN 模型结构与关键参数TextCNN 是新闻文本分类里性价比最高的模型之一结构简单、训练快、效果不差。核心思路是用不同尺寸的卷积核在词向量序列上滑动捕捉 2-gram、3-gram、4-gram 的局部特征然后做最大池化最后拼接全连接分类。相比 LSTMTextCNN 并行度高训练速度能快三到五倍。import torch.nn as nn import torch.nn.functional as F class TextCNN(nn.Module): def __init__(self, vocab_size, embed_dim128, num_classes10, filter_sizes(2, 3, 4), num_filters128, dropout0.5): super().__init__() # padding_idx0 让 pad 位置不更新梯度 self.embedding nn.Embedding(vocab_size, embed_dim, padding_idx0) # 三个不同尺寸的卷积核 self.convs nn.ModuleList([ nn.Conv1d(embed_dim, num_filters, kernel_sizefs) for fs in filter_sizes ]) self.dropout nn.Dropout(dropout) self.fc nn.Linear(num_filters * len(filter_sizes), num_classes) def forward(self, x): # x: [batch, seq_len] emb self.embedding(x) # [batch, seq_len, embed_dim] emb emb.permute(0, 2, 1) # [batch, embed_dim, seq_len] # 每个卷积核做卷积 ReLU 全局最大池化 conv_outs [] for conv in self.convs: c F.relu(conv(emb)) # [batch, num_filters, seq_len - fs 1] c F.max_pool1d(c, c.size(2)).squeeze(2) # [batch, num_filters] conv_outs.append(c) out torch.cat(conv_outs, dim1) # [batch, num_filters * 3] out self.dropout(out) return self.fc(out)embed_dim128是词向量维度新闻分类任务上 128 到 300 都常见越大越吃显存。filter_sizes(2,3,4)对应 2-gram 到 4-gram 特征这是 TextCNN 的经典配置。num_filters128是每个尺寸卷积核的数量三个尺寸加起来输出 384 维特征。dropout0.5在全连接层前做正则化新闻数据集如果只有几万条dropout 必须开否则训练集准确率能到 99% 但验证集只有 70%。3. 训练循环、评估指标与调参策略3.1 训练循环的五个必备组件一个完整的训练循环包含前向传播、损失计算、反向传播、参数更新、日志记录。新闻文本分类用CrossEntropyLoss作为损失函数优化器选Adam或AdamW学习率从 1e-3 开始试。每个 epoch 结束后在验证集上算准确率和 F1保存验证集 F1 最高的模型权重。from sklearn.metrics import f1_score import torch.optim as optim def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss 0 for batch_x, batch_y in loader: batch_x, batch_y batch_x.to(device), batch_y.to(device) optimizer.zero_grad() logits model(batch_x) loss criterion(logits, batch_y) loss.backward() # 梯度裁剪防止梯度爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm5.0) optimizer.step() total_loss loss.item() return total_loss / len(loader) def evaluate(model, loader, device): model.eval() preds, trues [], [] with torch.no_grad(): for batch_x, batch_y in loader: batch_x batch_x.to(device) logits model(batch_x) pred logits.argmax(dim1).cpu().tolist() preds.extend(pred) trues.extend(batch_y.tolist()) acc sum(p t for p, t in zip(preds, trues)) / len(trues) f1 f1_score(trues, preds, averagemacro) return acc, f1clip_grad_norm_的max_norm5.0是经验值TextCNN 一般不会梯度爆炸但加上更保险。f1_score用macro平均因为新闻类别通常不均衡准确率会被大类主导F1 更能反映小类效果。3.2 学习率、batch size 和 epoch 的配合关系这三个参数不是独立的。batch size 越大梯度估计越稳可以用更大的学习率batch size 越小梯度噪声越大学习率要相应调小。新闻文本分类的常见组合是 batch size 64 或 128学习率 1e-3epoch 10 到 20。如果验证集 loss 在 3 个 epoch 内不降先把学习率除以 5 再试。如果训练集 loss 降但验证集 loss 升说明过拟合加 dropout 或减模型参数量。参数推荐范围调整方向batch size32 / 64 / 128显存够就选大学习率1e-3 ~ 1e-4不收敛就降epoch10 ~ 20看验证集早停dropout0.3 ~ 0.5过拟合就加max_len128 ~ 512标题短正文长3.3 类别不均衡时的处理手段新闻数据集里体育和娱乐的样本量往往是科技类的五到十倍。直接训练会导致模型偏向大类小类 F1 很低。常见做法有三种在损失函数里给每个类别加权权重和类别频率成反比对大类做欠采样或对小类做过采样用 Focal Loss 替代 CrossEntropyLoss降低易分类样本的权重。# 方案一类别加权 class_counts [1000, 800, 200, 150] # 每个类别的样本数 weights torch.tensor([1.0 / c for c in class_counts]) weights weights / weights.sum() * len(class_counts) criterion nn.CrossEntropyLoss(weightweights.to(device))weight参数会让损失函数对小类的误分类施加更大惩罚。注意权重需要归一化否则 loss 的数值范围会变影响学习率的选择。4. 新闻文本分类的避坑与排查记录4.1 验证集准确率远高于测试集现象训练时验证集准确率 92%换测试集跑只有 75%。原因通常是数据切分时没有打乱或者测试集里出现了训练集没见过的词全部映射成了unk。解决方法是切分前先shuffle并且检查测试集的 OOV 率。如果 OOV 超过 10%说明词表太小或训练集覆盖不够把max_size调大或min_freq调小。4.2 训练 loss 不降或变成 NaN现象第一个 epoch loss 就是 NaN或者一直停在 2.3 左右不动。原因可能是学习率太大、输入 ID 超出词表范围、或者标签从 1 开始编号而模型输出从 0 开始。解决方法是先检查标签的min和max确保在[0, num_classes-1]范围内再把学习率降到 1e-4 试一个 epoch最后检查Embedding的vocab_size是否大于等于实际词表大小。4.3 DataLoader 多进程报错或卡死现象num_workers设为 4 后程序直接卡住或者报BrokenPipeError。原因在 Windows 和部分 Linux 环境下多进程和 jieba 的初始化有冲突。解决方法是在__init__里提前完成分词__getitem__只做纯数值操作或者把num_workers设为 0用主进程加载数据。如果数据量不大num_workers0完全够用。4.4 模型在 CPU 上训练慢到无法接受现象一个 epoch 要跑半小时GPU 利用率接近 0。原因通常是数据加载成了瓶颈或者模型没有搬到 GPU 上。解决方法是先确认model.to(device)和batch_x.to(device)都执行了再用torch.cuda.is_available()确认 GPU 可用如果数据加载慢把预分词结果存成 numpy 数组或 tensor 文件避免每次 epoch 重新查词表。4.5 推理时单条预测和批量预测结果不一致现象用model(x)单条预测和用DataLoader批量预测同一条文本得到不同类别。原因通常是单条预测时没有加model.eval()dropout 还在起作用或者单条文本的 padding 长度和批量时不一致。解决方法是在推理前统一调用model.eval()和torch.no_grad()并且单条预测也按照max_len做 padding不要用实际长度。5. 从 TextCNN 到 BERT 微调的进阶路径TextCNN 跑通之后下一步通常是换更强的模型。新闻文本分类上BERT 微调能把 F1 再拉高 5 到 10 个百分点但代价是显存和训练时间成倍增加。我的习惯是先用 TextCNN 把数据管线和评估流程跑通确认数据没有问题再上 BERT。如果数据量小于一万条BERT 微调容易过拟合反而不如 TextCNN 稳。换 BERT 时数据预处理要从分词查词表改成用BertTokenizer编码输出input_ids、attention_mask和token_type_ids。模型部分把TextCNN换成BertForSequenceClassification学习率降到 2e-5 到 5e-5epoch 控制在 3 到 5。这里有一个容易翻车的点BERT 的max_len一般是 512但新闻标题只有几十个字设成 128 能省一半显存效果几乎不掉。from transformers import BertTokenizer, BertForSequenceClassification tokenizer BertTokenizer.from_pretrained(bert-base-chinese) model BertForSequenceClassification.from_pretrained( bert-base-chinese, num_labelsnum_classes ) # 编码示例 encoded tokenizer( text, max_length128, paddingmax_length, truncationTrue, return_tensorspt ) # encoded 包含 input_ids, attention_mask, token_type_ids验证模型有没有真正学到东西我一般会做两个检查一是把验证集里预测错误的样本抽出来看如果错的全是长文本或含大量专有名词的样本说明max_len不够或词表覆盖不足二是把Embedding层降维可视化同类样本的词向量应该聚在一起。这两个检查做完基本能判断是数据问题还是模型问题。最后说一个我踩过的坑有一次用新闻数据集训练验证集 F1 到了 0.91我直接拿去跑线上结果线上准确率只有 0.6。后来发现训练数据里同一篇新闻出现了多次训练集和验证集之间有重叠。从那以后我切分数据前一定先按文本内容去重再去切分。这个习惯帮我省了很多后悔药。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【笔记】latex做表格:用 TaoToken 统一 Key 跑通 AI 辅助生成 LaTeX 表格配置 2026/9/28 19:57:42

【笔记】latex做表格:用 TaoToken 统一 Key 跑通 AI 辅助生成 LaTeX 表格配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
国产视频协议栈升级:ONVIF-Go v2、国标全支持与嵌入式ONVIF-C发布 2026/9/28 19:57:42

国产视频协议栈升级:ONVIF-Go v2、国标全支持与嵌入式ONVIF-C发布

1. 协议库集中发版背后的行业信号:一次被低估的国产视频接入基础设施升级 最近在几个嵌入式设备厂商的内部技术群和开源协议栈维护者的小圈子中,几乎同时刷出一条消息:“onvif-go 进 v2、国标设备侧收官、onvif-c 首发”——没有通稿&#x…

阅读更多 →
Codex 桌面版接入 DeepSeek API Key 教程:用 cc-switch 管理多模型配置 2026/9/28 19:57:35

Codex 桌面版接入 DeepSeek API Key 教程:用 cc-switch 管理多模型配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
RISC-V内核工业低功耗MCU选型:CH32L103资源与外设实战解析 2026/9/28 19:57:35

RISC-V内核工业低功耗MCU选型:CH32L103资源与外设实战解析

聊工业低功耗MCU选型,大多数人脑子里蹦出来的还是ST那一串L系列,或者是GD32L233这类跟着做的国产型号。但最近一年我陆续在几个传感器终端和协议转换器项目里用了沁恒的CH32L103,发现这颗RISC-V内核的工业MCU,在低功耗和外设集成度…

阅读更多 →
CH585M多外设隔离与低功耗模式设计实战 2026/9/28 19:57:35

CH585M多外设隔离与低功耗模式设计实战

1. 为什么"关不干净的外设"是低功耗设备的头号杀手做低功耗项目最磨人的阶段,往往不是算法设计,而是拿着万用表一点一点查电流。芯片明明According to手册进了睡眠模式,实测电流就是下不来,最后发现是某个外设没有真正隔…

阅读更多 →
leetcode 困难题 1847. Closest Room 2026/9/28 19:57:35

leetcode 困难题 1847. Closest Room

Problem: 1847. 最近的房间 二分查找的&#xff0c;只需要重放的&#xff0c;然后排序的&#xff0c;最后二分查找满足条件的房间&#xff0c;最后找到id最小的房间即可 Code class Solution { public:vector<int> closestRoom(vector<vector<int>>& r…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉