Como criar uma solução end-to-end de IA Generativa?

Como criar uma solução end-to-end de IA Generativa?

Neste artigo, daremos uma olhada na construção de uma solução completa de IA generativa e utilizaremos algumas ferramentas diferentes para operacionalizar esse fluxo de trabalho:

  • LangChain : LangChain é uma estrutura Python popular que ajuda a simplificar aplicativos de IA generativa, fornecendo módulos prontos que ajudam na engenharia de prompt, implementação de RAG e orquestração de fluxo de trabalho LLM.
  • OpenAI : LangChain cuidará da orquestração de nosso aplicativo Generative AI, mas o cérebro ainda é o modelo. Neste caso, usamos um LLM fornecido pela OpenAI, mas LangChain também se integra com diferentes fontes de modelo, como SageMaker Endpoints, Cohere, etc.

NOTA : Este artigo pressupõe um conhecimento intermediário de Python e um conhecimento básico de LangChain em específico. Eu sugeriria seguir este artigo para entender melhor o LangChain e construir melhor aplicativos de IA generativa.


Visão geral do problema

Os Large Language Models (LLMs) por si só são incrivelmente poderosos e muitas vezes podem responder a muitas perguntas sem ajuda de ajuste fino ou conhecimento/contexto adicional.

No entanto, isso pode se tornar um gargalo quando você precisar acessar outras fontes específicas de dados e, especialmente, dados recentes. Por exemplo, embora a OpenAI tenha sido treinada em um grande corpus de dados, ela não tem conhecimento dos artigos recentes.

Os modelos da OpenAI já possuem algum conhecimento do Amazon SageMaker a partir do corpus em que foram treinados. O que queremos ver é quanto desempenho podemos ganhar fornecendo a esses LLMs acesso aos artigos do Medium. Eles podem servir quase como uma espécie de folha de dicas para LLMs que já possuem um grande banco de conhecimento.

Como fornecemos a esses LLMs acesso a esse conhecimento e informações adicionais? 🤔


Por que precisamos do RAG

É aqui que entra em jogo a Geração Aumentada de Recuperação (RAG). Com o RAG fornecemos um sistema de recuperação de informação que nos dá acesso aos dados adicionais de que necessitamos. Isso nos ajudará a responder perguntas mais avançadas sobre o SageMaker e a aumentar nossa base de conhecimento de LLMs. Para implementar um sistema RAG básico precisamos de alguns componentes:

  • Modelo de incorporação: para os dados aos quais fornecemos acesso, não pode ser simplesmente um monte de texto ou imagens, mas eles precisam ser capturados em um formato numérico/vetorial para que todos os modelos de PNL (incluindo LLMs) possam ser compreendidos. Para transformar nossos dados, utilizamos o modelo OpenAI Embeddings, mas há uma variedade de opções diferentes, como Cohere, Amazon Titan, etc., que você pode avaliar quanto ao desempenho.
  • Armazenamento de vetores: assim que tivermos nossos embeddings, precisamos utilizar um armazenamento de dados de vetores que não apenas armazene esses vetores, mas também forneça uma maneira eficiente de indexar e recuperar dados relevantes. Quando um usuário faz uma consulta, queremos retornar qualquer contexto relevante que contenha semelhança com esta entrada. A maioria desses armazenamentos de vetores é alimentada por KNN e outros algoritmos vizinhos mais próximos para fornecer contexto relevante para a questão inicial. No caso desta solução utilizamos a biblioteca FAISS do Facebook, que pode ser utilizada para busca eficiente de similaridade e agrupamento de vetores.
  • Modelo LLM: Temos dois modelos neste caso, o modelo de embeddings para criar os embeddings, mas ainda precisamos também do LLM principal que usa esses embeddings e a entrada do usuário para retornar uma saída. Neste caso também usamos o modelo ChatOpenAI padrão.


RAG Flow


Essencialmente, você pode pensar no RAG como um melhorador de desempenho dos LLMs, fornecendo conhecimento extra que o LLM básico talvez ainda não tenha. Na próxima seção, veremos como podemos implementar esses conceitos utilizando LangChain e OpenAI.


Aplicação de IA generativa e inferência de amostra

Para começar, você precisa de uma chave de API OpenAI, que pode ser encontrada e instalada no link a seguir. Observe as cobranças por taxa/limite de API para que você entenda a estrutura de preços. Para desenvolvimento trabalhamos em uma SageMaker Classic Notebook Instance, mas qualquer ambiente com OpenAI e LangChain instalados deve ser suficiente.

import os
os.environ['OPENAI_API_KEY'] = 'Enter your API Key here'

# necessary langchain imports
import langchain
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.embeddings.cache import CacheBackedEmbeddings
from langchain.vectorstores import FAISS
from langchain.storage import LocalFileStore
from langchain.document_loaders import PyPDFDirectoryLoader
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI        

Depois de configurar o LangChain e o OpenAI, criamos um diretório local com dez artigos populares do Medium armazenados como PDFs. Estes serão os dados/informações adicionais que disponibilizaremos para meu LLM.

Arquivos PDFs gerados a partir de artigos do Medium


Como próximos passos, precisamos ser capazes de carregar esses dados e também criar um diretório onde possamos armazenar os embeddings que geramos. LangChain tem muitos utilitários que carregam automaticamente e também dividem/separam seus dados para você. A fragmentação é especificamente importante porque não queremos conjuntos maiores de dados para os embeddings que geramos. Quanto maiores os dados, maior o ruído potencial que pode ser introduzido.

Neste caso, usamos o carregador de PDF fornecido pelo LangChain para carregar e dividir nossos dados.

# onde nossos embeddings serão armazenados
store = LocalFileStore("./cache/")

# instancia um carregador: isso carrega nossos dados, use PDF neste caso
loader = PyPDFDirectoryLoader("sagemaker-articles/")

# por padrão o carregador de PDF carrega e divide os documentos para nós
pages = loader.load_and_split()
print(len(pages))        

Em seguida, instanciamos nosso modelo de embeddings OpenAI. Usamos o modelo Embeddings para criar nossos embeddings e preencher o diretório de cache local que criamos.

# instancia o modelo de incorporação
embeddings_model = OpenAIEmbeddings()

embedder = CacheBackedEmbeddings.from_bytes_store(
    embeddings_model,
    store
)        
Embeddings gerados

Em seguida, criamos nossa FAISS Vector Store e enviamos nossos documentos incorporados.

# criamos armazenamento de vetores, usamos FAISS neste caso
vector_store = FAISS.from_documents(pages, embedder)        

Em seguida, usamos uma cadeia RetrievalQA para reunir todas essas partes móveis. Especificamos nosso armazenamento de vetores que criamos acima e também passamos o LLM padrão do ChatOpenAI como nosso modelo que receberá a entrada e os documentos relevantes para o contexto.

# este é todo o sistema de recuperação
medium_qa_chain = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(),
    retriever=vector_store.as_retriever(),
    return_source_documents=True,
    verbose=True
)        

Podemos então comparar o desempenho do modelo sem RAG em oposição à nossa cadeia baseada em RAG, passando os mesmos prompts e observando os resultados. Vamos executar um loop de exemplos de prompts de dificuldades variadas.

sample_prompts = [ "Sobre o que Ram Vegiraju escreve?" , 
                 "O que é Amazon SageMaker?" , 
                 "O que é inferência do Amazon SageMaker?" , 
                 "Quais são as diferentes opções de hospedagem para Amazon SageMaker?" , 
                 "O que é inferência sem servidor com Amazon SageMaker?" , 
                 "Qual é a diferença entre endpoints multimodelos e endpoints multicontêineres?" , 
                 "Quais SDKs posso usar para trabalhar com o Amazon SageMaker?" ] 

para prompt em sample_prompts: 
    
    #vanilla OpenAI Response
     response = openai.Completion.create( 
        engine= "text-davinci-003" , 
        prompt=prompt, 
        max_tokens = 500 ) 

    # RAG Augmented Response
     response_rag = middle_qa_chain({ "query" :prompt })        

Vemos que a primeira pergunta em si é muito específica para minha escrita. Sabemos que o modelo OpenAI não tem nenhum acesso ou conhecimento dos artigos, por isso produz uma descrição bastante aleatória e imprecisa.

Resposta OpenAI

Alternativamente, nossa rede RAG teve acesso a alguns de meus artigos no Medium e produz um resumo um tanto preciso de minha escrita.

Resposta RAG


Podemos então testar ambas as abordagens fazendo algumas perguntas específicas ao SageMaker. Começamos com uma pergunta muito básica: O que é Amazon SageMaker? Como o OpenAI LLM tem conhecimento deste assunto, ele responde com uma resposta bastante precisa e comparável à nossa abordagem baseada em RAG.

Resposta OpenAI vs RAG

Começamos a ver os benefícios reais do RAG à medida que as questões começam a ficar mais específicas e difíceis. Um exemplo disso é o prompt comparando as duas opções de hospedagem avançada: Multi-Model Endpoints (MME) e Multi-Container Endpoints (MCE).

MME vs MCE

Aqui vemos que a resposta do Vanilla OpenAI dá uma resposta completamente imprecisa, pois não tem conhecimento desses dois recursos recentes. Artigo específico do Medium sobre MCE vs MME, no entanto, fornece o contexto do modelo em torno dessas ofertas e, portanto, é capaz de responder à consulta com precisão.

Com o RAG podemos aumentar o conhecimento básico que nosso LLM já possui sobre o SageMaker. Na próxima seção podemos examinar diferentes métodos para melhorar este protótipo que construímos.


Como podemos melhorar o desempenho?

Embora esta seja uma solução interessante, ainda há muito espaço para melhorias. Alguns métodos potenciais que você pode usar para melhorar o desempenho baseado em RAG incluem o seguinte:

  • Tamanho e qualidade dos dados: neste caso, fornecemos apenas dez artigos e ainda observamos um desempenho sólido. Para impulsionar isso, também poderíamos fornecer acesso a todo o conjunto de artigos do Medium ou qualquer coisa com a tag “SageMaker”, por exemplo. Também copiamos diretamente os artigos sem qualquer formatação e os próprios PDFs são muito desestruturados. Limpar o formato dos dados pode ajudar a tornar a fragmentação e, portanto, o desempenho mais ideal.
  • Otimização do armazenamento de vetores: neste caso, utilizamos a configuração padrão do armazenamento de vetores FAISS. Os itens que você pode ajustar são a velocidade da indexação do armazenamento de vetores, bem como o número de documentos a serem recuperados e fornecidos ao seu LLM.
  • Ajuste fino vs RAG : Embora o RAG ajude a obter conhecimento específico de domínio, o ajuste fino também é outro método para ajudar um LLM a obter um conjunto de conhecimento específico. Você também deseja avaliar seu caso de uso aqui para ver se o ajuste fino faz mais sentido ou uma combinação de ambos. Geralmente, o ajuste fino tem muito desempenho se você tiver dados de qualidade disponíveis. Nesse caso, com o RAG, nem necessariamente formatamos ou modelamos nossos dados, mas ainda assim conseguimos produzir resultados sólidos. Com o ajuste fino, a disponibilidade e a qualidade dos dados são essenciais.


Obrigado por ler, fique à vontade para deixar qualquer comentário e fique com Deus! 🙏

Entre para ver ou adicionar um comentário

Outros artigos de Diogo Santos

Outras pessoas também visualizaram

Conferir tópicos