就在一周前,我与一位 DeepEval 用户通话时,她提到将大型语言模型(LLMs)的测试与评测视为两个不同的概念。当被问及她对LLM测试的定义时,她是这样回答的:
对我们而言,评测LLMs更多是通过基准测试选择正确的LLMs,而LLM测试则更侧重于探索不同场景下可能出现的意外问题。
鉴于我已撰写过一篇全面介绍LLM评测指标的文章,本文将转而深入探讨如何将这些指标应用于LLM测试。我们将解析LLM测试的本质、不同的测试方法及需警惕的边界案例,强调LLM测试的最佳实践,并展示如何通过开源LLM测试框架 DeepEval 执行LLM测试。
趁我还记得,这里有一张我常用的“图表”来说明测试的重要性,特别是针对 RAG 应用的单元测试。
被说服了吗?让我们直接开始吧
什么是LLM测试?
LLM 测试是通过评测一个 LLM 输出来确保其基于预期应用目的满足所有特定评测标准(如准确性、连贯性、公平性和安全性等)的过程。关键在于采用一套稳健的测试方法,以大规模评测和回归测试 LLM 系统。
单元测试包括功能、性能和职责测试,这些共同构成了回归测试
评测LLMs是一个复杂的过程,因为与传统软件开发不同,后者的结果可预测且错误可通过调试解决,逻辑可归因于特定代码块,而LLMs则如同黑箱,拥有无限可能的输入及对应输出。
然而,这并不意味着传统软件测试的概念无法应用于测试LLMs——它们只是有所不同。单元测试构成了功能、性能和职责测试,这些共同组成了对LLM的回归测试。
单元测试
单元测试涉及测试应用程序中最小的可测试部分,对于LLMs而言,即基于某些明确定义的标准,评测LLM对给定输入的响应。
例如,在一个单元测试中,若你试图评测由LLM生成的摘要质量,评判标准可能包括摘要是否包含足够的信息,以及是否从原文中引入了任何虚构内容。标准的评分则通过所谓的LLM评测指标来完成(后续将详细讨论)。
你可以选择实现自己的LLM测试框架,但在本文中我们将使用 DeepEval 来创建和评测单元测试用例:
pip install deepeval
然后,创建一个测试用例:
from deepeval.test_case import LLMTestCase
original_text="""In the rapidly evolving digital landscape, the
proliferation of artificial intelligence (AI) technologies has
been a game-changer in various industries, ranging from
healthcare to finance. The integration of AI in these sectors has
not only streamlined operations but also opened up new avenues for
innovation and growth."""
summary="""Artificial Intelligence (AI) is significantly influencing
numerous industries, notably healthcare and finance."""
test_case = LLMTestCase(
input=original_text,
actual_output=summary
)
在这里,input 是你 LLM 的输入,而 actual_output 是你 LLM 的输出
最后,使用 DeepEval 的摘要指标评测此测试用例:
export OPENAI_API_KEY="..."
from deepeval.metrics import SummarizationMetric
...
metric = SummarizationMetric(threshold=0.5)
metric.measure(test_case)
print(metric.score)
print(metric.reason)
print(metric.is_successful())
功能测试
功能测试LLMs涉及评测LLMs在特定任务上的表现。与传统软件功能测试(例如,通过测试整个登录流程来验证用户是否能成功登录)不同,针对LLMs的功能测试评测模型在某一特定任务(如文本摘要)内对多种输入的熟练程度。换言之,功能测试由针对特定用例的多个单元测试组成。
要将单元测试分组以执行功能测试,首先创建一个测试文件:
touch test_summarization.py
我们在此使用的示例任务是文本摘要。接着,定义一组单元测试用例:
from deepeval.test_case import LLMTestCase
# Hypothetical test data from your test dataset,
# containing the original text and summary to
# evaluate a summarization task
test_data = [
{
"original_text": "...",
"summary": "..."
},
{
"original_text": "...",
"summary": "..."
}
]
test_cases = []
for data in test_data:
test_case = LLMTestCase(
input=data.get("original_text", None),
actual_output=data.get("input", None)
)
test_cases.append(test_case)
最后,使用 DeepEval 的 Pytest 集成批量遍历单元测试用例,并执行测试文件:
import pytest
from deepeval.metrics import SummarizationMetric
from deepeval import assert_test
...
@pytest.mark.parametrize(
"test_case",
test_cases,
)
def test_summarization(test_case: LLMTestCase):
metric = SummarizationMetric()
assert_test(test_case, [metric])
deepeval test run test_summarization.py
请注意,功能测试的稳健性完全取决于单元测试的覆盖率。因此,在为特定功能测试构建单元测试时,应尽可能覆盖更多的边缘情况。
回归测试
回归测试指的是每次迭代时在同一组测试用例上评测LLM,以防止破坏性变更。采用定量LLM评测指标进行LLM评测的优势在于,我们可以设定明确的阈值来界定何为“破坏性变更”,同时还能监控LLM在多轮迭代中性能的变化情况。
多项功能测试可构成回归测试。例如,我可以评测LLM在执行摘要生成和代码生成两方面的能力,作为回归测试的一部分,衡量其在每次迭代后是否仍能完成这些任务。
额外提示:DeepEval 附带一个可选专用平台 Confident AI,当你在使用 Confident AI + DeepEval 时运行多次测试,你将获得一个并排比较工具,用于捕捉回归/识别改进点。
Confident AI回归测试套件
性能测试
当我们提及性能测试时,并非指测试某个LLM能否完成特定任务,而是关注诸如每秒处理的令牌数(推理速度)及每令牌成本(推理成本)等通用性能指标。性能测试的主要目的在于优化成本与延迟。
需注意,性能测试也是回归测试的一部分。
责任性测试
这是唯一一种不属于传统软件开发概念的测试形式。责任测试的核心在于,不论当前任务为何,都要针对LLM的输出在负责任人工智能指标(如偏见、毒性内容和公平性)上进行测试的理念。例如,即使明确要求LLM总结一篇带有偏见的新闻报道时,也应确保其不会执行该操作。
DeepEval 提供了一些即插即用的负责任人工智能指标供你使用:
touch test_responsibility.py
# test_responsibility.py
from deepeval.metrics import BiasMetric, ToxicityMetric
from deepeval.test_case import LLMTestCase
from deepeval import assert_test
bias_metric = BiasMetric()
toxicity_metric = ToxicityMetric()
def test_responsibility():
test_case = LLMTestCase(input="...", actual_output="...")
assert_test(test_case, [bias_metric, toxicity_metric])
deepeval test run test_responsibility.py
指标驱动测试
另一种思考LLM测试的方式,不同于上述传统视角,是基于指标标准来测试LLM系统。让我们来看看三种最常见的指标(注意,我们不做精确匹配)。
正确性测试
可能是其中最为直接的一种。正确性测试就像传统监督式机器学习中的典型测试集,给定整个训练数据集后,我们预留一小部分子集,以目标标签为参考,检验新训练的模型是否能给出正确答案。
然而,在LLMs的情境下,正确性测试可能稍显微妙,因为目标标签未必非黑即白、对错分明。确实,对于像 MMLU 这样的基准测试,目标标签直接是多选题的答案,通过精确匹配即可轻松量化性能;但在其他情况下,我们需要更好的方法。以此输入为例:
狗把猫追的爬上了树。谁上了树?
正确答案当然是猫!但如果你的LLM(系统)仅输出“猫”这个词呢?你肯定希望LLM测试方法能将其判定为正确。
为此,你可以使用一种名为 G-Eval 的先进LLM评测指标,灵活定义用于正确性的LLM评测指标。
from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams, LLMTestCase
correctness_metric = GEval(
name="Correctness",
criteria="Determine if the actual output is correct with regard to the expected output.",
evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.EXPECTED_OUTPUT],
strict_mode=True
)
test_case = LLMTestCase(
input="The dog chased the cat up the tree. Who went up the tree?",
actual_output="Cat",
expected_output="The cat"
)
correctness_metric.measure(test_case)
print(correctness_metric.is_successful())
请注意,strict_mode=True 参数会使度量输出一个二进制的 0 或 1 分数,这对于正确性用例来说是完美的。
相似性测试
与正确性类似(无意双关),相似性也不是传统自然语言处理指标能轻易衡量的。没错,BERTScore,我说的就是你。
同样,你可以使用 G-Eval 来计算语义相似度。这对于较长的文本尤其有用,因为那些忽视语义的传统自然语言处理指标在此类情况下表现欠佳。
from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams, LLMTestCase
similarity_metric = GEval(
name="Similarity",
criteria="Determine if the actual output is semantically similar to the expected output.",
evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.EXPECTED_OUTPUT]
)
test_case = LLMTestCase(
input="The dog chased the cat up the tree. Who went up the tree?",
actual_output="Cat",
expected_output="The cat"
)
similarity_metric.measure(test_case)
print(similarity_metric.is_successful())
幻觉测试
最后,你需要测试幻觉现象,有几种方法可以实现这一点。幻觉既可作为无参考指标,也可作为基于参考的指标来处理,此时需要真实数据来判断LLM输出内容的事实准确性。
你可能也注意到了我使用了“准确性”这个词。然而,幻觉现象需要单独测试,因为产生幻觉的输出未必在事实上是错误的。感到困惑吗?想象一下,如果你的LLM输出了不在其训练数据中的信息。即便这些信息在现实世界中是事实正确的,仍被视为幻觉。
你可以通过一种名为 SelfCheckGPT 的无参考技术来测试幻觉,或者采用基于参考的方法,即提供一些基础上下文,并利用LLM-Eval 来验证其相对于给定上下文的正确性。
测试LLMs的最佳实践
你可能已经注意到,通过遵循这些测试技巧,我们可以很好地将功能测试、性能测试和职责测试分别放在不同的测试文件中。
以下是构建你的LLM测试的方法:
llm_tests
├── test_summarimzation.py
├── test_code_generation.py
├── test_performance.py
├── test_responsibility.py
...
注意这里有一个额外的 test_code_generation.py 文件,它是另一个假设的测试文件,代表除摘要功能外的代码生成功能测试。
所有这些测试文件都由你之前看到的单元测试组成,它们共同构成了你的回归测试。
稳健的LLM评测指标
这一点或许显而易见,但LLM评测指标极难做到精确,你常会看到准确性与可靠性之间的权衡。
例如,ROUGE 等传统评分技术虽然可靠,却极不准确,因为它们无法在评测一段LLM生成的文本时考虑语义(不,n-gram 并不足够,而且如果你输出的是 JSON 呢?)
对于 DeepEval,我们发现LLM-评测(使用LLMs进行评测的指标)表现最佳,并已实现了几种评分技术:
- G-Eval:最先进的框架,用于要求LLMs根据评分标准生成分数。
- DAG(有向无环图):一种创建基于决策的框架,LLM作为确定性评分的评判标准。
- QAG(问答生成):一种技术,首先生成LLM以回答一些封闭式问题,然后基于这些答案生成分数和理由。
评测指标的稳健性极为重要,因为它们最终决定了测试是否通过。以下是一些你可以考虑的指标:
-
摘要生成
-
幻觉
- 连贯性
- 代码正确性
- 偏见(用于责任测试)
CI/CD 中的自动化测试你需要做的一件事是,为每次你或队友所做的更改建立一个自动测试LLM的方法。在传统软件开发中,尤其是在团队环境中,CI/CD 中的自动化测试对于防止未被注意到的破坏性变更至关重要。得益于像 DeepEval 这样的框架,在 CI/CD 中测试LLMs也是可行的。还记得我们之前创建的包含回归测试所需所有文件的 llm_tests 文件夹吗?只需在你的 CI/CD 环境中执行这个文件夹,就可以开始在 CI/CD 中测试LLMs:
deepeval test run llm_tests
如果你使用 GitHub Actions 来测试每个拉取请求中的LLMs,可以将其包含在你的 GitHub 工作流 YAML 文件中。结论在本文中,我们了解了LLM测试包含的内容,以及单元测试、功能测试、性能测试、责任测试和回归测试等LLM测试类型及其涉及的内容。简而言之,单元测试构成了功能、性能和责任测试的基础,你可以利用这些来进行LLM的回归测试。一个测试需要测试标准、构建测试用例,并应用LLM评测指标来判断测试是否通过。如果你想构建自己的测试框架,请随意;或者,你可以使用 DeepEval,这个开源的LLM测试框架。 我们已经为你完成了所有繁重的工作,它旨在通过一系列健壮的即用型LLM评测指标和 CI/CD 管道中的自动化测试来强制执行最佳测试实践