Cuando se trata de procesamiento del lenguaje natural (PNL) y recuperación de información, la capacidad de recuperar información relevante de manera eficiente y precisa es primordial. A medida que el campo continúa evolucionando, se están desarrollando nuevas técnicas y metodologías para mejorar el rendimiento de los sistemas de recuperación, particularmente en el contexto de la Generación Aumentada de Recuperación (RAG). Una de esas técnicas, conocida como recuperación en dos etapas con reclasificadores, ha surgido como una solución poderosa para abordar las limitaciones inherentes de los métodos de recuperación tradicionales.
En este artículo analizamos las complejidades de la recuperación en dos etapas y los reordenadores, explorando sus principios subyacentes, estrategias de implementación y los beneficios que ofrecen para mejorar la precisión y eficiencia de los sistemas RAG. También proporcionaremos ejemplos prácticos y fragmentos de código para ilustrar los conceptos y facilitar una comprensión más profunda de esta técnica de vanguardia.
Comprensión de la generación aumentada de recuperación (RAG)
Antes de profundizar en los detalles de la recuperación en dos etapas y los reclasificadores, revisemos brevemente el concepto de Generación Aumentada de Recuperación (RAG). RAG es una técnica que amplía el conocimiento y las capacidades de los modelos de lenguaje grandes (LLM) proporcionándoles acceso a fuentes de información externas, como bases de datos o colecciones de documentos. Consulte más información del artículo “Una inmersión profunda en la generación aumentada de recuperación en LLM”.
El proceso típico de RAG implica los siguientes pasos:
- Consulta: Un usuario plantea una pregunta o proporciona una instrucción al sistema.
- Recuperación: El sistema consulta una base de datos vectorial o una colección de documentos para encontrar información relevante para la consulta del usuario.
- Aumento: La información recuperada se combina con la consulta o instrucción original del usuario.
- Generación: El modelo de lenguaje procesa la entrada aumentada y genera una respuesta, aprovechando la información externa para mejorar la precisión y amplitud de su salida.
Si bien RAG ha demostrado ser una técnica poderosa, no está exenta de desafíos. Uno de los problemas clave radica en la etapa de recuperación, donde los métodos de recuperación tradicionales pueden no identificar los documentos más relevantes, lo que genera respuestas subóptimas o inexactas del modelo de lenguaje.
La necesidad de recuperación y reclasificación en dos etapas
Los métodos de recuperación tradicionales, como los basados en la concordancia de palabras clave o en modelos de espacio vectorial, a menudo tienen dificultades para capturar las relaciones semánticas matizadas entre consultas y documentos. Esta limitación puede dar lugar a la recuperación de documentos que sólo son superficialmente relevantes o que omiten información crucial que podría mejorar significativamente la calidad de la respuesta generada.
Para abordar este desafío, investigadores y profesionales han recurrido a la recuperación en dos etapas con rerankers. Este enfoque implica un proceso de dos pasos:
- Recuperación inicial: En la primera etapa, se recupera un conjunto relativamente grande de documentos potencialmente relevantes utilizando un método de recuperación rápido y eficiente, como un modelo de espacio vectorial o una búsqueda basada en palabras clave.
- Reclasificación: En la segunda etapa, se emplea un modelo de reclasificación más sofisticado para reordenar los documentos recuperados inicialmente en función de su relevancia para la consulta, colocando efectivamente los documentos más relevantes al principio de la lista.
El modelo de reclasificación, a menudo una red neuronal o una arquitectura basada en transformadores, está específicamente entrenado para evaluar la relevancia de un documento para una consulta determinada. Al aprovechar las capacidades avanzadas de comprensión del lenguaje natural, el reclasificador puede capturar los matices semánticos y las relaciones contextuales entre la consulta y los documentos, lo que da como resultado una clasificación más precisa y relevante.
Beneficios de la recuperación en dos etapas y los rerankers
La adopción de la recuperación en dos etapas con rerankers ofrece varios beneficios importantes en el contexto de los sistemas RAG:
- Precisión mejorada: Al reclasificar los documentos recuperados inicialmente y promover los más relevantes a la cima, el sistema puede proporcionar información más exacta y precisa al modelo de lenguaje, lo que genera respuestas generadas de mayor calidad.
- Problemas fuera del dominio mitigados: Los modelos de incrustación utilizados para la recuperación tradicional a menudo se entrenan en corpus de texto de propósito general, que pueden no capturar adecuadamente el lenguaje y la semántica de un dominio específico. Los modelos de reclasificación, por otro lado, se pueden entrenar con datos de dominios específicos, mitigando el problema “fuera del dominio” y mejorando la relevancia de los documentos recuperados dentro de dominios especializados.
- Escalabilidad: El enfoque de dos etapas permite un escalado eficiente al aprovechar métodos de recuperación rápidos y livianos en la etapa inicial, al tiempo que reserva el proceso de reclasificación más intensivo desde el punto de vista computacional para un subconjunto más pequeño de documentos.
- Flexibilidad: Los modelos de reclasificación se pueden intercambiar o actualizar independientemente del método de recuperación inicial, lo que proporciona flexibilidad y adaptabilidad a las necesidades cambiantes del sistema.
ColBERT: interacción tardía eficiente y eficaz
Uno de los modelos más destacados en el ámbito del reranking es ColBERT (Interacción tardía contextualizada sobre BERT). ColBERT es un modelo de reclasificación de documentos que aprovecha las capacidades de comprensión profunda del lenguaje de BERT al tiempo que introduce un mecanismo de interacción novedoso conocido como “interacción tardía”.
ColBERT: búsqueda de pasajes eficiente y eficaz mediante interacción tardía contextualizada sobre BERT
El mecanismo de interacción tardía en ColBERT permite una recuperación eficiente y precisa al procesar consultas y documentos por separado hasta las etapas finales del proceso de recuperación. Específicamente, ColBERT codifica de forma independiente la consulta y el documento usando BERT, y luego emplea un paso de interacción liviano pero poderoso que modela su similitud detallada. Al retrasar pero retener esta interacción detallada, ColBERT puede aprovechar la expresividad de los modelos de lenguaje profundo y, al mismo tiempo, obtener la capacidad de precalcular representaciones de documentos fuera de línea, acelerando considerablemente el procesamiento de consultas.
La arquitectura de interacción tardía de ColBERT ofrece varios beneficios, incluida una eficiencia computacional mejorada, escalabilidad con el tamaño de la colección de documentos y aplicabilidad práctica para escenarios del mundo real. Además, ColBERT se ha mejorado aún más con técnicas como la supervisión sin ruido y la compresión residual (en ColBERTv2), que refinan el proceso de entrenamiento y reducen la huella espacial del modelo mientras mantienen una alta efectividad de recuperación.
Este fragmento de código demuestra cómo configurar y utilizar el modelo jina-colbert-v1-en para indexar una colección de documentos, aprovechando su capacidad para manejar contextos largos de manera eficiente.
Implementación de recuperación en dos etapas con rerankers
Ahora que comprendemos los principios detrás de la recuperación en dos etapas y los reordenadores, exploremos su implementación práctica dentro del contexto de un sistema RAG. Aprovecharemos bibliotecas y marcos populares para demostrar la integración de estas técnicas.
Configurar el entorno
Antes de sumergirnos en el código, configuremos nuestro entorno de desarrollo. Usaremos Python y varias bibliotecas populares de PNL, incluidas Hugging Face Transformers, Sentence Transformers y LanceDB.
# Install required libraries
!pip install datasets huggingface_hub sentence_transformers lancedb
Preparación de datos
Para fines de demostración, utilizaremos el conjunto de datos “ai-arxiv-chunked” de Hugging Face Datasets, que contiene más de 400 artículos de ArXiv sobre aprendizaje automático, procesamiento de lenguaje natural y modelos de lenguaje grandes.
from datasets import load_dataset
dataset = load_dataset("jamescalam/ai-arxiv-chunked", split="train")
Next, we'll preprocess the data and split it into smaller chunks to facilitate efficient retrieval and processing.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
def chunk_text(text, chunk_size=512, overlap=64):
tokens = tokenizer.encode(text, return_tensors="pt", truncation=True)
chunks = tokens.split(chunk_size - overlap)
texts = (tokenizer.decode(chunk) for chunk in chunks)
return texts
chunked_data = ()
for doc in dataset:
text = doc("chunk")
chunked_texts = chunk_text(text)
chunked_data.extend(chunked_texts)
For the initial retrieval stage, we'll use a Sentence Transformer model to encode our documents and queries into dense vector representations, and then perform approximate nearest neighbor search using a vector database like LanceDB.
from sentence_transformers import SentenceTransformer
from lancedb import lancedb
# Load Sentence Transformer model
model = SentenceTransformer('all-MiniLM-L6-v2')
# Create LanceDB vector store
db = lancedb.lancedb('/path/to/store')
db.create_collection('docs', vector_dimension=model.get_sentence_embedding_dimension())
# Index documents
for text in chunked_data:
vector = model.encode(text).tolist()
db.insert_document('docs', vector, text)
from sentence_transformers import SentenceTransformer
from lancedb import lancedb
# Load Sentence Transformer model
model = SentenceTransformer('all-MiniLM-L6-v2')
# Create LanceDB vector store
db = lancedb.lancedb('/path/to/store')
db.create_collection('docs', vector_dimension=model.get_sentence_embedding_dimension())
# Index documents
for text in chunked_data:
vector = model.encode(text).tolist()
db.insert_document('docs', vector, text)
Con nuestros documentos indexados, podemos realizar la recuperación inicial encontrando los vecinos más cercanos a un vector de consulta determinado.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
def chunk_text(text, chunk_size=512, overlap=64):
tokens = tokenizer.encode(text, return_tensors="pt", truncation=True)
chunks = tokens.split(chunk_size - overlap)
texts = (tokenizer.decode(chunk) for chunk in chunks)
return texts
chunked_data = ()
for doc in dataset:
text = doc("chunk")
chunked_texts = chunk_text(text)
chunked_data.extend(chunked_texts)
Reclasificación
Después de la recuperación inicial, emplearemos un modelo de reclasificación para reordenar los documentos recuperados según su relevancia para la consulta. En este ejemplo, usaremos el reclasificador ColBERT, un modelo rápido y preciso basado en transformador diseñado específicamente para la clasificación de documentos.
from lancedb.rerankers import ColbertReranker
reranker = ColbertReranker()
# Rerank initial documents
reranked_docs = reranker.rerank(query, initial_docs)
El reranked_docs
La lista ahora contiene los documentos reordenados según su relevancia para la consulta, según lo determinado por el reclasificador ColBERT.
Aumento y Generación
Con los documentos relevantes y reclasificados en la mano, podemos proceder a las etapas de aumento y generación del oleoducto RAG. Usaremos un modelo de lenguaje de la biblioteca Hugging Face Transformers para generar la respuesta final.
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
tokenizer = AutoTokenizer.from_pretrained("t5-base")
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")
# Augment query with reranked documents
augmented_query = query + " " + " ".join(reranked_docs(:3))
# Generate response from language model
input_ids = tokenizer.encode(augmented_query, return_tensors="pt")
output_ids = model.generate(input_ids, max_length=500)
response = tokenizer.decode(output_ids(0), skip_special_tokens=True)
print(response)