“明明说好了能打 80 分,为什么接上业务就翻车?”——模型排行榜的分数和实际表现差了十万八千里,原因很简单:评测基准不对。本文教你用工业级工具标准化评测大模型,摆脱「体感评测」的魔咒。
你为什么不能信排行榜?
HuggingFace Open LLM Leaderboard 是开发者选模型的重要参考,但它的「高分」模型到了你的场景可能一塌糊涂,原因有三个:
| 排行榜的坑 | 解释 |
|---|---|
| 数据泄漏 | 训练数据里混进了评测集,模型作弊了自己都不知道 |
| 评测单一 | 只测英文/数学/代码,你的中文客服场景没人测 |
| Prompt 不匹配 | 你用的 System Prompt 和评测时不一样,输出天差地别 |
| 格式敏感 | LLM 对 JSON 格式、Few-shot 示例数量极度敏感,差一点差很多 |
结论:自己的场景必须自己测。
lm-evaluation-harness 是什么?
lm-evaluation-harness 是 EleutherAI 维护的工业级 LLM 评测框架,HuggingFace Open LLM Leaderboard 用的就是它。
核心能力:
┌─────────────────────────────────────────────────────┐
│ lm-evaluation-harness │
│ │
│ ┌─────────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ 200+ 标准任务 │ │ 自定义任务 │ │ 多模型后端 │ │
│ │ MMLU/GSM8K │ │ YAML 配置 │ │ HF/vLLM/API │ │
│ │ HellaSwag │ │ 本地数据集 │ │ OpenAI/本地 │ │
│ └─────────────┘ └───────────┘ └──────────────┘ │
│ │
│ 输出: JSON 分数 + 详细统计 + 置信区间 │
└─────────────────────────────────────────────────────┘
5 步搭建评测流水线
第 1 步:安装
# 创建虚拟环境
python -m venv eval_env
source eval_env/bin/activate # Windows: eval_env\Scripts\activate
# 安装主库
pip install lm-eval
# 如果要测评本地模型,还需要这些
pip install transformers torch accelerate
pip install vllm # 可选,推理加速
验证安装:
lm_eval --help
# 看到帮助信息就是成功了
第 2 步:跑第一个评测——MMLU
MMLU(Massive Multitask Language Understanding)是 57 个学科的综合知识测试,是目前最通用的模型能力基准。
# 用 HuggingFace 模型跑 MMLU(基础版)
lm_eval \
--model hf \
--model_args pretrained=Qwen/Qwen2.5-7B-Instruct,dtype=bfloat16 \
--tasks mmlu \
--num_fewshot 5 \
--batch_size auto \
--output_path ./results \
--log_samples
参数说明:
| 参数 | 含义 |
|---|---|
--model hf |
使用 HuggingFace Transformers 后端 |
--model_args |
模型路径 + 推理参数 |
--tasks mmlu |
评测任务(可多个,逗号分隔) |
--num_fewshot 5 |
每个问题前提供几个示例 |
--batch_size auto |
自动检测最大 batch |
--output_path |
结果保存目录 |
--log_samples |
保存每个样本的详细输出 |
第 3 步:多模型对比评测脚本
"""
多模型对比评测流水线
自动跑 MMLU / GSM8K / HellaSwag 三个基准测试
输出: 汇总 CSV + JSON 详细结果
"""
import subprocess
import json
import csv
from pathlib import Path
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
# ==================== 配置区 ====================
MODELS = [
{
"name": "Qwen2.5-7B-Instruct",
"pretrained": "Qwen/Qwen2.5-7B-Instruct",
"dtype": "bfloat16",
},
{
"name": "DeepSeek-R1-Distill-Qwen-7B",
"pretrained": "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B",
"dtype": "bfloat16",
},
{
"name": "Llama-3.1-8B-Instruct",
"pretrained": "meta-llama/Llama-3.1-8B-Instruct",
"dtype": "bfloat16",
},
]
TASKS = [
{
"name": "mmlu",
"task": "mmlu",
"fewshot": 5,
"metric": "acc,none",
},
{
"name": "gsm8k",
"task": "gsm8k",
"fewshot": 5,
"metric": "exact_match,flexible-extract",
},
{
"name": "hellaswag",
"task": "hellaswag",
"fewshot": 10,
"metric": "acc_norm,none",
},
]
OUTPUT_DIR = Path("./eval_results")
OUTPUT_DIR.mkdir(exist_ok=True)
def run_evaluation(model: dict, task: dict) -> dict:
"""运行单个 模型 × 任务 的评测"""
model_name = model["name"]
task_name = task["name"]
output_file = OUTPUT_DIR / f"{model_name}_{task_name}.json"
cmd = [
"lm_eval",
"--model", "hf",
"--model_args", f"pretrained={model['pretrained']},dtype={model['dtype']}",
"--tasks", task["task"],
"--num_fewshot", str(task["fewshot"]),
"--batch_size", "auto",
"--output_path", str(OUTPUT_DIR),
"--log_samples",
]
print(f"🚀 开始评测: {model_name} × {task_name}")
start = datetime.now()
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
elapsed = (datetime.now() - start).total_seconds()
if result.returncode != 0:
return {
"model": model_name,
"task": task_name,
"score": "ERROR",
"time_sec": elapsed,
"error": result.stderr[-500:],
}
# 解析输出中的分数
score = "N/A"
for line in result.stdout.split("\n"):
if task["metric"].split(",")[0] in line.lower().replace(" ", "_"):
parts = line.strip().split("|")
if len(parts) >= 3:
score = parts[2].strip()
break
print(f"✅ {model_name} × {task_name}: {score} ({elapsed:.0f}s)")
return {
"model": model_name,
"task": task_name,
"score": score,
"time_sec": elapsed,
}
except subprocess.TimeoutExpired:
return {
"model": model_name, "task": task_name,
"score": "TIMEOUT", "time_sec": 3600,
}
def generate_report(results: list[dict]):
"""生成评测汇总报告"""
# 写入 CSV
csv_path = OUTPUT_DIR / "leaderboard.csv"
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["model", "task", "score", "time_sec"])
writer.writeheader()
writer.writerows(results)
# 写入 JSON
json_path = OUTPUT_DIR / "leaderboard.json"
json_path.write_text(json.dumps(results, indent=2, ensure_ascii=False))
# 打印汇总表格
print(f"\n{'='*70}")
print("📊 模型评测汇总")
print(f"{'='*70}")
# 按模型分组
models_data = {}
for r in results:
if r["model"] not in models_data:
models_data[r["model"]] = {}
models_data[r["model"]][r["task"]] = r["score"]
# 表头
headers = ["模型"] + [t["name"] for t in TASKS]
header_line = "| " + " | ".join(headers) + " |"
sep_line = "|" + "|".join(["------"] * len(headers)) + "|"
print(header_line)
print(sep_line)
for model_name, scores in models_data.items():
row = f"| {model_name} "
for task in TASKS:
row += f"| {scores.get(task['name'], 'N/A')} "
row += "|"
print(row)
print(f"\n📁 详细结果: {OUTPUT_DIR}/")
print(f"📄 CSV 汇总: {csv_path}")
if __name__ == "__main__":
print(f"🔬 模型评测流水线启动 @ {datetime.now()}")
print(f" 模型数: {len(MODELS)}, 任务数: {len(TASKS)}")
print(f" 总评测数: {len(MODELS) * len(TASKS)}\n")
# 串行执行(GPU 只能跑一个模型)
results = []
for model in MODELS:
for task in TASKS:
r = run_evaluation(model, task)
results.append(r)
generate_report(results)
第 4 步:自定义评测任务
标准任务不一定适合你的场景。lm-eval 支持用 YAML 定义自定义任务:
# custom_tasks/chinese_customer_service.yaml
task: chinese_customer_service
dataset_path: json
dataset_name: null
dataset_kwargs:
data_files:
train: ./data/cs_train.jsonl
validation: ./data/cs_val.jsonl
# 指标
metric_list:
- metric: exact_match
aggregation: mean
higher_is_better: true
- metric: f1
aggregation: mean
higher_is_better: true
# 生成 Prompt
doc_to_text: |
你是客服助手,请根据用户问题给出专业回复。
用户问题:{{question}}
回复:
doc_to_target: "{{answer}}"
# 输出解析
output_type: generate_until
generation_kwargs:
until:
- "\n用户"
- "\n\n"
max_gen_toks: 256
do_sample: false
temperature: 0.0
准备数据文件 data/cs_val.jsonl:
{"question": "我的订单什么时候发货?", "answer": "您好,订单付款后24小时内发货,您可以在订单详情中查看物流信息。"}
{"question": "可以退款吗?", "answer": "您好,支持7天无理由退款,请在订单页面提交退款申请。"}
{"question": "你们几点下班?", "answer": "您好,我们的客服工作时间是每天9:00-21:00。"}
运行自定义任务:
lm_eval \
--model hf \
--model_args pretrained=Qwen/Qwen2.5-7B-Instruct \
--tasks chinese_customer_service \
--include_path ./custom_tasks \
--output_path ./results
第 5 步:用 vLLM 加速评测
HuggingFace 默认推理太慢了,用 vLLM 可以直接提速 5-10 倍:
# 安装 vLLM
pip install vllm
# 用 vLLM 后端评测
lm_eval \
--model vllm \
--model_args pretrained=Qwen/Qwen2.5-7B-Instruct,dtype=auto,gpu_memory_utilization=0.9,tensor_parallel_size=1 \
--tasks mmlu,gsm8k,hellaswag \
--batch_size auto \
--output_path ./results_vllm
性能对比:
| 后端 | MMLU (14k 题) | GSM8K (1.3k 题) | 显存占用 |
|---|---|---|---|
| Transformers | ~45 分钟 | ~8 分钟 | 16GB |
| vLLM | ~6 分钟 | ~1.5 分钟 | 18GB |
| 加速比 | 7.5x | 5.3x | — |
评测指标解读
核心指标速查
| 指标 | 含义 | 适用场景 |
|---|---|---|
| Accuracy (acc) | 精确匹配的正确率 | 分类、选择题 |
| acc_norm | 归一化准确率(忽略大小写/标点) | HellaSwag 等 |
| Exact Match (EM) | 完全匹配率 | 数学、代码生成 |
| F1 Score | 精确率和召回率的调和平均 | 生成类、QA |
| Perplexity (PPL) | 困惑度(越低越好) | 语言建模能力 |
| ROUGE-L | 最长公共子序列 | 摘要、翻译 |
实战经验:怎么读分数?
MMLU 70%:基础常识不错,能理解各领域概念
MMLU 80%+:接近 GPT-4 水平,知识覆盖面广
GSM8K 80%+:数学推理达标,能处理初中数学
GSM8K 90%+:数学推理强,适合量化、数据分析场景
HellaSwag 85%+:常识推理优秀,不会犯低级逻辑错误
但是这些指标加了 Few-shot 后变化很大:
| 模型 | MMLU (0-shot) | MMLU (5-shot) |
|---|---|---|
| Qwen2.5-7B | 71.2 | 74.8 (+3.6) |
| Llama-3.1-8B | 65.3 | 68.9 (+3.6) |
| DeepSeek-R1-Distill-7B | 70.5 | 71.1 (+0.6) |
💡 DeepSeek-R1 对 Few-shot 不那么敏感,因为它本身推理能力就强。
自动化评测流水线
把上面的脚本接入 CI/CD,每次模型更新自动跑评测:
# .github/workflows/model_eval.yml
name: Model Evaluation
on:
push:
paths:
- "models/**"
- "eval/**"
jobs:
evaluate:
runs-on: [self-hosted, gpu]
steps:
- uses: actions/checkout@v4
- name: Run evaluation
run: |
python eval_pipeline.py
- name: Check score regression
run: |
python -c "
import json
results = json.load(open('eval_results/leaderboard.json'))
for r in results:
score = float(r['score'].replace('%',''))
if score < 65:
raise Exception(f'{r[\"model\"]} {r[\"task\"]} score {score}% below threshold 65%')
"
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: eval-results
path: eval_results/
👤 作者简介
一枚在大中原腹地(河南)卖公有云的从业者,主营腾讯云/阿里云/火山云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「公有云cloud」,围观AI前沿动态~
博客:y1cloud.cn
