去年接手一个客户反馈分类项目,数据量8000条,6个类别。团队里有人建议用BERT微调,有人建议直接用GPT做零样本。最后两种方案都试了,效果差距不大但成本差了10倍。这篇文章把这个选型过程和踩坑经验整理出来。
方案演进之路
文本分类经历了四个阶段:
- 规则方法(关键词+正则):不需要训练数据,实现简单,适合规则明确的场景;缺点是召回率低、维护成本高;
- 传统机器学习(TF-IDF + SVM/朴素贝叶斯):需要标注数据但不多,效果稳定,可解释性好;在数据量小、类别少的场景仍有竞争力;
- 深度学习(BERT/RoBERTa微调):需要中等量级的标注数据,准确率高,泛化能力强;目前是大多数任务的首选;
- LLM零样本/少样本:不需要标注数据或只需几个示例,灵活度最高但成本也最高;适合快速验证或标注成本极高的场景。
BERT微调实战
数据准备阶段,先看一下类别分布。8000条数据里"产品质量"占40%、“物流"占25%、“售后"占20%、“价格"占15%,有明显的类别不平衡,后面要处理。
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import TrainingArguments, Trainer
import torch
model_name = "bert-base-chinese"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=6)
def tokenize(batch):
return tokenizer(batch["text"], padding=True, truncation=True, max_length=128)
# 数据预处理
dataset = dataset.map(tokenize, batched=True)
dataset = dataset.train_test_split(test_size=0.2)
args = TrainingArguments(
output_dir="./results",
num_train_epochs=3,
per_device_train_batch_size=16,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="f1",
)
trainer = Trainer(model=model, args=args, train_dataset=dataset["train"],
eval_dataset=dataset["test"])
trainer.train()
训练3个epoch大概20分钟(RTX 4090),最终测试集F1达到0.92。
类别不平衡处理
这是最容易踩的坑。1000条数据里800条是"产品质量”,200条是其他类别,模型会倾向于预测多数类。几个解决方案:
- 过采样:对少数类用SMOTE或简单复制,增加样本数量;
- 欠采样:对多数类随机丢弃一部分,减少样本数量;
- 加权损失:在loss函数里给少数类更高的权重,让模型更重视它们;
- Focal Loss:自动降低容易分类的样本的loss权重,让模型专注学难分的样本。
实测加权损失最简单有效,Focal Loss效果最好但调参麻烦。
LLM零样本分类
没有标注数据时,用LLM做零样本分类:
from openai import OpenAI
client = OpenAI()
def classify(text, categories):
prompt = "将以下文本分类到最合适的类别中。\n"
prompt += f"类别:{', '.join(categories)}\n"
prompt += f"文本:{text}\n"
prompt += "只回答类别名称,不要解释。"
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0
)
return resp.choices[0].message.content.strip()
零样本分类的准确率通常在70-85%之间,不如微调模型(90%+),但优势是不需要标注数据、可以随时增减类别。
方法对比
| 方法 | 标注需求 | 准确率 | 推理速度 | 适用场景 |
|---|---|---|---|---|
| 关键词规则 | 无 | 60-70% | 极快 | 简单过滤 |
| TF-IDF+SVM | 几百条 | 80-85% | 快 | 数据少 |
| BERT微调 | 几千条 | 90-95% | 中等 | 主流方案 |
| LLM零样本 | 0-10条 | 70-85% | 慢 | 快速验证 |
踩坑记录
标签噪声比你想象的严重。人工标注的一致性通常只有85-90%,同一条文本不同标注员可能给出不同标签。建议每个样本至少两个人标注,用多数投票确定最终标签。
领域迁移效果衰减明显。在通用语料上预训练的模型,迁移到特定领域时效果会打折扣。如果领域差异大,考虑用领域语料继续预训练。
推理优化很重要。BERT模型推理延迟约10-20ms/条,批量推理可以降到2-5ms/条。ONNX导出后还能再快一倍。对延迟敏感的场景一定要做推理优化。
写在最后
文本分类没有银弹。数据少用规则或LLM,数据中等用BERT微调,数据多类别复杂可以考虑多阶段方案。关键是先跑通baseline,再逐步优化。