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/) 转载或引用必须申明原指尖魔法屋来源及源地址!