Cómo medir la fiabilidad de la respuesta de un modelo de idioma grande

El principio básico de los modelos de lenguaje grande (LLMS) es muy simple: predecir la siguiente palabra (o token) en una secuencia de palabras basadas en patrones estadísticos en sus datos de entrenamiento. Sin embargo, esta capacidad aparentemente simple resulta ser increíblemente sofisticada cuando puede hacer una serie de tareas sorprendentes, como resumen de texto, generación de ideas, lluvia de ideas, generación de códigos, procesamiento de información y creación de contenido. Dicho esto, los LLM no tienen ningún recuerdo, ¿realmente “entienden” nada, aparte de apegarse a su función básica? Predecir la siguiente palabra.

El proceso de predicción de la próxima palabra es probabilístico. El LLM tiene que seleccionar cada palabra de una distribución de probabilidad. En el proceso, a menudo generan contenido falso, fabricado o inconsistente en un intento de producir respuestas coherentes y llenar los vacíos con información de aspecto plausible pero incorrecto. Este fenómeno se llama alucinación, una característica inevitable y conocida de LLM que garantiza la validación y la corroboración de sus resultados.

Los métodos de generación de aumentos de recuperación (RAG), que hacen que un LLM funcione con fuentes de conocimiento externas, minimizan las alucinaciones hasta cierto punto, pero no pueden erradicarlas por completo. Aunque los trapos avanzados pueden proporcionar citas en el texto y URL, verificar estas referencias podría ser agitado y lento. Por lo tanto, necesitamos un criterio objetivo para evaluar la confiabilidad o confiabilidad de la respuesta de una LLM, ya sea que se genere a partir de su propio conocimiento o una base de conocimiento externa (RAG).

En este artículo, discutiremos cómo la producción de un LLM puede ser evaluada por su confiabilidad por un modelo de lenguaje confiable que asigna una puntuación a la producción de la LLM. Primero discutiremos cómo podemos usar un modelo de idioma confiable para asignar puntajes a la respuesta de una LLM y explicar la confiabilidad. Posteriormente, desarrollaremos un trapo de ejemplo con llamado y Llamado Eso evalúa las respuestas del trapo para la confiabilidad.

El código completo de este artículo está disponible en el cuaderno de Jupyter en Github.

Asignar un puntaje de confiabilidad a la respuesta de un LLM

Para demostrar cómo podemos asignar un puntaje de confiabilidad a un LLMRespuesta, usaré Modelo de lenguaje confiable (TLM) de CleanLab (TLM). Tales TLM usan una combinación de cuantificación de incertidumbre y análisis de consistencia para calcular los puntajes y explicaciones de confiabilidad para las respuestas de LLM.

Cleanlab Ofrece API de prueba gratuitas que se pueden obtener mediante la creación de una cuenta en su sitio web. Primero necesitamos instalar el cliente Python de CleanLab:

pip install --upgrade cleanlab-studio

CleanLab admite varios modelos patentados como ‘GPT-4O‘,’GPT-4O-Mini‘,’previsión de O1‘,’Claude-3-Sonnet‘,’Claude-3.5-Sonnet‘,’Claude-3.5-Sonnet-V2‘Y otros. Así es como TLM asigna un puntaje de confianza a la respuesta de GPT-4O. El puntaje de confiabilidad varía de 0 a 1, donde los valores más altos indican una mayor confiabilidad.

from cleanlab_studio import Studio
studio = Studio("<CLEANLAB_API_KEY>")  # Get your API key from above
tlm = studio.TLM(options={"log": ["explanation"], "model": "gpt-4o"}) # GPT, Claude, etc
#set the prompt
out = tlm.prompt("How many vowels are there in the word 'Abracadabra'.?")
#the TLM response contains the actual output 'response', trustworthiness score and explanation
print(f"Model's response = {out['response']}")
print(f"Trustworthiness score = {out['trustworthiness_score']}")
print(f"Explanation = {out['log']['explanation']}")

El código anterior probó la respuesta de GPT-4O para la pregunta “¿Cuántas vocales hay en la palabra ‘Abracadabra’?“. La salida del TLM contiene la respuesta (respuesta), el puntaje de confianza y la explicación del modelo. Aquí está la salida de este código.

Model's response = The word "Abracadabra" contains 6 vowels. The vowels are: A, a, a, a, a, and a.
Trustworthiness score = 0.6842228802750124
Explanation = This response is untrustworthy due to a lack of consistency in possible responses from the model. Here's one inconsistent alternate response that the model considered (which may not be accurate either):
5.

Se puede ver cómo el modelo de idioma más avanzado alucina para tareas tan simples y produce la salida incorrecta. Aquí está el puntaje de respuesta y confiabilidad para la misma pregunta para Claude-3.5-Sonnet-V2.

Model's response = Let me count the vowels in 'Abracadabra':
A-b-r-a-c-a-d-a-b-r-a

The vowels are: A, a, a, a, a

There are 5 vowels in the word 'Abracadabra'.
Trustworthiness score = 0.9378276048845285
Explanation = Did not find a reason to doubt trustworthiness.

Claude-3.5-Sonnet-V2 produce la salida correcta. Comparemos las respuestas de los dos modelos con otra pregunta.

from cleanlab_studio import Studio
import markdown
from IPython.core.display import display, Markdown

# Initialize the Cleanlab Studio with API key
studio = Studio("<CLEANLAB_API_KEY>")  # Replace with your actual API key

# List of models to evaluate
models = ["gpt-4o", "claude-3.5-sonnet-v2"]

# Define the prompt
prompt_text = "Which one of 9.11 and 9.9 is bigger?"

# Loop through each model and evaluate
for model in models:
   tlm = studio.TLM(options={"log": ["explanation"], "model": model})
   out = tlm.prompt(prompt_text)
  
   md_content = f"""
## Model: {model}

**Response:** {out['response']}

**Trustworthiness Score:** {out['trustworthiness_score']}

**Explanation:** {out['log']['explanation']}

---
"""
   display(Markdown(md_content))

Aquí está la respuesta de los dos modelos:

Salidas incorrectas generadas por GPT-4O y Claude-3.5-Sonnet-V2, representadas por un puntaje de baja confiabilidad

También podemos generar un puntaje de confiabilidad para LLM de código abierto. Vamos a ver el reciente LLM de código abierto, muy publicitada: Deepseek-R1. Yo usaré Deepseek-r1-Distill-llama-70bbasado en meta LLAMA-3.3–70B-INSTRUST MODELO y destilado del modelo de Modelo de Expertos (MOE) más grande de Deepseek de 671 mil millones de parámetros. Destilación de conocimiento es un Aprendizaje automático Técnica que tiene como objetivo transferir los aprendizajes de un gran modelo previamente capacitado, el “modelo de maestro”, a un “modelo de estudiante” más pequeño.

import streamlit as st
from langchain_groq.chat_models import ChatGroq
import os
os.environ["GROQ_API_KEY"]=st.secrets["GROQ_API_KEY"]
# Initialize the Groq Llama Instant model
groq_llm = ChatGroq(model="deepseek-r1-distill-llama-70b", temperature=0.5)
prompt = "Which one of 9.11 and 9.9 is bigger?"
# Get the response from the model
response = groq_llm.invoke(prompt)
#Initialize Cleanlab's studio
studio = Studio("226eeab91e944b23bd817a46dbe3c8ae") 
cleanlab_tlm = studio.TLM(options={"log": ["explanation"]})  #for explanations
#Get the output containing trustworthiness score and explanation
output = cleanlab_tlm.get_trustworthiness_score(prompt, response=response.content.strip())
md_content = f"""
## Model: {model}
**Response:** {response.content.strip()}
**Trustworthiness Score:** {output['trustworthiness_score']}
**Explanation:** {output['log']['explanation']}
---
"""
display(Markdown(md_content))

Aquí está el resultado de Deepseek-r1-Distill-llama-70b modelo.

La salida correcta de Deepseek-R1-Distill-Llama-70B modelo con una puntuación de alta confiabilidad

Desarrollar un trapo confiable

Ahora desarrollaremos un trapo para demostrar cómo podemos medir la confiabilidad de una respuesta de LLM en RAG. Este trapo se desarrollará raspando datos de enlaces dados, analizándolo en formato de Markdown y creando una tienda vectorial.

Las siguientes bibliotecas deben instalarse para el siguiente código.

pip install llama-parse llama-index-core llama-index-embeddings-huggingface 
llama-index-llms-cleanlab requests beautifulsoup4 pdfkit nest-asyncio

Para convertir HTML en formato PDF, también necesitamos instalar wkhtmltopdf Herramienta de línea de comandos desde su sitio web.

Se importarán las siguientes bibliotecas:

from llama_parse import LlamaParse
from llama_index.core import VectorStoreIndex
import requests
from bs4 import BeautifulSoup
import pdfkit
from llama_index.readers.docling import DoclingReader
from llama_index.core import Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.cleanlab import CleanlabTLM
from typing import Dict, List, ClassVar
from llama_index.core.instrumentation.events import BaseEvent
from llama_index.core.instrumentation.event_handlers import BaseEventHandler
from llama_index.core.instrumentation import get_dispatcher
from llama_index.core.instrumentation.events.llm import LLMCompletionEndEvent
import nest_asyncio
import os

Los siguientes pasos implicarán raspar datos de URL dadas que usan Python’s Beautifulsoup biblioteca, guardando los datos raspados en los archivos PDF utilizando pdfkity analizar los datos de PDF (s) para marcar el archivo utilizando Duende que es una plataforma de análisis de documentos nativo de Genai construida con LLM y para casos de uso de LLM.

Primero configuraremos el LLM para ser utilizado por CleanLabTLM y el modelo de incrustación (Cara de abrazo modelo de incrustación Baai/BGE-Small-en-V1.5) que se utilizarán para calcular los incrustaciones de los datos raspados para crear el almacén vectorial.

options = {
   "model": "gpt-4o",
   "max_tokens": 512,
   "log": ["explanation"]
}
llm = CleanlabTLM(api_key="<CLEANLAB_API_KEY>", options=options)#Get your free API from https://cleanlab.ai/
Settings.llm = llm
Settings.embed_model = HuggingFaceEmbedding(
   model_name="BAAI/bge-small-en-v1.5"
)

Ahora definiremos un controlador de eventos personalizado, GetTrustworthinessscoreque se deriva de una clase de controlador de eventos base. Este controlador se desencadena al final de una finalización de LLM y extrae un puntaje de confiabilidad de los metadatos de respuesta. Una función de ayudante, Display_Responsemuestra la respuesta de la LLM junto con su puntaje de confiabilidad.

# Event Handler for Trustworthiness Score
class GetTrustworthinessScore(BaseEventHandler):
   events: ClassVar[List[BaseEvent]] = []
   trustworthiness_score: float = 0.0
   @classmethod
   def class_name(cls) -> str:
       return "GetTrustworthinessScore"
   def handle(self, event: BaseEvent) -> Dict:
       if isinstance(event, LLMCompletionEndEvent):
           self.trustworthiness_score = event.response.additional_kwargs.get("trustworthiness_score", 0.0)
           self.events.append(event)
       return {}
# Helper function to display LLM's response
def display_response(response):
   response_str = response.response
   trustworthiness_score = event_handler.trustworthiness_score
   print(f"Response: {response_str}")
   print(f"Trustworthiness score: {round(trustworthiness_score, 2)}")

Ahora generaremos PDF raspando datos de URL dadas. Para la demostración, eliminaremos los datos de Este artículo de Wikipedia sobre modelos de idiomas grandes (Creative Commons Attribution-Sharealike 4.0 Licencia).

Nota: Se recomienda a los lectores que siempre verifiquen el estado del contenido/datos que están a punto de raspar y se aseguran de que se les permita hacerlo.

La siguiente pieza de código raspa los datos de las URL dadas haciendo una solicitud HTTP y utilizando Beautifulsoup Biblioteca de Python para analizar el contenido HTML. El contenido de HTML se limpia convirtiendo las URL relacionadas con el protocolo a las absolutas. Posteriormente, el contenido raspado se convierte en un archivo PDF utilizando pdfkit.

##########################################
# PDF Generation from Multiple URLs
##########################################
# Configure wkhtmltopdf path
wkhtml_path = r'C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe'
config = pdfkit.configuration(wkhtmltopdf=wkhtml_path)
# Define URLs and assign document names
urls = {
   "LLMs": "https://en.wikipedia.org/wiki/Large_language_model"
}
# Directory to save PDFs
pdf_directory = "PDFs"
os.makedirs(pdf_directory, exist_ok=True)
pdf_paths = {}
for doc_name, url in urls.items():
   try:
       print(f"Processing {doc_name} from {url} ...")
       response = requests.get(url)
       soup = BeautifulSoup(response.text, "html.parser")
       main_content = soup.find("div", {"id": "mw-content-text"})
       if main_content is None:
           raise ValueError("Main content not found")
       # Replace protocol-relative URLs with absolute URLs
       html_string = str(main_content).replace('src="https://', 'src="https://').replace('href="https://', 'href="https://')
       pdf_file_path = os.path.join(pdf_directory, f"{doc_name}.pdf")
       pdfkit.from_string(
           html_string,
           pdf_file_path,
           options={'encoding': 'UTF-8', 'quiet': ''},
           configuration=config
       )
       pdf_paths[doc_name] = pdf_file_path
       print(f"Saved PDF for {doc_name} at {pdf_file_path}")
   except Exception as e:
       print(f"Error processing {doc_name}: {e}")

Después de generar PDF a partir de los datos raspados, analizamos estos PDF usando Duende. Establecemos las instrucciones de análisis para extraer el contenido en formato de Markdown y analizar los documentos en cuanto a la página junto con el nombre del documento y el número de página. Estas entidades extraídas (páginas) se denominan nodos. El analizador itera sobre los nodos extraídos y actualiza los metadatos de cada nodo al agregar un encabezado de citas que facilita la referencia posterior.

##########################################
# Parse PDFs with LlamaParse and Inject Metadata
##########################################

# Define parsing instructions (if your parser supports it)
parsing_instructions = """Extract the document content in markdown.
Split the document into nodes (for example, by page).
Ensure each node has metadata for document name and page number."""
      
# Create a LlamaParse instance
parser = LlamaParse(
   api_key="<LLAMACLOUD_API_KEY>",  #Replace with your actual key
   parsing_instructions=parsing_instructions,
   result_type="markdown",
   premium_mode=True,
   max_timeout=600
)
# Directory to save combined Markdown files (one per PDF)
output_md_dir = os.path.join(pdf_directory, "markdown_docs")
os.makedirs(output_md_dir, exist_ok=True)
# List to hold all updated nodes for indexing
all_nodes = []
for doc_name, pdf_path in pdf_paths.items():
   try:
       print(f"Parsing PDF for {doc_name} from {pdf_path} ...")
       nodes = parser.load_data(pdf_path)  # Returns a list of nodes
       updated_nodes = []
       # Process each node: update metadata and inject citation header into the text.
       for i, node in enumerate(nodes, start=1):
           # Copy existing metadata (if any) and add our own keys.
           new_metadata = dict(node.metadata) if node.metadata else {}
           new_metadata["document_name"] = doc_name
           if "page_number" not in new_metadata:
               new_metadata["page_number"] = str(i)
           # Build the citation header.
           citation_header = f"[{new_metadata['document_name']}, page {new_metadata['page_number']}]\n\n"
           # Prepend the citation header to the node's text.
           updated_text = citation_header + node.text
           new_node = node.__class__(text=updated_text, metadata=new_metadata)
           updated_nodes.append(new_node)
       # Save a single combined Markdown file for the document using the updated node texts.
       combined_texts = [node.text for node in updated_nodes]
       combined_md = "\n\n---\n\n".join(combined_texts)
       md_filename = f"{doc_name}.md"
       md_filepath = os.path.join(output_md_dir, md_filename)
       with open(md_filepath, "w", encoding="utf-8") as f:
           f.write(combined_md)
       print(f"Saved combined markdown for {doc_name} to {md_filepath}")
       # Add the updated nodes to the global list for indexing.
       all_nodes.extend(updated_nodes)
       print(f"Parsed {len(updated_nodes)} nodes from {doc_name}.")
   except Exception as e:
       print(f"Error parsing {doc_name}: {e}")

Ahora creamos una tienda vectorial y un motor de consulta. Definimos una plantilla de solicitud del cliente para guiar el comportamiento de la LLM en responder las preguntas. Finalmente, creamos un motor de consulta con el índice creado para responder consultas. Para cada consulta, recuperamos los 3 nodos principales de la tienda Vector en función de su similitud semántica con la consulta. El LLM usa estos nodos recuperados para generar la respuesta final.

##########################################
# Create Index and Query Engine
##########################################
# Create an index from all nodes.
index = VectorStoreIndex.from_documents(documents=all_nodes)
# Define a custom prompt template that forces the inclusion of citations.
prompt_template = """
You are an AI assistant with expertise in the subject matter.
Answer the question using ONLY the provided context.
Answer in well-formatted Markdown with bullets and sections wherever necessary.
If the provided context does not support an answer, respond with "I don't know."
Context:
{context_str}
Question:
{query_str}
Answer:
"""
# Create a query engine with the custom prompt.
query_engine = index.as_query_engine(similarity_top_k=3, llm=llm, prompt_template = prompt_template)
print("Combined index and query engine created successfully!")

Ahora probemos el trapo para algunas consultas y sus puntajes de confianza correspondientes.

query = "When is mixture of experts approach used?"
response = query_engine.query(query)
display_response(response)
Respuesta a la consulta “¿Cuándo se utiliza la mezcla de expertos en el enfoque?” (Imagen por autor)
query = "How do you compare Deepseek model with OpenAI's models?"
response = query_engine.query(query)
display_response(response)
Respuesta a la consulta ‘¿Cómo se compara el modelo Deepseek con los modelos de OpenAi?’ (Imagen por autor)

Asignar un puntaje de confiabilidad a la respuesta de LLM, ya sea generada a través de la inferencia directa o el trapo, ayuda a definir la confiabilidad de la producción de IA y priorizar la verificación humana cuando sea necesario. Esto es particularmente importante para los dominios críticos donde una respuesta incorrecta o poco confiable podría tener consecuencias graves.

¡Eso es toda la gente! Si te gusta el artículo, sígueme en Medio y LinkedIn.