由 DALLE-3 生成的图像 | 以等距风格进行的机器人检查
考虑到 LLM 领域“链”、“代理”、“聊天机器人”和其他文本生成 AI 用例的快速发展,评估语言模型的表现对于理解其能力和局限性至关重要。特别是能够根据业务目标调整这些指标至关重要。
虽然标准指标如困惑度、BLEU 分数和句子距离可以提供对模型性能的一般指示,但根据我的经验,它们往往在捕捉现实应用中的细微差别和特定要求方面表现不佳。
例如,考虑一个简单的 RAG QA 应用程序。在构建问答系统时,所谓的“RAG 三位一体”因素,如上下文相关性、事实基础性和查询与响应之间的语言一致性,同样重要。标准指标根本无法有效地捕捉这些细微的方面。
这就是基于 LLM 的“黑盒”指标派上用场的地方。虽然这个想法可能听起来很天真,但基于 LLM 的“黑盒”指标背后的概念相当吸引人。**这些指标利用大型语言模型自身的力量来评估生成文本的质量和其他方面。**通过使用预训练的语言模型作为“评判者”,我们可以根据语言模型对语言的理解和预定义的标准来评估生成的文本。
在这篇文章中,我将展示构建提示、运行和跟踪评估的端到端示例。
由于 LangChain 几乎是构建聊天机器人和 RAG 最流行的框架,因此我将在其上构建应用程序示例。它更容易集成到 MVP 中,并且它内部具有简单的评估功能。然而,您可以使用任何其他框架来构建自己的应用程序。本文的主要价值在于管道和提示。
如何操作?
让我们深入代码并探索创建自定义评估器的过程。我们将通过几个关键示例,并讨论它们的实现。
示例 #1 | 翻译质量
让我们以这个简单的翻译 LLM 链为例
from langchain_openai import OpenAI
sys_msg = """You are a helpful assistant that translates English to French.
Your task is to translate as a fluent speaker of both languages.
Translate this sentence from English to French: {sentence}
"""
translator_chain = LLMChain(llm=OpenAI(), prompt=template)
translator_chain.invoke({"sentence": "Hello, how are you?"})
>> {'sentence': 'Hello, how are you?', 'text': 'nBonjour, comment vas-tu ?'}
好的,现在我们想使用“更智能”的 LLM,例如 GPT-4 或 Claude Opus 来估计它。首先和最重要的是——我们需要一个合适的提示。
提示创建适当的评估提示是成功的关键。需要记住的关键点:
-
指定标准
-
定义一个具有明确意义的数值评分尺度
-
请求评分理由以深入了解推理
-
在提示中提供查询和上下文以供参考
-
需要严格的响应格式以便于解析
作者图片 | 提示结构
编码
首先,我们需要从 LLMs 的答案中解析出分数和推理。
def _parse_string_eval_output(text: str) -> dict:
score_pattern = r"SCORE: (d+)"
reasoning_pattern = r"REASONING: (.*)"
score_match = re.search(score_pattern, text)
reasoning_match = re.search(reasoning_pattern, text)
score = int(score_match.group(1)) if score_match else None
reasoning = reasoning_match.group(1).strip() if reasoning_match else None
return {"score": score, "reasoning": reasoning}pyp
由于我们将使用 LLM 进行评估,我们需要为其创建一个简单的包装器来解析输出分数。
class BaseEvalChain(LLMChain, StringEvaluator, LLMEvalChain):
def _prepare_output(self, result: dict) -> dict:
# parsing the output to extract the score and reasoning
parsed_result = _parse_string_eval_output(result[self.output_key])
if RUN_KEY in result:
parsed_result[RUN_KEY] = result[RUN_KEY]
return parsed_result
现在我们可以实例化并创建我们的自定义评估链,并使用适当的输入名称等。
class LanguageConsistencyEvalChain(BaseEvalChain):
@property
def evaluation_name(self) -> str:
return "Language Consistency"
def _evaluate_strings(self, *, prediction: str, reference: Optional[str] = None, input: Optional[str] = None, callbacks: Callbacks = None, include_run_info: bool = False, **kwargs: Any) -> dict:
result = self({"query": input, "result": prediction}, callbacks=callbacks, include_run_info=include_run_info)
return self._prepare_output(result)
你实际上现在可以使用这个评估链原样使用。然而,你可能还想添加一些像:
-
K 次运行和评分平均以考虑元素的随机性
-
与框架集成以利用大部分好处(可选)
在这个情况下,就像之前的情况一样,我们可以从默认的“RunEvaluator”实例化,并对其进行自定义,例如 LK-async 运行和评分平均,输入名称等。
class BaseEvaluator(RunEvaluator):
...
async def evaluate_run_async(self, run: Run, example: Optional[Example] = None) -> EvaluationResult:
tasks = []
for _ in range(self.k):
task = asyncio.create_task(self._evaluate_run_async(run, example))
tasks.append(task)
evaluations = await asyncio.gather(*tasks)
scores = [eval["score"] for eval in evaluations]
reasonings = [eval["reasoning"] for eval in evaluations]
avg_score = sum(scores) / len(scores)
closest_reasoning_index = min(range(len(scores)), key=lambda i: abs(scores[i] - avg_score))
closest_reasoning = reasonings[closest_reasoning_index]
return EvaluationResult(
key=self.evaluator.evaluation_name.lower().replace(" ", "_"),
score=avg_score,
comment=closest_reasoning,
)
def evaluate_run(self, run: Run, example: Optional[Example] = None) -> EvaluationResult:
return asyncio.run(self.evaluate_run_async(run, example))
让我们开始吧 🚀
它能找到错误吗?
# Create the evaluation LLM
eval_llm = ChatOpenAI(model="gpt-4-turbo-preview")
# Create the evaluator and pass the eval LLM
langconst_evaluator = LanguageConsistencyEvaluator(llm=eval_llm)
# Run it
result = langconst_evaluator.evaluator.invoke({
"query": "How are you? You good?",
"result": "Comment ça va? Tu vas good?"
})
>> SCORE: 6
REASONING: Translation maintains casual tone and overall meaning
but mixes languages ("Tu vas good?"), affecting fluency and appropriateness in
a purely French context.
我们可以看到它正确地识别了问题并做出了合理的推理。
在完美的情况下它会起作用吗?
# Run it
result = langconst_evaluator.evaluator.invoke({
"query": "How are you? You good?",
"result": "Comment ça va? Tu vas bien?"
})
>> SCORE: 10
REASONING: The translated text accurately conveys the meaning,
maintains the informal tone, and reads naturally in French,
perfectly matching the source text's intent and style
明显给出了最高分。根据任务的不同,你可能会调整提示中的规则来调整对轻微错别字的评分应该有多严格,例如。
示例 #2 | 上下文相关性
在这种情况下,我们只需要更改提示和输入名称。在这里,我们将估计与所问的“问题”相关的检索“上下文”。
作者图片 | 上下文相关性提示
以及微小的代码更改
class ContextRelevanceEvalChain(BaseEvalChain):
@property
def evaluation_name(self) -> str:
return "Context Relevance"
...
class ContextRelevanceEvaluator(BaseEvaluator):
def __init__(self, llm: Optional[OpenAI] = None, prompt: Optional[PromptTemplate] = None):
super().__init__(llm, prompt or CONTEXT_RELEVANCE_PROMPT, ContextRelevanceEvalChain)
...
让我们开始吧 🚀
# Run the test evaluation
context_evaluator = ContextRelevanceEvaluator(llm=eval_llm)
# Some placeholder context imitation
context = [
"Bla",
"Bla bla",
"Bla bla bla",
]
result = context_evaluator.evaluator.invoke({
"query": "When was a moon landing?",
"context": context
})
print(result["text"])
>>SCORE: 0
REASONING: The provided context consists only of placeholder
text with no information relevant to any moon landing.
逻辑推理合理,并正确识别了情况(占位符 bla-bla)
那么更多相关的上下文呢?
context = [
"The first successful manned Moon landing was accomplished by the Apollo 11 mission of NASA, with astronauts Neil Armstrong and Buzz Aldrin landing on the Moon on July 20, 1969."
"There have been a total of six manned U.S. landings",
"To make an apple juice you need to peel the apple and ...",
]
result = context_evaluator.evaluator.invoke({"query": "When was a moon landing?", "context": context})
>>>SCORE: 8
REASONING: The context directly addresses the query with the date of the
first manned Moon landing but includes unrelated information
about making apple juice.
评分合理,并正确识别了无关文档。这在开发过程中将非常有用,可以识别错误的原因。
端到端评估运行
记得我们的翻译链从英语到法语吗?现在我们可以在我们的数据集上运行这个链的评估并跟踪实验。
请注意,对于结果的可视化和跟踪,我将使用 Langsmith。然而,你可以自由使用任何其他平台——只需使用原始输出。
制作数据集我们只需创建一个要运行翻译链的问题列表并将其保存到 Langsmith 客户端。你也可以在本地做这件事。
from langsmith import Client
from langchain.smith import RunEvalConfig, run_on_dataset
# Create a dataset
dataset_inputs = [
"How are you?",
"Where is the nearest restaurant?",
"All in all, actions speak louder than words when you're burning the candle at both ends. ",
]
client = Client()
dataset_name = "Test_FR"
配置设置要使用的评估器,你可以在一次运行中使用多个自定义评估器。
# Configure evaluation using off-the-shelf metrics
evaluation_config = RunEvalConfig(
custom_evaluators=[
LanguageConsistencyEvaluator(llm=eval_llm),
],
)
运行它我们使用我们的配置并在测试问题上运行评估。
run_on_dataset(
dataset_name=dataset_name,
llm_or_chain_factory=translator_chain,
client=client,
evaluation=evaluation_config,
project_name="v1",
)
>>
{'project_name': 'v1',
'results': {'XXX': {'input': {'question': 'How are you?'},
'feedback': [EvaluationResult(key='language_consistency',
score=10.0, value=None,
comment='The translation accurately captures the meaning, uses natural and fluent French, and maintains the informal tone appropriate for the context.', correction=None, evaluator_info={}, feedback_config=None, source_run_id=None, target_run_id=None)],
'execution_time': 0.506393,
'run_id': 'XXX',
'output': {'sentence': 'How are you?', 'text': 'nComment vas-tu ?n'}},
...
}
你在输出对象中拥有所有信息,如分数、评论等。如果你决定使用 Langsmith,你将能够在数据集文件夹下跟踪实验。
作者图片 | Langsmith 仪表板截图
结论
那么,这在现实世界的案例中意味着什么呢?这对于具有非常特定要求的应用程序来说,是一种颠覆性的变革。简而言之,通过这种对模型性能的定制控制,公司可以构建符合其独特商业目标的 AI 系统。基本上,任何特定的需求——无论是确保翻译保持文化细微差别,还是验证响应保持上下文适当。
所以进行实验;根据您的用例创建自己的定制评估器。所有代码都可在 GitHub 上找到
AlchemyLab/Custom_Eval/custom-eval-langchain.ipynb at main · kinivi/AlchemyLab
享受评估的乐趣!
6262

被折叠的 条评论
为什么被折叠?



