Exercícios Práticos - Treinamento de Foundation Models

NotaSobre estes exercícios

Estes exercícios complementam o Capítulo 2: Treinamento de Foundation Models.

Recomenda-se completar a leitura do capítulo antes de iniciar os exercícios práticos.

Agora que você compreende como foundation models são treinados, tokenização e dados de pré-treinamento, vamos aplicar esse conhecimento na prática.

Exercício 1: Análise de Eficiência de Tokenização

Objetivo: Entender o impacto da tokenização em diferentes idiomas e calcular custos de API.

Passo a Passo:

# Instalação necessária: pip install tiktoken transformers

import tiktoken

# Carregar tokenizer GPT-4
try:
    enc = tiktoken.encoding_for_model("gpt-4")
except KeyError:
    enc = tiktoken.get_encoding("cl100k_base")

def analyze_tokenization(text, language):
    """Analisa eficiência de tokenização para um texto"""
    tokens = enc.encode(text)
    token_count = len(tokens)

    print(f"\n{'='*60}")
    print(f"Idioma: {language}")
    print(f"{'='*60}")
    print(f"Texto original: {text}")
    print(f"Número de tokens: {token_count}")
    print(f"Caracteres: {len(text)}")
    print(f"Razão caracteres/token: {len(text)/token_count:.2f}")

    # Visualizar tokens
    print("\nTokens decodificados:")
    for i, token in enumerate(tokens[:10]):  # Mostrar apenas primeiros 10
        decoded = enc.decode([token])
        print(f"  Token {i}: '{decoded}' (ID: {token})")

    if len(tokens) > 10:
        print(f"  ... e mais {len(tokens) - 10} tokens")

    return token_count

# Teste 1: Comparar eficiência entre idiomas
print("\n🔍 TESTE 1: Eficiência Multilíngue\n")

texts = {
    "Inglês": "The quick brown fox jumps over the lazy dog. Machine learning models require large datasets.",
    "Português": "A rápida raposa marrom pula sobre o cachorro preguiçoso. Modelos de aprendizado de máquina requerem grandes conjuntos de dados.",
    "Japonês": "素早い茶色の狐が怠け者の犬を飛び越える。機械学習モデルは大規模なデータセットを必要とする。",
    "Espanhol": "El rápido zorro marrón salta sobre el perro perezoso. Los modelos de aprendizaje automático requieren grandes conjuntos de datos."
}

results = {}
for lang, text in texts.items():
    results[lang] = analyze_tokenization(text, lang)

# Comparação
print(f"\n{'='*60}")
print("📊 COMPARAÇÃO DE EFICIÊNCIA")
print(f"{'='*60}")
baseline = results["Inglês"]
for lang, count in results.items():
    efficiency = (count / baseline) * 100
    print(f"{lang:12} {count:4} tokens ({efficiency:6.1f}% vs Inglês)")

# Teste 2: Estimar custos de API
print("\n\n💰 TESTE 2: Estimativa de Custos de API\n")

# Preços típicos (valores aproximados em USD por 1K tokens)
pricing = {
    "GPT-4 Turbo": {"input": 0.01, "output": 0.03},
    "GPT-3.5 Turbo": {"input": 0.0005, "output": 0.0015},
    "Claude 3 Opus": {"input": 0.015, "output": 0.075},
    "Claude 3 Haiku": {"input": 0.00025, "output": 0.00125}
}

# Simular prompt típico
prompt = texts["Português"]
expected_output_tokens = 200  # Resposta esperada
input_tokens = results["Português"]

print(f"Prompt: '{prompt[:50]}...'")
print(f"Tokens de input: {input_tokens}")
print(f"Tokens de output estimados: {expected_output_tokens}")
print(f"\nCustos estimados por requisição:\n")

for model, prices in pricing.items():
    input_cost = (input_tokens / 1000) * prices["input"]
    output_cost = (expected_output_tokens / 1000) * prices["output"]
    total_cost = input_cost + output_cost
    print(f"{model:20} ${total_cost:.6f} (input: ${input_cost:.6f} + output: ${output_cost:.6f})")

# Teste 3: Otimização de Prompt
print("\n\n✂️ TESTE 3: Otimização de Prompt para Reduzir Tokens\n")

verbose_prompt = """
Por favor, você poderia me ajudar a analisar o seguinte texto e me fornecer uma análise
completa e detalhada sobre o sentimento expresso no texto, incluindo se é positivo,
negativo ou neutro? Aqui está o texto que eu gostaria que você analisasse:

"Este produto é excelente! Recomendo fortemente."

Muito obrigado pela sua ajuda!
"""

optimized_prompt = """
Classifique o sentimento (positivo/negativo/neutro):

"Este produto é excelente! Recomendo fortemente."
"""

verbose_tokens = len(enc.encode(verbose_prompt))
optimized_tokens = len(enc.encode(optimized_prompt))
reduction = ((verbose_tokens - optimized_tokens) / verbose_tokens) * 100

print(f"Prompt verboso: {verbose_tokens} tokens")
print(f"Prompt otimizado: {optimized_tokens} tokens")
print(f"Redução: {reduction:.1f}%")
print(f"\nEconomia em 1000 requisições (GPT-4):")
saved_tokens = (verbose_tokens - optimized_tokens) * 1000
saved_cost = (saved_tokens / 1000) * pricing["GPT-4 Turbo"]["input"]
print(f"  Tokens economizados: {saved_tokens:,}")
print(f"  Custo economizado: ${saved_cost:.2f}")

Desafios adicionais:

  1. Compare tokenizers diferentes: Use AutoTokenizer.from_pretrained() para comparar GPT-4, LLaMA e Mistral
  2. Analise código: Tokenize código Python/JavaScript e veja como diferentes tokenizers lidam com sintaxe
  3. Crie um otimizador de prompts: Função que sugere versões mais curtas mantendo o significado

Exercício 2: Explorando Dados de Pré-Treinamento

Objetivo: Entender a composição e qualidade de datasets usados para treinar foundation models.

Passo a Passo:

# Instalação: pip install datasets

from datasets import load_dataset
from collections import Counter
import re

# Carregar uma amostra do dataset C4 (usado para treinar T5, LLaMA)
print("📦 Carregando amostra do dataset C4 (Common Crawl)...\n")

# Carregar apenas uma pequena amostra para análise
dataset = load_dataset("c4", "en", split="train", streaming=True)

# Analisar primeiros 100 exemplos
sample_size = 100
texts = []
domains = []

print(f"Analisando {sample_size} documentos...\n")

for i, example in enumerate(dataset):
    if i >= sample_size:
        break

    text = example['text']
    url = example.get('url', '')

    texts.append(text)

    # Extrair domínio do URL
    domain_match = re.search(r'https?://(?:www\.)?([^/]+)', url)
    if domain_match:
        domains.append(domain_match.group(1))

# Análise 1: Estatísticas de comprimento
print("="*60)
print("📊 ESTATÍSTICAS DE COMPRIMENTO")
print("="*60)

lengths = [len(text.split()) for text in texts]
avg_length = sum(lengths) / len(lengths)
min_length = min(lengths)
max_length = max(lengths)

print(f"Comprimento médio: {avg_length:.0f} palavras")
print(f"Comprimento mínimo: {min_length} palavras")
print(f"Comprimento máximo: {max_length} palavras")

# Análise 2: Distribuição de domínios
print(f"\n{'='*60}")
print("🌐 DISTRIBUIÇÃO DE DOMÍNIOS (Top 10)")
print(f"{'='*60}\n")

domain_counts = Counter(domains)
for domain, count in domain_counts.most_common(10):
    percentage = (count / len(domains)) * 100
    print(f"{domain:40} {count:3} ({percentage:5.1f}%)")

# Análise 3: Tipos de conteúdo (heurística simples)
print(f"\n{'='*60}")
print("📝 TIPOS DE CONTEÚDO (baseado em heurísticas)")
print(f"{'='*60}\n")

content_types = {
    "Código": 0,
    "Conversação/Forum": 0,
    "Notícia/Artigo": 0,
    "Técnico/Documentação": 0,
    "Outros": 0
}

for text in texts:
    # Heurísticas simples para classificar tipo de conteúdo
    if any(keyword in text.lower() for keyword in ['def ', 'class ', 'function', 'import ', 'return']):
        content_types["Código"] += 1
    elif any(keyword in text.lower() for keyword in ['posted by', 'reply', 'comment', 'said:']):
        content_types["Conversação/Forum"] += 1
    elif any(keyword in text.lower() for keyword in ['according to', 'reported', 'published']):
        content_types["Notícia/Artigo"] += 1
    elif any(keyword in text.lower() for keyword in ['tutorial', 'documentation', 'api', 'parameter']):
        content_types["Técnico/Documentação"] += 1
    else:
        content_types["Outros"] += 1

for content_type, count in content_types.items():
    percentage = (count / len(texts)) * 100
    print(f"{content_type:25} {count:3} ({percentage:5.1f}%)")

# Análise 4: Qualidade dos dados (heurísticas)
print(f"\n{'='*60}")
print("✅ QUALIDADE DOS DADOS")
print(f"{'='*60}\n")

quality_issues = {
    "Muito curtos (< 50 palavras)": 0,
    "Muita repetição": 0,
    "Excesso de URLs": 0,
    "Caracteres não-ASCII": 0
}

for text in texts:
    words = text.split()

    if len(words) < 50:
        quality_issues["Muito curtos (< 50 palavras)"] += 1

    # Detectar repetição (palavras duplicadas consecutivas)
    if len(words) > 10:
        repetitions = sum(1 for i in range(len(words)-1) if words[i] == words[i+1])
        if repetitions > len(words) * 0.1:  # Mais de 10% de repetição
            quality_issues["Muita repetição"] += 1

    # URLs
    url_count = len(re.findall(r'http[s]?://', text))
    if url_count > 5:
        quality_issues["Excesso de URLs"] += 1

    # Caracteres não-ASCII (pode indicar encoding issues)
    non_ascii = sum(1 for c in text if ord(c) > 127)
    if non_ascii > len(text) * 0.05:  # Mais de 5%
        quality_issues["Caracteres não-ASCII"] += 1

for issue, count in quality_issues.items():
    percentage = (count / len(texts)) * 100
    print(f"{issue:35} {count:3} ({percentage:5.1f}%)")

print(f"\n{'='*60}")
print("💡 INSIGHTS")
print(f"{'='*60}")
print("""
1. Datasets de pré-treinamento contêm conteúdo extremamente diverso
2. Qualidade varia significativamente entre documentos
3. Data cleaning é crucial para remover conteúdo problemático
4. Balanceamento de domínios afeta conhecimento do modelo
5. Comprimento dos documentos influencia context window necessário
""")

Desafios adicionais:

  1. Compare datasets: Analise C4, Wikipedia, OpenWebText
  2. Data cleaning pipeline: Implemente filtros para remover conteúdo de baixa qualidade
  3. Análise de viés: Detecte viés de domínio/tópico no dataset

Exercício 3: Experimente com Tokenizers Diferentes

Objetivo: Comparar comportamento de diferentes estratégias de tokenização.

from transformers import AutoTokenizer

# Carregar diferentes tokenizers
tokenizers = {
    "GPT-2": AutoTokenizer.from_pretrained("gpt2"),
    "LLaMA 2": AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-hf"),
    "Code Llama": AutoTokenizer.from_pretrained("codellama/CodeLlama-7b-hf"),
    "Mistral": AutoTokenizer.from_pretrained("mistralai/Mistral-7B-v0.1")
}

# Teste 1: Texto natural
print("="*60)
print("TESTE 1: Texto Natural")
print("="*60)

text_natural = "The transformer architecture revolutionized natural language processing."

for name, tokenizer in tokenizers.items():
    tokens = tokenizer.encode(text_natural)
    print(f"\n{name}:")
    print(f"  Tokens: {len(tokens)}")
    print(f"  Decodificação: {tokenizer.convert_ids_to_tokens(tokens)[:10]}")

# Teste 2: Código
print(f"\n{'='*60}")
print("TESTE 2: Código Python")
print("="*60)

code = """
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
"""

for name, tokenizer in tokenizers.items():
    tokens = tokenizer.encode(code)
    print(f"\n{name}:")
    print(f"  Tokens: {len(tokens)}")
    decoded_tokens = tokenizer.convert_ids_to_tokens(tokens)
    print(f"  Primeiros tokens: {decoded_tokens[:15]}")

print("\n💡 Observe que Code Llama tokeniza código mais eficientemente!")

Questões para reflexão:

  1. Qual tokenizer é mais eficiente para texto em português?
  2. Como tokenizers otimizados para código diferem dos genéricos?
  3. Qual impacto isso tem em aplicações de agentes que processam código?