第六周是文本分类。课程内容不算难,但作业让我做了七组对照实验——BERT 三种池化方式、加权损失、LLM 零样本、LoRA 微调、全参微调。
跑完之后有一个结果和我预期完全相反:LoRA 赢了全参微调。这篇主要讲这个。
一、为什么 Agent 时代还要学文本分类
这是我这周第一个意外收获。我原以为有了大模型,分类这种「老任务」就没意义了。课上给的答案很有说服力:
在 Agent 系统里,文本分类无处不在——
- 多 Agent 路由:用户输入 → 分类判断意图 → 分发到搜索/代码/数据 Agent
- 中控调度:子任务类型分类,决定调用哪个工具
- 输出安全过滤:对生成内容做合规分类
- Query 路由:判断走 RAG 检索、长期记忆,还是直接回答
那为什么不让大模型自己判断? 四个理由:
| 维度 | 轻量分类模型 | 大模型 |
|---|---|---|
| 延迟 | <20ms | >500ms |
| 成本 | —— | 高 2~3 个数量级 |
| 行为 | 输出固定枚举,不会幻觉 | 可能跑偏 |
| 监控 | 置信度可记录 | 难量化 |
这个视角挺重要的:不是所有事都该交给大模型,能用小模型确定性解决的,就别用大模型概率性解决。
二、课程主线
从 Token 序列到句子向量
BERT 输出的是每个 token 一个向量,但分类需要一个固定维度的句子向量。三条路:
- 取
[CLS]位置的向量 - 对所有 token 做 Mean Pooling
- 对所有 token 做 Max Pooling
这三种我都跑了,结果在下面。
单标签 vs 多标签
单标签 → Softmax(概率和为 1,选最大的)
多标签 → Sigmoid(每个类独立判断,可多选)
这个区别看似简单,但输出层选错了训练会完全学不动。
三、实验:七组对照
数据集是 CLUE / TNEWS 今日头条中文新闻标题,15 分类,训练集 53,360 条。
先看数据:两个必须注意的事实
① 类别极度不均衡
最多类 科技 5,955 条(11.2%)
最少类 证券 257 条(0.5%)
不均衡比 23.2 倍
验证集里「证券」只有 45 条。 这个数字要牢记——后面所有关于证券类指标的讨论都建立在这 45 条上,波动天然很大。
② 文本极短,max_length 有优化空间
字符长度:均值 22.1,P95 = 32,P99 = 39
max_length=64 时截断比例:0.00%
默认的 max_length=64 一条都没截断,实际 48 就够。注意力复杂度是 O(L²),64→48 能省约 44% 的注意力计算量——这是个免费的优化点。
数据本身有噪声
我从数据里随手挑了几条,发现标签本身就不干净——有些标题从人的角度看也分不清该归哪一类。这解释了后面所有模型的准确率天花板:不是模型不够强,是数据的上限就在那。
这也是我这周学到的一条经验:跑模型之前先看数据。如果我一上来就调参,会一直纳闷为什么怎么调都上不去。
四、最反直觉的结果:LoRA 赢了全参微调
用 Qwen2-0.5B-Instruct 做 SFT,对比 LoRA 和全参微调:
| LoRA | 全量微调 | |
|---|---|---|
| 学习率 | 2e-4 | 2e-5(必须比 LoRA 小 10 倍) |
| 可训练参数 | 0.22% | 100% |
| 准确率 | 0.5700(285/500) | 更低 |
| 格式错误率 | 0.4% | 0.4% |
全参微调输了。
这和「全参微调上限更高」的常识相反。我的理解是:
LoRA 只训 0.22% 的参数,这个约束本身就是一种强正则化。 它没有足够的自由度去背训练集,反而泛化得更好。
训练过程也能看出来——全参微调的 train_loss 掉得更快,但验证集表现更差,是典型的过拟合特征。
结论:LoRA 的价值不只是「省显存」。在小数据场景下,它本身就是效果更好的方法。
顺带一个对比:zero-shot 的格式问题
| 准确率 | 格式错误率 | |
|---|---|---|
| zero-shot | 29.0% | 29.0% |
| LoRA SFT | 57.0% | 0.4% |
zero-shot 的格式错误率高达 29%——模型经常不按要求输出类别名,而是加一堆解释。SFT 最直接的收益之一就是让输出格式变得可靠,这一点在做 Agent 时特别重要(工具调用最怕格式不稳定)。
五、三种池化方式的对比
| 池化方式 | 表现 |
|---|---|
[CLS] | 基线 |
| Mean Pooling | 略优 |
| Max Pooling | 相近 |
差距不大。这类「选哪个更好」的问题,跑一遍比查资料快——而且结论只对你自己的数据成立。
另外我还跑了一组加权损失(按类别频率倒数加权)来对抗不均衡。效果是尾部类别的召回上来了,但整体准确率略降——这是个取舍,不是免费午餐。
这周的收获
1. 先看数据,再调模型。
如果不是先跑了数据探查,我不会知道:标签本身有噪声(决定了天花板)、max_length 可以砍掉 25%(免费优化)、验证集某个类只有 45 条(决定了哪些数字不可信)。
2. 「常识」要在自己的数据上验证。
「全参微调效果更好」是常识,但在我这个 5 万条数据的场景下不成立。方法的优劣依赖数据规模和任务特性。
3. 分类任务在 Agent 时代没有过时,只是换了位置。
它从「终端产品」变成了「系统里的一个组件」——路由、过滤、调度。延迟和成本上的两三个数量级差距,决定了这个位置大模型替代不了。
下周是序列标注(NER),要做 BERT + CRF。