Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Produtoschevron-right
Atlaschevron-right

Como escolher a estratégia de chunking certa para seu aplicativo LLM

Apoorva Joshi15 min read • Published Jun 17, 2024 • Updated Jun 17, 2024
IAPythonAtlas
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Na parte 1 desta série sobre Recuperação de Geração Aumentada (RAG), analisamos como escolher o modelo de incorporação correto para seu aplicativo RAG. Embora a escolha do modelo de incorporação seja uma consideração importante para garantir a recuperação de boa qualidade do RAG, há uma decisão chave a ser tomada antes do estágio de incorporação que pode ter um impacto significativo a seguir: escolher a estratégia de chunking correta para seus dados.
Neste tutorial, abordaremos o seguinte:
  • O que é chunking e por que ele é importante para o RAG?
  • Escolhendo a estratégia de chunking correta para seu aplicativo RAG
  • Avaliando diferentes metodologias de chunking em um conjunto de dados

RAG — uma atualização muito rápida

Arquitetura básica do RAG
Em um aplicativo RAG, o objetivo é obter respostas mais precisas de um grande modelo de linguagem (LLM) sobre assuntos que podem não estar bem representados em seu conhecimento paramétrico — por exemplo, os dados da sua organização. Isso normalmente é feito recuperando informações relevantes de uma base de conhecimento usando pesquisa semântica. Essa técnica usa um modelo de incorporação para criar representações vetoriais da query do usuário e das informações na base de conhecimento.
Dado uma consulta do usuário e sua incorporação, ele recupera os documentos mais relevantes da base de conhecimento usando a pesquisa de similaridade de vetores. Os documentos recuperados, a query do usuário e quaisquer prompts do usuário são então passados como contexto para um LLM, para gerar uma resposta para a pergunta do usuário.

O que é chunking e por que ele é importante para o RAG?

A maioria dos casos de uso do RAG atualmente gira em torno do uso de LLMs para responder a perguntas sobre grandes repositórios de dados, como documentação técnica, documentos de integração etc. Embora os fornecedores de modelos LLM estejam constantemente aumentando as Windows de contexto de seus modelos, eles também cobram pelo número de tokens de entrada. Isso significa que tentar ajustar documentos grandes na janela de contexto do modelo pode ser caro.
Passar documentos grandes como entrada para o LLM também significa que o LLM precisa analisar as informações mais relevantes do documento para responder às queries do usuário. Isso pode ser um desafio, pois os LLMs são projetados para processar entrada sequencialmente, token por token. Embora eles possam capturar dependências e contexto de longo alcance até certo ponto, sua capacidade de fazer isso efetivamente diminui à medida que a sequência de entrada se torna mais longa. É aqui que o chunking ajuda.
A fragmentação é o processo de dividir grandes partes de texto em segmentos ou partes menores. No contexto do RAG, incorporar partes menores em vez de documentos inteiros para criar a base de conhecimento significa que, dada uma consulta do usuário, você só precisa recuperar as partes mais relevantes do documento, resultando em menos tokens de entrada e um contexto mais direcionado para o LLM trabalhar.

Escolhendo a estratégia de chunking correta para seu aplicativo RAG

Não existe uma solução “one size fits all” quando se trata de escolher uma estratégia de chunking para o RAG — ela depende da estrutura dos documentos que estão sendo usados para criar a base de conhecimento e terá uma aparência diferente dependendo se você estiver trabalhando com texto bem formatado documentos ou documentos com trechos de código, tabelas, imagens etc. Os três componentes principais de uma estratégia de chunking são os seguintes:
  • Técnica de divisão: determina onde os limites dos blocos serão colocados — com base nos limites dos parágrafos, separadores específicos da linguagem de programação, tokens ou até mesmo limites semânticos
  • Tamanho do bloco: o número máximo de caracteres ou tokens permitidos para cada bloco
  • Sobreposiçãode blocos: número de caracteres ou tokens sobrepostos entre blocos; blocos sobrepostos podem ajudar a preservar o contexto entre blocos; o grau de sobreposição é normalmente especificado como uma porcentagem do tamanho do bloco
Assim como na escolha de modelos de incorporação ou conclusão para o RAG, recomendamos escolher algumas opções diferentes para cada componente; avaliá-los em um pequeno conjunto de dados selecionado manualmente de queries de usuários antecipadas para seu aplicativo; e escolher aquele que oferece o melhor desempenho em termos de precisão de recuperação e recuperação nesse conjunto de dados de avaliação. Como alternativa, você pode começar criando um conjunto de dados sintético gerado pelo LLM com base em sua melhor estimativa de como os usuários usarão seu aplicativo.
Neste tutorial, avaliaremos diferentes combinações de tamanhos de chunks, sobreposições e técnicas de divisão disponíveis no LangChain. Escolhemos o LangChain aqui por uma questão de continuação da parte 2 desta série de tutoriais. A maioria dessas técnicas também é suportada no LlamaIndex, que é outra estrutura popular para criar aplicativos RAG.
O LangChain e o LlamaIndex suportam várias técnicas de divisão de texto, mas os divisores de texto podem não funcionar bem se você estiver trabalhando com documentos contendo tabelas, imagens etc. Unstructured é uma biblioteca que suporta fragmentação de documentos contendo dados semiestruturados , multimodais , puramente tabulares, etc.

Antes de começarmos

Dados

Para este tutorial, vamos presumir que estamos criando um aplicativo de bate-papo para novos desenvolvedores Python. Usaremos o Python Enhancement Proposals (PEP) para construir a base de conhecimento para o sistema RAG. OsPEPs são documentos que fornecem informações sobre as convenções de codificação e as melhores práticas de codificação do Python para a comunidade do Python.

Ferramentas

Usaremos o LangChain para divisão de texto e para criar componentes de um aplicativo RAG conforme necessário. Usaremos a estruturaRagas que introduzimos na Parte 2 desta série de tutoriais para avaliar a melhor estratégia de agrupamento para nossos dados.

Onde está o código?

O Jupyter Notebook para este tutorial pode ser encontrado no GitHub.

Estratégias de chunking

Analisando os documentos PEP, notamos que eles contêm principalmente texto e alguns trechos de código Python. Com isso em mente, testaremos as seguintes estratégias de chunking:

Token fixo sem sobreposição

Nesta técnica, dividimos os documentos em blocos com um número fixo de tokens, sem sobreposição de tokens entre blocos. Isso pode funcionar bem se houver limites contextuais rígidos entre chunks — ou seja, o contexto varia drasticamente entre chunks adjacentes. Na verdade, esse raramente é o caso, mas manteremos isso como linha de base.
Uma representação visual desta estratégia é a seguinte:
Segmentação de token fixa sem sobreposição

Token fixo com sobreposição

Nessa técnica, dividimos os documentos em partes com um número fixo de tokens, com alguma sobreposição de tokens entre as partes. A sobreposição de chunks garante que as informações contextuais nos limites dos chunks não sejam perdidas durante o chunking, melhorando assim as chances de as informações corretas serem recuperadas durante a pesquisa semântica, mesmo que se estendam por vários chunks.
Uma representação visual desta estratégia é a seguinte:
Corrigido fragmentação de token com sobreposição

Recursivo com sobreposição

Nessa técnica, primeiro dividimos os documentos por uma lista parametrizada de caracteres como \n\n, \netc. e, em seguida, mesclamos recursivamente os caracteres em tokens usando um tokenizador desde o tamanho do bloco (em termos do número de tokens) é menor que o tamanho de chunk especificado. Isso tem o efeito de tentar manter todos os número s (e depois frases e depois palavras) juntos o maior tempo possível, pois esses parecem genericamente ser os pedaços de texto semanticamente mais fortes relacionados.
Uma representação visual desta estratégia é a seguinte:
Chunking recursivo com sobreposição

Divisor de Python recursivo com sobreposição

Como os PEPs contêm alguns trechos de código Python, também incluiremos uma técnica de chunking específica do Python em nossa avaliação. Essa técnica é a mesma que recursiva com sobreposição, exceto que a lista de caracteres para divisão também inclui separadores específicos do Python, como \nclass, \ndef, etc.

Semântico

Nesta técnica, os documentos são divididos com base na similaridade semântica. Os documentos são primeiro divididos em grupos de frases de três frases usando uma janela deslizante. As incorporações são geradas para cada grupo de frases, e grupos semelhantes no espaço de incorporações são mesclados para formar blocos. O limite de similaridade para fusão é determinado usando métricas como percentil, desvio padrão e distância interquartil. Como resultado, o tamanho do pedaço pode variar entre os pedaços.
Embora esse método seja mais dispendioso em termos de computação do que os anteriores, ele pode ser útil para a fragmentação de documentos em que os limites contextuais não são óbvios - por exemplo, redações que podem não ter títulos para indicar quebras contextuais lógicas.
Uma representação visual desta estratégia é a seguinte:
Fragmentação semântica

Etapa 1: instalar as bibliotecas necessárias

Vamos precisar das seguintes bibliotecas para este tutorial:
  • langchain: biblioteca Python para desenvolver aplicativos LLM usando LangChain
  • langchain-openai: pacote Python para usar modelos OpenAI no LangChain
  • langchain-mongodb: pacote Python para usar o MongoDB Atlas como armazenamento de vetores com LangChain
  • langchain-experimental: pacote Python para os recursos experimentais do LangChain, como chunking semântica
  • ragas: Biblioteca Python para a estrutura Ragas
  • pymongo: driver Python para interagir com o MongoDB
  • tqdm: módulo Python para mostrar um medidor de progresso para loops
1! pip install -qU langchain langchain-openai langchain-mongodb langchain-experimental ragas pymongo tqdm

Etapa 2: configurar pré-requisitos

Neste tutorial, usaremos o MongoDB Atlas para criar a base de conhecimento (armazenamento de vetores) para nosso aplicativo RAG . Mas, primeiro, você precisará de uma conta MongoDB Atlas com um cluster de banco de dados. Você também precisará obter a string de conexão para se conectar ao cluster. Siga estas etapas para configurar:
Não se lembre de adicionar o IP da sua máquina host à lista de acesso IP do seu cluster.
1import getpass
2MONGODB_URI = getpass.getpass("Enter your MongoDB connection string:")
Usaremos os modelos de incorporação e conclusão de chat do OpenAI, então você também precisará obter uma chave de API do OpenAI e defini-la como uma variável de ambiente para o cliente do OpenAI usar:
1import os
2from openai import OpenAI
3os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API Key:")
4openai_client = OpenAI()

Etapa 3: carregar o conjunto de dados

Conforme mencionado anteriormente, usaremos PEPs para construir a base de conhecimento para nosso aplicativo RAG. Usaremos o carregador de documentosWebBaseLoader da LangChain, que carrega todo o texto de páginas da web em HTML em um formato de documento mais legível, removendo tags HTML etc.
1from langchain_community.document_loaders import WebBaseLoader
2
3web_loader = WebBaseLoader(
4 [
5 "https://peps.python.org/pep-0483/",
6 "https://peps.python.org/pep-0008/",
7 "https://peps.python.org/pep-0257/",
8 ]
9)
10
11pages = web_loader.load()
O métodoload dos carregadores de documentos no LangChain cria uma lista de objetos de documento LangChain (vamos nos referir a eles como "documents " neste tutorial), cada um consistindo em dois atributos - a saber, page_content e metadata. page_content, como o nome sugere, corresponde ao conteúdo do documento, e metadata são alguns metadados básicos extraídos pelo carregador de documentos, como origem, título, descrição, idioma etc.
Segue um exemplo de objeto de documento:
1Document(
2 page_content="\nThe goal of this PEP is to propose such a systematic way of defining types\nfor type annotations of variables and functions using PEP 3107 syntax.",
3 metadata= {'source': 'https://peps.python.org/pep-0483/', 'title': 'PEP 483 – The Theory of Type Hints | peps.python.org', 'description': 'Python Enhancement Proposals (PEPs)', 'language': 'en'}
4)
Se você estiver utilizando LlamaIndex, utilize a classeSimpleWebPageReader para carregar texto de páginas da web em HTML.

Etapa 4: defina as funções de fragmentação

Em seguida, vamos definir funções para dar suporte a cada uma das estratégias de chunking que queremos experimentar.
1from langchain.text_splitter import TokenTextSplitter
2
3def fixed_token_split(
4 docs: List[Document], chunk_size: int, chunk_overlap: int
5) -> List[Document]:
6 """
7 Fixed token chunking
8
9 Args:
10 docs (List[Document]): List of documents to chunk
11 chunk_size (int): Chunk size (number of tokens)
12 chunk_overlap (int): Token overlap between chunks
13
14 Returns:
15 List[Document]: List of chunked documents
16 """
17 splitter = TokenTextSplitter(
18 encoding_name="cl100k_base", chunk_size=chunk_size, chunk_overlap=chunk_overlap
19 )
20 return splitter.split_documents(docs)
No código acima, a funçãofixed_token_split utiliza a classeTokenTextSplitter em LangChain para dividir documentos em partes, cada um consistindo em um número fixo de tokens. A classeTokenTextSplitter usa os seguintes argumentos:
  • chunk_size: Número de tokens em cada pedaço
  • chunk_overlap: Número de tokens sobrepostos entre partes adjacentes
  • encoding_name: o modelo a ser usado para gerar tokens
Usaremos esta função para criar chunks com e sem sobreposição. Para criar chunks sem sobreposição, definiremos o argumento chunk_overlapcomo0 ao chamar a função. Para criar chunks com sobreposição, vamos defini-lo para um valor diferente de zero.
Se você estiver usando o LlamaIndex, use a classeTokenTextSplitter para agrupamento de token fixo.
Em seguida, vamos criar uma função para agrupamento recursivo:
1from langchain.text_splitter import (
2 Language,
3 RecursiveCharacterTextSplitter,
4)
5
6def recursive_split(
7 docs: List[Document],
8 chunk_size: int,
9 chunk_overlap: int,
10 language: Optional[Language] = None,
11) -> List[Document]:
12 """
13 Recursive chunking
14
15 Args:
16 docs (List[Document]): List of documents to chunk
17 chunk_size (int): Chunk size (number of tokens)
18 chunk_overlap (int): Token overlap between chunks
19 language (Optional[Language], optional): Programming language enum. Defaults to None.
20
21 Returns:
22 List[Document]: List of chunked documents
23 """
24 separators = ["\n\n", "\n", " ", ""]
25
26 if language is not None:
27 try:
28 separators = RecursiveCharacterTextSplitter.get_separators_for_language(
29 language
30 )
31 except (NameError, ValueError) as e:
32 print(f"No separators found for language {language}. Using defaults.")
33
34 splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
35 encoding_name="cl100k_base",
36 chunk_size=chunk_size,
37 chunk_overlap=chunk_overlap,
38 separators=separators,
39 )
40 return splitter.split_documents(docs)
No código acima, a funçãorecursive_split usa o método tiktoken_encoderda classeRecursiveCharacterTextSplitter em LangChain para primeiro dividir documentos por uma lista de caracteres e, em seguida, mesclar recursivamente as divisões em tokens usando o tokenizadortiktoken .
Além de chunk_size e chunk_overlap, a função usa um parâmetrolanguageadicional para suportar a divisão com base em separadores específicos de linguagem para linguagens de programação suportadas no enumLanguage em LangChain. Se nenhum idioma ou um idioma não suportado for especificado, a lista padrão de separadores - ou seja, ["\n\n", "\n", " ", ""] - será usada para a divisão. Caso contrário, uma lista específica do idioma será usada. Esta lista é obtida utilizando o método get_separators_for_languageda classeRecursiveCharacterTextSplitter. Para Python, definiremos o parâmetro de idioma como Language.PYTHON na chamada de função mais tarde.
Se você estiver usando o LlamaIndex, use a classeSentenceSplitter para agrupamento recursivo e divisão com base em separadores específicos da linguagem de programação.
Finalmente, vamos definir uma função para a divisão semântica:
1from langchain_experimental.text_splitter import SemanticChunker
2from langchain_openai.embeddings import OpenAIEmbeddings
3
4def semantic_split(docs: List[Document]) -> List[Document]:
5 """
6 Semantic chunking
7
8 Args:
9 docs (List[Document]): List of documents to chunk
10
11 Returns:
12 List[Document]: List of chunked documents
13 """
14 splitter = SemanticChunker(
15 OpenAIEmbeddings(), breakpoint_threshold_type="percentile"
16 )
17 return splitter.split_documents(docs)
No código acima, definimos a funçãosemantic_split que utiliza a classeSemanticChunker no LangChain para dividir documentos com base na similaridade semântica. Usamos um modelo de incorporação OpenAI (text-embedding-ada-002 por padrão) para incorporar grupos de frases e definimos o parâmetro breakpoint_threshold_typecomopercentile. Nesse método, as distâncias de incorporação entre grupos de frases adjacentes são calculadas e as divisões são criadas em pontos em que a distância de incorporação é maior que o 95percentil de distâncias. Outras opções para breakpoint_threshold_type incluem standard_deviation e interquartile.
Se você estiver usando o LlamaIndex, use a classeSemanticSplitterNodeParser para chunking semântica.

Etapa 5: gere o conjunto de dados de avaliação

Conforme mencionado anteriormente, você pode usar um conjunto de dados com curadoria manual de consultas antecipadas de usuários para avaliação ou começar usando um conjunto de dados gerado pelo LLM. Vamos explorar a última opção usando os recursos de geração de dados sintéticos da bibliotecaragas.
1from ragas import RunConfig
2from ragas.testset.generator import TestsetGenerator
3from ragas.testset.evolutions import simple, reasoning, multi_context
4from langchain_openai import ChatOpenAI, OpenAIEmbeddings
5
6RUN_CONFIG = RunConfig(max_workers=4, max_wait=180)
7
8# generator with openai models
9generator_llm = ChatOpenAI(model="gpt-3.5-turbo-16k")
10critic_llm = ChatOpenAI(model="gpt-4")
11embeddings = OpenAIEmbeddings()
12
13generator = TestsetGenerator.from_langchain(generator_llm, critic_llm, embeddings)
14
15# Change resulting question type distribution
16distributions = {simple: 0.5, multi_context: 0.4, reasoning: 0.1}
17
18testset = generator.generate_with_langchain_docs(
19 pages, 10, distributions, run_config=RUN_CONFIG
20)
O código acima:
  • Cria uma configuração de tempo de execução para que o Ragas substitua as configurações padrão de simultaneidade e de repetição — isso é feito para evitar atingir os limites de taxa da OpenAI , mas isso pode não ser um problema dependendo do seu nível de uso ou se você não estiver usando modelos da OpenAI para teste geração de definir.
  • Especifica o generator_llm — ou seja, o modelo a ser usado para gerar as perguntas, as respostas e as informações básicas para avaliação.
  • Especifica o critic_llm que é o modelo a ser usado para validar os dados gerados pelo generator_llm.
  • Especifica embeddings — ou seja, o modelo de incorporação para construir o armazenamento de vetor para o gerador de conjunto de teste.
  • Cria um gerador de conjunto de testes utilizando os modelos especificados. Usamos o métodofrom_langchainda TestsetGenerator, pois estamos usando modelos OpenAI por meio do LangChain.
  • Especifica a distribuição dos tipos de perguntas a serem incluídas no conjunto de testes: simple corresponde a perguntas diretas que podem ser facilmente respondidas usando os dados de origem. multi_context significa perguntas que exigiriam informações de várias seções ou partes relacionadas para formularuma resposta. As perguntas reasoningsão aquelas que exigiriam que um LLM raciocinasse para responder efetivamente à pergunta. Você desejará definir essa distribuição com base em sua melhor estimativa do tipo de perguntas que você esperaria de seus usuários.
  • Gera um conjunto de teste a partir do conjunto de dados (pages) que criamos na etapa 3. Usamos o métodogenerate_with_langchain_docs, pois nosso conjunto de dados é uma lista de documentos LangChain.
O Ragas também suporta a geração de conjuntos de teste usando o LlamaIndex. Consulte a documentaçãodeles para obter mais informações.

Etapa 6: Avalie estratégias de chunking

Agora que definimos nossas funções de agrupamento e criamos nosso conjunto de dados de avaliação, estamos prontos para avaliar as diferentes estratégias de agrupamento para encontrar aquela que nos oferece a melhor qualidade de recuperação em nosso conjunto de dados de avaliação.
Vamos começar criando uma collection MongoDB com um índice de armazenamento de vetor para nossa avaliação. Para fazer isso, navegue até a 2 Collectionsdo cluster que você criou na etapa . Clique em Create Database para criar um banco de dados chamado evals com uma coleção chamada chunking.
Criando um banco de dados na IU do MongoDB Atlas
Depois de fazer isso, crie um índice de vetor chamado vector_index na collectionchunking com a seguinte definição de índice:
1{
2 "fields": [
3 {
4 "numDimensions": 1536,
5 "path": "embedding",
6 "similarity": "cosine",
7 "type": "vector"
8 }
9 ]
10}
O número de dimensões de incorporação na definição de índice é 1536 , pois usaremos o modelo text-embedding-3-small do OpenAI para gerar incorporações para nosso armazenamento de vetores.
Agora, vamos definir uma função para criar um armazenamento de vetores do MongoDB Atlas usando a coleção que criamos:
1from langchain_mongodb import MongoDBAtlasVectorSearch
2from pymongo import MongoClient
3
4client = MongoClient(MONGODB_URI)
5DB_NAME = "evals"
6COLLECTION_NAME = "chunking"
7ATLAS_VECTOR_SEARCH_INDEX_NAME = "vector_index"
8MONGODB_COLLECTION = client[DB_NAME][COLLECTION_NAME]
9
10def create_vector_store(docs: List[Document]) -> MongoDBAtlasVectorSearch:
11 """
12 Create MongoDB Atlas vector store
13
14 Args:
15 docs (List[Document]): List of documents to create the vector store
16
17 Returns:
18 MongoDBAtlasVectorSearch: MongoDB Atlas vector store
19 """
20 vector_store = MongoDBAtlasVectorSearch.from_documents(
21 documents=docs,
22 embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
23 collection=MONGODB_COLLECTION,
24 index_name=ATLAS_VECTOR_SEARCH_INDEX_NAME,
25 )
26
27 return vector_store
O código acima:
  • Cria um PyMongo (client) para se conectar ao nosso MongoDB Atlas cluster.
  • Especifica o banco de dados (DB_NAME) e a coleção (MONGODB_COLLECTION) aos quais se conectar.
  • Especifica o índice de pesquisa vetorial (ATLAS_VECTOR_SEARCH_INDEX_NAME``) a ser usado para pesquisa vetorial.
  • Define uma create_vector_store função que recebe uma lista de documentos e retorna um objeto de armazenamento de vetor do MongoDB Atlas. Ele usa o from_documents método da MongoDBAtlasVectorSearch classe da langchain-mongodb integração para criar um armazenamento de vetor do MongoDB Atlas diretamente usando documentos do LangChain.
Em seguida, vamos definir uma função para avaliar diferentes estratégias de chunking no conjunto de dados de teste que criamos na etapa 5:
1from tqdm import tqdm
2from datasets import Dataset
3from ragas import evaluate
4from ragas.metrics import context_precision, context_recall
5import nest_asyncio
6
7# Allow nested use of asyncio (used by Ragas)
8nest_asyncio.apply()
9
10# Disable tqdm locks
11tqdm.get_lock().locks = []
12
13QUESTIONS = testset.question.to_list()
14GROUND_TRUTH = testset.ground_truth.to_list()
15
16def perform_eval(docs: List[Document]) -> Dict[str, float]:
17 """
18 Perform Ragas evaluation
19
20 Args:
21 docs (List[Document]): List of documents to create the vector store
22
23 Returns:
24 Dict[str, float]: Dictionary of evaluation metrics
25 """
26 eval_data = {
27 "question": QUESTIONS,
28 "ground_truth": GROUND_TRUTH,
29 "contexts": [],
30 }
31
32 print(f"Deleting existing documents in the collection {DB_NAME}.{COLLECTION_NAME}")
33 MONGODB_COLLECTION.delete_many({})
34 print(f"Deletion complete")
35 vector_store = create_vector_store(docs)
36
37 # Getting relevant documents for the evaluation dataset
38 print(f"Getting contexts for evaluation set")
39 for question in tqdm(QUESTIONS):
40 eval_data["contexts"].append(
41 [doc.page_content for doc in retriever.similarity_search(question, k=3)]
42 )
43 # RAGAS expects a Dataset object
44 dataset = Dataset.from_dict(eval_data)
45
46 print(f"Running evals")
47 result = evaluate(
48 dataset=dataset,
49 metrics=[context_precision, context_recall],
50 run_config=RUN_CONFIG,
51 raise_exceptions=False,
52 )
53 return result
O código acima define uma funçãoperform_evalque recebe como entrada uma lista de documentos fragmentados (docs) produzidos usando uma determinada estratégia de fragmentação e retorna um dicionário que consiste em métricas de qualidade de recuperação para essa estratégia, em nosso conjunto de dados de avaliação. A seguir, apresentamos um detalhamento do que a função faz:
  • Cria um dicionário (eval_data) com question, ground_truthe contexts como chaves, correspondentes às perguntas no conjunto de dados de avaliação, suas respostas de verdade fundamental e contextos recuperados
  • Exclui quaisquer documentos existentes da coleção do MongoDB que estamos usando para teste
  • Cria um armazenamento de vetores do MongoDB Atlas usando a lista atual de documentos em partes
  • Usa o métodosimilarity_search para recuperar os três blocos mais relevantes do armazenamento de vetores para cada pergunta no conjunto de dados da avaliação e os adiciona à lista contexts no dicionárioeval_data
  • Converte o dicionárioeval_data em um objeto do conjunto de dados
  • Usa o evaluate método da bibliotecaragas para calcular as context_precision context_recall métricas e para o conjunto de dados de avaliação
A precisão do contexto avalia a capacidade do retriever de classificar os itens recuperados em ordem de relevância para a resposta de verdade fundamental. A recordação de contexto mede até que ponto o contexto recuperado se alinha com a resposta de verdade básica.
Por fim, vamos executar a avaliação das cinco estratégias de chunking que listamos previamente para nosso conjunto de dados PEP:
1for chunk_size in [100, 200, 500, 1000]:
2 chunk_overlap = int(0.15 * chunk_size)
3 print(f"CHUNK SIZE: {chunk_size}")
4 print("------ Fixed token without overlap ------")
5 print(f"Result: {perform_eval(fixed_token_split(pages, chunk_size, 0))}")
6 print("------ Fixed token with overlap ------")
7 print(
8 f"Result: {perform_eval(fixed_token_split(pages, chunk_size, chunk_overlap))}"
9 )
10 print("------ Recursive with overlap ------")
11 print(f"Result: {perform_eval(recursive_split(pages, chunk_size, chunk_overlap))}")
12 print("------ Recursive Python splitter with overlap ------")
13 print(
14 f"Result: {perform_eval(recursive_split(pages, chunk_size, chunk_overlap, Language.PYTHON))}"
15 )
16print("------ Semantic chunking ------")
17print(f"Result: {perform_eval(semantic_split(pages))}")
Algumas coisas a observar sobre o código acima:
  • Estamos avaliando diferentes tamanhos de chunk para encontrar o tamanho ideal de chunk para cada estratégia de chunking.
  • Para estratégias de chunking com sobreposição de token, definimos a sobreposição para 15% do tamanho do chunk. Embora tenhamos mantido a porcentagem de sobreposição constante aqui, você pode experimentar valores diferentes, se necessário. Uma sobreposição de bloco entre 5% e 20% do tamanho do bloco é recomendada para a maioria dos conjuntos de dados.
  • Não existe um conceito de tamanho de bloco para o chunking semântico, pois ele usa um limite de distância de incorporação para determinar os limites do bloco.
Os resultados da avaliação para as diferentes estratégias de chunking têm a seguinte aparência em nosso conjunto de dados de avaliação:
Estratégia de chunkingTamanho do blocoPrecisão do contextoLembrete de contexto
Token fixo sem sobreposição1000.85830.7833
2000.90.9
5000.88330.95
10000.90.8909
Token fixo com sobreposição1000.90.95
2001.00.9383
5000.70.9
10000.78330.8909
Recursivo com sobreposição1000.90.9833
2000.90.9008
5000.56670.8236
10000.78330.88
Divisor de Python recursivo com sobreposição1000.98330.9833
2001.00.8583
5000.60.88
10000.80.8709
Fragmentação semânticaN/A0.90.8187
Com base nos resultados acima, uma técnica de divisão recursiva específica do Python com uma sobreposição de tokens 15 e um tamanho de chunk de 100 é a melhor estratégia de chunking para nosso conjunto de dados.
Lembre-se de que usamos um conjunto de dados de avaliação muito pequeno de amostras 10 para demonstração, portanto, os resultados acima não podem ser tratados como totalmente conclusivos. Na realidade, você desejará usar um conjunto de dados de avaliação maior e mais representativo.

Conclusão

Neste tutorial, aprendemos como escolher a estratégia certa de chunking para RAG. A divisão de documentos grandes em partes menores para o RAG resulta em menos tokens passados como entrada para os LLMs e um contexto mais direcionado para os LLMs trabalharem. As estratégias de chunking são compostas por três componentes principais – técnica de divisão, tamanho do chunk e sobreposição de chunks. Escolher a estratégia certa envolve experimentar diferentes combinações dos três componentes.
Agora que você tem um bom entendimento do chunking para RAG, aceite o desafio de avaliar diferentes estratégias de chunking em conjuntos de dados que foram usados em alguns de nossos tutoriais anteriores:
Se você tiver mais dúvidas sobre RAG, Vector Search ou AI em geral, entre em contato conosco em nossos fóruns da comunidade de AI generativa e fique atento ao último tutorial da série RAG. Os tutoriais anteriores da série podem ser encontrados abaixo:
Principais comentários nos fóruns
Avatar do Comentarista do Fórum
Scheersh_SexenaScheersh Saxena3 meses atrás

no final do código, estou encontrando um erro de handshake SSL.

Veja mais nos fóruns

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Notícias e anúncios

Transforme suas habilidades de desenvolvimento de AI com a missão de desenvolvedor RAG to Riches!


Jul 10, 2024 | 1 min read
Artigo

Conjuntos de dados de amostra do MongoDB Atlas


Oct 01, 2024 | 8 min read
Tutorial

Crie um mecanismo de pesquisa de imagens com Python e MongoDB


Sep 18, 2024 | 8 min read
Tutorial

Parte #3: pesquise seus dados semanticamente com o Atlas Vector Search do MongoDB


Sep 18, 2024 | 6 min read
Sumário