Al preparar datos para incrustarlos y recuperarlos en un sistema RAG, es fundamental dividir el texto en fragmentos del tamaño adecuado. Este proceso está guiado por dos factores principales: las restricciones del modelo y la eficacia de la recuperación.
Restricciones del modelo
Los modelos integrados tienen una longitud máxima de token para la entrada; todo lo que supere este límite se trunca. Tenga en cuenta las limitaciones del modelo elegido y asegúrese de que cada fragmento de datos no exceda esta longitud máxima de token.
Los modelos multilingües, en particular, suelen tener límites de secuencia más cortos en comparación con sus homólogos en inglés. Por ejemplo, el modelo multilingüe MiniLM-L12 v2 de Paraphrase, ampliamente utilizado, tiene una ventana de contexto máxima de solo 128 tokens.
Además, considere la longitud del texto con la que se entrenó el modelo: algunos modelos técnicamente pueden aceptar entradas más largas, pero se entrenaron en fragmentos más cortos, lo que podría afectar el rendimiento en textos más largos. Un ejemplo de ello es el Base de control de calidad múltiple de SBERT como se ve a continuación,
Efectividad de recuperación
Si bien fragmentar los datos hasta la longitud máxima del modelo parece lógico, es posible que no siempre conduzca a los mejores resultados de recuperación. Los fragmentos más grandes ofrecen más contexto para el LLM, pero pueden oscurecer detalles clave, lo que dificulta la recuperación de coincidencias precisas. Por el contrario, los fragmentos más pequeños pueden mejorar la precisión de las coincidencias, pero pueden carecer del contexto necesario para respuestas completas. Los enfoques híbridos utilizan fragmentos más pequeños para la búsqueda, pero incluyen el contexto circundante en el momento de la consulta para mantener el equilibrio.
Si bien no hay una respuesta definitiva con respecto al tamaño del fragmento, las consideraciones sobre el tamaño del fragmento siguen siendo consistentes ya sea que esté trabajando en proyectos multilingües o en inglés. Recomendaría leer más sobre el tema en recursos como Evaluación del tamaño de fragmento ideal para el sistema RAG utilizando Llamaindex o Creación de aplicaciones LLM basadas en RAG para producción.
División de texto: métodos para dividir texto
El texto se puede dividir utilizando varios métodos., que se dividen principalmente en dos categorías: modelos basados en reglas (centrándose en el análisis de personajes) y modelos basados en aprendizaje automático. Los enfoques de aprendizaje automático, desde simples tokenizadores NLTK y Spacy hasta modelos de transformadores avanzados, a menudo dependen de una capacitación en un idioma específico, principalmente en inglés. Aunque los modelos simples como NLTK y Spacy admiten varios idiomas, abordan principalmente la división de oraciones, no la sección semántica.
Dado que los divisores de oraciones basados en ML actualmente funcionan mal para la mayoría de los idiomas distintos del inglés y requieren un uso intensivo de computación, recomiendo comenzar con un divisor simple basado en reglas. Si ha conservado la estructura sintáctica relevante de los datos originales y los ha formateado correctamente, el resultado será de buena calidad.
Un método común y eficaz es un divisor de texto de caracteres recursivos, como los que se utilizan en LangChain o LlamaIndex, que acorta las secciones encontrando el carácter dividido más cercano en una secuencia priorizada (por ejemplo, \n\n, \n, ., ?, !).
Tomando el texto formateado de la sección anterior, un ejemplo del uso del divisor de caracteres recursivo de LangChains sería el siguiente:
from langchain.text_splitter import RecursiveCharacterTextSplitter
from transformers import AutoTokenizertokenizer = AutoTokenizer.from_pretrained("intfloat/e5-base-v2")
def token_length_function(text_input):
return len(tokenizer.encode(text_input, add_special_tokens=False))
text_splitter = RecursiveCharacterTextSplitter(
# Set a really small chunk size, just to show.
chunk_size = 128,
chunk_overlap = 0,
length_function = token_length_function,
separators = ["\n\n", "\n", ". ", "? ", "! "]
)
split_texts = text_splitter(formatted_document['Boosting RAG: Picking the Best Embedding & Reranker models'])
Aquí es importante tener en cuenta que se debe definir el tokenizador como el modelo de incrustación que se pretende usar, ya que diferentes modelos “cuentan” las palabras de manera diferente. La función ahora, en orden de prioridad, dividirá cualquier texto de más de 128 tokens primero por el \n\n que introdujimos al final de las secciones, y si eso no es posible, luego por el final de los párrafos delimitados por \n y así sucesivamente. . Los primeros 3 trozos serán:
Token of text: 111 UPDATE: The pooling method for the Jina AI embeddings has been adjusted to use mean pooling, and the results have been updated accordingly. Notably, the JinaAI-v2-base-en with bge-reranker-largenow exhibits a Hit Rate of 0.938202 and an MRR (Mean Reciprocal Rank) of 0.868539 and withCohereRerank exhibits a Hit Rate of 0.932584, and an MRR of 0.873689.
-----------
Token of text: 112
When building a Retrieval Augmented Generation (RAG) pipeline, one key component is the Retriever. We have a variety of embedding models to choose from, including OpenAI, CohereAI, and open-source sentence transformers. Additionally, there are several rerankers available from CohereAI and sentence transformers.
But with all these options, how do we determine the best mix for top-notch retrieval performance? How do we know which embedding model fits our data best? Or which reranker boosts our results the most?
-----------
Token of text: 54
In this blog post, we’ll use the Retrieval Evaluation module from LlamaIndex to swiftly determine the best combination of embedding and reranker models. Let's dive in!
Let’s first start with understanding the metrics available in Retrieval Evaluation
Ahora que hemos dividido con éxito el texto de una manera semánticamente significativa, podemos pasar a la parte final de incrustar estos fragmentos para su almacenamiento.