Artigo
· Nov. 4 6min de leitura

Modelos LLM e Aplicações RAG passo-a-passo - Parte II - Criando o contexto

Continuamos com esta série de artigos sobre LLMs e aplicações RAG e neste artigo discutiremos a parte da caixa vermelha do seguinte diagrama:

No processo de criação de uma aplicação RAG, escolher um modelo LLM adequado às suas necessidades (treinado no assunto correspondente, custos, velocidade, etc.) é tão importante quanto ter um claro entendimento do contexto que você deseja fornecer. Vamos começar definindo o termo para ficarmos claros sobre o que entendemos por contexto.

O que é contexto?

Contexto refere-se a informações adicionais obtidas de uma fonte externa, como um banco de dados ou mecanismo de busca, para complementar ou melhorar as respostas geradas por um modelo de linguagem. O modelo de linguagem utiliza essas informações externas relevantes para gerar respostas mais precisas e detalhadas, em vez de confiar apenas no que aprendeu durante seu treinamento. O contexto ajuda a manter as respostas atualizadas e alinhadas com o tópico específico da consulta.

Este contexto pode ser informação armazenada em um banco de dados com ferramentas semelhantes às mostradas pelo nosso querido membro da comunidade  @José Pereira neste artigo ou informação não estruturada na forma de arquivos de texto com os quais alimentaremos o LLM, que será o caso que vamos tratar aqui.

Como gerar o contexto para nossa aplicação RAG?

A primeira e mais essencial coisa é, obviamente, ter toda a informação que consideramos relevante para as possíveis perguntas que vão ser feitas à nossa aplicação. Uma vez que esta informação está organizada de tal forma que seja acessível a partir da nossa aplicação, devemos ser capazes de identificar quais de todos os documentos disponíveis para o nosso contexto se referem à pergunta específica feita pelo utilizador. Para o nosso exemplo, temos uma série de documentos PDF (folhetos informativos de medicamentos) que queremos utilizar como possível contexto para as perguntas dos utilizadores da nossa aplicação.

Este ponto é fundamental para o sucesso de uma aplicação RAG, pois é tão ruim para a confiança do usuário responder com generalizações e vaguidão típicas de um LLM quanto responder com um contexto totalmente errado. É aqui que entram em cena nossos queridos bancos de dados vetoriais.

Bases de dados de vetores

Você provavelmente já ouviu falar de "bancos de dados vetoriais", como se fossem um novo tipo de banco de dados, como bancos de dados relacionais ou de documentos. Nada poderia estar mais longe da verdade. Esses bancos de dados vetoriais são bancos de dados padrão que suportam tipos de dados vetoriais, bem como operações relacionadas a eles. Vamos ver como esse tipo de dado será representado no projeto associado ao artigo:

Agora vamos ver como um registro seria exibido:

Bancos de dados de vetores... para quê?

Como explicamos no artigo anterior com LLMs, o uso de vetores é fundamental nos modelos de linguagem, pois eles podem representar conceitos e as relações entre eles em um espaço multidimensional. No caso em questão, essa representação multidimensional será a chave para identificar quais dos documentos em nosso contexto serão relevantes para a pergunta feita.

Perfeito, temos nosso banco de dados vetorial e os documentos que fornecerão o contexto, agora só precisamos registrar o conteúdo desses documentos em nosso banco de dados, mas... Com que critério?

Modelos para vetorização

Como? Outro modelo? O LLM não é suficiente para nós? Bem... não há necessidade de incomodar nosso LLM para vetorizar a informação do nosso contexto, podemos usar modelos de linguagem menores que sejam mais adequados às nossas necessidades para esta tarefa, como modelos treinados para detectar similaridades entre sentenças. Você pode encontrar uma infinidade deles no Hugging Face, cada um treinado com um conjunto específico de dados que nos permitirá melhorar a vetorização dos nossos dados.

E se isso não te convencer a usar um desses modelos para vetorização, basta dizer que geralmente esse tipo de modelo...

(São grátis)

Vamos ver em nosso exemplo como invocamos o modelo escolhido para essas vetorizações:

if not os.path.isdir('/app/data/model/'):
    model = sentence_transformers.SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')            
    model.save('/app/data/model/')

Aqui estamos baixando o modelo escolhido para o nosso caso para o nosso computador local. Este mini-LM é multilíngue, por isso podemos vetorizar tanto em espanhol quanto em inglês sem nenhum problema.

Chunking

Se você já brincou com modelos de linguagem, provavelmente já enfrentou o desafio do chunking. O que é esse chunking? Muito simples, é a divisão do texto em fragmentos menores que podem conter um significado relevante. Por meio desse chunking do nosso contexto, podemos fazer consultas em nosso banco de dados vetorial que extraiam aqueles documentos do nosso contexto que possam ser relevantes em relação à pergunta feita.

Quais são os critérios para esse chunking? Bem, não há realmente um critério mágico que nos permita saber o quão longos nossos pedaços devem ser para serem o mais precisos possível. Em nosso exemplo, estamos usando uma biblioteca Python fornecida pelo langchain para realizar esse chunking, embora qualquer outro método ou biblioteca possa ser usado para isso:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 700,
    chunk_overlap  = 50,
)
path = "/app/data"
loader = PyPDFDirectoryLoader(path)
docs_before_split = loader.load()
docs_after_split = text_splitter.split_documents(docs_before_split)

Como você pode ver, o tamanho escolhido é de 700 caracteres, com uma sobreposição de 50 para evitar cortar palavras. Esses fragmentos extraídos de nossos documentos serão os que vetorizaremos e inseriremos em nosso banco de dados.

Esse processo de chunking pode ser otimizado tanto quanto você quiser por meio de "lematização", através da qual podemos transformar as palavras em seus respectivos lemmas (sem tempos, plurais, gênero, etc.) e assim eliminar certo ruído para a geração do vetor, mas não vamos entrar nisso, nesta página você pode ver uma explicação mais detalhada.

Vetorização de fragmentos

Ok, temos nossos fragmentos extraídos de cada um de nossos documentos, é hora de vetorizar e inserir em nosso banco de dados, vamos dar uma olhada no código para entender como podemos fazer isso.

for doc in docs_after_split:
    embeddings = model.encode(doc.page_content, normalize_embeddings=True)
    array = np.array(embeddings)
    formatted_array = np.vectorize('{:.12f}'.format)(array)
    parameters = []
    parameters.append(doc.metadata['source'])
    parameters.append(str(doc.page_content))
    parameters.append(str(','.join(formatted_array)))
    cursorIRIS.execute("INSERT INTO LLMRAG.DOCUMENTCHUNK (Document, Phrase, VectorizedPhrase) VALUES (?, ?, TO_VECTOR(?,DECIMAL))", parameters)
connectionIRIS.commit()

Como você pode ver, realizaremos os seguintes passos:

  1. Percorremos a lista de todos os fragmentos obtidos de todos os documentos que formarão nosso contexto.
  2. Para cada fragmento, vetorizamos o texto (usando a biblioteca sentence_transformers).
  3. Criamos um array usando a biblioteca numpy com o vetor formatado e o transformamos em uma string.
  4. Registramos as informações do documento com seu vetor associado em nosso banco de dados. Se você observar, estamos executando o comando TO_VECTOR que transformará a string do vetor que passamos para o formato apropriado.

Conclusão

Neste artigo vimos a necessidade de ter um banco de dados vetorial para a criação do contexto necessário em nossa aplicação RAG, também revisamos como cortar e vetorizar a informação do nosso contexto para seu registro nesse banco de dados.

No próximo artigo veremos como consultar nosso banco de dados vetorial com base na pergunta que o usuário envia ao modelo LLM e como, procurando por similaridades, construiremos o contexto que passaremos ao modelo. Não perca!

Discussão (0)1
Entre ou crie uma conta para continuar