Duração estimada deste módulo: 1.5 - 2 horas
Objetivo: Construir um sistema completo de question answering extrativo que recebe um texto de contexto e uma pergunta, e retorna a resposta mais provável extraída diretamente do texto.
Requisitos: Apenas o que você aprendeu nos módulos anteriores + um editor de código (ou Google Colab).
Antes de começar, vamos esclarecer o tipo de QA que construiremos.
🔹 QA Gerativo:
O modelo inventa uma nova resposta, com suas próprias palavras.
Pergunta: "O que é um Transformer?"
Resposta Gerada: "Um Transformer é uma arquitetura de rede neural que usa mecanismos de atenção para processar sequências..."
🔹 QA Extrativo (o que faremos):
O modelo extrai um fragmento literal do texto de contexto.
Pergunta: "O que é um Transformer?"
Contexto: "...o Transformer, introduzido em 2017, é uma arquitetura baseada em atenção que processa todas as palavras simultaneamente..."
Resposta Extraída: "uma arquitetura baseada em atenção que processa todas as palavras simultaneamente"
✅ Vantagens do QA Extrativo:
Para este projeto, usaremos um modelo de tipo encoder-only, especificamente treinado para QA extrativo.
🔹 Modelo Escolhido: deepset/roberta-base-squad2
🌐 Você pode vê-lo no Model Hub
Primeiro, instale (se ainda não tiver feito) e carregue o modelo e o tokenizer.
from transformers import AutoTokenizer, AutoModelForQuestionAnswering, pipeline
# Nome do modelo
model_name = "deepset/roberta-base-squad2"
# Carregar tokenizer e modelo
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForQuestionAnswering.from_pretrained(model_name)
# Opcional: criar um pipeline para simplificar
qa_pipeline = pipeline("question-answering", model=model, tokenizer=tokenizer)
Usaremos um texto de exemplo. Pode ser qualquer coisa: um artigo, um trecho de livro, um manual, etc.
context = """
Transformers são uma arquitetura de rede neural introduzida em 2017 por Vaswani et al.
no artigo "Attention Is All You Need". Ao contrário de redes recorrentes,
Transformers processam todas as palavras de uma sequência simultaneamente,
usando um mecanismo chamado "atenção" que permite a cada palavra se relacionar
com qualquer outra na frase. Essa arquitetura é a base de modelos como
BERT, GPT, T5 e muitos outros que dominam hoje o processamento de linguagem natural.
"""
question = "Que mecanismo os Transformers usam para relacionar palavras?"
result = qa_pipeline(question=question, context=context)
print("Pergunta:", question)
print("Resposta:", result['answer'])
print("Score:", result['score'])
print("Início:", result['start'])
print("Fim:", result['end'])
Saída esperada:
Pergunta: Que mecanismo os Transformers usam para relacionar palavras?
Resposta: atenção
Score: 0.9321
Início: 234
Fim: 242
Funciona! O modelo extraiu a palavra "atenção" como resposta.
Agora, vamos fazer passo a passo, como no Módulo 5, para ver o que acontece internamente.
# Tokenizar pergunta + contexto (juntos)
inputs = tokenizer(question, context, return_tensors="pt", truncation=True)
# Passar pelo modelo
outputs = model(**inputs)
# Obter logits de início e fim da resposta
start_logits = outputs.start_logits
end_logits = outputs.end_logits
# Encontrar as posições com maior probabilidade
start_index = torch.argmax(start_logits)
end_index = torch.argmax(end_logits)
# Converter os tokens de volta para texto
answer_tokens = inputs.input_ids[0][start_index:end_index + 1]
answer = tokenizer.decode(answer_tokens)
print("Resposta (manual):", answer)
Saída:
Resposta (manual): atenção
🔹 O que o modelo faz?
Prediz duas coisas:
Depois, extrai todos os tokens entre essas duas posições.
Às vezes, o modelo pode errar se só pegar o start_index e end_index com maior pontuação. Uma prática melhor é considerar combinações válidas (start <= end) e escolher a de maior pontuação combinada.
import torch
def get_best_answer(start_logits, end_logits, input_ids, tokenizer, top_k=5):
# Obter top_k índices para início e fim
start_probs, start_indices = torch.topk(start_logits, top_k)
end_probs, end_indices = torch.topk(end_logits, top_k)
best_score = -float('inf')
best_answer = ""
# Testar combinações válidas
for i in range(top_k):
for j in range(top_k):
start = start_indices[i].item()
end = end_indices[j].item()
if start <= end: # válido
score = (start_probs[i] + end_probs[j]).item()
if score > best_score:
best_score = score
answer_tokens = input_ids[0][start:end+1]
best_answer = tokenizer.decode(answer_tokens, skip_special_tokens=True)
return best_answer, best_score
# Usar a função
answer, score = get_best_answer(start_logits, end_logits, inputs.input_ids, tokenizer)
print("Melhor resposta:", answer)
print("Melhor score:", score)
Isso torna o sistema mais robusto contra erros ocasionais.
Agora é sua vez de experimentar! Tente:
context2 = """
A inteligência artificial generativa permite criar novo conteúdo: texto, imagens, música, código.
Modelos como DALL-E, Stable Diffusion e GPT-4 são exemplos populares.
Esses modelos aprendem padrões de grandes conjuntos de dados e depois geram saídas originais
baseadas em prompts ou instruções dadas pelo usuário.
"""
question2 = "Que tipo de conteúdo a IA generativa pode criar?"
result2 = qa_pipeline(question=question2, context=context2)
print(result2['answer']) # Esperado: "texto, imagens, música, código"
Ou em português:
context_pt = """
São Paulo é uma cidade localizada na região sudeste do Brasil.
É conhecida por sua arquitetura diversificada, especialmente as obras de Oscar Niemeyer,
como o Edifício Copan e o Conjunto Nacional. Também é famosa por sua culinária,
parques e vida noturna vibrante.
"""
question_pt = "Que arquiteto é famoso em São Paulo?"
result_pt = qa_pipeline(question=question_pt, context=context_pt)
print(result_pt['answer']) # Esperado: "Oscar Niemeyer"
🔹 Limitação 1: O modelo só pode responder se a resposta estiver no texto.
Se você perguntar "Qual é a capital da França?" e o texto não mencionar Paris, o modelo pode inventar algo ou dar uma resposta errada.
🔹 Solução:
handle_impossible_answer=True (se o modelo suportar, como SQuAD 2.0). if result['score'] < 0.1:
print("Não encontrei uma resposta confiável no texto.")
else:
print("Resposta:", result['answer'])
🔹 Limitação 2: O contexto tem limite de comprimento (~512 tokens).
Se o texto for muito longo, é truncado e informações são perdidas.
🔹 Solução:
Pegue um artigo da Wikipédia (ou um capítulo de livro) que você goste.
Copie 3-4 parágrafos como contexto.
Formule 5 perguntas diferentes (fáceis, difíceis, ambíguas).
Execute o sistema QA e avalie:
- Quantas respostas estão corretas?
- Onde ele falha?
- Como poderia melhorá-lo?
[Pergunta + Contexto] → Tokenizer → input_ids → Modelo QA → start_logits + end_logits →
↑ ↑ ↑ ↑
Texto plano Converte em IDs de tokens Prediz posições
tokens + (pergunta + de início e fim
separadores contexto)
→ Seleciona melhor combinação início-fim → Extrai tokens → Decodifica → [Resposta final]
Parabéns! Você acabou de construir um sistema funcional de inteligência artificial, baseado em um dos modelos mais avançados do mundo (Transformer), sem treinar nada, sem GPUs caras e em menos de 50 linhas de código.
Esse sistema pode ser a base de:
- Um chatbot para manuais técnicos.
- Um assistente para ler artigos científicos.
- Um tutor que responde perguntas sobre um livro.
E o melhor: agora você sabe como ele funciona por dentro! Não é uma caixa preta. Você sabe o que é um embedding, o que a atenção faz, como a posição é codificada e como o modelo escolhe a resposta.