LLM 开发技能总是学起来容易实践起来贵!

最有价值的 LLM 开发技能总是学起来容易实践起来贵!

如何避免在评估模型和系统上浪费预算?

LLM 评估技能易于掌握(但实践成本高昂)

你可以用两种方法建造堡垒: 开始一块一块地堆砌砖块, 或者绘制一幅你将要建造的堡垒的图画, 并计划其执行过程;然后, 根据你的计划不断对其进行评估.

我们都知道, 第二种方法是我们有可能建造堡垒的唯一方法.

有时候, 我是最不听劝告的人. 我说的是直接跳到笔记本上建立一个 LLM 应用程序. 可是, 有时候, 这就是毁掉我们项目的最容易的方式.

在我们开始做任何事情之前, 我们需要一种机制来告诉我们正在朝着正确的方向前进 -- 可以说我们上一次尝试的东西比以前(或其他方面)更好.

在软件工程中, 这被称为测试驱动开发. 对于机器学习来说, 它就是评估.

开发由 LLM 驱动的应用程序的第一步, 也是最有价值的技能, 就是确定如何评估你的项目.

评估 LLM 应用程序与软件测试完全不同. 我不会贬低软件测试的挑战, 但评估 LLM 并不像测试那样简单.

LLM 的语言很像我们的自然语言. 你可以用无数种不同的方式来表达同一件事. 随便拉出一个你加入的 WhatsApp 群组, 看看大家是怎么说"早上好"的. 同样, LLM 也可以用多种不同的措辞得出正确答案.

然而, 评估编程结果往往需要一一对应. 一个将两个数字相加的函数, 如果输入分别是 2 和 1, 那么返回值总是应该是 3.

值得庆幸的是, LLM 社区已经找到了评估此类模糊响应的方法. 通常, 这需要另一个 LLM 作为评估者. 但这并不是验证模型的唯一方法.

在开发过程中, LLM 辅助评估更快, 更有帮助, 因为我们可以重复运行它. 但是, 每次 LLM 调用都是有代价的. 虽然在提供商的定价页面上, 价格似乎很低, 但实际上价格总是很快就会增加.

本篇文章主要介绍 LLM 辅助评估技术, 尤其是针对 RAG 的评估技术. 不过, 我们也会讨论降低评估成本的方法. 最后, 我们还将讨论不涉及 LLM 的评估技术.

要进行评估, 你需要一个数据集.

让我们不要把评估过于复杂化. 你如何判断一个人是否正确回答了你的问题?

你已经有了一份带有正确答案的问题清单 -- 可以写在纸上, 也可以记在脑子里. 然后, 你会从这个问题库中提出问题, 并判断对方的答案是否正确.

软件测试与这种方法相差无几, LLM 的评估技术也是如此.

因此, 我们需要一个数据集, 一个输入, 提示, 问题(无论你怎么称呼它们)和预期输出的列表. 然后, 我们可以统计与预期输出相匹配的生成输出, 从而得到 LLM 性能的可测量值.

这就是评估的基本原理. 但是, LLM 可能出于各种不同的原因对我们进行瞎扯蛋. 这一点我们稍后再谈. 但首先, 我们需要准备数据集.

这些是你的最终用户更有可能输入的前 n 个提示. 根据你的使用情况, 这个数字可能是 10, 100, 1000, 甚至更多更大的数字. 此外, 这也是 ML 工程师和领域专家需要完成的任务.

下面的函数将根据输入和预期输出评估 LLM 输出的正确性.

ini 复制代码
# pip install openai
# Set OPENAI_API_KEY environment variable

import os
from openai import OpenAI

# Assuming 'openai.api_key' is set elsewhere in the code
client = OpenAI(
    # This is the default and can be omitted
    api_key=os.environ.get("OPENAI_API_KEY"),
)

def evaluate_correctness(input, expected_output, actual_output):
    prompt = f"""
    Input: {input}
    Expected Output: {expected_output}
    Actual Output: {actual_output}
    
    Based on the above information, evaluate the correctness of the Actual Output compared to the Expected Output. 
    Provide a score from 0 to 1, where 0 is completely incorrect and 1 is perfectly correct.
    Only return the numerical score.
    """

    response = client.chat.completions.create(
        model="gpt-4",
        messages=[
            {
                "role": "system",
                "content": "You are an AI assistant tasked with evaluating the correctness of outputs.",
            },
            {"role": "user", "content": prompt},
        ],
        temperature=0,
    )

    return float(response.choices[0].message.content.strip())

if __name__ == "__main__":
    dummy_input = "What is the capital of France?"
    dummy_expected_output = "Paris"
    dummy_actual_output = "Paris corner"

    dummy_score = evaluate_correctness(
        dummy_input, dummy_expected_output, dummy_actual_output
    )
    print(f"Correctness Score: {dummy_score:.2f}")


>> Correctness Score: 0.50

使用框架进行评估

在上一节中, 我们使用另一个 LLM 根据预期输出评估了 LLM 输出. 然而, 独自编码是你最不愿意做的事情. 既然有更先进, 更复杂的解决方案, 我们为什么还要重新发明轮子呢?

在本节中, 我将使用一个名为 Deepeval 的库来评估 LLM 响应. 但 Deepeval 并不是唯一能做到这一点的库. MLFlow LLM Evaluate, RAGAs 和其他框架都被广泛用于 LLM 评估. 我之所以选择 Deepeval, 是因为这是我最擅长的.

使用框架评估 LLM 的一个好处是它们有许多不同的评估指标. Deepeval 提供 14 种以上的评估指标. 此外, 你还可以根据需要创建自定义评估函数.

我们将在下一节详细讨论评估指标.

这些框架的另一个好处是, 它们可以为你生成一个评估数据集. 例如, 下面的脚本将使用 Deepeval 从 PDF 中生成多达 100 对问答.

ini 复制代码
from deepeval.dataset import EvaluationDataset

dataset = EvaluationDataset()
dataset.generate_goldens_from_docs(
    document_paths=['path/to/doc.pdf'],
    max_goldens_per_document=10
)

这些框架生成的数据集并不完美. 但它们是一个良好的开端. 你可以将其拿给主题专家, 丰富你的实际评估数据集.

下面是一个基本示例, 说明如何使用 deepeval 评估数据集中的测试用例.

ini 复制代码
from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric

# Initialize the relevancy metric with a threshold value
relevancy_metric = AnswerRelevancyMetric(threshold=0.5)

# Define the test case with input, the LLM's response, and relevant context
test_case = LLMTestCase(
    input="What options do I have if I'm unhappy with my order?",
    actual_output="You can return it within 30 days for a full refund.",
    retrieval_context=["Our policy allows returns within 30 days for a full refund."]
)

# Directly evaluate the test case using the specified metric
assert_test(test_case, [relevancy_metric])

这段代码创建了一个名为 LLMTestCase 的对象. 该对象包含测试所需的所有属性. 不同的评估指标需要不同的属性, 但大多数指标至少需要一个输入.

关于评估指标的更多内容将在下一节讨论.

现在我们有了一个可以使用的框架和一个可以测试的评估数据集, 让我们看看如何针对不同的评估指标运行评估.

评估指标和 RAG

到目前为止, 我们只讨论了 LLM 生成内容的正确性. 换句话说, 答案会直接与输入和预期输出进行比较, 并酌情进行评估.

这通常是一个必备指标. 不过, 在整个项目中, 你还需要跟踪其他更重要的指标.

让我们考虑一下 RAG 应用程序. 为了满足用户的提示, 我们会查询数据存储中的相关信息, 并利用这些信息生成更合适的答案.

正如你所猜测的那样, RAG 系统可能会以多种方式生成错误答案. 本篇文章不讨论如何构建 RAG 系统. 这里的重点只是评估 RAG 的输出.

在评估 RAG 系统时, 有四个指标非常流行: 答案相关性, 上下文前移, 上下文召回率和忠实度指标.

答案相关性

如果有人问你第一个登上月球的人是谁, 而你回答哥伦布是第一个发现美洲的人, 那么你的答案就完全无关紧要了.

这就是我们测试答案相关性的方法.

我们使用 LLM 来测试人工智能生成的文本是否至少与提示相关.

以下代码使用 Deepeval 检查答案相关性.

python 复制代码
from deepeval import evaluate
from deepeval.metrics import AnswerRelevancyMetric
from deepeval.test_case import LLMTestCase

# Define your LLM output and test case
output = "Our working hours are Monday to Friday, 9 AM to 6 PM."
test_case = LLMTestCase(
    input="What are your business hours?", 
    actual_output=output
)

# Initialize the relevancy metric
metric = AnswerRelevancyMetric(threshold=0.7)

# Measure and print the score
metric.measure(test_case)
print(f"Score: {metric.score}, Reason: {metric.reason}")

如果得分达到设置为 0.7 的阈值, 则上述代码通过测试. 我们问的是工作时间, 答案也是关于工作时间的. 因此, 它得分很高, 通过了测试.

需要注意的是, 我们不知道这个测试的答案是否正确. 这可能是一家晚上 6 点以后才开门的夜店. 然而, 这是一个完全不同的衡量标准.

上下文前移

上下文前移是评估检索系统是否将相关文档排序高于其他文档的关键指标.

假设你向某人询问阿波罗 11 号任务期间的美国总统是谁. 在这种情况下, 这个人会说:"本-拉登被消灭时, 奥巴马是总统;阿姆斯特朗登上月球时, 约翰-肯尼迪是总统".

这个人有答案, 但却先提出了一个无关紧要的答案. 这就降低了语境前置得分.

下面是使用 Deepeval 的方法.

ini 复制代码
from deepeval import evaluate
from deepeval.metrics import ContextualPrecisionMetric
from deepeval.test_case import LLMTestCase

# New LLM output and expected response
generated_output = "Our phone support is available 24/7 for premium users."
expected_response = "Premium users have 24/7 access to phone support."

# Contextual information retrieved from RAG pipeline
retrieved_context = [
    "General users don't have phone support", 
    "Premium members can reach our phone support team at any time, day or night.",
    "General users can get email support"
]

# Set up the metric and test case
metric = ContextualPrecisionMetric(threshold=0.8)
test_case = LLMTestCase(
    input="What support options do premium users have?",
    actual_output=generated_output,
    expected_output=expected_response,
    retrieval_context=retrieved_context
)

# Measure and display results
metric.measure(test_case)
print(f"Score: {metric.score}, Reason: {metric.reason}")

>> Score: 0.5, Reason: The score is 0.50 because the first and third nodes in the retrieval context, providing information about 'general users' support options', are ranked higher than they should be, as they do not provide specific information about premium user support. However, the score is not zero because the second node, stating 'Premium members can reach our phone support team at any time, day or night', which is highly relevant, is correctly identified and ranked.

不出所料, 由于排序不正确, 该测试没有达到 0.8 的阈值. 不过, 它确实在检索的上下文中找到了正确答案.

上下文召回率

上下文召回率衡量的是检索上下文是否足以回答问题.

我们还是以讨论上下文前置性时使用的例子为例. 检索上下文中的第二个文档提供了足够的信息来回答用户的问题. 因此, 它应该在这一指标上获得高分.

下面是 Deepeval 中的工作原理.

ini 复制代码
from deepeval import evaluate
from deepeval.metrics import ContextualRecallMetric
from deepeval.test_case import LLMTestCase

# New LLM output and expected response
generated_output = "Premium users get access to 24/7 phone support."
expected_response = "Premium users have 24/7 access to phone support."

# Contextual information retrieved from RAG pipeline
retrieved_context = [
    "General users do not have access to phone support.",
    "Premium members can reach our phone support team at any time, day or night.",
    "General users can only get email support."
]

# Set up the recall metric and test case
metric = ContextualRecallMetric(threshold=0.8)
test_case = LLMTestCase(
    input="What support options do premium users have?",
    actual_output=generated_output,
    expected_output=expected_response,
    retrieval_context=retrieved_context
)

# Measure and display results
metric.measure(test_case)
print(f"Recall Score: {metric.score}, Reason: {metric.reason}")

>> Recall Score: 1.0, Reason: The score is 1.00 because the expected output perfectly matches the information contained in the 2nd node in the retrieval context.
忠实度指标

帮助我们评估 RAG 系统的另一个关键指标是忠实度指标. 该指标纯粹评估最终 LLM 根据所提供的上下文生成输出结果的能力.

换句话说, 答案相关性是一个整体检查, 上下文前移和对检索系统的召回测试, 而忠实度指标则是测试为用户生成输出结果的 LLM.

如果检索到的上下文与输出结果(反之亦然)之间没有任何矛盾, 就能获得高分.

下面是如何使用 Deepeval 实现这一功能:

ini 复制代码
from deepeval import evaluate
from deepeval.metrics import FaithfulnessMetric
from deepeval.test_case import LLMTestCase

# New LLM output and corresponding context
actual_output = "Basic plan users can upgrade anytime to the premium plan for additional features."

# Contextual information retrieved from RAG pipeline
retrieved_context = [
    "Users on the Basic plan have the option to upgrade to Premium at any time to gain access to advanced features.",
    "The Premium plan includes additional benefits like 24/7 support and extended storage capacity."
]

# Set up the faithfulness metric and test case
metric = FaithfulnessMetric(threshold=0.75)
test_case = LLMTestCase(
    input="Can Basic plan users upgrade to Premium anytime?",
    actual_output=actual_output,
    retrieval_context=retrieved_context
)

# Measure and display results
metric.measure(test_case)
print(f"Faithfulness Score: {metric.score}, Reason: {metric.reason}")

正如我所说的, 在 LLM 开发中, 这些并不是唯一会用到的指标. 例如, Deepeval 提供了用于幻觉检查, 偏差和毒性的开箱即用指标. 然而, 这些都是用于评估 LLM 和 RAG 系统的常用指标.

RAGAS

这四项测试的组合版本称为 RAGAS. RAGAS 是我们讨论过的四项测试的平均值. 它是一个单一的数字, 可以与不同的 RAG 系统进行比较.

在 Deepeval 中, 你可以用一个指标来实现它, 而不用手动调用所有四个指标并手动求平均值.

ini 复制代码
from deepeval import evaluate
from deepeval.metrics.ragas import RagasMetric
from deepeval.test_case import LLMTestCase

# LLM-generated response, expected response, and retrieved context used to compare model accuracy for a query about product warranty.
llm_response = "The device includes a one-year warranty with free repairs."
target_response = "This product comes with a 12-month warranty and no-cost repairs."
retrieval_context = [
  "All electronic products are backed by a 12-month warranty, including free repair services."
]

# Initialize the Ragas metric with a specific threshold and model configuration
metric = RagasMetric(threshold=0.6)

# Create a test case for the given input and output comparison
test_case = LLMTestCase(
    input="Does this product come with a warranty?",
    actual_output=llm_response,
    expected_output=target_response,
    retrieval_context=retrieval_context
)

# Calculate the metric score for this specific test case
score = metric.measure(test_case)
print(f"Metric Score: {score}")

>> Metric Score: 0.9768281253135719

虽然 RAGAS 指标得分有利于比较模型和系统, 但不应完全依赖它. 单个指标会告诉你在哪些方面可以改进. 而 RAGAS 则无法做到这一点.

评估的成本

我最近对一个 50 个问答的小型评估数据集进行了 RAGAS 评估. 我使用了 Deepeval 的默认设置, 它调用 OpenAI 的 GPT-4o 模型进行评估. 让我们看看控制面板, 研究一下成本.

你会觉得 1.13 美元对评估来说并不重要. 但评估是需要经常进行的. 如果你更换了十个模型, 你就会进行十次评估. 但是, 对于大型应用项目, 你会在整个项目中进行数百次改进 -- 每件事都应该进行评估.

这是因为 RAGAS 触发的 LLM 调用不是一次, 而是多次. 例如, 我的 50 个测试用例数据集触发了 290 次 LLM 调用, 总共生成了 425,086 个标记.

如果上下文更多, 生成的输出更长, 成本会更高.

此外, 该成本仅针对 50 对问答. 在实际项目中, 这往往是一个过于重大的数字. 另外, 随着用户反馈越来越多, 你的评估数据集也会开始增长.

现在, 大多数小公司都负担不起这笔费用, 即使是大公司也不应该白白浪费.

值得庆幸的是, 你可以通过一些方法将其减少到较小的数量. 以下是我的两个降低评估成本的技巧.

#1 转换模型.

Deepeval 默认使用 GPT-4o 进行评估. 在 o1-preview 推出之前, 这是他们最昂贵的模型.

最简单的成本节约技巧是将模型更改为 GPT-3.5-turbo 或 GPT-4o-mini. 例如, GPT-4o-mini 每 100 万个输入令牌只需 0.15 美元, 而 GPT-4o 则需要 2.5 美元.

在 Deepeval 中, 你可以在度量级别上指定使用哪种模型. 下面是我们为刚刚使用的 RAGAS 指标指定模型的方法.

ini 复制代码
ragas = RagasMetric(threshold=0.7, model='gpt-4o-mini')

# ragas = RagasMetric(threshold=0.7, model='gpt-3.5-turbo')

这里有一个注意事项.

评估模型需要具备出色的推理能力. 虽然 GPT-4o 模型在推理方面更胜一筹, 但迷你版在大多数情况下也不会让你失望.

#2 切换模型提供商

我同意 Open AI 是最受欢迎的 LLM API 提供商.

但他们并不是唯一的供应商. 你可以换一个低成本的模型提供商, 比如 Together.ai 或 Openrouter.

使用不同模型提供商的好处是可以探索许多开源 LLM. 你可以试试 Llama 3.1 8B, 价格大约是 GPT-4o-mini 的三分之一. 不过, 该模型的推理能力很强, 可能是你评估过程中的一个不错候选.

选择供应商时, 请考虑你的评估框架是否支持该供应商. Deepeval 并不直接支持 Together AI 等提供商, 但它支持本地运行模型. 如果不同的提供商提供与 Open AI 兼容的 API, 我们就可以使用这一功能来配置它们--Together AI 和 Openrouter 都提供了兼容的 API.

ini 复制代码
deepeval set-local-model --model-name=<model_name> \
    --base-url="http://localhost:11434/v1/" \
    --api-key="TOGETHER_AI_API KEY"

上面的示例展示了如何为 TogetherAI 配置 deepeval. 你所需要的只是一个与 OpenAI 兼容的 API 和一个密钥. 如果使用 Openrouter, 情况也一样.

#3 自己托管模型

我建议你采用的最后一项技术是自行托管模型.

之所以排在最后是有原因的.

人们声称本地托管的模型更便宜. 但事实并非如此.

另外, 建立本地 LLM 需要强大的 GPU, 或者你可能需要在线租用 GPU. 这就带来了额外的技术开销, 而这是你不想面对的.

不过, 如果你已经在运行本地模型, 不妨将其用于评估.

在不涉及另一个 LLM 的情况下评估 LLM.

评估 LLM 驱动的应用程序具有挑战性, 因为输出有时会有所不同. 不过, 即使没有 LLM 评估器, 你也可以对它们进行评估.

最常用的评估技术是人工介入. 虽然这种方法不适合开发中的应用程序, 但对于已经运行或处于用户接受度测试阶段的应用程序来说, 它往往是最好的方法.

如果你使用过 ChatGPT 等聊天机器人, 你会在每个回复的末尾看到一个小小的反馈区. 你可以用拇指向上或向下点击回复, 这些反馈将作为改进后续版本模型的输入.

这种技术最好的原因有两个: 不花钱, 而且最准确.

另一种无 LLM 评估技术是使用相似度得分 . 当然, 这需要一个嵌入模型. 不过, 与文本生成模型相比, 嵌入模型的成本要低得多. 此外, 你还可以在本地一台相当不错的电脑上运行像 E5这样的模型.

下面是一个检查预期输出和实际输出之间相似性的小片段. 我们使用 OpenAI embeddings.

ini 复制代码
import openai
import numpy as np

def get_embedding(text, model="text-embedding-ada-002"):
    response = openai.Embedding.create(
        input=text,
        model=model
    )
    return response['data'][0]['embedding']

def cosine_similarity(vec1, vec2):
    return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))

expected_output = "The data processing pipeline completed successfully without any errors."
actual_output = "The data processing pipeline finished successfully with no errors."

embedding_expected = get_embedding(expected_output)
embedding_actual = get_embedding(actual_output)

similarity_score = cosine_similarity(embedding_expected, embedding_actual)

print(f"Similarity between expected and actual outputs: {similarity_score:.4f}")

像这样的分数很容易计算, 而且成本更低, 有助于自动优化 LLM 驱动的应用程序.

最后的想法

不仅仅是 LLM 应用程序, 也不仅仅是软件开发项目, 任何项目都需要以系统化, 可重现的方式进行评估. 对我来说, 这是最有价值的技能.

巧妙的是, 评估 LLM 应用程序要比预测结果复杂得多. 我们必须依靠另一个 LLM 来为我们进行大规模的自动评估 -- 一个具有出色推理能力的 LLM.

值得庆幸的是, 有了 RAGAS 和 Deepeval 这样的工具, 我们就不必反复编写系统代码了. 我们可以使用数十种指标对它们进行快速评估.

在这篇文章中, 我们讨论了我们需要评估的关键指标, 尤其是 RAG 系统, 如何控制成本以及评估项目的非 LLM 技术.

好了, 今天的内容就分享到这里啦!

一家之言, 欢迎拍砖!

Happy Coding! Stay GOLDEN!

相关推荐
井底哇哇4 小时前
ChatGPT是强人工智能吗?
人工智能·chatgpt
Coovally AI模型快速验证4 小时前
MMYOLO:打破单一模式限制,多模态目标检测的革命性突破!
人工智能·算法·yolo·目标检测·机器学习·计算机视觉·目标跟踪
AI浩4 小时前
【面试总结】FFN(前馈神经网络)在Transformer模型中先升维再降维的原因
人工智能·深度学习·计算机视觉·transformer
可为测控4 小时前
图像处理基础(4):高斯滤波器详解
人工智能·算法·计算机视觉
一水鉴天5 小时前
为AI聊天工具添加一个知识系统 之63 详细设计 之4:AI操作系统 之2 智能合约
开发语言·人工智能·python
倔强的石头1065 小时前
解锁辅助驾驶新境界:基于昇腾 AI 异构计算架构 CANN 的应用探秘
人工智能·架构
佛州小李哥6 小时前
Agent群舞,在亚马逊云科技搭建数字营销多代理(Multi-Agent)(下篇)
人工智能·科技·ai·语言模型·云计算·aws·亚马逊云科技
说私域6 小时前
社群裂变+2+1链动新纪元:S2B2C小程序如何重塑企业客户管理版图?
大数据·人工智能·小程序·开源
程序猿阿伟7 小时前
《探秘鸿蒙Next:如何保障AI模型轻量化后多设备协同功能一致》
人工智能·华为·harmonyos
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter