Files
my-vault/01_Projects/Personal-Tech/LLM_Evaluation/04-Reference/evaluation-guidebook/04-Troubleshooting.md
T

403 lines
27 KiB
Markdown
Raw Normal View History

---
type: reference
tags:
- llm-evaluation
- evaluation-guidebook
- troubleshooting
status: active
created: 2026-08-21
source: https://github.com/huggingface/evaluation-guidebook
---
# Troubleshooting(排错)
> 本页提炼自 [HuggingFace LLM Evaluation Guidebook](https://github.com/huggingface/evaluation-guidebook) 的 **Troubleshooting** 章节(共三小节:推理排错、LaTeX 数学解析、可复现性排错)。这是整本指南中最实操的部分——当评测跑不起来、跑得慢、分数对不上时,先查这里。文内 ⭐ 标记为作者特别推荐的资源。
## 本节速览(TL;DR
| 症状 / 诉求 | 第一反应 | 详见 |
|---|---|---|
| 评测跑得太慢 | 增大 batch size → 数据并行 → 换推理库 → 降精度 | §1.1 |
| 模型太大放不进 GPU | 量化 → 模型并行(pipeline / tensor)→ CPU offloading | §1.2 |
| 装得下却 OOM | 怀疑 context size,用 dummy 数据测试 | §1.3 |
| 数学评测分数异常偏低 | 检查 LaTeX 解析器(sympy 自洽只有 0.94 | §2 |
| 复现不出论文分数 | 逐项对齐:代码库 / seed / 指标 / normalization / prompt / 生成参数 / 加载方式 | §3 |
> 贯穿全章的三条核心心法:**① 评测工具链本身也是被测对象**——解析器、normalization、指标实现都会系统性改变分数;**② 评测不只是"模型回答问题",而是"在特定环境里跑特定管道"**——环境细节决定一切;**③ 想对比两个分数,先确认它们是不是用同一套管道算出来的。**
## 1 推理排错(Troubleshooting Inference
评测中跑模型推理会遇到三类典型问题:**慢**、**大**(内存装不下)、以及**装得下却 OOM**。本节逐一给出排查思路。先给一张按症状定位的速查表:
| 症状 | 直接原因 | 对策 | 代价 / 注意 |
|---|---|---|---|
| 推理慢 | batch size 太小 | 增大 batch size | 破坏绝对可复现性 |
| 推理慢 | 只用了单张 GPU | 数据并行(多 GPU) | 需同节点,避免节点间瓶颈 |
| 推理慢 | 推理库实现不够优化 | 换更快推理库 / 参考优化清单 | 需要实测 |
| 推理慢 | 精度过高 | 降到 `bfloat16`/`float16`,或 8bit/4bit 量化 | 精度损失,部分量化库本身偏慢 |
| 模型太大 | 精度过高 | 量化 | 中规模模型建议停在 `float16`/8bit |
| 模型太大 | 单卡放不下 | 模型并行 / CPU offloading | 并行较难编码;offloading 显著更慢 |
| 装得下却 OOM | context size 过大 | dummy 测试 + 降 batch + 逆序呈现样本 | 让失败尽早暴露 |
### 1.1 模型太慢怎么办
#### ① 修改 batch size
- 如果你追求**绝对可复现**(给定特定硬件与特定评测 prompt),通常只能用 batch size = 1。
- 但只要内存放得下,**调大 batch size 几乎必然显著加速**评测——这是最简单的提速手段。
#### ② 数据并行(data parallelism
- 思路:把模型复制到多张 GPU 上(而不是只加载到一张卡),把数据切成子集分给每张卡的副本,各自计算后再**聚合结果**。
- 效果:多条数据流同时被处理,总执行时间约**除以 GPU 数量**。
- 注意:尽量让所有 GPU 在**同一个节点(node)**上,避免节点间通信成为瓶颈(inter-node bottleneck)。
#### ③ 更换推理代码
- 不是所有推理库速度都一样,有些实现优化得更好,需要针对你的用例**实测对比**。
- 如果使用 PyTorch,作者推荐参考 [PyTorch 模型推理性能优化清单](https://pytorch.org/serve/performance_checklist.html)。
#### ④ 降低精度(precision
- `float32` 每个数占 32 bit,计算精确但**内存与算力开销都大**。
- 降到 **`bfloat16` / `float16`(一半精度)**,速度约翻倍,精度损失几乎可以忽略。
- 想进一步提速可量化到 **8 bit 或 4 bit**(例如用 `gptq``bitsandbytes`),n-bit 矩阵计算更快、模型占内存更少。
- ⚠️ 某些量化库本身可能偏慢,需要针对你的场景实测。
> 贯穿 1.1 的原则:以上手段**不是二选一的单选题,而是可以叠加的组合拳**(如"大 batch + 数据并行 + `bfloat16`"),且**提速效果必须在你的硬件与任务上实测确认**——作者多次强调"test things out for your use cases"。
### 1.2 模型太大(GPU 装不下)怎么办
#### 估算内存需求
用如下公式估算加载模型所需的**最小理论内存**:
```
<memory (in GB)> = <number of parameters (in G)> × <precision factor>
```
原理:1 Byte = 8 bit,总内存 ≈ 参数量 × 每个参数占用的 Byte 数。precision factor 取值:
| 精度 | precision factor |
|---|---|
| `float32` | 4 |
| `float16` / `bfloat16` | 2 |
| 8 bit 量化 | 1 |
| 4 bit 量化 | 0.5 |
更稳妥的推荐公式(留出推理时的 batch 等额外开销):
```
<memory (in GB)> = <number of parameters (in G)> × (<precision factor> × 110%)
```
> 示例估算(用推荐公式 `参数(G) × factor × 110%`,取 7B 与 70B 两个常见规模):
| 模型规模 | `float32` | `float16`/`bfloat16` | 8 bit | 4 bit |
|---|---|---|---|---|
| 7B | 7×4×1.1 ≈ **30.8 GB** | 7×2×1.1 ≈ **15.4 GB** | 7×1×1.1 ≈ **7.7 GB** | 7×0.5×1.1 ≈ **3.9 GB** |
| 70B | 70×4×1.1 ≈ **308 GB** | 70×2×1.1 ≈ **154 GB** | 70×1×1.1 ≈ **77 GB** | 70×0.5×1.1 ≈ **38.5 GB** |
> 由此可快速判断硬件选型:单张 80GB 的 GPU 要跑 70B 模型,`float16`154GB)不行,需要 8bit77GB,一张卡勉强)或 4bit 量化 / 模型并行;而 7B 模型在 `float16` 下单张 16–24GB 的消费级卡即可胜任。
#### ① 量化(quantization
- 最直接的手段:调小上面的 precision factor。从 `float32`**4 bit**,内存需求直接**缩小 8 倍**。
- ⚠️ 精度过低会损害评测结果。对**中规模模型**建议保守停在 `float16` 或 8 bit。
- 经验观察:量化对**超大模型**的性能影响反而较小(可能因为存在信息冗余)。
#### ② 模型并行(model parallelism
把模型切成小块,分别加载/运行在不同的 GPU 上;因为从不一次性加载完整模型,所以省内存,但**可能更慢**。两种主要类型:
| 类型 | 切分粒度 | 机制 | 代价 / 特点 |
|---|---|---|---|
| **Pipeline parallelism**(流水线并行) | 整层(layer)级别 | 按层分派到不同 GPU,层 1 输出是层 2 输入 | GPU 会空转等待,产生所谓 **"bubble"**;把输入拆成更小的 batch 可缓解 bubble;需要 GPU 间传输数据 |
| **Tensor parallelism**(张量并行) | 矩阵计算级别 | 把矩阵按行/列切开,各 GPU 算完再聚合 | 只要 GPU 都在同一节点就**极其高效**(避免节点间网络瓶颈),但**难以编码** |
- Pipeline parallelism 正被原生加入 PyTorch 的 [`PiPPy`](https://github.com/pytorch/PiPPy) 库,也是 `accelerate` 底层使用的并行方式。
- Tensor parallelism 在 `vllm` 库中有很好的实现,作者形容其带来 **"insane speedups"(惊人的加速)**。
- 各类并行(含用于提速的 data parallelism)的最佳参考文档:[Transformers 官方并行文档](https://huggingface.co/docs/transformers/v4.15.0/en/parallelism)。
#### ③ CPU offloading
- 把部分计算和模型参数挪到 CPU,以降低 GPU 显存占用。
- ⚠️ **比上面任何方法都慢得多**——因为要持续在设备之间搬运数据。
- 典型实现:DeepSpeed 的 [ZeRO-Offload](https://arxiv.org/abs/2101.06840)(在 ZeRO-2 优化之上):优化阶段的梯度、optimizer states、fp32 参数计算放 CPUGPU 上保留 fp16 参数与前向/反向传播,以"用 CPU 内存 + GPU 算力、最小化通信"为设计目标。
### 1.3 模型装得下,但还是 OOM
大概率是 **context size(上下文长度)** 的问题——显存峰值往往由"batch size × 序列长度"决定,模型权重本身反而放得下。作者建议:
1. **先用虚拟推理数据(dummy inference data)测试**模型是否真的放得下;虚拟数据要使用**足够大、能代表你任务**的 context size。
2. **降低 batch size**;如果你开了 auto-batch size search(自动搜索 batch size),考虑关掉——它可能导致意外的 OOM。
3. 一般性原则:**让样本按 context size 逆序(从大到小)呈现**给模型——这样如果 context 太大,会**一开始就立刻失败**,而不是跑了几小时后才崩。
## 2 数学能力评测中的 LaTeX 解析问题(MATH & sympy
> 📌 **新版(2025 Space)更新**:新版指南把 LaTeX 解析问题并入「Designing your automatic evaluation」的 *Normalization* 小节,并推荐使用专门的数学解析库 **Math-Verify**(替代 sympy 手工解析/字符串比较,见 [[08-2025-Edition]] §5)。下方记录的是旧版(sympy)的探索过程与教训,仍有参考价值。
### 2.1 问题背景
- 解析 LaTeX **非常困难**。当评测任务期望模型输出 LaTeX 时(典型如 [MATH benchmark](https://huggingface.co/datasets/lighteval/MATH),它用 LaTeX 表示数学计算与符号),问题就来了。
- 评测这类任务本应只是"解析并比较 ground truth 与模型输出",但**实际上不存在"正确"的 LaTeX 解析方式**sympy 文档自己也这么说)。
### 2.2 sympy 的局限:0.94 的自洽准确率
- `lm-evaluation-harness` 使用 **`sympy`**Python 符号数学库)解析 LaTeX 并比较表达式。
- 残酷的事实:**即使用 ground truth 去解析 ground truth 本身**(自己和自己比),`sympy` 也只有约 **0.94 的准确率**
- 原因:`sympy` 无法解析一部分**本身正确**的 LaTeX 表达式。
- 这个"用 ground truth 自比"的测试本质是一个**解析器健全性检查(round-trip test)**:它排除了模型因素,单独测量解析管道的上限。0.94 意味着即使模型 100% 输出正确答案,评测系统也会因解析失败丢掉约 6% 的分数——解析器成了评测误差的主要来源之一。
### 2.3 典型报错示例
以下三个示例中,`sympy` 都因语法问题拒绝了正确的 LaTeX:
**例 1:半开区间 `[0,1)`interval**
```
couldn't parse one of [0,1) or [0,1), I expected one of these: ']'
[0,1)
~~^
```
**例 2:并集 `\cup` 与无穷 `\infty`(此处写作 `\iny`**
```
couldn't parse one of (-\iny,-5]\cup[5,\iny) or (-\iny,-5]\cup[5,\iny), I expected something else here
(-\iny,-5]\cup[5,\iny)
~~~~~~^
```
**例 3:分数中的空分组 `\frac{1}{{}2x}`**
```
couldn't parse one of -\frac{1}{{}2x} or -\frac{1}{{}2x}, I don't understand this
-\frac{1}{{}2x}
~~~~~~~~~~~^
```
> 小结:把上述失败归纳成三类典型盲区——
| 失败类别 | 报错示例 | 原因 |
|---|---|---|
| 区间表示(interval | `[0,1)` | 半开区间 `[`/`)` 混用,sympy 期待 `]` 之类的闭合符号 |
| 集合运算与特殊符号 | `(-\iny,-5]\cup[5,\iny)` | `\cup`(并集)等集合运算符、`\infty`(示例中写作 `\iny`)不被支持 |
| 多余分组 | `-\frac{1}{{}2x}` | 分子/分母里的空分组 `{}` 导致语法不识别 |
三类都是**合法、正确的 LaTeX**,却会被解析器拒绝——这正是"解析器不是标准答案"的实证。
### 2.4 怎么绕过
两条路:
1. **重写 LaTeX grammar**:向 [`sympy` 的 LaTeX 语法文件 `latex.lark`](https://github.com/sympy/sympy/blob/master/sympy/parsing/latex/lark/grammar/latex.lark) 添加所需特性(为解析器增加新规则)。
2. **在代码里加手动检查**:为模型输出增加字符串比较等后处理兜底。
作者的选择:在几乎掉进"重写 grammar"这个深坑之后,**决定只给代码加上字符串比较(string comparison)检查**就足够了——即对 `lm-evaluation-harness` 打一个修复补丁(见原文附图中的 LM Eval Harness fix)。
> 为什么选"字符串比较"而不是"重写 grammar"?重写 parser grammar 是个无底洞:LaTeX 语法变体无穷无尽(区间、集合、特殊符号、各种分组写法),每补一个规则都可能引入新冲突,维护成本高。而字符串比较实现简单、可立刻生效——解析失败时退而求其次做规范化后的字符串比对,足以覆盖评测场景中大多数"模型其实答对了"的情况。**对评测工程而言,够用、可维护,比理论上完美更重要。**
### 2.5 修复效果:MATH 上旧/新解析器对比
修复后对 MATH benchmark 前 25 个模型的分数(原始解析器 vs 修复后解析器)对比如下(分数列为 0–100;"提升"为两列差值):
| 模型 | 原始 | 修复后 | 提升 | 排名变化 |
|---|---|---|---|---|
| rombodawg/Rombos-LLM-V2.5-Qwen-72b | 47.58 | 50.68 | +3.10 | 1 → 1 |
| MaziyarPanahi/calme-2.2-qwen2-72b | 41.16 | 43.43 | +2.27 | 2 → 2 |
| arcee-ai/Arcee-Nova | 40.48 | 42.90 | +2.42 | 3 → 3 |
| fblgit/TheBeagle-v2beta-32B-MGS | 39.43 | 42.52 | +3.09 | 4 → 4 |
| rombodawg/Rombos-LLM-V2.5-Qwen-32b | 39.12 | 41.99 | +2.87 | 5 → 5 |
| dnhkng/RYS-XLarge | 38.97 | 41.24 | +2.27 | 6 → 6 |
| dfurman/CalmeRys-78B-Orpo-v0.1 | 37.92 | 40.71 | +2.79 | 8 → 7 |
| MaziyarPanahi/calme-2.2-rys-78b | 37.92 | 39.95 | +2.03 | 8 → 9 |
| MaziyarPanahi/calme-2.4-rys-78b | 37.69 | 40.41 | +2.72 | 9 → 8 |
| MaziyarPanahi/calme-2.3-rys-78b | 36.56 | 38.97 | +2.41 | 10 → 10 |
| MaziyarPanahi/calme-2.1-rys-78b | 36.40 | 38.90 | +2.50 | 11 → 11 |
| Qwen/Qwen2.5-72B | 36.10 | 38.67 | +2.57 | 12 → 12 |
| MaziyarPanahi/calme-2.1-qwen2-72b | 36.03 | 38.07 | +2.04 | 13 → 15 |
| Qwen/Qwen2-Math-72B-Instruct | 35.95 | 38.14 | +2.19 | 14 → 14 |
| dfurman/Qwen2-72B-Orpo-v0.1 | 35.42 | 38.14 | +2.72 | 15 → 13 |
| abacusai/Smaug-Qwen2-72B-Instruct | 35.35 | 37.46 | +2.11 | 16 → 19 |
| anthracite-org/magnum-v1-72b | 35.27 | 37.69 | +2.42 | 18 → 16 |
| alpindale/magnum-72b-v1 | 35.27 | 37.69 | +2.42 | 18 → 16 |
| Qwen/Qwen2-72B-Instruct | 35.12 | 37.69 | +2.57 | 19 → 18 |
| dnhkng/RYS-XLarge-base | 34.67 | 37.16 | +2.49 | 20 → 20 |
| Undi95/MG-FinalMix-72B | 33.61 | 36.10 | +2.49 | 22 → 21 |
| abacusai/Dracarys-72B-Instruct | 33.61 | 35.65 | +2.04 | 22 → 22 |
| Qwen/Qwen2.5-32B | 32.85 | 35.50 | +2.65 | 23 → 23 |
| anthracite-org/magnum-v2-72b | 31.65 | 34.06 | +2.41 | 24 → 24 |
| dnhkng/RYS-Huge-bnb-4bit | 31.57 | 33.84 | +2.27 | 25 → 25 |
**关键观察**
- 所有模型的分数都提升了约 **23 分**(最低 +2.03,最高 +3.10)——这个量级的差距足以改变一个模型的"好不好"结论,说明解析器(而非模型)曾经拖累了大量分数。
- 排名大体稳定,但个别模型有 ±1~3 位的变化(如 Smaug-Qwen2-72B-Instruct 从 16 掉到 19Qwen2-72B-Orpo-v0.1 从 15 升到 13)——若按排名做基准对比,解析差异会**改变排行榜序**。
**为什么分数整体平移、排名却会变?** 解析器对每个模型的"扣分"并不均匀:模型 A 输出的 LaTeX 恰好多是 sympy 的盲区(区间、集合、多余分组),被多扣;模型 B 的输出风格恰好全被解析,少扣。所以修复解析器后,**被多扣的模型分数抬升更多**,相对位置就动了。结论:解析器不只是"对所有人一视同仁的常量误差",它还可能掩盖模型之间的真实差距。
> 引申教训:评测指标管道(metric pipeline)本身也是被测对象的一部分。解析器、normalization、后处理的实现差异会系统性抬高/压低某些模型的分数。
## 3 可复现性排错(Troubleshooting Reproducibility
场景:你读了某篇最新技术报告,想在本地复现它的分数,却**复现不出来**?以下是作者拆解的原因清单。
### 3.1 不同的代码库(code base
- 要复现到小数点,第一步是**使用与论文完全相同的代码库**。
- 通常意味着:用**作者提供的评测默认代码**,或用参考库中的标准实现——如 Eleuther AI 的 `lm_eval`、HuggingFace 的 `lighteval`。这两个库是社区事实上的"标准管道":`lm_eval`(EleutherAI)是使用最广的评测框架,`lighteval`HuggingFace)由 Open LLM Leaderboard 团队维护——用它们,等于和其他人共用同一套任务定义与指标实现,这是复现的前提。
- 如果论文**没提供评测代码**:几乎不可能精确复现,只能放弃精确比对。
- ⭐ 想直观理解不同实现带来的差异,读作者团队写的这篇博客:[MMLU 在不同评测实现下的差异](https://huggingface.co/blog/open-llm-leaderboard-mmlu)——它研究了 MMLU 在 `lm_eval``helm` 与原始作者实现三种版本下的分数差异。
- 背景:正因为如此,HuggingFace 团队才发起 [Open LLM Leaderboard](https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard),用统一、同质的评测来做模型间的横向比较。
### 3.2 同一代码库内也容易踩的坑
即使代码库相同,以下细节也容易出错:
#### ① 不同的随机种子(random seed
- 推理受 seed 影响通常比训练小,但仍可能影响:
- 部分 **CUDA 操作**(参考 [PyTorch 可复现性文档](https://pytorch.org/docs/stable/notes/randomness.html));
- **非 greedy 生成策略**下的预测结果;
- 使用 few-shot 时的 **prompt 内容**(抽样选出哪些示例);
- 某些**前处理 / 后处理函数**。
- 一个小 seed 差异就可能带来**几分之差**。
#### ② 同名但实际不同的指标(metric)
指标名相同 ≠ 计算方式相同,例如:
- **log likelihood 版 `exact match`**(计算不同候选答案的对数概率) vs **生成式 `exact match`**(只把 greedy 生成与参考答案比较)——两者分数完全不同。
- 代码库里不少任务名叫 `exact match`,实际却是:
- **`prefix exact match`**(只比生成的开头与参考);
- **`suffix exact match`**(反过来,只比结尾);
- **`quasi exact match`**(带 normalization 的 exact match)。
- **结论:不能只靠指标名判断评测做了什么,必须看代码。**
#### ③ 不同的 normalization
- 回到上面生成式 `exact match` 的例子:`lm_eval` v1 中不少任务只叫 generative `exact match`,你会以为预测是"原样与参考比较";但看代码会发现预测**先经过 normalization**(去标点、数字同质化等)再比较——这会**大幅改变结果**。
- `lm_eval` **v2 已把 normalization 的名字写进大多数指标名**
- 这是最容易被搞砸的点,尤其对**需要大量 normalization / 答案后处理的任务**,比如数学评测(需要从模型生成的解释里**抽取最终答案**再比较)。
### 3.3 不同的 prompt
prompt 变化有三个来源。
#### ① prompt 本身(格式)
prompt 格式对分数的影响**巨大**。以多选题为例,仅呈现选项的格式就有多种"语义等价"的变体:
```text
Question: <text of the question>
Choices:
```
```markdown
| A. <Choice A> | (A) <Choice A> | <Choice A> |
| B. <Choice B> | (B) <Choice B> | <Choice B> |
| C. <Choice C> | (C) <Choice C> | <Choice C> |
| D. <Choice D> | (D) <Choice D> | <Choice D> |
```
```text
Answer:
```
并要求模型预测 `A`/`B`/`C`/`D`,或直接输出 `<Choice A/B/C/D>` 的完整文本。
- 这些 prompt **语义等价**(内容完全相同),但对同一模型仍可能差**好几分**:
- 作者团队实验([原帖](https://x.com/clefourrier/status/1777319187913875893/photo/1))观察到同一模型**最高差 7 分**;
- [相关论文](https://arxiv.org/abs/2310.11324)也观察到了类似结果。
- 有些任务还会带**任务前缀 prompt**(如 `The following questions are about <topic>`),其有无也会影响分数。
- ⭐ [这篇论文](https://arxiv.org/abs/2407.07890)还揭示一个副作用:**不少模型被训练成"过拟合基准的 prompt 与答案格式"**,代价是在评测时对其他 prompt 的适应能力下降。
- 实例(Open LLM Leaderboard 2 上的 Llama3.1):这些模型在 MATH-Hard 评测中**能预测出正确答案,分数却很低**——因为它们过拟合了 GSM8K(另一个数学评测)的 prompt 与答案格式,无法适配 few-shot 中提供的模板。
#### ② system prompt 与 chat template
- Chat 模型通常经过指令/偏好训练(instruction/preference training 或 fine-tuning),这个阶段它们学会了**遵循特定模板**推理。
- 常见模板要素:
- 每轮对话以**system prompt** 开头(通常以特定 token 前缀,如 `System: `),用于给模型高层指令(人设、回答风格等);
- 对话轮次给文本加**前缀关键词**,如 `User`(提问)与 `Assistant`(回答)。
- 使用 few-shot 时还要决定:示例**按多轮(multi-turn,模拟 user/assistant 轮次)提供**,还是**一次性放在单条 user prompt 里**。
- ⚠️ **不遵循模型期望的 chat template,会严重损害性能**——因为它把模型输出推向其已收敛的概率空间之外。
#### ③ few-shot 样本
- 显然要用**与参考任务相同的 few-shot 数量**。
- 还要用**完全相同的样本**——不同样本会改变结果(这不太意外:有些样本更能表达任务)。
- 更反直觉的一点:**样本完全相同还不够,顺序也必须完全相同**。作者团队观察到:同样的样本仅改变顺序,在 **MMLU 的某些子集上最多差 3 分**[结果见这里](https://huggingface.co/blog/evaluation-structured-outputs),第三个 colorgrid)。
- 因此这里同样要**注意随机种子**。
### 3.4 不同的生成参数(generation parameters
对生成式评测,需要对齐:
- 使用**相同的 end of sentence tokenEOS token**
- 允许模型**生成相同数量的 token**;
- 如果使用 sampling,确保**相同的 seed / temperature 参数**。
### 3.5 不同的模型加载(model loading
已观察到的差异来源:
| 来源 | 说明 |
|---|---|
| **不同硬件** | PyTorch **不保证**非确定性操作在不同硬件上可复现 |
| **不同推理库** | 例如用 `transformers` 还是 `vllm` 作为推理后端,矩阵计算的实现方式并不完全相同 |
| **不同 batch size** | 多个评测库与模型后端都有文档记录:**batch size 不同会改变推理结果**;要完全可复现就固定 batch size(虽然内存受限时不一定总能做到) |
| **不同加载精度** | 用更低精度加载权重可省内存与推理成本,但用的是不同版本的权重,**数值结果必然改变** |
> 排查复现问题时的总思路:**从"最可能、最容易改"的项开始逐项对齐**——先核对 prompt 与 few-shot(改动最频繁、影响最直接),再查指标与 normalization(需要看代码确认),最后核对生成参数与加载方式(涉及硬件环境)。每锁定一项就重跑一次对比,通常很快能找到分差来源。
### 3.6 可复现性检查清单(速查)
综合 3.1–3.5,复现一份评测分数前逐项核对(任一项不满足,分数就可能对不上):
- [ ] 使用与参考完全相同的**代码库/评测框架**(`lm_eval` / `lighteval` / 作者实现)
- [ ] 相同**随机种子**(尤其非 greedy 生成、few-shot 抽样)
- [ ] 相同的**指标定义**log-likelihood vs generative、prefix/suffix/quasi exact match,看代码确认)
- [ ] 相同的 **normalization / 后处理**(数学评测尤其注意答案抽取)
- [ ] 相同的 **prompt 格式、system prompt、chat template**(多轮 vs 单次 few-shot
- [ ] 相同的 **few-shot 数量、样本与顺序**
- [ ] 相同的**生成参数**EOS token、max tokens、seed / temperature
- [ ] 相同的**硬件、推理库、batch size、加载精度**
## 核心要点(一句话版)
1. **提速四杠杆**(按性价比排序):调大 batch size → 数据并行 → 换更快推理库 → 降精度(`bfloat16`/`float16` → 8bit/4bit 量化)。
2. **内存估算**`memory(GB) = 参数(G) × precision factor × 110%`factor 为 `float32`=4、`float16`/`bfloat16`=2、8bit=1、4bit=0.5。
3. **放不下时**:先量化(最多省 8 倍内存),再模型并行(pipeline 慢在 bubble、tensor 慢在难编码但同节点极快),CPU offloading 是最后手段(显著更慢)。
4. **装得下却 OOM**:八成是 context size 问题——用大 context 的 dummy 数据先测、降 batch、样本按 context 逆序呈现让失败提前暴露。
5. **数学评测的 LaTeX 解析没有标准答案**`lm-evaluation-harness` 用的 `sympy` 连 ground truth 自比都只有约 0.94 准确率;别去重写 grammar,加字符串比较兜底就够(修复后 25 个模型 MATH 分数全部 +2~3 分)。
6. **复现不出分数 = 某个环境细节没对齐**:代码库、随机种子、指标真实定义(prefix/suffix/quasi exact match)、normalization、prompt 格式与 chat template、few-shot 的数量/样本/顺序(顺序变化最多差 3 分)、生成参数(EOS、max tokens、seed/temperature)、硬件/推理库/batch size/加载精度——每一项都可能差几分。
7. **最容易被低估的两处**:① 同名指标的实现差异(必须看代码,别信名字);② prompt 格式的微小变化——语义等价也能差 7 分,还有模型专门过拟合基准 prompt 格式,换格式分数暴跌。
8. **工具取向**:优先使用维护中的标准框架(`lm_eval` v2、`lighteval`),因为它们把 normalization 等细节暴露在指标名里,减少"实现悄悄不同"的踩坑概率;升级框架版本时,也要顺手核对指标定义是否变化。
## 参考资料
### 原文(GitHub 仓库)
- [Troubleshooting inference(推理排错)](https://github.com/huggingface/evaluation-guidebook/blob/main/contents/troubleshooting/troubleshooting-inference.md)
- [Using LaTeX to evaluate MATH capabilitiesLaTeX 解析)](https://github.com/huggingface/evaluation-guidebook/blob/main/contents/troubleshooting/troubleshooting-math-parsing.md)
- [Troubleshooting reproducibility(可复现性排错)](https://github.com/huggingface/evaluation-guidebook/blob/main/contents/troubleshooting/troubleshooting-reproducibility.md)
- 仓库主页:[huggingface/evaluation-guidebook](https://github.com/huggingface/evaluation-guidebook)
### 文中引用的外部链接
- [PyTorch 模型推理性能优化清单](https://pytorch.org/serve/performance_checklist.html)
- [PyTorch PiPPypipeline parallelism](https://github.com/pytorch/PiPPy)
- [Transformers 并行化文档(data / pipeline / tensor parallelism](https://huggingface.co/docs/transformers/v4.15.0/en/parallelism)
- [ZeRO-Offload 论文(arXiv:2101.06840](https://arxiv.org/abs/2101.06840)
- [MATH benchmarklighteval/MATH](https://huggingface.co/datasets/lighteval/MATH)
- [sympy(符号数学库)](https://github.com/sympy/sympy)
- [sympy 的 LaTeX grammarlatex.lark](https://github.com/sympy/sympy/blob/master/sympy/parsing/latex/lark/grammar/latex.lark)
- ⭐ [MMLU 在不同评测实现下的差异(HF 博客)](https://huggingface.co/blog/open-llm-leaderboard-mmlu)
- [Open LLM Leaderboard](https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard)
- [PyTorch 可复现性文档](https://pytorch.org/docs/stable/notes/randomness.html)
- [prompt 格式对分数影响的实验(X 原帖,最高差 7 分)](https://x.com/clefourrier/status/1777319187913875893/photo/1)
- [Few-shot prompt 变化的影响(论文 arXiv:2310.11324](https://arxiv.org/abs/2310.11324)
- ⭐ [模型过拟合基准 prompt 格式(论文 arXiv:2407.07890](https://arxiv.org/abs/2407.07890)
- [few-shot 顺序对 MMLU 子集分数的影响(HF 博客)](https://huggingface.co/blog/evaluation-structured-outputs)