Construcción de un Sistema de Preguntas y Respuestas sobre Documentos con FAISS y Hugging Face
Descubre cómo implementar una arquitectura de Generación Aumentada por Recuperación (RAG) utilizando FAISS como base de datos vectorial y modelos de Hugging Face para procesar y responder consultas basadas exclusivamente en tus documentos privados.
🚀 Introducción al Procesamiento de Documentos con IA
En la era de la información masiva, las organizaciones y los investigadores se enfrentan a un desafío constante: ¿cómo extraer respuestas precisas de miles de páginas de texto sin perderse en el intento? Los modelos de lenguaje grande (LLMs) son herramientas potentes, pero tienen limitaciones críticas, como las alucinaciones y la falta de conocimiento sobre datos privados o específicos de una empresa. Aquí es donde entra en juego la arquitectura RAG (Retrieval-Augmented Generation), combinando la búsqueda eficiente de información con la potencia de generación de los modelos de transformers.
En este tutorial completo, aprenderás a construir un sistema de Preguntas y Respuestas (Q&A) de extremo a extremo que procesa documentos PDF o de texto plano, genera representaciones vectoriales (embeddings), las almacena en una base de datos ultrarrápida utilizando FAISS (Facebook AI Similarity Search) y utiliza un modelo de Hugging Face para formular respuestas contextuales y precisas.
🛠️ Requisitos Previos y Configuración del Entorno
Antes de escribir código, asegúrate de tener instalado Python 3.8 o superior en tu sistema. Vamos a utilizar un entorno virtual para mantener nuestras dependencias limpias y organizadas.
Abre tu terminal y ejecuta los siguientes comandos para crear y activar el entorno virtual:
python -m venv venv_rag
source venv_rag/bin/activate
Ahora, instalaremos las bibliotecas necesarias. Utilizaremos transformers para los modelos de lenguaje, sentence-transformers para la vectorización eficiente, faiss-cpu (o faiss-gpu si cuentas con una tarjeta gráfica compatible) para la indexación y pypdf para la lectura de documentos PDF.
pip install transformers sentence-transformers faiss-cpu pypdf torch
Verifica que la instalación se haya realizado correctamente ejecutando un script rápido de prueba en Python:
import torch
import transformers
import sentence_transformers
import faiss
print("Todas las librerías se han importado correctamente.")
📚 Arquitectura General del Sistema RAG
Para entender qué vamos a programar, es fundamental visualizar el flujo de datos. Un sistema RAG consta de dos fases principales: la indexación (preparación del documento) y la recuperación y generación (respuesta a la consulta del usuario).
Como se observa en el diagrama, el proceso transforma texto plano en vectores matemáticos que capturan el significado semántico, permitiendo buscar conceptos similares y no solo coincidencias exactas de palabras.
📝 Paso 1: Carga y Fragmentación (Chunking) de Documentos
El primer paso consiste en leer nuestro archivo de texto o PDF y dividirlo en fragmentos más pequeños (chunks). Los modelos de lenguaje tienen límites en la cantidad de tokens que pueden procesar de una sola vez, y los fragmentos más pequeños permiten recuperar información mucho más específica.
Crea un archivo llamado rag_system.py y añade el siguiente código para leer un documento PDF y dividirlo en bloques manejables:
from pypdf import PdfReader
def cargar_y_dividir_pdf(ruta_pdf, tamaño_chunk=500, solapamiento=50):
lector = PdfReader(ruta_pdf)
texto_completo = ""
for pagina in lector.pages:
texto_pagina = pagina.extract_text()
if texto_pagina:
texto_completo += texto_pagina + " "
# Dividir el texto en chunks con solapamiento para no perder contexto
chunks = []
inicio = 0
while inicio < len(texto_completo):
fin = inicio + tamaño_chunk
chunk = texto_completo[inicio:fin]
chunks.append(chunk)
inicio += (tamaño_chunk - solapamiento)
print(f"Documento procesado: {len(chunks)} fragmentos creados.")
return chunks
# Ejemplo de uso (asegúrate de tener un archivo 'documento.pdf' en tu directorio)
# chunks = cargar_y_dividir_pdf('documento.pdf')
🧠 Paso 2: Generación de Embeddings Vectoriales
Una vez que tenemos nuestros fragmentos de texto, debemos convertirlos en vectores numéricos. Utilizaremos la biblioteca sentence-transformers con un modelo preentrenado optimizado para la tarea de representación semántica: all-MiniLM-L6-v2.
Este modelo es rápido, ligero y ofrece un rendimiento excepcional en tareas de búsqueda semántica.
from sentence_transformers import SentenceTransformer
def generar_embeddings(chunks):
print("Cargando modelo de embeddings...")
modelo = SentenceTransformer('all-MiniLM-L6-v2')
print("Generando vectores para los fragmentos...")
vectores = modelo.encode(chunks, show_progress_bar=True)
return modelo, vectores
Los embeddings resultantes son matrices numéricas de alta dimensión (por ejemplo, 384 dimensiones para este modelo específico) que representan la semántica de cada fragmento de texto en un espacio vectorial.
🗄️ Paso 3: Indexación y Almacenamiento con FAISS
FAISS es una biblioteca desarrollada por Meta para la búsqueda eficiente de similitud y agrupamiento de vectores densos. Permite buscar entre millones de vectores en cuestión de milisegundos.
Vamos a crear el índice vectorial y a añadir nuestros embeddings:
import faiss
import numpy as np
def crear_indice_faiss(vectores):
dimension = vectores.shape[1]
# Creamos un índice basado en la distancia L2 (Euclidiana)
indice = faiss.IndexFlatL2(dimension)
# Añadimos los vectores al índice
indice.add(np.array(vectores).astype('float32'))
print(f"Índice FAISS creado con {indice.ntotal} vectores almacenados.")
return indice
IndexIVFFlat o IndexHNSW que aceleran drásticamente las búsquedas a costa de una mínima pérdida de precisión.🔍 Paso 4: Recuperación de Contexto y Generación de Respuestas
Con nuestro índice listo, el sistema ya puede recibir una pregunta, transformarla en un vector, buscar los fragmentos más similares en FAISS y pasárselos a un modelo generativo de Hugging Face.
Implementemos la función de consulta completa:
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
def configurar_generador():
# Usaremos un modelo ligero de lenguaje para demostración
nombre_modelo = "google/flan-t5-small"
tokenizer = AutoTokenizer.from_pretrained(nombre_modelo)
modelo = AutoModelForCausalLM.from_pretrained(nombre_modelo)
generador = pipeline(
"text2text-generation",
model=modelo,
tokenizer=tokenizer,
max_length=256
)
return generador
def responder_pregunta(pregunta, indice, modelo_embeddings, chunks, generador, k=3):
# 1. Vectorizar la pregunta
vector_pregunta = modelo_embeddings.encode([pregunta])
# 2. Buscar los k fragmentos más cercanos en FAISS
distancias, indices = indice.search(np.array(vector_pregunta).astype('float32'), k)
# 3. Recopilar el contexto de los fragmentos recuperados
contexto = "\n".join([chunks[i] for i in indices[0]])
# 4. Construir el prompt para el LLM
prompt = f"Responde a la pregunta basándote únicamente en el siguiente contexto:\n\nContexto:\n{contexto}\n\nPregunta: {pregunta}\nRespuesta:"
# 5. Generar la respuesta
respuesta = generador(prompt, max_length=150, do_sample=False)
return respuesta[0]['generated_text'], contexto
🧪 Script Completo de Ejecución
Para poner todo en marcha, unifiquemos los componentes en un script ejecutable que simule el flujo completo de trabajo:
import os
from pypdf import PdfReader
from sentence_transformers import SentenceTransformer
import faiss
import numpy as np
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, pipeline
def pipeline_completo():
# Simulamos un documento de texto si no hay PDF
texto_prueba = """
La inteligencia artificial (IA) es la inteligencia exhibida por máquinas.
En ciencia de la computación, un agente inteligente ideal es un sistema que percibe su entorno y toma acciones que maximizan sus posibilidades de éxito.
FAISS es una biblioteca creada por Facebook AI Research para la búsqueda eficiente de similitud de vectores.
El aprendizaje automático es un subcampo de la IA que se centra en el desarrollo de sistemas que aprenden de los datos.
"""
chunks = [c.strip() for c in texto_prueba.split(". ") if c.strip()]
# 1. Embeddings
modelo_emb = SentenceTransformer('all-MiniLM-L6-v2')
vectores = modelo_emb.encode(chunks)
# 2. FAISS
indice = faiss.IndexFlatL2(vectores.shape[1])
indice.add(np.array(vectores).astype('float32'))
# 3. Generador
print("Cargando modelo generativo...")
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-small")
modelo = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-small")
generador = pipeline("text2text-generation", model=modelo, tokenizer=tokenizer)
# 4. Consulta
pregunta = "¿Qué es FAISS?"
vector_q = modelo_emb.encode([pregunta])
_, indices = indice.search(np.array(vector_q).astype('float32'), k=2)
contexto = " ".join([chunks[i] for i in indices[0]])
prompt = f"Contexto: {contexto}\nPregunta: {pregunta}\nRespuesta:"
resultado = generador(prompt, max_length=100)
print(f"\nPregunta: {pregunta}")
print(f"Respuesta generada: {resultado[0]['generated_text']}")
if __name__ == "__main__":
pipeline_completo()
📊 Tabla Comparativa de Estrategias de Indexación en FAISS
Dependiendo del volumen de tus documentos, elegir el tipo de índice correcto marcará la diferencia entre una consulta instantánea y un cuellorazón de botella.
| Tipo de Índice FAISS | Velocidad de Búsqueda | Uso de Memoria | Precisión | Tamaño Recomendado de Dataset |
|---|---|---|---|---|
IndexFlatL2 | Lenta (Búsqueda exacta) | Alta | 100% (Exacta) | < 100,000 vectores |
IndexIVFFlat | Rápida (Aproximada) | Moderada | Alta (~95%) | 100,000 - 1,000,000 vectores |
IndexHNSW | Extremadamente Rápida | Muy Alta | Muy Alta (~98%) | Millones de vectores |
❓ Preguntas Frecuentes (FAQ)
¿Puedo utilizar modelos en español en lugar de inglés?
Sí, puedes reemplazarall-MiniLM-L6-v2 por modelos multilingües como paraphrase-multilingual-MiniLM-L12-v2, los cuales ofrecen un excelente rendimiento en español y otros idiomas.
¿Qué hago si mis documentos están en formato Word (.docx) o HTML?
Simplemente debes utilizar bibliotecas adicionales comopython-docx para archivos Word o BeautifulSoup para páginas web antes de aplicar la función de fragmentación de texto.
¿Es necesario contar con una GPU obligatoriamente?
No para este tutorial. FAISS y los modelos pequeños como FLAN-T5 o MiniLM funcionan perfectamente en la CPU de cualquier ordenador moderno, aunque una GPU acelerará notablemente la fase de indexación de documentos masivos.✨ Conclusión y Próximos Pasos
¡Felicidades! Has construido con éxito un sistema completo de Preguntas y Respuestas basado en recuperación vectorial utilizando FAISS, modelos de Hugging Face y la arquitectura RAG. Este sistema te permite consultar tus propios documentos privados con una precisión muy superior a la de un modelo de lenguaje genérico.
Como próximos pasos, te recomendamos explorar:
- Integrar una interfaz de usuario interactiva utilizando Streamlit o Gradio.
- Implementar persistencia en disco para guardar y cargar tu índice FAISS sin tener que recalcular los embeddings cada vez que inicies el programa.
- Experimentar con modelos generativos más potentes como Llama-3 o Mistral si dispones de infraestructura con GPU.
Tutoriales relacionados
- De Texto a Gráfico: Descubriendo Relaciones con Extracción de Grafos de Conocimiento en PNLintermediate18 min
- Detección Automática de Sarcasmo e Ironía en Redes Sociales con Transformersadvanced12 min
- Clasificación de Intenciones y Detección de Entidades en Diálogos Conversacionales con BERTadvanced12 min
- Desambiguación del Sentido de las Palabras (WSD): Comprendiendo el Contexto en PNLintermediate18 min
- Análisis y Detección de Ironía y Sarcasmo en Textos con PNL: Más Allá del Sentimiento Explícitointermediate18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!