RAG 评估指南:核心指标解析与代码实现
一、RAG 痛点介绍
Retrieval-Augmented Generation(RAG,检索增强生成)结合了信息检索与大语言模型的优势,但在实际落地中常面临诸多挑战。主要痛点包括检索内容不相关、生成内容幻觉、上下文窗口限制以及评估体系缺失等。
为了优化 RAG 系统,我们需要深入理解其工作流程中的关键环节,并建立科学的评估机制。
二、RAG 痛点优化策略
针对 RAG 系统的常见痛点,以下是经过实践验证的最佳实践总结及关键优化点:
1. 数据如何处理?
数据质量直接决定 RAG 的上限。在使用数据之前,必须确保数据是干净和结构化的。
- 清洗和预处理:去除噪音、处理缺失值、标准化格式。例如,统一编码、去除特殊符号。
- 分段和标注:将长文档分成较小的段落(chunks),并为每个段落添加相关标签或元数据,以便更好地进行检索和生成。
import pandas as pd
data = pd.read_csv('data.csv')
data.dropna(inplace=True)
data['text'] = data['text'].str.lower()
data['text'] = data['text'].str.replace(r'\d+', '', regex=True)
def chunk_text(text, chunk_size=200):
words = text.split()
chunks = [' '.join(words[i:i + chunk_size]) for i in range(0, len(words), chunk_size)]
return chunks
data['chunks'] = data['text'].apply(chunk_text)
2. Chunk Size 如何设置?
Chunk size 的设置需要平衡信息量和检索效率。
- 较短的 Chunk:提高检索准确性,减少噪声干扰,但可能丢失上下文信息。
- 较长的 Chunk:保留更多上下文,但可能增加冗余信息,导致向量相似度计算偏差。
常见的 chunk 大小在 100 到 300 个词之间,具体需根据应用场景调整。
chunk_size = 150
def chunk_text(text, chunk_size):
words = text.split()
chunks = [' '.join(words[i:i + chunk_size]) for i in range(0, len(words), chunk_size)]
return chunks
data['chunks'] = data['text'].apply(lambda x: chunk_text(x, chunk_size))
3. Embedding 模型使用什么?
选择合适的 Embedding 模型至关重要。
- 常用模型:BERT、RoBERTa、Sentence-BERT 等。选择时需考虑模型性能、训练数据和计算资源。
- 自定义微调:对于特定领域的应用,建议对预训练模型进行微调,以提高嵌入的相关性和准确性。
from transformers import BertTokenizer, BertModel
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
inputs = tokenizer(data['chunks'].sum(), return_tensors='pt', padding=True, truncation=True)
with torch.no_grad():
outputs = model(**inputs)
embeddings = outputs.last_hidden_state.mean(dim=1)
4. Prompt 如何设计?
Prompt 设计直接影响生成模型的输出质量。
- 清晰具体:避免模糊或开放性太强,明确指令意图。
- 角色设定:赋予模型特定的专家角色,提升回答的专业度。
- Few-Shot 示例:提供少量高质量示例,引导模型遵循特定格式。
5. 检索的 TopN 如何设置?
TopN 决定了在生成过程中使用多少个最相关的文档段落。
- 常见范围:3 到 10 之间。
- 调整策略:根据检索效果和生成质量动态调整。TopN 过小可能导致信息不足,过大可能引入噪声。
6. LLM 模型选择的什么?
- 生成能力:GPT-3.5、GPT-4、Qwen 等。选择时考虑推理速度和计算资源。
- 混合使用:结合检索模型和生成模型的优势,或在不同阶段使用不同模型。
7. RAG 检索生成效果优化策略
- 选择更大的模型:如果现有模型生成效果不理想,尝试使用更大的预训练模型,通常能带来更好的上下文理解能力。
- 调整 Chunk Size:重新评估和调整 chunk 的大小,找到信息量和检索效率之间的最佳平衡点。
- 多路召回:结合关键词检索和向量检索,提高召回率。
通过不断的实验和优化,可以找到最适合特定应用场景的 RAG 配置。
三、为什么需要对 RAG 系统评估?
评估是 AI 开发流程中的关键步骤,用于检查当前策略相对于其他策略的有效性。在进行 LLM 项目开发时,必须有衡量其表现的方法。
1. 评估类型
与传统的机器学习技术不同,传统技术有明确的定量度量指标(如 Gini 系数、R 平方、AIC、BIC、混淆矩阵等)。评估 RAG 系统更加复杂,因为生成的响应是非结构化文本,需要结合定性和定量指标。
通常遵循 TRIAD 框架:
- 上下文相关性 (Context Relevance):评估检索部分。衡量从大型数据集中准确检索到的文档。指标包括精确度、召回率、MRR 和 MAP。
- 忠实度 (Faithfulness):属于响应评估。检查生成的响应是否准确无误,且基于检索到的文档。防止幻觉。
- 答案相关性 (Answer Relevance):属于响应评估。衡量生成的响应对用户的查询提供了多少有用的信息。指标包括 BLEU、ROUGE、METEOR 和基于嵌入的评估。
2. 检索评估指标详解
检索评估适用于 RAG 系统的检索器组件,通常使用向量数据库。目标是评估上下文相关性。
精确度 (Precision)
精确度衡量了检索到的文档的准确性。它是检索到的相关文档数量与检索到的文档总数之比。
$$ Precision = \frac{\text{检索到的相关文档数}}{\text{检索到的文档总数}} $$
这意味着精确度评估了系统检索到的文档中有多少实际与用户查询相关。例如,如果检索器检索到了 10 个文档,其中 7 个是相关的,那么精确度将是 0.7。
def calculate_precision(retrieved_docs, relevant_docs):
"""
计算精确度
参数:
retrieved_docs (list): 检索到的文档列表
relevant_docs (list): 相关文档列表
返回:
float: 精确度
"""
relevant_retrieved_docs = [doc for doc in retrieved_docs if doc in relevant_docs]
precision = len(relevant_retrieved_docs) / len(retrieved_docs) if retrieved_docs else 0
return precision
retrieved_docs = ['doc1', 'doc2', 'doc3', 'doc4', 'doc5']
relevant_docs = ['doc2', 'doc4', 'doc6']
precision = calculate_precision(retrieved_docs, relevant_docs)
print(f"精确度:{precision:.2f}")
精确度评估的是'系统检索到的所有文档中,有多少实际上是相关的?'
召回率 (Recall)
召回率衡量了检索到的文档的全面性。它是检索到的相关文档数量与数据库中相关文档的总数之比。
$$ Recall = \frac{\text{检索到的相关文档数}}{\text{数据库中相关文档总数}} $$
这意味着召回率评估了数据库中存在的相关文档有多少被系统成功检索到。
def calculate_recall(retrieved_docs, relevant_docs):
"""
计算召回率
参数:
retrieved_docs (list): 检索到的文档列表
relevant_docs (list): 相关文档列表
返回:
float: 召回率
"""
relevant_retrieved_docs = [doc for doc in retrieved_docs if doc in relevant_docs]
recall = len(relevant_retrieved_docs) / len(relevant_docs) if relevant_docs else 0
return recall
retrieved_docs = ['doc1', 'doc2', 'doc3', 'doc4', 'doc5']
relevant_docs = ['doc2', 'doc4', 'doc6', 'doc7']
recall = calculate_recall(retrieved_docs, relevant_docs)
print(f"召回率:{recall:.2f}")
召回率评估的是'数据库中存在的所有相关文档中,系统成功检索到了多少个?'
精确度和召回率的平衡
通常需要平衡精确度和召回率,改善其中一个指标有时会降低另一个指标。F1 分数是精确度和召回率的调和平均值,常用于量化这种平衡。
平均倒数排名 (MRR)
MRR 是一种评估检索系统效果的度量指标,它考虑了第一个相关文档的排名位置。当只有第一个相关文档是主要关注的情况下,MRR 特别有用。
$$ MRR = \frac{1}{|Q|} \sum_{i=1}^{|Q|} \frac{1}{rank_i} $$
其中 Q 是查询的数量,$rank_i$ 是第 q 个查询的第一个相关文档的排名。
MRR 评估的是'平均来说,系统多快能够在响应用户查询时检索到第一个相关文档?'
平均准确率 (MAP)
MAP 是一种评估多个查询的检索精确度的度量指标。它同时考虑了检索的精确度和检索文档的顺序。
$$ MAP = \frac{1}{|Q|} \sum_{q=1}^{|Q|} AP(q) $$
其中 $AP(q)$ 是查询 q 的平均准确率。
MAP 评估的是'平均来说,系统在多个查询中排名靠前的文档的精确度如何?'
3. 响应评估指标详解
响应评估适用于系统的生成组件。衡量系统根据检索到的文档提供的上下文有效地生成响应的能力。
忠实度 (Faithfulness)
忠实度评估生成的响应是否准确,并且基于检索到的文档。它确保响应不包含幻觉或错误信息。
- 人工评估:专家手动评估生成的响应是否事实准确,是否正确引用了检索到的文档。
- 自动事实检查工具:将生成的响应与已验证事实的数据库进行比较。
- 一致性检查:评估模型在不同查询中是否始终提供相同的事实信息。
答案相关性 (Answer Relevance)
答案相关性衡量生成的响应对用户的查询有多好,并提供有用的信息。常用的自动化指标包括 BLEU、ROUGE 等,但在 RAG 场景下,基于 LLM 的评估往往更可靠。
四、使用 LangSmith 进行评估
了解了这些评估指标,该如何进行评估?推荐使用 LangSmith。LangSmith 是一个开发者平台,允许您调试、测试、评估和监控大语言模型 (LLM) 应用程序,并且能够与 LangChain 无缝集成。
1. 在 LangChain 代码中加入使用 LangSmith
第一步:获取一个 LangChain 的 API Key,登录 LangSmith 创建 API key。
第二步:设置以下参数后,LangSmith 会自动进行跟踪管理。
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "your-api-key-here"
2. 准备测试数据
整理三种准备测试数据的方式:数据导入,代码创建和使用 LangSmith 辅助创建。
第一种方法:CSV 文件导入
支持直接上传包含问题、答案、上下文的 CSV 文件构建测试集。
第二种方法:代码创建
下面的代码执行后,可以在 LangSmith 中新建一个 Retrieval QA Questions 测试集。
from langsmith import Client
import uuid
examples = [
(
"What is LangChain?",
"LangChain is an open-source framework for building applications using large language models.",
),
(
"Can I trace my Llama V2 llm?",
"So long as you are using one of LangChain's LLM implementations, all your calls can be traced",
),
]
client = Client()
dataset_name = f"Retrieval QA Questions {str(uuid.uuid4())}"
dataset = client.create_dataset(dataset_name=dataset_name)
for q, a in examples:
client.create_example(
inputs={"question": q}, outputs={"answer": a}, dataset_id=dataset.id
)
第三种方法:使用 LangSmith 辅助创建
建立 RAG 系统,准备问题并执行查询,如 rag_chain.invoke("Ollama 支持哪些模型?")。LangSmith 后台根据处理的结果,对测试数据进行管理,添加,修改等。
3. 使用 LangSmith 进行评估
测试数据准备好之后,就可以开始对系统进行评估。LangSmith 的后台可以生成评估的代码。
RAG QA 问答的正确性评估器(Evaluator)包含三个版本:
- qa:指示大语言模型 (LLM) 根据参考答案直接将响应评为'正确'或'错误'。
- context_qa:指示 LLM 使用参考'上下文'来确定响应的正确性。用于只有上下文和解答,没有答案的情况。
- cot_qa:与 context_qa 类似,但它指示 LLMChain 在得出最终结论前使用思维链'推理'。这通常会导致响应更好地与人工标签匹配,但会稍微增加 Token 和运行成本。
4. 完整代码示例
以下是一个完整的 RAG 系统构建与评估流程示例,集成了 LangSmith 评估配置。
import langsmith
from langchain import chat_models, prompts, smith
from langchain.schema import output_parser
from langchain_community.llms import Ollama
from langchain_community.embeddings import OllamaEmbeddings
from langchain import hub
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.prompts import ChatPromptTemplate
loader = WebBaseLoader("./sample.html")
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OllamaEmbeddings(model="mofanke/acge_text_embedding"))
retriever = vectorstore.as_retriever()
template = """你是一个回答问题的助手。请使用以下检索到的背景信息来回答问题。如果你不知道答案,直接说你不知道。请用最多三句话来保持回答的简洁性。
问题:{question}
背景:{context}
答案:
"""
prompt = ChatPromptTemplate.from_template(template)
llm = Ollama(model="qwen:72b", temperature=0)
def format_docs(docs):
return "\n\n".join(doc.page_content doc docs)
rag_chain = (
{: retriever | format_docs, : RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
result = rag_chain.invoke()
(result)
eval_config = smith.RunEvalConfig(
evaluators=[
],
custom_evaluators=[],
eval_llm=Ollama(model=, temperature=)
)
client = langsmith.Client()
chain_results = client.run_on_dataset(
dataset_name=,
llm_or_chain_factory=rag_chain,
evaluation=eval_config,
project_name=,
concurrency_level=,
verbose=,
)
五、总结
开发 RAG 系统本身并不困难,但评估 RAG 系统对于性能、实现持续改进、与业务目标保持一致、平衡成本、确保可靠性和适应新方法至关重要。这个全面的评估过程有助于构建强大、高效和以用户为中心的 RAG 系统。
在实际项目中,建议定期运行自动化评估,监控关键指标的变化趋势,并结合人工抽检,确保系统始终处于最优状态。