ChatGPT:领先的代码生成工具凭借近乎全知的 GPT-4 模型,ChatGPT 在过去一年中人气飙升。它能够针对未曾见过的上下文生成连贯且富有诗意的回答,这一能力推动了其他基础大型语言模型(LLM)的发展,例如 Anthropic 的 Claude、谷歌的 Bard 和 Meta 的开源 LLaMA 模型。由此,机器学习工程师得以围绕专有数据构建基于检索的 LLM 应用,其效率远超以往。然而,此类应用仍面临三大挑战:幻觉问题、信息滞后性、语境偏差。
作为全球首个专为LLM应用打造的开源评测基础设施Confident AI的创始人,我将在本文详解以下内容:如何评测LLM及其检索流程的技术实现路径、可供开发团队选择的评测工作流设计范式,以及如何通过系统化评测解决构建检索增强生成(RAG)应用时频发的典型技术陷阱。
评测 ≠ 肉眼观察输出结果
在开始前,请确认当前的评测流程是否类似以下模式:遍历提示词列表,对每个提示词运行LLM应用程序,等待一两分钟执行完毕,人工逐条检查结果,并试图根据每个输入判断输出质量。
如果这听起来似曾相识,那么本文正是为你准备的。(相信阅读结束后,你会掌握如何告别这种低效的"肉眼审核"工作模式。)
from llama_index import VectorStoreIndex, SimpleDirectoryReader
from llama_index import ServiceContext
service_context = ServiceContext.from_defaults(chunk_size=1000)
documents = SimpleDirectoryReader('data').load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=5)
def query(user_input):
return query_engine.query(user_input).response
prompts = [...]
for prompt in prompts:
print(query(prompt))
评测是一个多步骤的迭代过程
评测虽然是个复杂的过程,但当你需要持续优化LLM应用时,它能带来巨大的长期收益。不建立评测机制就开发LLM系统,就好比搭建没有自动化测试的分布式后端系统——初期也许能运行,但最终你会把更多时间花在修复崩溃问题上,而不是真正构建有价值的功能。(有个有趣的现象:AI类应用的首月留存率普遍偏低,正是因为用户不会反复使用不稳定的产品)
要评测LLM系统,你需要准备几个关键组件:动态优化的评测数据集、根据业务场景定制的评测指标(通常选择3-5个),以及支撑全生命周期实时评测的基础设施。
第一步——创建评测数据集
为LLM应用建立有效评测体系的第一步,就是创建评测数据集,至少需要明确应用可能接收的输入类型。这听起来很高大上,但其实你在手动检查输出质量时,已经在无意识地进行数据收集了。
回想前文提到的"肉眼审核"场景。容我做个推测:你实际上是在根据预期结果评判输出质量。你必然已经掌握知识库的基本情况,如果在检索流程中打印出检索到的文本块,你甚至能预判应该出现哪些相关内容。初始评测数据集无需面面俱到,建议从记录带有上下文的标准QA对开始:
dataset = [
{
"input": "...",
"expected_output": "...",
# context is a list of strings that represents ideally the
# additional context your LLM application will receive at query time
"context": ["..."]
},
...
]
在这个环节,"输入"是必选项,而"预期输出"和"上下文"则是可选项(后续会解释原因)。
若想实现自动化,你可以尝试遍历知识库(可能存储在Qdrant等向量数据库中),通过GPT-3.5批量生成QA对,而非手动完成。这种方式灵活高效,但受限于模型的训练数据。(讽刺的是,当你在专业垂直领域构建应用时——这类场景更依赖检索流程而非基础模型本身——反而更需要重视评测体系的建设)
最后你可能会想"既然已有HELM等标准评测基准,为何还要自建数据集?"究其原因,当评测基于私有数据的LLM应用时,斯坦福HELM这类通用基准就会失去针对性。
第二步——确定评测指标
为你的LLM应用确定评测体系时,需要明确要衡量的核心维度。典型指标包括:
• 事实一致性(输出与知识库的吻合度)
• 回答相关性(输出与输入问题的匹配度)
• 逻辑连贯性(输出的条理性)
• 有害内容检测(如暴力、歧视性内容)
• RAGAS(专用于RAG流程的评测框架)
• 偏见检测
关于指标体系的详细解析将另文阐述,这里有个你需要注意的地方,不同指标依赖不同的数据组件。例如事实一致性无需关注输入问题,而有害检测只需分析输出内容。从方法论层面,我们可将事实一致性归为"基于参考的指标"(需上下文支撑),而有害检测则是"无参考指标"。
第三步——构建指标评分系统
此阶段需要为每个指标开发量化评分逻辑。以事实一致性为例,其评分系统实现示例如下(代码源自DeepEval框架):
from sentence_transformers import CrossEncoder
def predict(self, text_a: str, text_b: str):
# https://huggingface.co/cross-encoder/nli-deberta-base
model = CrossEncoder('cross-encoder/nli-deberta-v3-large')
scores = model.predict([(text_a, text_b), (text_b, text_a)])
softmax_scores = softmax(scores)
score = softmax_scores[0][1]
second_score = softmax_scores[1][1]
return max(score, second_score)
在这个案例中,我们采用Hugging Face的自然语言推理模型,通过计算0-1区间的蕴含分数(entailment score)来量化事实一致性。实现方式无需拘泥于此,关键是要建立量化机制——你需要为每个指标设计评分逻辑并找到实现路径。特别提示:由于LLM输出具有概率性特征,评分系统应容忍语义等效的不同表述,避免对合理变体产生误判。
在 Confident AI,我们根据待评测指标的类型,综合运用基于模型、统计以及LLM的评分方法。例如,针对事实一致性(NLI 模型)和答案相关性(交叉编码器)等指标,我们采用基于模型的方法;而对于连贯性等更微妙的指标,我们则实现了名为 G-Eval 的评测框架(该框架结合LLMs与思维链技术,利用 GPT-4 进行评测)。(若感兴趣,这里有一篇介绍 GEval 的论文——一个利用LLMs进行评测的鲁棒框架)事实上,论文作者发现 G-Eval 在以下所有传统评分标准中表现更优:
- BLEU(通过比较机器生成文本与参考译文的 n-gram 匹配数量来评测)
- BERTScore(基于 BERT 嵌入的文本生成评测指标)
- ROUGE(一套用于评测自动文本摘要及机器翻译的指标体系)
- MoverScore(计算机器生成文本中词语的上下文嵌入与参考文本中对应词语嵌入之间的距离)
最终需为每个指标设定合格标准:即各项指标的评分需达到的临界值,只有满足该阈值,对应输入下的LLM应用输出才被视为合格。例如,对于上文所述事实一致性指标(评分范围0-1),合格标准可设为0.6;而对于输出0/1二元评分的指标(如合规性检测),合格标准则需设为1(即必须完全合规)。
第四步——将每个指标应用于你的评测数据集
一切准备就绪后,你现在可以遍历评测数据集并逐个评测每个数据点。算法大致如下:
-
遍历你的评测数据集。
-
对于每个数据点,根据给定的输入运行你的LLM应用程序。
- 当你的LLM应用程序为某个数据点生成输出后,为你之前定义的每个指标计算得分。
- 识别并记录未达标的指标(未满足通过标准的指标)
- 根据这些未达标的指标迭代优化你的LLM应用。
- 重复步骤 1 至 5,直至所有指标均达标。
现在,你无需再凭直觉判断输出结果,确保对LLM应用的信心就像通过测试用例一样简单。
第五步——将评测作为单元测试集成到 CI/CD 流水线中
完成所有设置固然很好,但为了将自动化评测推向更高层次,你可以将评测以单元测试的形式纳入 CI/CD 流水线,例如在 GitHub Actions 中实现。通过开源评测框架 DeepEval(LLM),你可以轻松做到这一点。DeepEval 提供了 14+种LLM评测指标,几乎涵盖你可能遇到的所有用例。这是我一直在努力的方向,旨在帮助其他开发者自动化检查LLM输出结果。
简而言之,首先需要安装 DeepEval:
pip install deepeval
接着,创建一个类似于 Pytest 的测试文件:
touch test_llm.py
编写一个简单的测试用例:
from deepeval import assert_test
from deepeval.metrics import AnswerRelevancy
from deepeval.test_case import LLMTestCase
def test_answer_relevancy():
answer_relevancy_metric = AnswerRelevancyMetric(threshold=0.5)
test_case = LLMTestCase(
input="What if these shoes don't fit?",
# Replace this with the actual output of your LLM application
actual_output="We offer a 30-day full refund at no extra cost.",
retrieval_context=["All customers are eligible for a 30 day full refund at no extra cost."]
)
assert_test(test_case, [answer_relevancy_metric])
你可以通过命令行界面执行:
deepeval test run test_llm.py
就这样!要将单元测试集成到 CI/CD 流程中,只需在测试文件中包含你的测试用例,并在例如 YAML 文件(假设你使用 GitHub 工作流)中执行相同的命令。
第六步——生产环境中的持续评测
最后一步涉及实时评测LLM的输出。这至关重要,因为它能让你及时察觉任何不尽人意的响应并迅速迭代改进。遗憾的是,目前尚无简便方法实现这一点。但若你希望在生产环境中进行实时评测,可以考虑 Confident AI,我们能帮助你在LLM开发周期的各个阶段自动化人工评测流程。
评测助你迭代出最佳超参数组合
建立一个评测框架能带来多重好处,它能让你快速迭代优化LLM应用/检索流程:
- 以基于 RAG 的应用为例,你现在可以通过运行多重嵌套循环来寻找最优超参数组合——包括文本块大小、Top K 检索数量、嵌入模型和提示模板等——这些组合能为你的评测数据集带来最高的指标得分。
- 你将能够在不担心未被注意到的破坏性变更的情况下进行边际改进。
然而,评测并非万无一失
尽管你的评测框架现已就位,但在部署到生产环境的初期阶段,它仍然脆弱且不稳定。这是因为用户会以你从未预料到的方式与你的应用程序互动,但这没关系。要构建一个真正健壮的LLM应用程序,你应该:
- 识别不满意的输出,标记它们以便复现,并将其添加到你的评测数据集中。这被称为持续评测,没有它,你会发现你的LLM应用程序将逐渐与用户最关心的内容脱节。有几种方法可以识别不良输出,但最可靠的方法是使用人类作为评测者。
- 在组件层面识别你的LLM流程中哪一部分导致了不理想的输出。这被称为带追踪的评测,若缺乏此步骤,你可能会因误判(例如认为检索组件未能获取相关文本片段,而实际是提示模板的问题)而做出不必要的调整。
其他评测方法
另一种评测LLM应用的方式是采用自动评测法,即用LLMs作为裁判,在呈现多个不同选项时选出最佳输出。事实上,Databricks 的数据表明,LLM作为裁判的判定与人工评分的吻合度超过 80%。使用LLM作为裁判时需注意以下几点:
- GPT-3.5 可以工作,但前提是你提供一个示例。
- GPT-4 即使没有示例也能很好地工作。
- 使用低精度评分量表(如 1-5 分制或二元评分)以保持精确性,而非采用类似 1-100 分的宽泛尺度。
自动评测的一种可能方法是:
- 针对所有不同的超参数组合生成输出结果。
- 要求 GPT-4 以成对比较的方式挑选出最佳输出集。
- 识别 GPT-4 所选最佳输出对应的超参数集。
我对这种方法存在的一个问题,也是我们在 Confident AI 尚未实现相关方案的原因,在于它无法为后续迭代改进提供任何可操作的依据。
结论
评测LLM流水线对于构建健壮应用至关重要,但评测是一个复杂且持续的过程,需要大量工作。若只想进行短暂、不可靠的评测,打印语句是个不错的选择。但若希望在当前开发流程中部署稳健的评测基础设施,可以使用 Confident AI。
from deepeval import assert_test
from deepeval.metrics import HallucinationMetric
from deepeval.test_case import LLMTestCase
def test_hallucination():
metric = HallucinationMetric(minimum_score=0.5)
test_case = LLMTestcase(input="...", actual_output="...")
assert_test(test_case, [metric])
它还配备了一个平台,可让你记录和调试历史评测结果,集中管理评测数据集,并在生产环境中运行实时评测。