从垃圾短信识别项目里,我复盘了一个中文文本分类系统

一次从短信分类项目出发的复盘:从需求、流程到问题定位,再到后续可以继续优化的方向。

学习路线:AI 入门与 PyTorch 基础(1~20) · 第 19 课
第 19 课一部手机放在深色木桌上,屏幕里是抽象的通知卡片,旁边摊着记录分析流程的笔记本。
Learning Path

AI 入门与 PyTorch 基础(1~20)

从 AI 基本概念、机器学习任务、深度学习直觉、PyTorch 入门到中文文本分类项目全貌。

学完本阶段你能做到:能说清 AI、机器学习、深度学习、PyTorch 训练流程和一个中文文本分类项目的大致链路。

推荐读法:零基础建议顺序阅读;已有基础的读者可以把它当作路线地图和查漏补缺清单。

查看完整阶段 · 20 篇
Lesson Guide

这一课怎么学

先看目标,再带着问题读正文。读完后用练习确认自己真的理解了。

本节你会学到

  • 说清楚「从垃圾短信识别项目里,我复盘了一个中文文本分类系统」解决的核心问题
  • 知道它在「AI 入门与 PyTorch 基础(1~20)」中的位置
  • 把 分类与回归、Pipeline、TF-IDF 这些关键词联系起来

前置知识

  • 建议先读完上一篇:一个 PyTorch 模型训练的完整流程
  • 能区分输入、输出、数据和模型目标。

概念回顾

  • 【分类与回归】判断邮件是不是垃圾邮件是分类,预测房价是多少是回归。 前面或后面会反复用到它。
  • 【Pipeline】Pipeline 让项目不再散落成一堆临时代码,也能减少训练和预测时步骤不一致的问题。 前面或后面会反复用到它。
  • 【TF-IDF】TF-IDF 会降低到处都出现的常见词权重,提高更能代表文本主题的词。 前面或后面会反复用到它。

常见误区

  • 不要只记定义,要追问它解决了什么问题。
  • 不要只看模型分数,要把数据、特征、接口和复盘一起看。

课后练习

  • 用 3 句话向一个零基础朋友解释「从垃圾短信识别项目里,我复盘了一个中文文本分类系统」。
  • 打开概念库里的「分类与回归」,补一遍它和本文的关系。
  • 读下一课「我怎样把一个中文文本分类项目,从训练脚本一路做到了 API 和前端」前,先写下你认为它会解决的问题。

自检练习与参考答案

1. 这个环节的输入和输出分别是什么?

项目文要先说清数据流。输入通常是原始数据或上一步结果,输出必须能被下一步稳定复用。

2. 如果训练和预测效果对不上,优先排查哪件事?

优先排查预处理是否一致,例如分词、TF-IDF 词表、标签映射、标准化器是否沿用训练阶段的同一套对象。

3. 为什么项目文章不能只看最终分数?

因为真实项目还要看数据质量、错误样本、部署入口、复用方式和失败边界。

下一步建议读「我怎样把一个中文文本分类项目,从训练脚本一路做到了 API 和前端」。

这是我最近整理的一次项目复盘,主题是一个中文垃圾短信识别系统。

这一篇是端到端体验:先看一个中文文本分类项目怎样跑起来。后面会在文本分类阶段逐环节拆开讲,分别吃透编码清洗、分词、向量化、评估、保存和演示页面。

一开始做这个项目,目标其实很直接:不靠固定关键词规则,而是让模型自己去学习短信文本里的语义特征,自动判断一条短信是正常消息,还是营销、诈骗一类的垃圾短信。

现在回头看,这个项目最有价值的地方,不只是“做出了一个模型”,而是让我第一次比较完整地走完了中文文本分类项目从数据处理、模型训练到问题定位的全流程。

第 19 课视频 · 垃圾短信识别项目复盘(约 1 分 46 秒)

项目想解决什么问题

移动通信已经很普及了,但垃圾短信和诈骗短信还是很常见。它们不仅影响日常体验,有些时候还会直接带来财产风险。

所以这个项目想做的是一个端侧轻量化的短信过滤系统,核心目标有四个:

  • 对中文短信做基础清洗、分词和预处理。
  • 把文本转换成可训练的数字特征。
  • 用模型自动完成正常短信和垃圾短信的二分类。
  • 支持批量测试,并输出准确率、精确率这类常见指标。

如果把它压缩成一句话,就是:让短信风控从“硬编码规则”走向“数据驱动识别”。

我是怎么把它串起来的

这个项目的主体流程可以拆成四个部分。

1. 词表与文本数字化

第一步是把原始短信文本转成模型能处理的数据格式。

完整流程大致是:

原始短信文本 -> 清洗与分词 -> 统计词汇并去重 -> 构建词和数字 ID 的映射 -> 把文本转成数字序列

这个阶段的意义很基础,但也最关键。因为后面的神经网络看不懂自然语言,它只能处理数字,所以这一步就是整个训练链路的入口。

2. 数据读取与数据集封装

在预处理之后,我把标签和短信文本拆开,再统计样本比例、最大文本长度,并通过自定义 Dataset 的方式,把数据统一整理成 PyTorch 可直接加载的格式。

这个阶段做的事情包括:

  • 拆分标签与文本内容。
  • 对短信序列进行截断或补零。
  • 统一张量格式,方便后续分批训练。

它解决的不是“模型聪不聪明”的问题,而是“数据能不能顺利稳定地喂给模型”的问题。

3. 构建 RNN 二分类模型

模型部分我使用的是一个比较基础的 RNN 结构,主链路可以概括成:

词嵌入层 -> RNN 循环层 -> 全连接层 -> 二分类输出

这部分负责从数字 ID 中提取时序语义特征,再将最后的特征映射成分类结果。虽然结构不复杂,但已经能完整跑通文本分类任务。

4. 训练、评估与保存模型

训练脚本把前面的模块全部串起来:

  • 加载词表与数据集。
  • 划分训练集和测试集。
  • 初始化损失函数和优化器。
  • 执行多轮训练并打印日志。
  • 评估测试集效果。
  • 保存训练好的模型权重。

到这里为止,一个能训练、能评估、能导出结果的项目基本就闭环了。

这次复盘里最重要的发现

真正让我印象最深的,其实不是模型结构,而是训练流程里暴露出来的几个问题。

测试集发生了信息泄漏

这是这次复盘里最关键的一点。

我发现词表是在划分训练集和测试集之前,用全部短信一起构建的。这样一来,测试集中的词汇信息其实已经提前泄漏给模型了。

更严重的是,数据里还有不少重复短信。随机切分之后,测试集中有一部分文本也同时出现在训练集中。这样得到的测试结果会比真实效果更乐观。

这让我意识到,评估不准比模型不强更危险。因为一个“看起来准确率不错”的模型,可能只是数据切分方式有问题。

模型保存得还不够完整

训练脚本只保存了模型权重,但没有把词表、最大长度、模型参数配置、标签含义一起保存下来。

这意味着模型虽然训练完了,但单独拿出去做推理会不稳定。因为新短信在部署时未必还能用和训练阶段完全一致的方式转成输入。

也就是说,它更像一个“训练实验结果”,还不能算一个真正可交付的推理组件。

一些模块耦合过重

还有一个比较实际的问题是:有的模块在导入时就会直接读取完整数据,甚至打印大量内容。

这种写法在刚开始练项目时很常见,但一旦项目变大,就会让调试和复用都变得很难受。复盘时再看,会明显感觉到代码边界不够干净。

这套方案的局限在哪里

这个项目能跑通,但它并不是一个终点。

目前最明显的局限有三类。

1. 模型结构偏基础

传统 RNN 在长文本上容易出现长距离依赖丢失和梯度问题,对复杂上下文的表达能力也有限。和 LSTM、GRU、Transformer 相比,上限会低很多。

2. 文本预处理还比较粗

目前主要还是基础分词,没有把停用词过滤、特殊符号清洗、噪声字符处理这些步骤做得足够细。

如果数据本身噪声比较多,模型很容易把注意力浪费在无效信息上。

3. 数据集构建方式还可以更严谨

固定长度补零虽然简单,但也会引入很多无效位置。再加上重复样本和切分策略的问题,都会影响模型最终的泛化能力。

如果继续做下去,我会怎么优化

如果给这个项目继续迭代,我会优先做四件事:

  1. 先按文本去重,再重新划分训练、验证和测试集。
  2. 只使用训练集构建词表,避免测试信息泄漏。
  3. 升级模型结构,从基础 RNN 往 GRU、LSTM 甚至 Transformer 方向走。
  4. 把推理所需的词表、最大长度、标签映射和模型配置一并保存,做成真正可部署的推理链路。

这篇复盘对我自己的意义

这次项目复盘让我更清楚地意识到,做机器学习项目不能只盯着“模型搭起来了没有”。

数据怎么处理、切分方式是否合理、评估是否可信、模型能不能真正被部署,这些问题往往比单纯跑出一个准确率更重要。

一个项目的质量,很多时候不是由最亮眼的那一层模型决定的,而是由那些容易被忽略的基础环节决定的。

这也是我想把这篇文章发出来的原因:把一次真实项目里踩过的坑、看见的问题和下一步打算,认认真真留档。

Discussion

留言讨论