大模型评估实战:生成任务 VS 分类任务——指标、代码与踩坑全解析
·
一、为什么“大模型”评估比小模型更棘手
- 生成空间爆炸:分类输出 3 个标签,生成输出 ∞ 文本,穷举参考答案不可能。
- 语义等价形式多:“北京是中国的首都”≈“中国首都是北京”,字符串匹配直接 0 分。
- 人工成本高:一篇 500 字摘要请 3 人双盲打分,成本 ≈ ¥15/条,1000 条就要 1.5 万。
- 任务边界模糊:同样一段用户 query,既能当“分类”也能当“生成”——先定任务,再选指标是第一步。
二、任务类型速定位(1 分钟自检)
| 场景举例 | 输出形式 | 任务类型 | 本文章节 |
|---|---|---|---|
| 垃圾评论识别 | 离散标签 {spam, normal} | 分类 | 三 |
| 情感极性 | 离散标签 {负, 中, 正} | 分类 | 三 |
| 摘要/对话/翻译 | 自由文本(≥0 字) | 生成 | 四 |
| 抽取式问答 | 原文片段 | 生成(可归为抽取) | 四 |
| 多标签分类 | 多标签同时成立 | 分类(特殊) | 三.5 |
三、分类任务:指标、代码、可视化一条龙
3.1 必备指标与解释
| 指标 | 公式 | 何时看 |
|---|---|---|
| Accuracy | (TP+TN)/N | 均衡数据 |
| Macro-F1 | ΣF1_i / k | 不均衡数据 |
| Weighted-F1 | 按支持度加权 | 不均衡+关心整体 |
| ROC-AUC | ∫TPR dFPR | 阈值未定时 |
| PR-AUC | ∫Precision dRecall | 正类极少 |
| Cohen’s κ | (Po-Pe)/(1-Pe) | 多人标注一致性 |
3.2 代码模板(单文件可跑)
# evaluate_cls.py
import evaluate, pandas as pd, seaborn as sns, matplotlib.pyplot as plt
from sklearn.metrics import classification_report, confusion_matrix, cohen_kappa_score
import numpy as np
labels = ["负", "中", "正"]
y_true = [0,1,2,0,1,2,0,0,1,1]
y_pred = [0,2,2,0,1,1,0,1,1,0]
# 1. 综合报告
print(classification_report(y_true, y_pred, target_names=labels, digits=3))
# 2. 混淆矩阵
plt.figure(figsize=(3,3))
sns.heatmap(confusion_matrix(y_true, y_pred),
annot=True, fmt="d", cmap="Blues",
xticklabels=labels, yticklabels=labels)
plt.ylabel("True"); plt.xlabel("Pred")
plt.title("Confusion Matrix")
plt.savefig("cm.png", dpi=200)
# 3. Cohen’s κ(多人标注场景)
κ = cohen_kappa_score(y_true, y_pred)
print("Cohen κ =", round(κ,3))
3.3 不均衡数据实战
数据:情感 3 类,比例 负:中:正 = 78% : 15% : 7%
结果:Accuracy=78% 看似高,Macro-F1=0.42 暴露模型只会判“负”。
修复:
- 采样:SMOTE 过正类 + 随机欠负类 → Macro-F1 提升至 0.71
- 代价敏感:class_weight=“balanced” → 0.69
- 阈值移动:PR-AUC 峰值处调阈值 → 0.73
结论:不均衡任务不看 Accuracy,只看 Macro-F1 / PR-AUC。
3.4 大模型“零样本”分类特别技巧
from transformers import pipeline
clf = pipeline("text-classification", model="bert-base-chinese")
# 标签在 ChineseBERT 之外,用“标签描述”映射
label2desc = {0:"负面情感",1:"中性",2:"正面情感"}
# 无标注也能看置信度分布
pred = clf("这破系统又崩了")[0]
print(pred) # {'label': 'LABEL_0', 'score': 0.92}
注意:零样本无 gold label,只能
- 统计置信度分布 → 看漂移
- 人工抽样 100 条算 Precision@K
四、生成任务:脚本指标 → 语义指标 → LLM 裁判
4.1 脚本指标(快速离线)
| 指标 | 适用 | 一行代码 |
|---|---|---|
| BLEU-1/2/3/4 | 机器翻译、摘要 | evaluate.load("bleu").compute(predictions=[pred], references=[[ref]]) |
| ROUGE-1/2/L | 摘要 | evaluate.load("rouge").compute(predictions=[pred], references=[ref]) |
| chrF | 多语言 | evaluate.load("chrf").compute(predictions=[pred], references=[ref]) |
4.2 语义指标(字符串不同但语义同)
| 指标 | 原理 | 代码 |
|---|---|---|
| BERTScore | 余弦相似度(上下文 embedding) | evaluate.load("bertscore").compute(predictions=[pred], references=[ref], lang="zh")["f1"][0] |
| BLEURT | 谷歌轻量回归模型 | evaluate.load("bleurt").compute(predictions=[pred], references=[ref])["scores"][0] |
4.3 LLM 裁判(无参考也 OK)
4.3.1 pairwise 胜负
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
llm = ChatOpenAI(model="gpt-4", temperature=0)
template = """你是一名公正评委,比较哪条回答更好。
问题:{q}
回答A:{a1}
回答B:{a2}
请只输出:A 胜 | B 胜 | 平
"""
prompt = ChatPromptTemplate.from_template(template)
chain = prompt | llm
winner = chain.invoke({"q": q, "a1": a1, "a2": a2}).content
4.3.2 多维度打分(1-5)
rubric = """对回答按 1-5 打分并 JSON 返回:
{"相关性":int, "准确性":int, "流畅度":int}
问题:{q} 回答:{a}"""
score_json = llm.invoke(rubric.format(q=q, a=a)).content
4.3.3 幻觉检测(RAG 必备)
from deepeval.metrics import HallucinationMetric
metric = HallucinationMetric(model="gpt-4")
metric.measure(
output="柏林是法国首都。",
context=["柏林是德国首都。"]
)
print(metric.score) # 0.0 → 严重幻觉
五、端到端实战:摘要模型对比实验
模型:
A) 原生 ChatGPT-3.5
B) 自己 LoRA 微调 7B(Chinese-Alpaca-7b + 6k 摘要数据)
数据:50 篇中文财经新闻(平均 800 字)
指标:ROUGE-L、BERTScore、LLM 裁判(相关性+准确性+流畅度)
成本:脚本指标 0 元,LLM 裁判 ¥0.08/条 × 50 = ¥4
5.1 批量生成
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch, json
model_path = "./chinese-alpaca-7b-lora"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.float16, device_map="auto")
def generate(text):
prompt = f"文章:{text}\n摘要:"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=120, do_sample=False)
return tokenizer.decode(out[0][inputs.input_ids.shape[-1]:], skip_special_tokens=True)
with open("news.json") as f:
data = json.load(f)
for d in data:
d["summary_7b"] = generate(d["article"])
d["summary_gpt"] = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": f"用 80 字概括下文:{d['article']}"}]
).choices[0].message.content
5.2 脚本指标
import evaluate
rouge = evaluate.load("rouge")
bertscore = evaluate.load("bertscore")
scores = {"7b": {"rougeL": [], "bertscore": []}, "gpt": {"rougeL": [], "bertscore": []}}
for d in data:
ref = d["summary_gt"] # 人工参考
scores["7b"]["rougeL"].append(rouge.compute(predictions=[d["summary_7b"]], references=[ref])["rougeL"])
scores["gpt"]["rougeL"].append(rouge.compute(predictions=[d["summary_gpt"]], references=[ref])["rougeL"])
bert_7b = bertscore.compute(predictions=[d["summary_7b"]], references=[ref], lang="zh")["f1"][0]
scores["7b"]["bertscore"].append(bert_7b)
bert_gpt = bertscore.compute(predictions=[d["summary_gpt"]], references=[ref], lang="zh")["f1"][0]
scores["gpt"]["bertscore"].append(bert_gpt)
print("平均 ROUGE-L | 7b:", np.mean(scores["7b"]["rougeL"]), "gpt:", np.mean(scores["gpt"]["rougeL"]))
print("平均 BERTScore | 7b:", np.mean(scores["7b"]["bertscore"]), "gpt:", np.mean(scores["gpt"]["bertscore"]))
结果(示例):
平均 ROUGE-L | 7b: 0.412 gpt: 0.489
平均 BERTScore | 7b: 0.834 gpt: 0.865
5.3 LLM 裁判
from langchain.schema import SystemMessage, HumanMessage
judge = openai.ChatCompletion.create(
model="gpt-4",
messages=[
{"role": "system", "content": "你是摘要评委,按 1-5 打分并 JSON 返回:{\"相关性\":int, \"准确性\":int, \"流畅度\":int}"},
{"role": "user", "content": f"问题:{d['article'][:200]}...\n回答A:{d['summary_7b']}\n回答B:{d['summary_gpt']}"}
]
)
统计 50 条平均分:
| 维度 | 7B LoRA | GPT-3.5 | 差值 |
|---|---|---|---|
| 相关性 | 4.1 | 4.5 | +0.4 |
| 准确性 | 4.0 | 4.6 | +0.6 |
| 流畅度 | 3.9 | 4.7 | +0.8 |
结论:
- 脚本指标:GPT 全面领先 ∼6-8%。
- LLM 裁判:流畅度差距最大,7B 模型存在“重复句、指代混乱”问题 → 后续加长度惩罚 + 多样性采样即可修复。
六、评估框架全景图(收藏级)
| 框架 | 最强场景 | 指标数 | 是否支持 LLM 裁判 | 一行安装 |
|---|---|---|---|---|
| HF Evaluate | 经典脚本指标 | 300+ | ❌ | pip install evaluate |
| LangSmith | 在线可观测+人工反馈 | ∞ | ✅ | 环境变量开关 |
| DeepEval | 生成任务专用 | 20+ | ✅ | pip install deepeval |
| RAGAS | RAG 幻觉检测 | 8 | ✅ | pip install ragas |
| OpenCompass | 大模型综合基准 | 50+ | ✅ | pip install opencompass |
七、生产级 checklist:从离线到在线
- 离线
- 分类:Macro-F1 + PR-AUC + Cohen κ ≥ 0.75
- 生成:ROUGE-L + BERTScore + LLM 裁判 ≥ 4.0/5
- 灰度
- A/B 测试:用户留存、投诉率、平均轮数
- 在线
- 实时看板:Accuracy@5min、Calibration-ECE、人工举报率
- 自动回滚:指标下跌 > 2σ 或举报率 > 1.5% 立即切流
八、总结
分类任务先看“数据均衡”再选指标:
- 均衡 → Accuracy
- 不均衡 → Macro-F1 + PR-AUC + Cohen κ
生成任务三层金字塔:
- 脚本指标(BLEU/ROUGE)快速迭代
- 语义指标(BERTScore/BLEURT)减少漏杀
- LLM 裁判(GPT-4 打分/幻觉检测)贴近人类
最后一条铁律:
“没有评估的模型上线,等同于蒙眼在高速倒车。”
AI大模型学习代码仓库
更多推荐
所有评论(0)