AI零样本:预训练到推理折腾手记
如果只能用一句话说AI零样本学习:先把失败复现出来。
之前的任务是给电商评论做情感分析,我大概需要准备 1000 条标注数据、训练两小时、然后发现效果一般。
为什么需要零样本
之前的任务是给电商评论做情感分析,我大概需要准备 1000 条标注数据、训练两小时、然后发现效果一般。换一个品类数据,又要重新收集标注、重新训练。这就是传统监督学习的死循环。
后来试了零样本学习,直接用预训练模型:
from transformers import pipeline
# 直接用预训练的情感分析模型
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
result = classifier(
"这个手机续航差劲,一天要充三次电",
candidate_labels=["正面", "负面", "中性"]
)
print(result['labels'][0]) # 输出:负面
print(result['scores'][0]) # 输出:0.894
没有收集数据、没有标注、没有训练,模型直接给出了正确答案。但这背后有一个前提:这个预训练模型在海量文本上见过类似的语境。
零样本学习的本质
零样本学习不是"不学习",而是"学得早"。模型在预训练阶段已经学好了语言理解能力、世界知识和推理模式,你只需要用自然语言告诉它要做什么任务。
但坑就在这里。很多人以为写个提示就行,结果发现模型完全理解错了你的意图。
我第一次尝试做意图分类时,写的是:
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
result = classifier(
"查一下明天的天气",
candidate_labels=["查询", "预定", "退款", "投诉"]
)
# 期望结果:查询
# 实际结果:预定(置信度 0.67)
模型把"查"理解成了"预定",因为训练数据里这种短语出现频率有问题。后来改了标签表述:
result = classifier(
"查一下明天的天气",
candidate_labels=["查询信息", "预定服务", "申请退款", "客户投诉"]
)
# 结果:查询信息(置信度 0.82)
零样本学习中,标签本身就是提示的一部分。标签怎么写、顺序怎么排,都会影响结果。
预训练模型的选择
预训练模型不是越大越好。我试过用 BERT-base 做零样本分类,效果一直很差,后来才知道 BERT 系列不适合零样本任务。
主流的选择是:
- BART-large-MNLI:适合零样本分类,推理速度中等
- T5-3B:效果更好但推理慢,适合对延迟不敏感的场景
- GPT-J-6B:多任务能力强,但需要更多显存
实际项目中,我用 BART-large-MNLI 最多:
from transformers import pipeline
# 根据硬件选择不同的量化版本
import torch
if torch.cuda.is_available():
classifier = pipeline(
"zero-shot-classification",
model="facebook/bart-large-mnli",
device=0 # 使用 GPU
)
else:
classifier = pipeline(
"zero-shot-classification",
model="valhalla/distilbart-mnli-12-1" # CPU 使用小模型
)
推理时间对比:
- BART-large-MNLI(GPU):约 50ms / 样本
- T5-3B(GPU):约 180ms / 样本
- DistilBART-MNLI(CPU):约 300ms / 样本
提示工程的实践
零样本学习中,提示设计直接影响效果。我总结了几条经验:
标签要具体
# 不好的标签
candidate_labels = ["好", "坏", "一般"]
# 好的标签
candidate_labels = ["服务态度好", "服务态度差", "服务态度一般"]
标签顺序有影响
# 模型对标签顺序敏感,这个顺序给"正面"更多机会
result = classifier(
"这个产品用起来还行,不算特别好也不算特别差",
candidate_labels=["正面", "负面", "中性"]
)
# 这个顺序给"中性"更多机会
result = classifier(
"这个产品用起来还行,不算特别好也不算特别差",
candidate_labels=["中性", "正面", "负面"]
)
使用假设模板
# BART-MNLI 支持自定义假设模板
result = classifier(
"我想取消订单",
candidate_labels=["取消订单", "查询订单", "修改订单"],
hypothesis_template="这句话的意图是 {}。"
)
踩过的坑
坑一:多语言问题
做中文任务时,直接用英文预训练模型效果很差:
# 英文模型处理中文
result = classifier(
"这个产品的质量不错",
candidate_labels=["质量好", "质量差", "质量一般"]
)
# 效果:置信度都很低,最高才 0.45
后来换用了中文预训练模型:
classifier = pipeline(
"zero-shot-classification",
model="uer/roberta-base-finetuned-chinanews-chinese-news-classifier"
)
result = classifier(
"这个产品的质量不错",
candidate_labels=["质量好", "质量差", "质量一般"]
)
# 效果:质量好(置信度 0.78)
坑二:标签长度影响
标签太长或太短都会影响效果:
# 标签太长
candidate_labels = ["用户表达了对产品价格的不满情绪",
"用户表达了对产品价格的基本满意",
"用户对产品价格没有明显情绪"]
# 标签太短
candidate_labels = ["价格不满意", "价格满意", "价格中立"]
# 标签长度适中
candidate_labels = ["价格偏高", "价格合理", "价格敏感度低"]
坑三:领域适配
在医疗领域做零样本分类时,通用模型完全不行:
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
result = classifier(
"患者出现头晕、恶心、呕吐症状",
candidate_labels=["神经系统症状", "消化系统症状", "正常反应"]
)
# 通用模型结果:正常反应(置信度 0.51)
换用医疗领域预训练模型:
classifier = pipeline(
"zero-shot-classification",
model="microsoft/BiomedNLP-PubMedBERT-base-uncased-abstract-fulltext"
)
# 医疗模型结果:神经系统症状(置信度 0.74)
推理策略优化
置信度阈值设定
零样本模型给出的置信度普遍比传统监督学习低,所以阈值要调整:
# 传统监督学习的阈值通常在 0.7-0.8
# 零样本学习的阈值要调低到 0.5-0.6
def classify_with_threshold(text, labels, threshold=0.55):
result = classifier(text, labels)
if result['scores'][0] > threshold:
return result['labels'][0]
else:
return "不确定"
多标签分类
有些样本属于多个类别:
# 启用多标签分类
result = classifier(
"这个产品价格还可以,但是物流太慢了",
candidate_labels=["价格相关", "物流相关", "服务相关"],
multi_label=True
)
# 结果:[("物流相关", 0.89), ("价格相关", 0.76), ("服务相关", 0.21)]
批量推理优化
处理大量数据时,批量推理能显著提升性能:
texts = ["产品不错", "物流太慢", "价格可以"] * 100
# 逐个推理(慢)
results = [classifier(text, labels) for text in texts]
# 批量推理(快)
results = classifier(texts, labels, batch_size=16)
性能对比:
- 逐个推理:3.2 秒 / 100 样本
- 批量推理(batch_size=16):0.8 秒 / 100 样本
与传统方法的权衡
零样本学习不是万能的,它在以下场景优势明显:
- 数据稀缺:标注数据太少,无法训练监督模型
- 快速迭代:需要频繁调整分类类别
- 多任务处理:一个模型处理多个不同任务
但它的劣势也很明显:
- 准确率上限低:同等算力下,监督学习通常效果更好
- 推理成本高:大模型推理比小模型慢很多
- 稳定性差:对提示敏感,容易出现意外结果
实际项目中,我的策略是:先用零样本模型做初步验证,如果效果好再考虑用监督学习进一步优化。
结语
零样本学习像是技术界的"方便面"——能快速解决温饱问题,但长期吃肯定不行。它是个好工具,但不能把它当成万能钥匙。
我现在做新项目,通常先用零样本模型快速验证可行性,等确定方向后再收集数据训练专门模型。这种组合既能快速启动,又能保证最终效果。
技术就是这样,没有银弹,只有合适的权衡。
版权声明: 本文首发于 指尖魔法屋-AI零样本:预训练到推理折腾手记(https://blog.thinkmoon.cn/post/226-ai-zero-shot-practice-pretrain-inference/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。