0Pricing
AI Engineering Academy · Aula

Preparando um conjunto de dados de treinamento de alta qualidade

Colete, limpe e formate dados de seguimento de instruções nos formatos Alpaca e ShareGPT, aplique deduplicação aos dados e divida-os em conjuntos de treinamento e validação.

Preparando um conjunto de dados de treinamento de alta qualidade é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.

Os dados são o fator mais importante do ajuste fino

No ajuste fino, a qualidade dos seus dados de treinamento é mais importante do que qualquer hiperparâmetro, escolha de arquitetura ou técnica de treinamento. 100 exemplos de alta qualidade superam 10.000 exemplos medíocres. Se entram dados ruins, saem resultados ruins: o modelo ajustado reproduzirá fielmente todos os padrões presentes nos seus dados, incluindo erros, vieses e inconsistências de formato. Investir na qualidade dos dados é a ação de maior impacto em qualquer projeto de ajuste fino.

Formatos para seguir instruções

A maior parte do ajuste fino para tarefas de seguir instruções usa um formato de mensagens conversacionais com as funções de sistema, usuário e assistente. A API de ajuste fino da OpenAI usa arquivos JSONL, nos quais cada linha é um exemplo completo de conversa. O formato Alpaca (instrução/entrada/saída) e o formato ShareGPT (lista de conversas) também são amplamente usados. Escolha o formato que corresponda à estrutura de ajuste fino que você planeja usar.

import json

# OpenAI fine-tuning format (JSONL)
# Each line is one training example
openai_example = {
    'messages': [
        {'role': 'system', 'content': 'You are a JSON extraction agent.'},
        {'role': 'user', 'content': 'Extract: "John Smith, age 32, from Seattle, joined 2023-01-15"'},
        {'role': 'assistant', 'content': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'}
    ]
}

# Alpaca format
alpaca_example = {
    'instruction': 'Extract structured data from the following text.',
    'input': 'John Smith, age 32, from Seattle, joined 2023-01-15',
    'output': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'
}

# Write as JSONL
with open('train.jsonl', 'w') as f:
    f.write(json.dumps(openai_example) + '\n')
    # Add more examples here...

Coleta de dados de treinamento: três estratégias

Há três estratégias principais para criar um conjunto de dados para ajuste fino. Geração humana: especialistas escrevem manualmente exemplos ideais — maior qualidade, mas de forma lenta e cara. Geração por LLM: um modelo avançado (GPT-4o) gera exemplos que depois são validados por pessoas — muito mais rápido e barato, com boa qualidade se houver validação. Mineração de registros: extração de pares de entrada e saída de registros de produção existentes, filtrando exemplos de alta qualidade com sinais como avaliações de usuários ou pontuação de julgamento por LLM.

from openai import OpenAI

client = OpenAI()

def generate_training_example_with_gpt4o(task_description: str, example_input: str) -> dict:
    '''Use GPT-4o to generate a training example for a smaller model.'''
    prompt = f'''You are creating a training example for fine-tuning a smaller model.
Task: {task_description}

Given this input:
{example_input}

Write the ideal assistant response that demonstrates the correct behavior for this task.
Be specific, accurate, and follow the expected format precisely.'''
    
    response = client.chat.completions.create(
        model='gpt-4o',  # teacher model
        messages=[{'role': 'user', 'content': prompt}]
    )
    
    return {
        'messages': [
            {'role': 'system', 'content': task_description},
            {'role': 'user', 'content': example_input},
            {'role': 'assistant', 'content': response.choices[0].message.content}
        ]
    }

Filtragem e validação da qualidade

Todo exemplo de treinamento deve passar por uma etapa de validação de qualidade antes de ser incluído. Valide se: a resposta está no formato correto; a resposta é precisa (em tarefas factuais); a resposta não contém alucinações nem conteúdo prejudicial; o par instrução-resposta é coerente; e o exemplo é representativo do caso de uso em produção. Use validação automatizada para verificar o formato e julgamento por LLM para avaliar a qualidade do conteúdo, com revisão humana de uma amostra.

import json

def validate_training_example(example: dict, schema_validator=None) -> dict:
    issues = []
    
    # Format check
    if 'messages' not in example:
        issues.append('Missing messages field')
        return {'valid': False, 'issues': issues}
    
    messages = example['messages']
    if not any(m['role'] == 'assistant' for m in messages):
        issues.append('No assistant message found')
    
    # Check assistant message quality
    assistant_content = next((m['content'] for m in messages if m['role'] == 'assistant'), '')
    
    if len(assistant_content) < 5:
        issues.append('Assistant response too short')
    
    # Schema validation for JSON output tasks
    if schema_validator:
        try:
            parsed = json.loads(assistant_content)
            schema_validator(parsed)  # raises if invalid
        except json.JSONDecodeError:
            issues.append('Assistant response is not valid JSON')
        except Exception as e:
            issues.append(f'Schema validation failed: {str(e)}')
    
    return {'valid': len(issues) == 0, 'issues': issues}

# Run validation on all examples before training
examples = load_training_examples('raw_dataset.jsonl')
valid_examples = [e for e in examples if validate_training_example(e)['valid']]
print(f'Valid examples: {len(valid_examples)}/{len(examples)}')

Deduplicação dos dados

Exemplos duplicados ou quase duplicados nos dados de treinamento são prejudiciais. Eles fazem com que o modelo se ajuste excessivamente a esses exemplos específicos, desperdiçando a capacidade de treinamento que poderia ter aprendido padrões diversificados. Execute a deduplicação antes de finalizar seu conjunto de dados. A deduplicação exata usa funções de resumo para encontrar exemplos idênticos. A deduplicação aproximada usa MinHash ou similaridade de incorporações para encontrar exemplos que diferem apenas em aspectos triviais.

import hashlib
from datasketch import MinHash, MinHashLSH

def exact_deduplicate(examples: list[dict]) -> list[dict]:
    seen_hashes = set()
    unique = []
    
    for ex in examples:
        # Hash the user and assistant messages
        content = str(ex['messages'])
        h = hashlib.md5(content.encode()).hexdigest()
        if h not in seen_hashes:
            seen_hashes.add(h)
            unique.append(ex)
    
    print(f'Exact dedup: {len(examples)} -> {len(unique)} ({len(examples)-len(unique)} removed)')
    return unique

def near_deduplicate_by_input(examples: list[dict], similarity_threshold=0.85) -> list[dict]:
    # Build index of input texts
    inputs = [next((m['content'] for m in ex['messages'] if m['role'] == 'user'), '') for ex in examples]
    
    lsh = MinHashLSH(threshold=similarity_threshold, num_perm=128)
    unique_indices = set()
    
    for i, text in enumerate(inputs):
        m = MinHash(num_perm=128)
        for word in text.lower().split():
            m.update(word.encode('utf-8'))
        if not lsh.query(m):  # no similar items found
            lsh.insert(str(i), m)
            unique_indices.add(i)
    
    return [examples[i] for i in sorted(unique_indices)]

Divisão entre treinamento e validação

Divida seu conjunto de dados em conjuntos de treinamento e validação antes de iniciar qualquer execução de ajuste fino. O conjunto de validação é usado para monitorar o sobreajuste durante o treinamento (se a perda de validação aumentar enquanto a perda de treinamento diminuir, o modelo está sofrendo sobreajuste). Uma divisão comum é 90% para treinamento e 10% para validação. Garanta que a divisão seja aleatória e estratificada se seu conjunto de dados tiver categorias relevantes (por exemplo, representação igual de todos os tipos de intenção nos dois conjuntos).

import random
import json

def split_dataset(examples: list[dict], val_fraction=0.1, seed=42) -> tuple[list, list]:
    random.seed(seed)  # reproducible split
    shuffled = examples.copy()
    random.shuffle(shuffled)
    
    n_val = max(1, int(len(shuffled) * val_fraction))
    val_set = shuffled[:n_val]
    train_set = shuffled[n_val:]
    
    print(f'Train: {len(train_set)} examples, Validation: {len(val_set)} examples')
    return train_set, val_set

def save_jsonl(examples: list[dict], path: str):
    with open(path, 'w') as f:
        for ex in examples:
            f.write(json.dumps(ex) + '\n')

# Split and save
examples = load_training_examples('clean_dataset.jsonl')
train, val = split_dataset(examples, val_fraction=0.1)
save_jsonl(train, 'train.jsonl')
save_jsonl(val, 'validation.jsonl')
print(f'Saved train.jsonl ({len(train)}) and validation.jsonl ({len(val)})')

Equilibrando o conjunto de dados

Conjuntos de dados desequilibrados fazem com que modelos ajustados se especializem excessivamente nos casos comuns e falhem nos casos raros, mas importantes. Se seu conjunto de dados tiver 900 exemplos da categoria A e 100 exemplos da categoria B, o modelo poderá aprender a sempre prever A. Equilibre o conjunto de dados: aumente a amostragem das categorias minoritárias (duplicando exemplos raros), reduza a amostragem das categorias majoritárias ou gere exemplos sintéticos para os casos sub-representados usando GPT-4o.

from collections import Counter
import random

def analyze_distribution(examples: list[dict], category_extractor) -> dict:
    categories = [category_extractor(ex) for ex in examples]
    counts = Counter(categories)
    print('Category distribution:')
    for cat, count in counts.most_common():
        print(f'  {cat}: {count} ({100*count/len(examples):.1f}%)')
    return counts

def oversample_minority(examples: list[dict], category_extractor, target_count: int) -> list[dict]:
    by_category = {}
    for ex in examples:
        cat = category_extractor(ex)
        by_category.setdefault(cat, []).append(ex)
    
    balanced = []
    for cat, cat_examples in by_category.items():
        if len(cat_examples) < target_count:
            # Oversample with replacement
            oversampled = random.choices(cat_examples, k=target_count)
            balanced.extend(oversampled)
        else:
            # Undersample to target_count
            balanced.extend(random.sample(cat_examples, target_count))
    
    random.shuffle(balanced)
    return balanced

Limpeza e normalização dos dados

Os dados de treinamento frequentemente contêm inconsistências que prejudicam o ajuste fino: uso misto de maiúsculas e minúsculas nos campos de saída, espaços em branco no final, uso inconsistente de aspas, formatos numéricos diferentes ou convenções variadas para nomear chaves JSON. Normalize esses elementos antes do treinamento. O modelo ajustado aprenderá a formatação exata presente nos seus dados — se seus dados tiverem inconsistências, o modelo as reproduzirá.

import json
import re

def normalize_json_output_example(example: dict) -> dict:
    '''Normalize JSON output in assistant messages for consistency.'''
    messages = example.get('messages', [])
    normalized = []
    
    for msg in messages:
        if msg['role'] == 'assistant':
            content = msg['content'].strip()
            
            # Try to parse and re-serialize JSON for consistent formatting
            try:
                parsed = json.loads(content)
                # Normalize: sort keys, consistent spacing
                content = json.dumps(parsed, ensure_ascii=False, sort_keys=True)
            except json.JSONDecodeError:
                pass  # Not JSON output - leave as is
            
            normalized.append({'role': 'assistant', 'content': content})
        else:
            normalized.append(msg)
    
    return {'messages': normalized}

def normalize_dataset(examples: list[dict]) -> list[dict]:
    return [normalize_json_output_example(ex) for ex in examples]

Medição das métricas de qualidade do conjunto de dados

Antes de enviar os dados para o ajuste fino, calcule métricas de qualidade para o conjunto de dados como um todo. Verifique: a contagem média e máxima de tokens por exemplo (exemplos longos custam mais para treinar e podem ser truncados); a cobertura do vocabulário (o conjunto de dados abrange toda a diversidade das entradas em produção?); e a pontuação de consistência (entradas semelhantes recebem saídas semelhantes?). A maioria dos provedores de ajuste fino tem um ponto de acesso de validação de dados que verifica erros de formato antes de cobrar por uma execução de treinamento malsucedida.

import tiktoken

def analyze_dataset_quality(examples: list[dict], model='gpt-4o-mini') -> dict:
    encoder = tiktoken.encoding_for_model(model)
    
    token_counts = []
    for ex in examples:
        total_tokens = sum(
            len(encoder.encode(m['content']))
            for m in ex['messages']
        )
        token_counts.append(total_tokens)
    
    report = {
        'total_examples': len(examples),
        'avg_tokens_per_example': sum(token_counts) / len(token_counts),
        'max_tokens': max(token_counts),
        'min_tokens': min(token_counts),
        'examples_over_4k_tokens': sum(1 for t in token_counts if t > 4096),
        'estimated_training_tokens': sum(token_counts),
        'estimated_cost': sum(token_counts) / 1_000_000 * 8.0  # ~$8/1M tokens for gpt-4o-mini
    }
    
    for key, value in report.items():
        print(f'{key}: {value}')
    return report

Melhoria iterativa do conjunto de dados

A preparação do conjunto de dados é iterativa. Faça o ajuste fino de um modelo pequeno com seu conjunto de dados inicial, avalie-o em exemplos reservados, identifique os modos de falha e rastreie-os até lacunas ou problemas de qualidade no conjunto de dados. Em seguida, corrija os dados e faça um novo treinamento. Esse ciclo de melhoria dos dados orientada por erros é a prática padrão no ajuste fino em produção e é muito mais eficaz do que tentar coletar um conjunto de dados perfeito de uma só vez.

Consistência do prompt de sistema entre os exemplos

Se o seu modelo ajustado sempre usar o mesmo prompt de sistema em produção, inclua exatamente esse prompt de sistema em todos os exemplos de treinamento. Se quiser que o modelo funcione sem nenhum prompt de sistema, treine sem um. Incompatibilidades entre as condições de treinamento e de inferência são uma das principais causas de resultados decepcionantes no ajuste fino. O modelo aprende comportamentos condicionados à estrutura exata do prompt que vê durante o treinamento.

Verificação rápida

Teste sua compreensão sobre como preparar um conjunto de dados de treinamento para ajuste fino a partir desta lição.

Recapitulação da lição

Nesta lição, você aprendeu que, no ajuste fino, a qualidade dos dados é mais importante que a quantidade — 100 exemplos excelentes superam 10.000 exemplos medianos; deduplicação, validação, balanceamento e normalização são as quatro etapas principais de limpeza dos dados antes de qualquer execução de treinamento; e a divisão entre treinamento e validação é essencial para detectar o sobreajuste durante o treinamento, antes que ele prejudique o desempenho no mundo real. A seguir, executaremos o ajuste fino com LoRA usando Hugging Face PEFT.

Perguntas Frequentes

A aula “Preparando um conjunto de dados de treinamento de alta qualidade” é grátis?

Sim — o texto completo de “Preparando um conjunto de dados de treinamento de alta qualidade” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.

O que vou aprender em “Preparando um conjunto de dados de treinamento de alta qualidade”?

Colete, limpe e formate dados de seguimento de instruções nos formatos Alpaca e ShareGPT, aplique deduplicação aos dados e divida-os em conjuntos de treinamento e validação. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Engineering Academy?

Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Preparando um conjunto de dados de treinamento de alta qualidade”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Engineering Academy?

Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Quando o ajuste fino supera a engenharia de prompts
  2. Preparando um conjunto de dados de treinamento de alta qualidade
  3. Ajuste fino com LoRA usando Hugging Face PEFT
  4. Avaliando e implantando seu modelo ajustado
← Voltar para AI Engineering Academy