在人工智能(AI)技术的快速发展中,大型语言模型(LLMs)已经成为我们日常生活的一部分。它们帮助我们写作、总结、翻译,甚至生成图像。然而,在所有领域中,代码生成可能是它们最重要的应用之一。代码是一种力量倍增器,它能够以戏剧性的方式转化努力。一个高效的函数可以自动化数小时的手工工作,而正确的算法可以使企业扩展到服务数百万用户。与质量主观的创意文本不同,代码的成功可以通过其功能、性能和可靠性来衡量。这使得代码生成能力在AI系统中特别有价值,因为它们直接转化为可衡量的生产率提升和技术优势,这些优势会随着时间的推移而累积。
代码生成的重要性
在AI时代,能够生成正确、高效和可维护代码的LLMs不仅仅是方便的工具——它们是变革性的。这也意味着我们不能仅凭直觉来评判它们。代码执行要求精确性,因此我们必须严格基准测试AI的编码能力,而不是依赖直觉。
HumanEval的起源
本文将讨论HumanEval,这是OpenAI首次用于评估LLMs代码生成能力的基准之一。这个基准是在2021年由chen等人发表的论文《Evaluating Large Language Models Trained on Code》中引入的,虽然时间不长,但在机器学习(ML)领域却像是千年前的事情(比ChatGPT早了一年多)。
论文主要关注三个主题:HumanEval、Codex(OpenAI的GPT语言模型在GitHub上公开可用的代码上进行微调,以及他们在基准测试上提高性能的过程)以及部署强大代码生成技术的潜在更广泛影响,涵盖安全、安保和经济等方面。正如本文标题所示,我们将专注于第一部分——这个基准将成为未来几年衡量LLMs编码能力的行业标准。
代码评估的挑战
评估LLM输出的挑战在传统机器学习中并不存在。在传统ML中,成功指标是明确的:你可以通过准确率来评估图像分类,通过精确度和召回率来评估推荐系统,或者通过均方误差来评估回归模型。但是,对于语言模型,评估变得更加复杂。你如何客观地衡量生成文本的质量?一个总结是否“足够好”?一个翻译是否捕捉到了细微差别和语调?这些问题引入了主观判断,而不是二元评估。
当涉及到代码生成时,我们面临类似的评估障碍。我们如何系统地测试AI能否写出好的代码?在这个背景下,“好”是什么意思?花一分钟时间思考:你会用什么方法来评估AI生成的代码?我们如何创建一个客观衡量编码能力的标准化基准?欢迎在评论中分享你的想法。
HumanEval的灵感来源
LeetCode提供了一个有趣的类比。对于不熟悉的人来说,LeetCode是一个平台,程序员在这里练习从简单的算法到复杂的系统设计的编码挑战。每个问题都包括:清晰的问题陈述、输入/输出示例、隐藏的测试用例来验证边缘情况以及一个自动化的评估系统来测试解决方案的正确性和效率。这种方法——提供问题描述并自动测试生成的代码——为评估创建了一个客观的框架。解决方案要么适用于所有测试用例,要么不适用。此外,LeetCode还评估性能指标,如执行时间和内存使用,增加了不仅仅是正确性的维度。
HumanEval从这个模型中汲取灵感,但特别关注衡量AI将自然语言描述转化为功能性代码的能力。它不是测试人类程序员的问题解决技能,而是测试AI是否能够解析函数描述(docstring)并生成适当处理所有要求的代码。
这种方法的美妙之处在于,它提供了一个清晰、客观的度量:生成的代码要么通过了所有测试用例,要么没有。这种二元结果为我们比较不同模型的编码能力提供了坚实的基础。
HumanEval数据集
数据集由164个原始的Python编程问题组成,评估语言理解、算法和简单的数学,有些类似于简单的软件面试问题。每个问题都以以下格式的json出现:
- task_id:只是一个唯一标识问题的id。
- prompt:代码片段,通常包含所需的导入、docstring和函数定义。LLM将看到的唯一输入。
- entry_point:函数名称,用于运行代码并指定生成的代码的位置。
- canonical_solution:问题的规范解决方案。
- test:问题的一或多个测试套件。
基准测试
要运行基准测试并评估LLM生成的代码,我们需要将提示(仅提示)传递给一个函数,该函数将调用LLM。在仓库中有一个简单的例子:
python
from human_eval.data import write_jsonl, read_problems
problems = read_problems()
num_samples_per_task = 200
samples = [
dict(task_id=task_id, completion=generate_one_completion(problems[task_id][“prompt”]))
for task_id in problems
for _ in range(num_samples_per_task)
]
write_jsonl(“samples.jsonl”, samples)
你只需要提供generate_one_completion
就可以让它工作。在仓库中还有一些函数来评估samples.jsonl
中的结果,并提供分数。在自己的机器上运行基准测试是非常危险的,应该在强大的安全沙箱之外进行,我们稍后会讨论这个问题。
理解Pass@k指标
HumanEval使用“功能正确性”而不是文本相似性来评估代码生成。Pass@k(Kulal等人,2020年)指标衡量至少有一个问题的最佳k个生成样本通过所有单元测试的概率。公式很简单:
- n:生成的总样本数
- c:正确样本数
- k:考虑的最佳样本数
Pass@k = 1 — C(n-c,k)/C(n,k)
这个指标反映了现实世界中的使用情况,开发者可能会生成多个解决方案,并选择一个有效的。它现在是代码生成基准的标准,包括跟踪pass@10和pass@100分数的Papers with Code排行榜。通过关注代码是否实际运行正确而不是外观,HumanEval提供了对代码生成能力的实用评估。
论文结果
在原始论文中,审查的模型得分相当低,GPT-3解决了0%的问题,GPT-J解决了11.4%的问题。论文中引入的模型,Codex,得分更高,解决了28.8%的问题。通过从模型中重复抽样,他们能够解决70.2%的问题。
HumanEval的局限性和问题
HumanEval存在一些问题,我们必须解决,其中一些是特定于基准的,但大多数在LLM基准的世界中普遍存在问题。主要问题包括:
- 基准泄露:基准的最大问题之一是它们的泄露,如果LLM已经知道规范解决方案并且只是重复它而不是解决问题怎么办?随着新模型刮取整个网络,问题变得越来越困难。
- 测试不够:HumanEval仅通过一件事来评分解决方案——它们是否通过了测试。主要有两个问题:通过测试并不一定意味着解决方案是正确的,LLM可能忽略了一些未测试的边缘情况,或者只解决了少数几个会使测试通过的问题。第二个问题是,除了正确性之外,好的代码还有其他参数,好的代码应该是可读的、可维护的和高效的,而HumanEval完全忽略了这些。
- 运行模型生成的代码:运行不受信任的模型生成的代码是非常危险的,不鼓励在强大的安全沙箱之外进行。一些评估者可能没有遵循这个规则…
- 通过自己的基准测试:设计自己的基准测试,然后设计一个在其中表现非常好的模型,存在固有的问题。
尽管存在这些主要问题,HumanEval是ML基准世界中的一个重要里程碑,自发布以来评估了数十个模型。
HumanEval的现状
LLMs在HumanEval上的表现一直在不断提高,如今顶级模型(如Claude 3.5 Sonnet)甚至在Pass@1上达到了100%的分数。你可以在下面的图表中看到多年来的逐步改进。
HumanEval现在被认为是一个相对容易的基准,但它为新的和更难的基准铺平了道路。其中一些包括:
- HumanEval-XL:一个多语言代码生成基准
- CodeXGLUE:评估LLMs理解和处理代码的能力,包括代码完成和翻译等各种任务
- Mostly Basic Python Programming(MBPP):适合初级程序员的1000个Python编程问题
- SWE-bench:评估LLMs解决现实世界GitHub问题的能力
等等,你想了解更多关于它们或其他基准的信息吗?在评论中告诉我!
我的个人观点
我是在TAU的NLP课程中从Ofir Press博士那里了解到HumanEval和LLM基准的。然后我们进行了一个最终项目,使用大型语言模型生成新的编程练习。我们进行了很多迭代,最后我们使用HumanEval框架作为基础来创建更多的问题(以相同的json格式),并取得了一些酷结果和疯狂的问题,比如这一个: