Me siga no X | Me siga no LinkedIn | Apoie a Newsletter | Solicite uma consultoria
A engenharia de prompt refere-se a formulação de perguntas melhores para Grandes Modelos de Linguagem a fim de obter respostas mais precisas. Em outras palavras, é a arte do design eficaz de prompts.
Quando a saída não atende às expectativas do usuário, eles podem modificar seus prompts na esperança de obter resultados melhores. A engenharia de prompt é o pequeno leme que direciona um navio de bilhões de parâmetros, na tentativa de apontar na direção certa.
Mas, como sabemos se estamos, de fato, apontando na direção certa? Quais abordagens podemos utilizar para garantir que a saída deste modelo esteja alinhada com nossas intenções? Embora tenhamos abordado a problemática engenharia de prompt aqui e ali nessa newsletter, pouco falamos sobre como podemos medir a qualidade de um prompt.
Este texto oferece uma visão inicial sobre a avaliação da qualidade de resultados de prompts. Para isso, vamos revisitar a literatura de Tradução de Máquinas (Machine Translation, MT), um dos campos de pesquisa precursores da área de Processamento de Linguagem Natural (Natural Language Processing, NL).
A confiabilidade dos sistemas de MT é convencionalmente medida por métricas de qualidade, como BLEU e METEOR. Simplificadamente, essas métricas foram projetadas para medir a similaridade semântica entre a documentação gerada e as referências, considerando os n-gramas sobrepostos entre eles. O modelo pode alcançar pontuações mais altas se houver mais palavras sobrepostas.
Embora essas métricas tenham sido inicialmente propostas para tarefas de tradução automática, trabalhos recentes de geração código utilizam-as para avaliar a qualidade do conteúdo gerado.
Nesse texto, vamos entender mais sobre essas métricas, e como utiliza-las para aprimorar nossos prompts — podendo ou não envolver código.
📚 Você é dev e quer aprender um pouco mais sobre a criação de aplicações baseadas em LLM?
Eu criei um curso que aborda aspectos teóricos e práticos do desenvolvimento de aplicações baseadas em LLMs. Alguns dos tópicos cobertos:
🟠 O que são e como criar embeddings
🟡 Como selecionar partes relevantes nos seus documentos
🔵 Como integrar essas partes documentos com uma LLM
Métricas de Machine Translation
Nos últimos anos, tem havido um interesse crescente em construir modelos avançados para ajudar desenvolvedores na tarefa de gerar artefatos de código mais precisos. Essas técnicas aproveitam de redes neurais e os grandes repositórios de código-fonte aberto disponíveis para capturar e treinar em cima das características lexicais e sintáticas do código-fonte.
Ao mesmo tempo, várias métricas automáticas, como BLEU, ROUGE e METEOR, introduzidas no domínio do Processamento de Linguagem Natural (PLN), foram adotadas para avaliar estes modelos utilizados em tarefas de geração de código.
Geralmente, essas métricas avaliam diferentes modelos comparando o texto sobreposto entre a referência e o texto gerado. O modelo pode alcançar pontuações mais altas se houver mais palavras sobrepostas.
As pontuações de BLEU, METEOR e ROUGE estão na faixa de [0,1] e geralmente são relatadas em porcentagens. Quanto maior a pontuação, mais próxima a documentação gerada está da referência. Se a documentação gerada for completamente igual à referência, essas pontuações se tornam 100%.
BLEU
A métrica padrão para avaliar sistemas de MT é o BLEU. De forma simples, o objetivo do BLEU é comparar n-gramas da tradução gerada com n-gramas da tradução de referência e contar o número de correspondências; quanto mais correspondências, melhor a tradução candidata.
Embora o BLEU forneça uma avaliação rápida e automatizada, é importante observar que nem sempre captura a essência e a coerência do conteúdo gerado, além de favorecer traduções mais curtas. Apesar de suas limitações, o BLEU ainda é o padrão de facto para a avaliação de desempenho de tradução automática, em particular devido a facilidade de calcular independentemente das línguas envolvidas.
Uma implementação do BLEU, disponível na biblioteca NLTK, pode ser visto abaixo.
from nltk.translate.bleu_score import sentence_bleu
referencia = ["def somar(a, b): return a + b"]
tokens_referencia = [ref.split() for ref in referencia]
gerado = "função para somar dois números: retornar a mais b"
tokens_gerado = gerado.split()
bleu = sentence_bleu(tokens_referencia, tokens_gerado)
print("Pontuação BLEU:", bleu)
METEOR
METEOR, por outro lado, incorpora informações semânticas à medida que avalia a tradução, calculando correspondência exata (palavra inteira), correspondência de radical (n-gram), ou de sinônimos, oferecendo uma perspectiva mais semelhante à humana na avaliação. Ele calcula uma pontuação com base em uma combinação de precisão, revocação e f-score dessas várias características, proporcionando uma visão mais abrangente da qualidade da tradução do que métricas como o BLEU. Diferente de BLEU, METEOR é um método orientado ao recall, que reflete o quanto os resultados traduzidos cobrem todo o conteúdo das referências.
Um exemplo de como utilizar essa métrica está abaixo.
import nltk
nltk.download('wordnet') # caso seja a primeira vez
from nltk.translate.meteor_score import meteor_score
referencia = ["def somar(a, b): return a + b"]
tokens_referencia = [ref.split() for ref in referencia]
gerado = "função para somar dois números: retornar a mais b"
tokens_gerado = gerado.split()
meteor = meteor_score(tokens_referencia, tokens_gerado)
print("Pontuação Meteor:", meteor)
ROUGE
O ROUGE é utilizado para avaliar a qualidade dos resumos gerados. Ele avalia a adequação de conteúdo contando n-gramas nos resumos de referência que aparecem nos resumos gerados. Também é amplamente utilizado para avaliar a qualidade dos documentos de software gerados. Entre as diferentes variações do ROUGE, o ROUGE-L é a métrica mais amplamente utilizada em tarefas de geração de documentação. Ele é calculado usando a subsequência comum mais longa.
Uma implementação desta métrica está disponível na biblioteca rouge_score
.
!pip install rouge_score # caso seja a primeira vez
from rouge_score import rouge_scorer
referencia = "def somar(a, b): return a + b"
gerado = "função para somar dois números: retornar a mais b"
rouge= rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'])
pontuacoes = rouge.score(referencia, gerado)
print("ROUGE-1:", pontuacoes['rouge1'].fmeasure)
print("ROUGE-2:", pontuacoes['rouge2'].fmeasure)
print("ROUGE-L:", pontuacoes['rougeL'].fmeasure)
Estudo de caso
Podemos agora utilizar algumas das técnicas de engenharia de prompt que vimos no último texto desse blog para explorar melhor o uso dessas métricas.
Considere que precisamos criar um prompt que questione qual a melhor linguagem de programação para uma pessoa iniciante possa aprender. Utilizando as técnicas como zero shot, few shot e chain of thoughts, teríamos os seguintes prompts.
Zero-shot Learning:
Prompt:
"Com base em seu conhecimento, qual linguagem de programação você sugeriria para alguém que está começando a programar? Explique os principais pontos que a tornam adequada para iniciantes. Responda em no máximo 100 palavras."
Resultado do prompt (usando gpt-3.5-turbo):
Eu recomendaria Python para iniciantes devido à sua sintaxe clara e legível, facilitando a compreensão de conceitos de programação. Sua vasta comunidade oferece amplo suporte e recursos de aprendizado, enquanto a versatilidade permite explorar várias áreas, como desenvolvimento web e análise de dados. A abordagem intuitiva e a transferência de habilidades para outras linguagens fazem de Python uma escolha sólida para construir uma base sólida em programação.
Few-shot Learning:
Prompt:
"Dentre as linguagens de programação populares, como Python, Java, C++ e Ruby, qual você acredita ser a melhor escolha para iniciantes? Use seu conhecimento para justificar sua resposta. Responda em no máximo 100 palavras."
Resultado do prompt:
"Python é a melhor escolha para iniciantes devido à sua sintaxe intuitiva e legível, que facilita a compreensão dos conceitos de programação. A vasta comunidade de apoio oferece amplos recursos e suporte, enquanto a versatilidade da linguagem permite explorar diversas áreas. Sua curva de aprendizado suave e capacidade de realizar tarefas complexas com menos código tornam Python uma opção sólida para construir confiança e habilidades fundamentais na programação."
Chain of Thought:
Prompt:
"Python, Java, C++ e Ruby são linguagens de programação"
R: Sim, Python, Java, C++ e Ruby
"Python, Java, C++ e Ruby são linguagens de programação de alto nível"
R: Sim, Python, Java, C++ e Ruby de alto nível
"Python, Java, C++ e Ruby são linguagens de programação ensinadas em cursos introdutórios de programação"
R: Sim, Python, Java, C++ e Ruby são ensinadas em cursos introdutórios de programação
"Dentre as linguagens de programação ensinadas em cursos introdutórios, como Python, Java, C++ e Ruby, qual é mais indicada para quem está aprendendo a programar? Pense passo a passo. Responda em até 100 palavras"
R:
Resultado do prompt.
"Python é frequentemente a escolha mais indicada para iniciantes devido à sua sintaxe clara, facilidade de aprendizado e vasta comunidade de suporte. Sua abordagem intuitiva facilita a compreensão dos conceitos de programação, permitindo que os alunos se concentrem nas bases. Além disso, a ampla gama de aplicações, desde desenvolvimento web até análise de dados, oferece aos iniciantes a flexibilidade de explorar diferentes campos. Isso faz de Python uma linguagem sólida para construir uma base sólida em programação, tornando-a uma opção preferencial em cursos introdutórios."
Perceba que todas as três saídas sugeriram Python como linguagem de programação para iniciantes, embora as respostas tenham abordado características ligeiramente diferentes para argumentar a favor da linguagem.
Fazendo uma busca rápida na internet, é possível encontrar diversos sites com indicações de linguagens de programação para iniciantes. Para construir nossa resposta de referência, utilizamos este link que comenta sobre o uso de Python para iniciantes. Para tornar a comparação ainda mais justa, pedimos também para o ChatGPT reduzir o sumarizar da página em até 100 palavras. A resposta que usamos como referência foi a seguinte:
"Python é uma linguagem poderosa para iniciantes. Com ela, você pode criar sistemas web, análise de dados, inteligência artificial, apps e mais. Diversos cursos online ensinam conceitos básicos como variáveis e funções. Você pode usar o interpretador puro ou o ipython. Quanto à IDE, escolha conforme preferência: Notepad++, Spyder, PyCharm, etc. Módulos podem ser padrão ou baixados, como Django ou Numpy. VirtualEnvs isolam bibliotecas, prevenindo conflitos. Indicam cursos e recursos para iniciantes, incluindo livros, cursos e materiais online. Comece explorando opções como 'Curso para acesso ao mercado Python de Tecnologia', 'Introdução à Programação com Python' e 'Python para Zumbis'."
Agora, podemos utilizar as métricas BLEU, ROUGE e METEOR comparar a resposta de referência, com as demais respostas que obtivemos pelos prompts acima. Algo como:
# instanciação do objeto sentence_bleu omitida
tokens_referencia = referencia.split()
bleu_zero_shot = sentence_bleu(tokens_referencia, gerado_zero_shot.split())
bleu_few_shot = sentence_bleu(tokens_referencia, gerado_few_shot.split())
bleu_cot = sentence_bleu(tokens_referencia, gerado_cot.split())
print("bleu: ", bleu_zero_shot, bleu_few_shot, bleu_cot)
# instanciação do objeto meteor_score omitida
tokens_referencia = [ref.split() for ref in referencia]
meteor_zero_shot = meteor_score(tokens_referencia, gerado_zero_shot.split())
meteor_few_shot = meteor_score(tokens_referencia, gerado_few_shot.split())
meteor_cot = meteor_score(tokens_referencia, gerado_cot.split())
print("meteor: ", meteor_zero_shot, meteor_few_shot, meteor_cot)
# instanciação do objeto rouge omitida
rouge= rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'])
rouge_zero_shot = rouge.score(referencia, gerado_zero_shot)
rouge_few_shot = rouge.score(referencia, gerado_few_shot)
rouge_cot = rouge.score(referencia, gerado_cot)
print("rouge: ", rouge_zero_shot, rouge_few_shot, rouge_cot)
Para este exemplo, as métricas reportam valores muito baixos: de 0.000 no Zero-shot até 0.27 no CoT, usando a métrica BLEU. Mas, o que isso significa? Sabemos que o valor da métrica varia entre 0 e 1, e que quanto maior o valor da métrica, melhor.
No entanto, valores de métrica acima de .30 indicam traduções que são inteligiveis, enquanto que valores acima de .50, geralmente indicam traduções boas ou fluentes.
Logo, usuários das métricas podem testar e evoluir seus prompts, sem necessariamente buscarem alcançar o máximo de score da métrica.
Conclusão
Para compreender o valor de seus prompts em todo um conjunto de dados, é importante testes quantitativos tanto quanto possível. Nossa abordagem para avaliar quantitativamente os prompts inclui os seguintes passos:
Desenhar bons prompts
Elaborar uma resposta de referência
Comparar a resposta gerada pelo prompt com a reposta de referência
Revisar e refinar o prompt
Com esses passos, qualquer pessoa pode medir automaticamente o quão bem seus LLMs seguem as especificações do prompt. Além disso, esses passos delineiam um paradigma de teste em que a análise qualitativa gradualmente perde relevância. Aqueles que desejam automação e escalabilidade com o mínimo de esforço humano devem adotar práticas de teste quantitativo.
Como de costume, o código utilizado nesse texto está disponível neste link.
Por que chamam de "engenharia de prompt" e não de "gambiarra de prompt"?