En este tutorial aprenderás a evaluar un Aplicación RAG. La evaluación le ayuda a elegir el modelo adecuado, garantizar que su rendimiento se adapte del prototipo a la producción y detectar regresiones de rendimiento.
En concreto, realizas las siguientes acciones:
Configura el entorno.
Descargue un conjunto de datos de evaluación.
Crear fragmentos de documentos e incrustaciones.
Ingerir las incrustaciones en Atlas.
Comparar modelos de incrustación para recuperación.
Comparar modelos de finalización para generación.
Medir el rendimiento general de RAG.
Realice un seguimiento del rendimiento a lo largo del tiempo con los gráficos de MongoDB.
Nota
Este tutorial se centra en la evaluación de aplicaciones LLM, no de modelos LLM. La evaluación de modelos LLM implica medir el rendimiento de un modelo determinado en diferentes tareas. La evaluación de aplicacionesLLM consiste en evaluar los diferentes componentes de una aplicación LLM, como los indicadores y los recuperadores, así como el sistema en su conjunto.
Trabaje con una versión ejecutable de este tutorial como Cuaderno de Python.
Segundo plano
Este tutorial utiliza el marco de evaluación de código abierto RAGAS para medir el rendimiento de RAG con las siguientes métricas:
Métricas de recuperación: la precisión del contexto y la recuperación del contexto miden qué tan bien su recuperador encuentra información relevante.
Métricas de generación: la fidelidad y la relevancia de las respuestas miden qué tan bien su LLM genera respuestas precisas y relevantes.
Métricas generales: la similitud de respuestas y la exactitud de las respuestas comparan las respuestas generadas con la verdad fundamental.
Para obtener más información sobre estas métricas, consulte Métricas RAGAS en la documentación de RAGAS.
Este tutorial utiliza el conjunto de datos ragas-wikiqa de Hugging Face, que contiene aproximadamente 230 preguntas de conocimiento general con respuestas basadas en la verdad fundamental.
Requisitos previos
Para completar este tutorial, debes tener lo siguiente:
Un clúster de MongoDB Atlas con MongoDB versión 6.0.11 o posterior. Asegúrese de que su dirección IP esté en la lista de acceso de su proyecto.
Una clave API de OpenAI para utilizar los modelos de integración y finalización de chat de OpenAI.
Un terminal configurado con lo siguiente:
Python 3.10 o posterior.
Un entorno para ejecutar cuadernos de Python interactivos, como VS Code o Jupyter Notebook.
Configurar el entorno
Configura tus credenciales
Ejecute el siguiente código en su cuaderno para configurar su cadena de conexión MongoDB y su clave API OpenAI:
import getpass import os from openai import OpenAI MONGODB_URI = getpass.getpass("Enter your MongoDB connection string:") os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API Key:") openai_client = OpenAI()
Descargar el conjunto de datos de evaluación
Descargue el conjunto de datos ragas-wikiqa de Hugging Face y conviértalo en un marco de datos de pandas:
from datasets import load_dataset import pandas as pd data = load_dataset("explodinggradients/ragas-wikiqa", split="train") df = pd.DataFrame(data)
El conjunto de datos contiene las siguientes columnas:
question:Preguntas de los usuarioscorrect_answerRespuestas de verdad fundamentalcontext:Lista de textos de referencia para responder las preguntas
Crear fragmentos de documentos
Divida los textos de referencia en fragmentos más pequeños antes de incrustarlos:
from langchain.text_splitter import RecursiveCharacterTextSplitter # Split text by tokens using the tiktoken tokenizer text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder( encoding_name="cl100k_base", keep_separator=False, chunk_size=200, chunk_overlap=30 ) def split_texts(texts): chunked_texts = [] for text in texts: chunks = text_splitter.create_documents([text]) chunked_texts.extend([chunk.page_content for chunk in chunks]) return chunked_texts # Split the context field into chunks df["chunks"] = df["context"].apply(lambda x: split_texts(x)) # Aggregate list of all chunks all_chunks = df["chunks"].tolist() docs = [item for chunk in all_chunks for item in chunk]
Tip
Experimente con diferentes estrategias de fragmentación al evaluar la recuperación. Este tutorial se centra en la evaluación de modelos de incrustación.
Crear incrustaciones e ingerir en gráficos de MongoDB
Incruste los documentos fragmentados e ingréselos en Atlas. Cree colecciones independientes para cada modelo de incrustación que desee comparar:
Definir una función de incrustación
Cree una función para generar incrustaciones utilizando la API de OpenAI:
from typing import List def get_embeddings(docs: List[str], model: str) -> List[List[float]]: """Generate embeddings using the OpenAI API.""" docs = [doc.replace("\n", " ") for doc in docs] response = openai_client.embeddings.create(input=docs, model=model) return [r.embedding for r in response.data]
Ingerir incrustaciones en Atlas
Incruste e ingiera los documentos fragmentados en colecciones de Atlas:
from pymongo import MongoClient from tqdm.auto import tqdm client = MongoClient(MONGODB_URI) DB_NAME = "ragas_evals" db = client[DB_NAME] batch_size = 128 EVAL_EMBEDDING_MODELS = ["text-embedding-ada-002", "text-embedding-3-small"] for model in EVAL_EMBEDDING_MODELS: embedded_docs = [] print(f"Getting embeddings for the {model} model") for i in tqdm(range(0, len(docs), batch_size)): end = min(len(docs), i + batch_size) batch = docs[i:end] batch_embeddings = get_embeddings(batch, model) batch_embedded_docs = [ {"text": batch[i], "embedding": batch_embeddings[i]} for i in range(len(batch)) ] embedded_docs.extend(batch_embedded_docs) collection = db[model] collection.delete_many({}) collection.insert_many(embedded_docs) print(f"Finished inserting embeddings for the {model} model")
Crear índices de búsqueda vectorial
Cree un índice de búsqueda vectorial de MongoDB para cada colección. Utilice la siguiente definición de índice con el nombre vector_index:
{ "fields": [ { "numDimensions": 1536, "path": "embedding", "similarity": "cosine", "type": "vector" } ] }
Para saber cómo crear el índice, consulte Crear un índice de búsqueda vectorial de MongoDB.
Tip
Tanto text-embedding-ada-002 como text-embedding-3-small tienen 1536 dimensiones, por lo que la misma definición de índice funciona para ambas colecciones.
Comparar modelos de incrustación
Para asegurar la recuperación del contexto correcto para el LLM, compare diferentes modelos de incrustación. Este tutorial compara text-embedding-ada-002 y.text-embedding-3-small
Crear una función de recuperación
Cree una función para obtener un recuperador de almacén de vectores utilizando LangChain y MongoDB Atlas:
from langchain_openai import OpenAIEmbeddings from langchain_mongodb import MongoDBAtlasVectorSearch from langchain_core.vectorstores import VectorStoreRetriever def get_retriever(model: str, k: int) -> VectorStoreRetriever: """ Get a vector store retriever for a given embedding model. Args: model (str): Embedding model to use k (int): Number of results to retrieve Returns: VectorStoreRetriever: A vector store retriever object """ embeddings = OpenAIEmbeddings(model=model) vector_store = MongoDBAtlasVectorSearch.from_connection_string( connection_string=MONGODB_URI, namespace=f"{DB_NAME}.{model}", embedding=embeddings, index_name="vector_index", text_key="text", ) retriever = vector_store.as_retriever( search_type="similarity", search_kwargs={"k": k} ) return retriever
Evaluar al retriever
Utilice las métricas context_precision y context_recall de la biblioteca RAGAS para evaluar cada modelo de inserción:
from datasets import Dataset from ragas import evaluate, RunConfig from ragas.metrics import context_precision, context_recall import nest_asyncio # Allow nested use of asyncio (used by RAGAS) nest_asyncio.apply() for model in EVAL_EMBEDDING_MODELS: data = {"question": [], "ground_truth": [], "contexts": []} data["question"] = QUESTIONS data["ground_truth"] = GROUND_TRUTH retriever = get_retriever(model, 2) # Get relevant documents for the evaluation dataset for i in tqdm(range(0, len(QUESTIONS))): data["contexts"].append( [doc.page_content for doc in retriever.invoke(QUESTIONS[i])] ) # RAGAS expects a Dataset object dataset = Dataset.from_dict(data) # RAGAS runtime settings to avoid hitting OpenAI rate limits run_config = RunConfig(max_workers=4, max_wait=180) result = evaluate( dataset=dataset, metrics=[context_precision, context_recall], run_config=run_config, raise_exceptions=False, ) print(f"Result for the {model} model: {result}")
Los resultados de la evaluación de los modelos de inserción en el conjunto de datos de muestra son los siguientes:
Modelo | Precisión del contexto | Recuerdo del contexto |
|---|---|---|
incrustación de texto ADA002 | 0.9310 | 0.8561 |
incrustación de texto-3-pequeño | 0.9116 | 0.8826 |
Con base en estos resultados, text-embedding-ada-002 clasifica los resultados más relevantes en un nivel superior, pero text-embedding-3-small recupera contextos más alineados con las respuestas de la verdad fundamental. Para este tutorial, utilice text-embedding-3-small como modelo de incrustación.
Comparar modelos de finalización
Ahora que ha seleccionado el mejor modelo de integración, compare los modelos de finalización para el componente de generación de su aplicación RAG.
Crear una cadena RAG
Cree una función que construya una cadena RAG usando LangChain:
from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnablePassthrough from langchain_core.runnables.base import RunnableSequence from langchain_core.output_parsers import StrOutputParser def get_rag_chain(retriever: VectorStoreRetriever, model: str) -> RunnableSequence: """ Create a basic RAG chain. Args: retriever (VectorStoreRetriever): Vector store retriever object model (str): Chat completion model to use Returns: RunnableSequence: A RAG chain """ # Generate context using the retriever, and pass the user question through retrieve = { "context": retriever | (lambda docs: "\n\n".join([d.page_content for d in docs])), "question": RunnablePassthrough(), } template = """Answer the question based only on the following context: \ {context} Question: {question} """ # Define the chat prompt prompt = ChatPromptTemplate.from_template(template) # Define the model for chat completion llm = ChatOpenAI(temperature=0, model=model) # Parse output as a string parse_output = StrOutputParser() # RAG chain rag_chain = retrieve | prompt | llm | parse_output return rag_chain
Evaluar los modelos de finalización
Utilice las métricas faithfulness y answer_relevancy para evaluar diferentes modelos de finalización:
from ragas.metrics import faithfulness, answer_relevancy for model in ["gpt-3.5-turbo-1106", "gpt-3.5-turbo"]: data = {"question": [], "ground_truth": [], "contexts": [], "answer": []} data["question"] = QUESTIONS data["ground_truth"] = GROUND_TRUTH # Use the best embedding model from the retriever evaluation retriever = get_retriever("text-embedding-3-small", 2) rag_chain = get_rag_chain(retriever, model) for i in tqdm(range(0, len(QUESTIONS))): question = QUESTIONS[i] data["answer"].append(rag_chain.invoke(question)) data["contexts"].append( [doc.page_content for doc in retriever.invoke(question)] ) # RAGAS expects a Dataset object dataset = Dataset.from_dict(data) # RAGAS runtime settings to avoid hitting OpenAI rate limits run_config = RunConfig(max_workers=4, max_wait=180) result = evaluate( dataset=dataset, metrics=[faithfulness, answer_relevancy], run_config=run_config, raise_exceptions=False, ) print(f"Result for the {model} model: {result}")
Los resultados de la evaluación de los modelos de finalización en el conjunto de datos de muestra son los siguientes:
Modelo | Fidelidad | Relevancia de la respuesta |
|---|---|---|
gpt-3.5-turbo | 0.9714 | 0.9087 |
gpt-3.5-turbo-1106 | 0.9671 | 0.9105 |
Con base en estos resultados, la versión más reciente gpt-3.5-turbo produce resultados más consistentes con los hechos, mientras que la versión anterior genera respuestas más pertinentes a la pregunta planteada. Para este tutorial, utilice gpt-3.5-turbo como modelo de finalización.
Tip
Si no desea elegir entre métricas, considere crear métricas consolidadas utilizando una suma ponderada o personalizar las indicaciones utilizadas para la evaluación.
Medir el rendimiento general
Evalúe el rendimiento general de su aplicación RAG utilizando los modelos de mejor rendimiento:
from ragas.metrics import answer_similarity, answer_correctness data = {"question": [], "ground_truth": [], "answer": []} data["question"] = QUESTIONS data["ground_truth"] = GROUND_TRUTH # Use the best embedding model from the retriever evaluation retriever = get_retriever("text-embedding-3-small", 2) # Use the best completion model from the generator evaluation rag_chain = get_rag_chain(retriever, "gpt-3.5-turbo") for question in tqdm(QUESTIONS): data["answer"].append(rag_chain.invoke(question)) dataset = Dataset.from_dict(data) run_config = RunConfig(max_workers=4, max_wait=180) result = evaluate( dataset=dataset, metrics=[answer_similarity, answer_correctness], run_config=run_config, raise_exceptions=False, ) print(f"Overall metrics: {result}")
Esta evaluación muestra que la cadena RAG produce una similitud de respuesta de 0.8873 y una corrección de respuesta de en el conjunto de datos de 0.5922 muestra.
Analizar los resultados
Para investigar más a fondo los resultados, conviértalos en un marco de datos de pandas y filtre las respuestas con puntaje bajo:
result_df = result.to_pandas() result_df[result_df["answer_correctness"] < 0.7]
Para un análisis visual, cree un mapa de calor de preguntas versus métricas:
import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize=(10, 8)) sns.heatmap( result_df[1:10].set_index("question")[["answer_similarity", "answer_correctness"]], annot=True, cmap="flare", ) plt.show()
El código anterior genera el siguiente mapa de calor:

Mapa de calor que visualiza el rendimiento de la aplicación RAG
Al investigar los resultados con puntuaciones bajas, es posible que encuentre lo siguiente:
Algunas respuestas de la verdad fundamental en el conjunto de datos de evaluación son incorrectas. Aunque la respuesta generada por LLM es correcta, no coincide con la verdad fundamental, lo que resulta en una puntuación baja.
Algunas respuestas verdaderas son oraciones completas, mientras que la respuesta generada por LLM es una sola palabra o número.
Estos hallazgos resaltan la importancia de verificar puntualmente las evaluaciones de LLM y conservar conjuntos de datos de evaluación precisos.
Seguimiento del rendimiento a lo largo del tiempo
La evaluación no debe ser un evento único. Cada vez que modifique un componente de su sistema, evalúe los cambios para evaluar su impacto en el rendimiento. Una vez que su aplicación esté en producción, monitoree el rendimiento en tiempo real y detecte los cambios.
Utilice gráficos para supervisar el rendimiento de su solicitud de Maestría en Derecho (LLM). Registre los resultados de la evaluación y las métricas de retroalimentación que desee monitorear en una colección de Atlas.
from datetime import datetime result["timestamp"] = datetime.now() collection = db["metrics"] collection.insert_one(result)
Este código añade un campo timestamp al resultado de la evaluación y lo escribe en una colección metrics de la base de datos ragas_evals. El documento en Atlas tiene este aspecto:
{ "answer_similarity": 0.8873, "answer_correctness": 0.5922, "timestamp": "2024-04-07T23:27:30.655+00:00" }
Cree un panel en MongoDB Charts para visualizar sus métricas a lo largo del tiempo. Para aprender a crear gráficos y paneles, consulte Crear gráficos.
Resumen
En este tutorial, aprendiste a evaluar una aplicación RAG con el framework RAGAS y MongoDB Atlas. Comparaste modelos de incrustación para la recuperación, modelos de finalización para la generación y mediste el rendimiento general de tu aplicación. También aprendiste a monitorizar el rendimiento a lo largo del tiempo con MongoDB Charts.
Para obtener más información sobre cómo crear aplicaciones RAG con MongoDB, consulte los siguientes recursos: