代码补全模型评估:HumanEval 与 Pass@k

FreeGuideOnline 最新 2026-06-22

为什么需要专门的代码评估基准

自然语言处理中常用的困惑度(Perplexity)或 BLEU 分数无法真实反映代码生成的质量。一个代码片段即使与参考答案文字相似度很高,但存在一个细微的逻辑错误,就完全没有用处。因此,代码补全模型需要任务导向的评估方式:给定问题描述,让模型生成代码,再通过执行测试用例来验证正确性。HumanEval 正是在这一思路下诞生的权威基准。

HumanEval 数据集解析

HumanEval 是一个手写编程问题集合,专门用于衡量从自然语言描述生成 Python 函数的能力。它不是为了考察复杂算法,而是聚焦于模型对自然语言的理解与基础编程技能的结合。

数据集结构与规模

  • 问题数量:164 个手写编程问题。
  • 题目组成:每个问题包含:
    • prompt:函数签名和文档字符串,描述需要实现的功能。
    • canonical_solution:人工编写的标准解答。
    • test:一组单元测试(通常 7~10 个测试用例),用于验证函数正确性。
    • entry_point:要测试的函数名称。
  • 语言:原始版本为 Python。现已扩展出多语言版本(如 HumanEval‑X)。

任务定义

模型接收仅包含函数签名和文档字符串的 prompt,需要补全函数体。以问题 HumanEval/0 为例:

from typing import List

def has_close_elements(numbers: List[float], threshold: float) -> bool:
    """Check if in given list of numbers, are any two numbers closer to each other than given threshold.
    >>> has_close_elements([1.0, 2.0, 3.0], 0.5)
    False
    >>> has_close_elements([1.0, 2.8, 3.0, 4.0, 5.0, 2.0], 0.3)
    True
    """

模型需要生成完整的函数体,随后其输出会执行 test 中定义的单元测试来判断是否通过。

为什么选择 HumanEval

  • 手写保证质量:题目和测试由人类精心设计,避免了大语言模型训练数据的污染。
  • 测试覆盖丰富:每个问题都包含边界条件和典型场景的测试用例。
  • 结果高度可解释:通过率直观反映模型解决编程任务的真实能力。

Pass@k 指标详解

Pass@k 是评估代码生成模型的核心指标,最早在 Codex 论文中形式化提出。它衡量“对于每个问题,生成 k 个候选答案时,至少有一个能通过所有单元测试的概率”。

原始定义

对于单个问题,模型生成 k 个代码样本。若其中任一通过测试,该问题即被记为一次成功。Pass@k 的计算公式为:

[ \text{pass@}k = \mathbb{E}_{\text{Problems}}\left[ 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}} \right] ]

其中:

  • ( n ):对每个问题实际生成的总样本数(通常 n ≥ k)。
  • ( c ):( n ) 个样本中通过测试的样本数。
  • ( \binom{n}{k} ):从 n 个中选 k 个的组合数。

这个形式的计算目的是在仅使用 n 个样本的情况下,无偏地估计 pass@k

为什么不能直接用“生成 k 次看是否通过”

直观想法是“对每个问题恰好生成 k 个样本,观察至少有一次通过的比例”。但这种方法需要为每个 k 重新采样,计算成本高。上述公式允许我们一次性生成 n 个样本(例如 n=200),然后用组合数学估计任意 k ≤ n 下的 pass@k,极大提高了评估效率。

无偏估计公式的直观解释

( 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}} ) 表示:从 n 个样本中不放回抽取 k 个,“k 个全都没通过”的概率的补集。这恰好等于“至少有一个样本通过”的概率。由于我们对每个问题都计算该值,再在所有问题上求平均,就得到了无偏的 pass@k 估计量。

如何实施 HumanEval 评估

1. 安装与配置

社区广泛使用 OpenAI 开源的 HumanEval 评估工具。安装命令:

pip install -e .

确保 Python 环境为 3.7 及以上。

2. 生成样本

需要提供一个脚本,对每个问题生成 num_samples_per_task 个代码补全(通常 n=200)。生成策略一般使用核采样(nucleus sampling)且温度 T > 0 以保持多样性。模型输出需要提取纯代码部分,去除上下文无关的文本。

3. 执行评估并计算 Pass@k

运行评估脚本:

evaluate_functional_correctness samples.jsonl

samples.jsonl 每一行必须包含:

  • task_id:问题 ID,如 HumanEval/0
  • completion:模型生成的完整函数体代码

该命令会执行所有单元测试,并输出 pass@k 结果,例如:

pass@1: 0.472
pass@10: 0.762
pass@100: 0.908

4. 重要实施细节

  • 沙盒执行:强烈建议在隔离环境(Docker)中运行测试,防止恶意代码破坏系统。
  • 超时机制:对每个测试用例设置有限执行时间,防止死循环。
  • 去除死代码:生成的代码可能包含无法抵达的语句,但这通常不影响正确性评估。
  • 禁止数据泄露:评估数据应与训练数据严格独立,HumanEval 的手写特性一定程度上缓解了此问题。

解读 Pass@k 结果

  • Pass@1 表示模型第一次回答就正确的概率。此指标反映了模型的“精准度”,对真实交互场景极为重要。
  • Pass@k (k>1) 表示在多轮采样下问题被解决的概率。k 越大,模型“在大量尝试中至少碰对一次”的能力越强。但过高的 k 值会掩盖模型真正理解问题的程度。
  • 实际意义:例如 Codex 12B 的 HumanEval pass@1 为 28.8%,pass@100 可达 72.3%,说明多次采样可以大幅提升问题解决率,但同时也暴露了模型首次生成质量的不足。

局限性及补充指标

  • 领域覆盖窄:HumanEval 仅有 164 个 Python 问题,且以简单函数为主。不能反映复杂软件工程、API 调用、多文件项目等能力。
  • 多语言场景:已涌现 MBPP、DS-1000、CodeContests 等更丰富的基准。对于多语言模型,还会用到 HumanEval‑X 这类多语言翻译版本。
  • Pass@k 依赖采样策略:不同的采样参数(温度、top_p)会显著影响 pass@k,模型对比时需统一生成配置。
  • 与真实使用存在差距:实际编码辅助工具中,用户依赖的是单次生成时的交互流畅性,而非多次重试。因此 pass@1 通常更受重视。

总结

HumanEval 搭配 Pass@k 为代码补全模型提供了标准化、可复现且具有客观正确性判断的评估框架。理解其运作原理和统计基础,有助于我们在模型对比、论文阅读以及实际选型中做出合理判断。

进一步学习建议

  • 复现评估流程,亲手测量开源的 StarCoder、Code Llama 等模型。
  • 阅读 Codex 论文中关于 Pass@k 无偏估计的数学推导。
  • 探索其他评估基准(如 MBPP、APPS),对比各自的设计异同。