Ei! Já se perguntou como alguns dos aplicativos atuais parecem quase magicamente inteligentes? Grande parte dessa magia vem de algo chamado RAG e LLM. Pense no RAG (Retrieval-Augmented Generation) como o leitor ávido inteligente do mundo da IA. Ele vasculha toneladas de informações para encontrar exatamente o que é necessário para sua pergunta. Depois, há o LLM (Large Language Model), como a famosa série GPT que irá gerar uma resposta suave com base em seus impressionantes recursos de geração de texto. Com esses dois juntos, você tem uma IA que não é apenas inteligente, mas também super relevante e consciente do contexto. É como combinar um assistente de pesquisa super rápido com um conversador espirituoso. Essa combinação é fantástica para qualquer coisa, desde ajudá-lo a encontrar informações específicas rapidamente até ter um bate-papo que parece surpreendentemente real.
Mas aqui está o problema: como sabemos se nossa IA está realmente sendo útil e não apenas usando jargões sofisticados? É aí que entra a avaliação. É crucial, não apenas algo bom de se ter. Precisamos ter certeza de que nossa IA não é apenas precisa, mas também relevante, útil e não sai por tangentes estranhas. Afinal, de que adianta um assistente inteligente se ele não consegue entender o que você precisa ou dá respostas erradas?
Avaliar nossa aplicação RAG + LLM é como uma verificação da realidade. Diz-nos se estamos realmente no caminho certo para criar uma IA que seja genuinamente útil e não apenas tecnicamente impressionante. Então, neste post, vamos nos aprofundar em como fazer exatamente isso – garantir que nossa IA seja tão incrível na prática quanto na teoria!
Na fase de desenvolvimento, é essencial pensar nos moldes de um pipeline típico de avaliação de modelo de aprendizado de máquina. Em uma configuração padrão de IA/ML, geralmente trabalhamos com vários conjuntos de dados, como conjuntos de desenvolvimento, treinamento e teste, e empregamos métricas quantitativas para avaliar a eficácia do modelo. No entanto, avaliar grandes modelos de linguagem (LLMs) apresenta desafios únicos. As métricas quantitativas tradicionais lutam para capturar a qualidade do resultado dos LLMs porque esses modelos se destacam na geração de uma linguagem variada e criativa. Consequentemente, é difícil ter um conjunto abrangente de rótulos para uma avaliação eficaz.
Nos círculos acadêmicos, os pesquisadores podem empregar benchmarks e pontuações como MMLU para classificar os LLMs, e especialistas humanos podem ser recrutados para avaliar a qualidade dos resultados do LLM. Esses métodos, no entanto, não fazem uma transição perfeita para o ambiente de produção, onde o ritmo de desenvolvimento é rápido e as aplicações práticas exigem resultados imediatos. Não se trata apenas do desempenho do LLM; as demandas do mundo real levam em consideração todo o processo, que inclui recuperação de dados, composição imediata e contribuição do LLM. Criar um benchmark com curadoria humana para cada nova iteração do sistema ou quando há alterações em documentos ou domínios é impraticável. Além disso, o ritmo acelerado de desenvolvimento na indústria não permite o luxo de longas esperas para que os testadores humanos avaliem cada atualização antes da implantação. Portanto, ajustar as estratégias de avaliação que funcionam na academia para se alinharem ao ambiente de produção ágil e focado em resultados apresenta um desafio considerável.
Portanto, se você se enquadra nesse caso, pode pensar em algo como uma pseudo pontuação fornecida por um LLM mestre. Esta pontuação poderia refletir uma combinação de métricas de avaliação automatizadas e uma essência destilada do julgamento humano. Essa abordagem híbrida visa preencher a lacuna entre a compreensão diferenciada dos avaliadores humanos e a análise escalonável e sistemática da avaliação das máquinas.
Por exemplo, se sua equipe estiver desenvolvendo um LLM interno treinado em seu domínio e dados específicos, o processo normalmente envolverá um esforço colaborativo de desenvolvedores, engenheiros imediatos e cientistas de dados. Cada membro desempenha um papel crítico:
Os desenvolvedores são os arquitetos. Eles constroem a estrutura do aplicativo, garantindo que a cadeia RAG + LLM esteja perfeitamente integrada e possa navegar por diferentes cenários sem esforço.
Os engenheiros imediatos são os criativos. Eles criam cenários e prompts que emulam as interações do usuário no mundo real. Eles ponderam sobre “e se” e forçam o sistema a lidar com um amplo espectro de tópicos e questões.
Os cientistas de dados são os estrategistas. Eles analisam as respostas, investigam os dados e utilizam seus conhecimentos estatísticos para avaliar se o desempenho da IA atende ao esperado.
O ciclo de feedback aqui é essencial. À medida que nossa IA responde às solicitações, a equipe examina cada resultado. A IA entendeu a pergunta? A resposta foi precisa e relevante? A linguagem poderia ser mais suave? Esse feedback é então retornado ao sistema para melhorias.
Para aumentar ainda mais, imagine usar um LLM mestre como o GPT-4 da OpenAI como referência para avaliar seu LLM autodesenvolvido. Seu objetivo é igualar ou até mesmo superar o desempenho da série GPT, que é conhecida por sua robustez e versatilidade. Veja como você pode proceder:
Gerando um conjunto de dados relevante: comece criando um conjunto de dados que reflita as nuances do seu domínio. Este conjunto de dados pode ser curado por especialistas ou sintetizado com a ajuda do GPT-4 para economizar tempo e garantir que corresponda ao seu padrão ouro.
Definindo Métricas para o Sucesso: Aproveite os pontos fortes do LLM mestre para auxiliar na definição de suas métricas. Você tem a liberdade de escolher as métricas que melhor se adaptam aos seus objetivos, visto que o mestre LLM pode lidar com tarefas mais complexas. No padrão da comunidade, você pode querer ver alguns trabalhos da Langchain e de algumas outras bibliotecas como ragas . Eles têm algumas métricas como fidelidade, recuperação de contexto, precisão de contexto, similaridade de respostas, etc.
Automatizando seu pipeline de avaliação: para acompanhar os rápidos ciclos de desenvolvimento, estabeleça um pipeline automatizado. Isso avaliará consistentemente o desempenho do aplicativo em relação às métricas predefinidas após cada atualização ou alteração. Ao automatizar o processo, você garante que sua avaliação não seja apenas completa, mas também eficientemente iterativa, permitindo rápida otimização e refinamento.
Por exemplo, na demonstração a seguir, mostrarei como avaliar automaticamente vários LLMs de código aberto em uma tarefa simples de conversação de recuperação de documentos usando o GPT-4 da OpenAI.
Primeiro, utilizamos OpenAI GPT-4 para criar um conjunto de dados sintetizado derivado de um documento conforme mostrado abaixo:
import os import json import pandas as pd from dataclasses import dataclass from langchain.chat_models import ChatOpenAI from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain.document_loaders import PyPDFLoader from langchain.text_splitter import CharacterTextSplitter from langchain.output_parsers import JsonOutputToolsParser, PydanticOutputParser from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate QA_DATASET_GENERATION_PROMPT = PromptTemplate.from_template( "You are an expert on generate question-and-answer dataset based on a given context. You are given a context. " "Your task is to generate a question and answer based on the context. The generated question should be able to" " to answer by leverage the given context. And the generated question-and-answer pair must be grammatically " "and semantically correct. Your response must be in a json format with 2 keys: question, answer. For example," "\n\n" "Context: France, in Western Europe, encompasses medieval cities, alpine villages and Mediterranean beaches. Paris, its capital, is famed for its fashion houses, classical art museums including the Louvre and monuments like the Eiffel Tower." "\n\n" "Response: {{" "\n" " \"question\": \"Where is France and what is it's capital?\"," "\n" " \"answer\": \"France is in Western Europe and it's capital is Paris.\"" "\n" "}}" "\n\n" "Context: The University of California, Berkeley is a public land-grant research university in Berkeley, California. Established in 1868 as the state's first land-grant university, it was the first campus of the University of California system and a founding member of the Association of American Universities." "\n\n" "Response: {{" "\n" " \"question\": \"When was the University of California, Berkeley established?\"," "\n" " \"answer\": \"The University of California, Berkeley was established in 1868.\"" "\n" "}}" "\n\n" "Now your task is to generate a question-and-answer dataset based on the following context:" "\n\n" "Context: {context}" "\n\n" "Response: ", ) OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.7, response_format={ "type": "json_object" }, ) chain = LLMChain( prompt=QA_DATASET_GENERATION_PROMPT, llm=llm ) file_loader = PyPDFLoader("./data/cidr_lakehouse.pdf") text_splitter = CharacterTextSplitter(chunk_size=1000) chunks = text_splitter.split_documents(file_loader.load()) questions, answers = [], [] for chunk in chunks: for _ in range(2): response = chain.invoke({ "context": chunk }) obj = json.loads(response["text"]) questions.append(obj["question"]) answers.append(obj["answer"]) df = pd.DataFrame({ "question": questions, "answer": answers }) df.to_csv("./data/cidr_lakehouse_qa.csv", index=False)
Após executar o código mencionado acima, obtemos como resultado um arquivo CSV. Este arquivo contém pares de perguntas e respostas relativas ao documento que inserimos, como segue:
Em seguida, construímos cadeias DocumentRetrievalQA simples usando Langchain e substituímos em vários LLMs de código aberto que operam localmente via Ollama. Você pode encontrar meu tutorial anterior sobre isso aqui.
from tqdm import tqdm from langchain.chains import RetrievalQA from langchain.chat_models import ChatOllama from langchain.vectorstores import FAISS from langchain.embeddings import HuggingFaceEmbeddings vector_store = FAISS.from_documents(chunks, HuggingFaceEmbeddings()) retriever = vector_store.as_retriever() def test_local_retrieval_qa(model: str): chain = RetrievalQA.from_llm( llm=ChatOllama(model=model), retriever=retriever, ) predictions = [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "query": row["question"] }) predictions.append(resp["result"]) df[f"{model}_result"] = predictions test_local_retrieval_qa("mistral") test_local_retrieval_qa("llama2") test_local_retrieval_qa("zephyr") test_local_retrieval_qa("orca-mini") test_local_retrieval_qa("phi") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction.csv", index=False)
Em resumo, o código acima estabelece uma cadeia simples de recuperação de documentos. Executamos essa cadeia utilizando diversos modelos, como Mistral, Llama2, Zephyr, Orca-mini e Phi. Como resultado, adicionamos cinco colunas adicionais ao nosso DataFrame existente para armazenar os resultados de previsão de cada modelo LLM.
Agora, vamos definir uma cadeia mestre usando GPT-4 da OpenAI para avaliar os resultados da previsão. Nesta configuração, calcularemos uma pontuação de acerto semelhante a uma pontuação F1 aproximada, como é comum em problemas tradicionais de IA/ML. Para isso, aplicaremos conceitos paralelos como Verdadeiros Positivos (TP), Falsos Positivos (FP) e Falsos Negativos (FN), definidos a seguir:
Com essas definições, podemos calcular a precisão, o recall e a pontuação F1 usando as fórmulas abaixo:
import os import numpy as np import pandas as pd from tqdm import tqdm from langchain.chains import LLMChain from langchain.chat_models import ChatOpenAI from langchain.prompts import PromptTemplate OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") if OPENAI_API_KEY is None: raise ValueError("OPENAI_API_KEY is not set") CORRECTNESS_PROMPT = PromptTemplate.from_template( """ Extract following from given question and ground truth. Your response must be in a json format with 3 keys and does not need to be in any specific order: - statements that are present in both the answer and the ground truth - statements present in the answer but not found in the ground truth - relevant statements found in the ground truth but omitted in the answer Please be concise and do not include any unnecessary information. You should classify the statements as claims, facts, or opinions with semantic matching, no need exact word-by-word matching. Question:What powers the sun and what is its primary function? Answer: The sun is powered by nuclear fission, similar to nuclear reactors on Earth, and its primary function is to provide light to the solar system. Ground truth: The sun is actually powered by nuclear fusion, not fission. In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy. This energy is what lights up the sun and provides heat and light, essential for life on Earth. The sun's light also plays a critical role in Earth's climate system and helps to drive the weather and ocean currents. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The sun's primary function is to provide light"], "statements present in the answer but not found in the ground truth": ["The sun is powered by nuclear fission", "similar to nuclear reactors on Earth"], "relevant statements found in the ground truth but omitted in the answer": ["The sun is powered by nuclear fusion, not fission", "In its core, hydrogen atoms fuse to form helium, releasing a tremendous amount of energy", "This energy provides heat and light, essential for life on Earth", "The sun's light plays a critical role in Earth's climate system", "The sun helps to drive the weather and ocean currents"] }} ] Question: What is the boiling point of water? Answer: The boiling point of water is 100 degrees Celsius at sea level. Ground truth: The boiling point of water is 100 degrees Celsius (212 degrees Fahrenheit) at sea level, but it can change with altitude. Extracted statements: [ {{ "statements that are present in both the answer and the ground truth": ["The boiling point of water is 100 degrees Celsius at sea level"], "statements present in the answer but not found in the ground truth": [], "relevant statements found in the ground truth but omitted in the answer": ["The boiling point can change with altitude", "The boiling point of water is 212 degrees Fahrenheit at sea level"] }} ] Question: {question} Answer: {answer} Ground truth: {ground_truth} Extracted statements:""", ) judy_llm = ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) judy_chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=judy_llm ) def evaluate_correctness(column_name: str): chain = LLMChain( prompt=CORRECTNESS_PROMPT, llm=ChatOpenAI( model="gpt-4-1106-preview", api_key=OPENAI_API_KEY, temperature=0.0, response_format={ "type": "json_object" }, ) ) key_map = { "TP": "statements that are present in both the answer and the ground truth", "FP": "statements present in the answer but not found in the ground truth", "FN": "relevant statements found in the ground truth but omitted in the answer", # noqa: E501 } TP, FP, FN = [], [], [] for it, row in tqdm(df.iterrows(), total=len(df)): resp = chain.invoke({ "question": row["question"], "answer": row[column_name], "ground_truth": row["answer"] }) obj = json.loads(resp["text"]) TP.append(len(obj[key_map["TP"]])) FP.append(len(obj[key_map["FP"]])) FN.append(len(obj[key_map["FN"]])) # convert to numpy array TP = np.array(TP) FP = np.array(FP) FN = np.array(FN) df[f"{column_name}_recall"] = TP / (TP + FN) df[f"{column_name}_precision"] = TP / (TP + FP) df[f"{column_name}_correctness"] = 2 * df[f"{column_name}_recall"] * df[f"{column_name}_precision"] / (df[f"{column_name}_recall"] + df[f"{column_name}_precision"]) evaluate_correctness("mistral_result") evaluate_correctness("llama2_result") evaluate_correctness("zephyr_result") evaluate_correctness("orca-mini_result") evaluate_correctness("phi_result") print("|====Model====|=== Recall ===|== Precision ==|== Correctness ==|") print(f"|mistral | {df['mistral_result_recall'].mean():.4f} | {df['mistral_result_precision'].mean():.4f} | {df['mistral_result_correctness'].mean():.4f} |") print(f"|llama2 | {df['llama2_result_recall'].mean():.4f} | {df['llama2_result_precision'].mean():.4f} | {df['llama2_result_correctness'].mean():.4f} |") print(f"|zephyr | {df['zephyr_result_recall'].mean():.4f} | {df['zephyr_result_precision'].mean():.4f} | {df['zephyr_result_correctness'].mean():.4f} |") print(f"|orca-mini | {df['orca-mini_result_recall'].mean():.4f} | {df['orca-mini_result_precision'].mean():.4f} | {df['orca-mini_result_correctness'].mean():.4f} |") print(f"|phi | {df['phi_result_recall'].mean():.4f} | {df['phi_result_precision'].mean():.4f} | {df['phi_result_correctness'].mean():.4f} |") print("|==============================================================|") df.to_csv("./data/cidr_lakehouse_qa_retrieval_prediction_correctness.csv", index=False)
Ok, agora temos um benchmark simples para vários modelos. Isto pode ser considerado como um indicador preliminar de como cada modelo lida com a tarefa de recuperação de documentos. Embora esses números ofereçam um retrato, eles são apenas o começo da história. Eles servem como base para a compreensão de quais modelos são melhores na recuperação de informações precisas e relevantes de um determinado corpus. Você pode encontrar o código fonte aqui .
Quando se trata de ajustar nossa IA por meio de Feedback Human-In-Loop, a sinergia entre os testadores humanos e o Master LLM é fundamental. Esta relação não se trata apenas de obter feedback, mas de criar um sistema de IA responsivo que se adapte e aprenda com a contribuição humana.
O Master LLM funciona tanto como uma referência para o LLM desenvolvido internamente quanto como um participante ativo no ciclo de feedback. Ele avalia o feedback, ajusta os prompts ou os parâmetros do modelo e, essencialmente, “aprende” com as interações humanas.
Este processo é transformador. Permite que a IA se adapte em tempo real, tornando-a mais ágil e alinhada com as complexidades da linguagem humana e dos processos de pensamento. Essa adaptação em tempo real garante que a curva de aprendizagem da IA seja acentuada e contínua.
Através deste ciclo de interação, feedback e adaptação, a nossa IA torna-se mais do que apenas uma ferramenta; torna-se uma entidade de aprendizagem, capaz de melhorar a cada interação com um testador humano. Este modelo humano garante que nossa IA não estagnou, mas evoluiu para se tornar um assistente mais eficiente e intuitivo.
Em resumo, o Human-In-Loop Feedback não se trata apenas de coletar insights humanos – trata-se de criar uma IA dinâmica e adaptável que possa ajustar seu comportamento para atender melhor aos usuários. Esse processo iterativo garante que nossos aplicativos RAG + LLM permaneçam na vanguarda, fornecendo não apenas respostas, mas respostas diferenciadas e contextualmente conscientes que refletem uma compreensão genuína das necessidades do usuário.
Para uma demonstração simples, você pode assistir como o ClearML usa esse conceito para aprimorar o Promptimizer neste vídeo .
A transição para a Fase de Operação é como passar dos ensaios gerais para a noite de estreia. Aqui, nossas aplicações RAG + LLM não são mais entidades hipotéticas; eles se tornam participantes ativos nos fluxos de trabalho diários de usuários reais. Esta fase é o teste decisivo para toda a preparação e ajuste feito na fase de desenvolvimento.
Nesta fase, nossas equipes – operações, produtos e analistas – se alinham para implantar e gerenciar os aplicativos, garantindo que tudo o que construímos não apenas funcione, mas também prospere em um ambiente ativo. É aqui que podemos considerar a implementação de estratégias de testes A/B para medir a eficácia das nossas aplicações de forma controlada.
Estrutura de teste A/B: dividimos nossa base de usuários em dois segmentos – o segmento de controle, que continua a usar a versão estabelecida do aplicativo (versão 1), e o segmento de teste, que testa os novos recursos da versão 2 (na verdade você também pode executar vários testes A/B ao mesmo tempo). Isso nos permite coletar dados comparativos sobre a experiência do usuário, receptividade de recursos e desempenho geral.
Implementação operacional: A equipe de operações tem a tarefa de implementar suavemente ambas as versões, garantindo que a infraestrutura seja robusta e que quaisquer transições de versão sejam perfeitas para o usuário.
Evolução do produto: A equipe do produto, acompanhando o feedback do usuário, trabalha para iterar o produto. Essa equipe garante que os novos recursos estejam alinhados às necessidades do usuário e à visão geral do produto.
Insights analíticos: A equipe de analistas examina rigorosamente os dados coletados no teste A/B. Seus insights são essenciais para determinar se a nova versão supera a antiga e se está pronta para um lançamento mais amplo.
Métricas de Desempenho: Os principais indicadores de desempenho (KPIs) são monitorados para medir o sucesso de cada versão. Isso inclui métricas de envolvimento do usuário, pontuações de satisfação e a precisão dos resultados do aplicativo.
A fase de operação é dinâmica, informada por ciclos de feedback contínuos que não apenas melhoram os aplicativos, mas também aumentam o envolvimento e a satisfação do usuário. É uma fase caracterizada por monitoramento, análise, iteração e, acima de tudo, aprendizado com dados ao vivo.
À medida que navegamos nesta fase, nosso objetivo é não apenas manter os altos padrões estabelecidos pela fase de desenvolvimento, mas também superá-los, garantindo que nosso aplicativo RAG + LLM permaneça na vanguarda da inovação e usabilidade.
Em resumo, a integração da Geração Aumentada de Recuperação (RAG) e dos Modelos de Linguagem Grande (LLMs) marca um avanço significativo na IA, combinando recuperação profunda de dados com geração sofisticada de texto. No entanto, precisamos de um método adequado e eficaz de avaliação e de uma estratégia de desenvolvimento iterativa. A fase de desenvolvimento enfatiza a personalização da avaliação da IA e o seu aprimoramento com feedback humano, garantindo que esses sistemas sejam empáticos e adaptáveis a cenários do mundo real. Esta abordagem destaca a evolução da IA de uma mera ferramenta para um parceiro colaborativo. A fase operacional testa essas aplicações em cenários do mundo real, usando estratégias como testes A/B e ciclos de feedback contínuos para garantir eficácia e evolução contínua com base na interação do usuário.