Neste guia sobre o ajuste fino do GPT-OSS com dados da Web, você aprenderá:
- O que é o Unsloth e por que ele acelera o ajuste fino
- Como coletar dados de treinamento de qualidade usando as APIs de raspagem da Bright Data
- Como configurar seu ambiente para um ajuste fino eficiente
- Como fazer o ajuste fino do GPT-OSS com um tutorial passo a passo completo
Vamos começar!
O que é o Unsloth e por que usá-lo para o ajuste fino?
Unsloth é uma biblioteca leve que torna o ajuste fino do LLM significativamente mais rápido e, ao mesmo tempo, é totalmente compatível com o ecossistema Hugging Face (Hub, transformadores, PEFT, TRL). A biblioteca é compatível com a maioria das GPUs NVIDIA, desde a GTX 1070 até as H100s, e funciona perfeitamente com todo o conjunto de treinadores da biblioteca TRL.

As melhorias de desempenho que o Unsloth proporciona são impressionantes. Em benchmarks, ele atinge velocidades de treinamento duas vezes mais rápidas em comparação com as implementações de transformadores padrão, enquanto usa 40% menos memória. Isso significa que você pode treinar modelos maiores ou usar tamanhos de lote maiores no mesmo hardware. Talvez o mais importante seja que ele apresenta 0% de degradação da precisão, de modo que você obtém todos esses benefícios sem sacrificar a qualidade do modelo.
Entendendo os modelos GPT-OSS
O lançamento do GPT-OSS pela OpenAI marca uma mudança significativa em sua abordagem ao desenvolvimento de IA. Pela primeira vez, temos acesso a modelos GPT genuínos sem limitações de API, faturamento baseado em uso ou limites de taxa.
O GPT-OSS vem em duas variantes principais:
- GPT-OSS-120B: esse modelo maior corresponde à qualidade do GPT-4, mas requer pelo menos 80 GB de memória de GPU
- GPT-OSS-20B: comparável ao desempenho do GPT-3.5, esse modelo é executado com eficiência em GPUs de 16 GB (perfeito para o nosso tutorial)
Um recurso exclusivo que diferencia o GPT-OSS de outros modelos abertos é o controle do esforço de raciocínio. Você pode ajustar a profundidade com que o modelo analisa os problemas, definindo o nível de raciocínio como “baixo”, “médio” ou “alto”. Isso permite equilibrar entre velocidade e precisão, dependendo do seu caso de uso específico.
Por que os dados de qualidade são importantes para o ajuste fino
O ajuste fino é tão bom quanto os dados que você fornece a ele. Poderíamos ter a configuração de treinamento mais sofisticada, mas se nossos dados forem ruidosos, inconsistentes ou mal formatados, seu modelo aprenderá esses mesmos problemas. É por isso que usaremos as APIs do Web Scraper da Bright Data para obter dados limpos, bem formatados e precisos.
A Bright Data lida com as partes complexas do Scraping de dados que geralmente atrapalham as soluções personalizadas. Ela gerencia a rotação de IP para evitar a limitação de taxa, a Resolução de CAPTCHAs automaticamente, o tratamento de conteúdo dinâmico renderizado em JavaScript e a manutenção da qualidade consistente dos dados em milhões de solicitações.
Em nosso tutorial, usaremos a API da Bright Data para coletar documentação Python, que será transformada em dados de treinamento para nosso modelo.
Pré-requisitos e configuração do ambiente
Antes de começarmos, vamos garantir que você tenha tudo o que é necessário para um ajuste fino bem-sucedido. Usaremos o Google Colab porque ele oferece acesso gratuito à GPU, mas o mesmo processo funciona em qualquer máquina com pelo menos 16 GB de VRAM.
Requisitos de hardware
Para este tutorial, você precisará de:
- Uma GPU com pelo menos 16 GB de VRAM (T4, V100 ou superior)
- 25 GB de espaço livre em disco para pesos de modelos e pontos de controle
- Conexão estável com a Internet para fazer download de modelos e dependências
No Google Colab, você pode acessar uma GPU T4 gratuitamente:
- Abrir um novo notebook
- Ir para Runtime → Alterar tipo de tempo de execução
- Selecionar GPU como acelerador de hardware
- Clicar em Salvar para aplicar as alterações

Instalação do Unsloth e das dependências
Quando o tempo de execução da GPU estiver pronto, instalaremos o Unsloth e todas as dependências necessárias. O processo de instalação é otimizado para evitar conflitos entre diferentes versões de pacotes:
%%capture
# Instalar o Unsloth e as dependências principais
!pip install --upgrade -qqq uv
try: import numpy; get_numpy = f "numpy=={numpy.__version__}"
exceto: get_numpy = "numpy"
!uv pip install -qqq
"torch>=2.8.0" "triton>=3.4.0" {get_numpy} torchvision bitsandbytes "transformers>=4.55.3"
"unsloth_zoo[base] @ git+https://github.com/unslothai/unsloth-zoo"
"unsloth[base] @ git+https://github.com/unslothai/unsloth"
git+https://github.com/triton-lang/triton.git@05b2c186c1b6c9a08375389d5efe9cb4c401c075#subdirectory=python/triton_kernels
!uv pip install --upgrade --no-deps transformers==4.56.2 tokenizers
!uv pip install --no-deps trl==0.22.2
!pip install -q brightdata-sdk
Esse script de instalação lida com vários detalhes importantes. Primeiro, ele usa o uv para uma resolução mais rápida dos pacotes. Ele também fixa versões específicas para evitar problemas de compatibilidade, instala os kernels Triton personalizados do Unsloth para otimizar o desempenho e inclui o Bright Data SDK para nossa etapa de coleta de dados.
Verificação da configuração da GPU
Após a instalação, vamos verificar se a GPU foi detectada corretamente e se tem memória suficiente:
import torch
# Obter informações da GPU
gpu_stats = torch.cuda.get_device_properties(0)
max_memory = round(gpu_stats.total_memory / 1024 / 1024 / 1024, 3)
print(f "GPU = {gpu_stats.name}")
print(f "Memória máxima = {max_memory} GB")
print(f "CUDA version = {torch.version.cuda}")
print(f "Versão do PyTorch = {torch.__version__}")
# Verificar os requisitos mínimos
se max_memory < 15:
print("⚠️ Warning: Your GPU might not have enough memory for GPT-OSS-20B")
else:
print("✅ Sua GPU tem memória suficiente para o ajuste fino")
Você deve ver pelo menos 15 GB de memória disponível na GPU. A GPU T4 no Colab gratuito fornece 16 GB, o que é perfeito para nossas necessidades com as otimizações do Unsloth.
Carregando o GPT-OSS com o Unsloth
Agora vamos carregar o modelo GPT-OSS usando o carregador otimizado do Unsloth. O processo é extremamente simples em comparação com os transformadores padrão, pois o Unsloth lida com todos os detalhes de otimização automaticamente.

Carregando o modelo básico
from unsloth import FastLanguageModel
importar torch
# Configuração
max_seq_length = 1024 # Ajuste com base em seus dados
dtype = None # Detecta automaticamente o melhor dtype para sua GPU
# O Unsloth fornece modelos pré-quantizados para carregamento mais rápido
fourbit_models = [
"unsloth/gpt-oss-20b-unsloth-bnb-4bit", # BitsAndBytes 4bit
"unsloth/gpt-oss-120b-unsloth-bnb-4bit",
"unsloth/gpt-oss-20b", # Formato MXFP4
"unsloth/gpt-oss-120b",
]
# Carregar o modelo
model, tokenizer = FastLanguageModel.from_pretrained(
nome_do_modelo = "unsloth/gpt-oss-20b",
dtype = dtype,
max_seq_length = max_seq_length,
load_in_4bit = True, # Essencial para ajuste em 16 GB
full_finetuning = False, # Use LoRA para obter eficiência
)
print(f"✅ Modelo carregado com sucesso!")
print(f "Tamanho do modelo: {model.num_parameters():,} parâmetros")
print(f "Usando o dispositivo: {model.device}")
O método FastLanguageModel.from_pretrained() faz várias coisas nos bastidores. Ele detecta automaticamente os recursos de sua GPU e os otimiza de acordo, aplica quantização de 4 bits para reduzir o uso da memória em 75%, configura o modelo para treinamento LoRA em vez de ajuste fino completo e configura mecanismos de atenção com eficiência de memória.
Configuração de adaptadores LoRA
O LoRA (Low-Rank Adaptation) é o que torna o ajuste fino viável no hardware do consumidor. Em vez de atualizar todos os parâmetros do modelo, treinamos apenas pequenas matrizes de adaptadores que são inseridas nas camadas principais:
model = FastLanguageModel.get_peft_model(
model,
r = 8, # classificação LoRA - maior = mais capacidade, mas mais lento
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_alpha = 16, # Fator de escala do LoRA
lora_dropout = 0, # Dropout desativado para treinamento mais rápido
bias = "none", # Não treina termos de bias
use_gradient_checkpointing = "unsloth", # Crítico para economia de memória
random_state = 3407,
use_rslora = False, # LoRA padrão funciona melhor na maioria dos casos
loftq_config = Nenhum,
)
# Exibir estatísticas de treinamento
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
all_params = sum(p.numel() for p in model.parameters())
trainable_percent = 100 * trainable_params / all_params
print(f "Treinando parâmetros {trainable_params:,} de {all_params:,}")
print(f "Isso é apenas {trainable_percent:.2f}% de todos os parâmetros!")
print(f "Memory saved: ~{(1 - trainable_percent/100) * 40:.1f}GB")
Essa configuração atinge um equilíbrio entre a eficiência do treinamento e a capacidade do modelo. Com r=8, estamos treinando menos de 1% do total de parâmetros e, ao mesmo tempo, obtemos excelentes resultados de ajuste fino. Somente o checkpointing de gradiente economiza cerca de 30% de memória, o que pode ser a diferença entre ajustar o modelo na memória ou obter erros OOM (Out of Memory).
Teste do controle de esforço de raciocínio do GPT-OSS
Antes de começarmos a fazer o ajuste fino, vamos explorar o recurso exclusivo de esforço de raciocínio do GPT-OSS. Isso permite que você controle a quantidade de “raciocínio” que o modelo faz antes de responder:
from transformers import TextStreamer
# Problema de teste que requer raciocínio matemático
mensagens = [
{"role": "user", "content": "Resolva x^5 + 3x^4 - 10 = 3. Explique sua abordagem."},
]
# Teste com esforço de raciocínio BAIXO
imprimir("="*60)
print("LOW REASONING (Rápido, mas menos completo)")
print("="*60)
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt = True,
return_tensors = "pt",
return_dict = True,
reasoning_effort = "low",
).to("cuda")
text_streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
_ = model.generate(**inputs, max_new_tokens = 128, streamer = text_streamer)
# Teste com ALTO esforço de raciocínio
print("n" + "="*60)
print("HIGH REASONING (Mais lento, porém mais preciso)")
print("="*60)
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt = True,
return_tensors = "pt",
return_dict = True,
reasoning_effort = "high",
).to("cuda")
_ = model.generate(**inputs, max_new_tokens = 512, streamer = text_streamer)
Quando executarmos esse código, veremos que, com o raciocínio “baixo”, o modelo fornece uma resposta rápida e aproximada, enquanto o raciocínio “alto” produz uma solução mais detalhada com trabalho passo a passo. Esse recurso é inestimável para equilibrar velocidade e precisão nas implementações de produção.
Coleta de dados de treinamento com a Bright Data
Agora, coletaremos dados de treinamento de alta qualidade usando a API Web Scraper da Bright Data. Essa abordagem é muito mais confiável do que criar seu próprio Scraper, pois a Bright Data lida com toda a infraestrutura complexa necessária para o Scraping de dados em grande escala.
Configuração do coletor de dados
da brightdata import bdclient
de typing import List, Dict
import re
importar json
class DataCollector:
def __init__(self, api_token: str):
"""
Inicializar o cliente Bright Data para scraping de dados.
Args:
api_token: Seu token da API da Bright Data
"""
self.client = bdclient(api_token=api_token)
self.collected_data = []
print("✅ Cliente da Bright Data inicializado")
def collect_documentation(self, urls: List[str]) -> List[Dict]:
"""
Extrair páginas de documentação e converter em dados de treinamento.
Esse método lida com a coleta de URLs em lote e individual,
voltando automaticamente para solicitações individuais se o lote falhar.
"""
print(f "Começando a extrair {len(urls)} URLs...")
try:
# Tentativa de raspagem em lote para aumentar a eficiência
results = self.client.scrape(urls, data_format="markdown")
if isinstance(results, str):
# Retornou um único resultado
print("Processando um único resultado...")
training_data = self.process_single_result(results)
elif isinstance(results, list):
# Vários resultados retornados
print(f "Processando {len(results)} resultados...")
dados_de_treinamento = []
for i, content in enumerate(results, 1):
if content:
print(f" Processing result {i}/{len(results)}")
examples = self.process_single_result(content)
training_data.extend(examples)
else:
print(f "Tipo de resultado inesperado: {type(results)}")
dados_de_treinamento = []
exceto Exception as e:
print(f "Falha na coleta de dados em lote: {e}")
print("Retornando à coleta individual de URLs...")
# Retorno: raspagem de URLs um a um
dados_treinamento = []
para url em urls:
try:
print(f" Scraping: {url}")
content = self.client.scrape(url, data_format="markdown")
if content:
examples = self.process_single_result(content)
training_data.extend(examples)
print(f" ✓ Extracted {len(examples)} examples")
exceto Exception as url_error:
print(f" ✗ Falha: {url_error}")
self.collected_data = training_data
print(f"n✅ Coleção concluída: {len(self.collected_data)} exemplos de treinamento")
return self.collected_data
O que esse código faz:
- Estratégia de fallback inteligente: O coletor tenta primeiro a raspagem em lote para aumentar a eficiência. Se isso falhar (devido a problemas de rede ou limites da API), ele voltará automaticamente para a coleta individual de URLs.
- Acompanhamento de progresso: Atualizações em tempo real nos mostram exatamente o que está acontecendo durante o processo de raspagem, facilitando a depuração.
- Resiliência a erros: Cada URL é envolvido em seu próprio bloco try-catch, portanto, um URL com falha não interromperá todo o processo de coleta.
- Formato Markdown: Solicitamos dados no formato Markdown porque é mais limpo do que HTML e mais fácil de processar em dados de treinamento.
O cliente da Bright Data lida com várias tarefas complexas para nós:
- Rotação de endereços IP para evitar limitação de taxa
- Resolução automática de CAPTCHAs
- Renderização de páginas com muito JavaScript
- Repetição de solicitações com falha com backoff exponencial
Processamento de conteúdo raspado em dados de treinamento
A chave para um bom ajuste fino são dados limpos e bem formatados. Veja como processamos o conteúdo raspado bruto em pares de perguntas e respostas:
def process_single_result(self, content: str) -> List[Dict]:
"""
Processar o conteúdo extraído em pares de treinamento de perguntas e respostas limpos.
Esse método executa uma limpeza agressiva para remover todos os
artefatos de formatação e criar exemplos que soem naturais.
"""
exemplos = []
# Etapa 1: remover toda a formatação HTML e Markdown
content = re.sub(r'<[^>]+>', '', content) # Tags HTML
content = re.sub(r'', '', content) # Imagens
content = re.sub(r'[([^]]+)]([^)]+)', r'1', content) # Links
content = re.sub(r'```[^`]*```', '', content) # Blocos de código
content = re.sub(r'`[^`]+``', '', content) # Código em linha
content = re.sub(r'[#*_~>`|-]+', ' ', content) # Símbolos Markdown
content = re.sub(r'\(.)', r'1', content) # Sequências de escape
content = re.sub(r'https?://[^s]+', '', content) # URLs
content = re.sub(r'S+.w+', '', content) # Caminhos de arquivos
content = re.sub(r's+', ' ' ', content) # Normaliza os espaços em branco
# Etapa 2: dividir em sentenças
sentences = re.split(r'(?<=[.!?])s+', content)
# Etapa 3: filtrar o conteúdo de navegação e boilerplate
clean_sentences = []
skip_patterns = ['navigation', 'copyright', 'index',
'table of contents', 'previous', 'next',
'clique aqui', 'download', 'compartilhar']
for sent in sentences:
sent = sent.strip()
# Manter apenas as frases substanciais
se (len(sent) > 30 and
not any(skip in sent.lower() for skip in skip_patterns)):
clean_sentences.append(sent)
# Etapa 4: criar pares de perguntas e respostas a partir de frases consecutivas
for i in range(0, len(clean_sentences) - 1):
instruction = clean_sentences[i][:200].strip()
response = clean_sentences[i + 1][:300].strip()
# Certifique-se de que ambas as partes sejam substanciais
if len(instruction) > 20 and len(response) > 30:
examples.append({
"instruction": instrução,
"response": resposta
})
retornar exemplos
Como o processamento funciona:
O método process_single_result transforma o conteúdo bruto da Web em dados de treinamento limpos por meio de quatro etapas críticas:
- Etapa 1 – Limpeza agressiva: Removemos todos os artefatos de formatação que poderiam confundir o modelo:
- Tags HTML que podem ter sobrevivido à conversão do Markdown
- Referências a imagens e links que não agregam valor à compreensão do texto
- Blocos de código e código inline (queremos prosa, não amostras de código)
- Caracteres especiais e sequências de escape que criam ruído
- Etapa 2 – Segmentação de frases: Dividimos o conteúdo em frases individuais usando marcadores de pontuação. Isso nos dá unidades lógicas de texto para trabalhar.
- Etapa 3 – Filtragem de qualidade: Removemos:
- Frases curtas (menos de 30 caracteres) que não têm substância
- Elementos de navegação como “clique aqui” ou “próxima página”
- Conteúdo padrão, como avisos de direitos autorais
- Qualquer frase que contenha padrões comuns de navegação na Web
- Etapa 4 – Criação de pares: Criamos pares de treinamento tratando frases consecutivas como pares de perguntas e respostas. Isso funciona porque a documentação geralmente segue um padrão de declaração de um conceito e, em seguida, de explicação.
O resultado são dados de treinamento limpos e contextuais que ensinam o fluxo natural do modelo e os padrões de resposta.
Coleta e validação dos dados
Agora vamos juntar tudo e coletar nossos dados de treinamento:
# Inicialize o coletor com seu token de API
# Obtenha seu token em: /cp/api_tokens
BRIGHTDATA_API_TOKEN = "your_brightdata_api_token_here"
collector = DataCollector(api_token=BRIGHTDATA_API_TOKEN)
# URLs a serem extraídas - a documentação do Python é um excelente dado de treinamento
urls = [
"https://docs.python.org/3/tutorial/introduction.html",
"https://docs.python.org/3/tutorial/controlflow.html",
"https://docs.python.org/3/tutorial/datastructures.html",
"https://docs.python.org/3/tutorial/modules.html",
"https://docs.python.org/3/tutorial/classes.html",
]
print("="*60)
print("INICIANDO A COLETA DE DADOS")
print("="*60)
dados_de_treinamento = collector.collect_documentation(urls)
# Validação de que temos dados
if len(training_data) == 0:
print("⚠️ ERROR: Nenhum dado de treinamento foi coletado!")
print("nEtapas de solução de problemas:")
print("1. verifique se o token da API da Bright Data está correto")
print("2. verifique se sua conta tem créditos suficientes")
print("3. Tente primeiro com um único URL para testar a conectividade")
raise ValueError("Nenhum dado de treinamento foi coletado")
Entendendo a configuração da coleta de dados:
- Token da API: Você precisará se inscrever em uma conta da Bright Data para obter seu token de API. Ela oferece uma avaliação gratuita com créditos para você começar.
- Seleção de URL: Estamos usando a documentação do Python porque:
- Ela é bem estruturada e consistente
- Contém conteúdo técnico perfeito para treinar um assistente de codificação
- O estilo explicativo se traduz bem no formato de perguntas e respostas
- Está disponível publicamente e é de origem ética
- Tratamento de erros: A verificação de validação garante que você não prossiga com um Conjuntos de dados vazio, o que causaria falha no treinamento posteriormente. As etapas de solução de problemas ajudam a diagnosticar problemas comuns.
Validação e limpeza final dos dados
Antes de usar os dados para treinamento, realizamos uma última passagem de limpeza:
# Validação e limpeza finais
def final_validation(examples: List[Dict]) -> List[Dict]:
"""
Realizar a validação final e a deduplicação dos exemplos de treinamento.
"""
dados_limpos = []
seen_instructions = set()
for ex in examples:
instruction = ex.get('instruction', '').strip()
response = ex.get('response', '').strip()
# Passagem final de limpeza
instruction = re.sub(r'[^a-zA-Z0-9s.,?!]', '', instruction)
response = re.sub(r'[^a-zA-Z0-9s.,?!]', '', response)
# Remover duplicatas e garantir a qualidade
if (len(instruction) > 10 and
len(response) > 20 e
instrução não estiver em seen_instructions):
seen_instructions.add(instruction)
clean_data.append({
"instruction": instrução,
"response": resposta
})
return clean_data
dados_de_treinamento = final_validation(dados_de_treinamento)
print(f"n✅ Conjuntos de dados finais: {len(training_data)} exemplos exclusivos")
print("nAmostra de exemplos de treinamento:")
print("="*60)
for i, example in enumerate(training_data[:3], 1):
print(f"nExemplo {i}:")
print(f "Q: {example['instruction']}")
print(f "A: {example['response']}")
O que a validação realiza:
- Deduplicação: O conjunto
seen_instructionsgarante que não tenhamos perguntas duplicadas, o que poderia levar a um ajuste excessivo durante o treinamento. - Limpeza final de caracteres: Removemos todos os caracteres especiais restantes, exceto a pontuação básica, garantindo que o texto esteja limpo e consistente.
- Validação de comprimento: Aplicamos comprimentos mínimos para garantir que os exemplos tenham substância:
- As instruções devem ter pelo menos 10 caracteres
- As respostas devem ter pelo menos 20 caracteres
- Garantia de qualidade: Ao imprimir exemplos de amostra, você pode verificar visualmente a qualidade dos dados antes de prosseguir com o treinamento.
A saída final deve mostrar pares de Q&A limpos e legíveis que façam sentido como dados de treinamento. Se os exemplos parecerem sem sentido ou mal formatados, talvez seja necessário ajustar os parâmetros de processamento ou escolher URLs de origem diferentes.
Dica profissional: para casos de uso de produção, considere usar o marketplace da Bright Data para Conjuntos de dados pré-coletados. Ele oferece Conjuntos de dados selecionados para vários domínios que podem economizar muito tempo e garantir uma qualidade consistente.
Formatação de dados para o treinamento do GPT-OSS
O GPT-OSS espera dados em um formato de bate-papo específico. Usaremos os utilitários do Unsloth para garantir que nossos dados sejam formatados adequadamente para obter resultados de treinamento ideais:
from unsloth.chat_templates import standardize_sharegpt
from datasets import Dataset
def prepare_dataset(raw_data: List[Dict]):
"""
Converta pares de perguntas e respostas brutos em um conjunto de dados de treinamento devidamente formatado.
Essa função lida com:
1. Conversão para o formato de mensagem
2. Aplicar o modelo de bate-papo GPT-OSS
3. Correção de problemas de formatação
"""
print("Preparando o conjunto de dados para treinamento...")
# Etapa 1: converter para o formato de mensagem de bate-papo
dados_formatados = []
for item in raw_data:
formatted_data.append({
"messages": [
{"role": "user", "content": item["instruction"]},
{"role": "assistant" (assistente), "content" (conteúdo): item["response"]}
]
})
# Etapa 2: criar o conjunto de dados HuggingFace
Conjunto de dados = Dataset.from_list(formatted_data)
print(f "Created dataset with {len(dataset)} examples")
# Etapa 3: Padronizar para o formato ShareGPT
Conjuntos de dados = standardize_sharegpt(dataset)
O que está acontecendo nesta primeira parte:
- Conversão do formato da mensagem: Transformamos nossos pares simples de perguntas e respostas em um formato de conversa que os modelos GPT esperam. Cada exemplo de treinamento torna-se uma conversa de duas voltas com uma pergunta do usuário e uma resposta do assistente.
- Criação de conjuntos de dados: A classe Conjuntos de dados do HuggingFace fornece um manuseio eficiente dos dados, incluindo:
- Acesso mapeado na memória para grandes Conjuntos de dados
- Loteamento e embaralhamento incorporados
- Compatibilidade com todo o ecossistema HuggingFace
- Padronização do ShareGPT: A função
standardize_sharegptgarante que nossos dados correspondam ao formato ShareGPT, que se tornou o padrão de fato para o treinamento de modelos de bate-papo. Isso lida com casos extremos e garante a consistência.
Aplicação do modelo de chat
Agora, aplicamos os requisitos de formatação específicos do GPT-OSS:
# Etapa 4: aplicar o modelo de bate-papo específico do GPT-OSS
def formatting_prompts_func(examples):
"""Aplicar o modelo de bate-papo do GPT-OSS a cada exemplo."""
convos = examples["messages"]
texts = []
for convo in convos:
# Aplicar o modelo sem prompt de geração (estamos treinando)
text = tokenizer.apply_chat_template(
convo,
tokenize = False,
add_generation_prompt = False
)
texts.append(text)
return {"text": texts}
Conjuntos de dados = dataset.map(
formatting_prompts_func,
batched = True,
desc = "Aplicando modelo de bate-papo"
)
Entendendo o aplicativo de modelo:
- Modelo de bate-papo Objetivo: cada família de modelos tem seus próprios tokens e formatação especiais. O GPT-OSS usa tags como
<|start|>,<|message|>e<|channel|>para delinear diferentes partes da conversa. - Sem prompt de geração: Definimos
add_generation_prompt = Falseporque estamos treinando, não gerando. Durante o treinamento, queremos que o modelo veja conversas completas, não prompts aguardando a conclusão. - Processamento em lote: O parâmetro
batched = Trueprocessa vários exemplos de uma só vez, acelerando significativamente o processo de formatação de grandes Conjuntos de dados. - Saída de texto: Mantemos a saída como texto (não tokenizado) neste estágio porque o instrutor tratará da tokenização com suas próprias configurações.
Verificação e correção de problemas de formatação
O GPT-OSS tem um requisito específico para a tag de canal que precisamos verificar:
# Etapa 5: Verificar e corrigir a tag do canal, se necessário
sample_text = Conjuntos de dados [0]['texto']
print("nVerificando o formato...")
print(f "Amostra (primeiros 200 caracteres): {sample_text[:200]}")
se "<|channel|>" não estiver em sample_text:
print("⚠️ Tag de canal ausente, corrigindo o formato...")
def fix_formatting(examples):
"""Adicionar a tag de canal para compatibilidade com GPT-OSS."""
fixed_texts = []
for text in examples["text"]:
# O GPT-OSS espera a tag de canal entre a função e a mensagem
text = text.replace(
"<|início|>assistente<|mensagem|>",
"<|início|>assistente<|canal|>final<|mensagem|>"
)
fixed_texts.append(text)
return {"text": fixed_texts}
Conjuntos de dados = dataset.map(
fix_formatting,
batched = True,
desc = "Adicionando tags de canal"
)
print("✅ Formato corrigido")
print(f"n✅ Conjuntos de dados prontos: {len(dataset)} exemplos formatados")
retornar Conjuntos de dados
# Preparar o conjunto de dados
dataset = prepare_dataset(training_data)
Por que a tag de canal é importante:
- Função da tag de canal: A tag
<|channel|>finalinforma ao GPT-OSS que essa é a resposta final, não uma etapa intermediária de raciocínio. Isso faz parte do sistema de controle de esforço de raciocínio exclusivo do GPT-OSS. - Verificação de formato: Verificamos se a tag existe e a adicionamos se estiver faltando. Isso evita falhas de treinamento devido a incompatibilidades de formato.
- Correção automática: A operação de substituição garante a compatibilidade sem a necessidade de intervenção manual. Isso é especialmente importante ao usar diferentes versões de tokenizadores que podem ter comportamentos padrão diferentes.
Estatísticas e validação de conjuntos de dados
Por fim, vamos verificar nosso conjunto de dados preparado:
# Exibir estatísticas
print("nEstatísticas do conjunto de dados:")
print(f "Número de exemplos: {len(dataset)}")
print(f "Average text length: {sum(len(x['text']) for x in dataset) / len(dataset):.0f} chars")
# Mostrar um exemplo completo formatado
print("nExemplo formatado:")
print("="*60)
print(Conjuntos de dados[0]['texto'][:500])
print("="*60)
# Verificar se todos os exemplos têm o formato correto
format_checks = {
"has_user_tag": all("<|start|>user" in ex['text'] for ex in Conjuntos de dados),
"has_assistant_tag": all("<|start|>assistant" in ex['text'] for ex in Conjuntos de dados),
"has_channel_tag": all("<|channel|>" in ex['text'] for ex in Conjuntos de dados),
"has_message_tags": all("<|message|>" in ex['text'] for ex in Conjuntos de dados),
}
print("nValidação de formato:")
para check, passado em format_checks.items():
status = "✅" if passed else "❌"
print(f"{status} {check}: {passed}")
O que procurar na validação:
- Estatísticas de comprimento: O comprimento médio do texto ajuda você a definir o comprimento adequado da sequência para treinamento. Se for muito longo, talvez seja necessário truncar ou usar um max_seq_length maior.
- Completude do formato: Todas as quatro verificações devem ser aprovadas:
- As tags de usuário indicam onde começa a entrada do usuário
- As tags de assistente marcam as respostas do modelo
- As tags de canal especificam o tipo de resposta
- As tags de mensagem contêm o conteúdo real
- Inspeção visual: O exemplo impresso permite que você veja exatamente no que o modelo será treinado. Ele deve se parecer com:
<|início|>usuário<|mensagem|>Sua pergunta aqui<|end|>
<|início|>assistente<|canal|>final<|mensagem|>A resposta aqui<|fim|>
Se alguma validação falhar, o treinamento poderá não funcionar corretamente ou o modelo poderá aprender padrões incorretos. A correção automática deve resolver a maioria dos problemas, mas a inspeção manual ajuda a detectar casos extremos.
Configuração do treinamento com Unsloth e TRL
Agora vamos definir a configuração do treinamento. O Unsloth se integra perfeitamente à biblioteca TRL da Hugging Face, o que nos dá o melhor dos dois mundos: as otimizações de velocidade do Unsloth e os algoritmos de treinamento comprovados da TRL.
from trl import SFTConfig, SFTTrainer
from unsloth.chat_templates import train_on_responses_only
# Criar a configuração de treinamento
training_config = SFTConfig(
# Configurações básicas
per_device_train_batch_size = 2, # Ajuste com base na memória da GPU
gradient_accumulation_steps = 4, # Tamanho efetivo do lote = 2 * 4 = 8
warmup_steps = 5,
max_steps = 60, # Para testes rápidos; aumente para produção
# Configurações da taxa de aprendizado
learning_rate = 2e-4,
lr_scheduler_type = "linear",
# Configurações de otimização
optim = "adamw_8bit", # O otimizador de 8 bits economiza memória
weight_decay = 0,01,
# Registro e salvamento de logs
logging_steps = 1,
save_steps = 20,
output_dir = "outputs",
# Configurações avançadas
seed = 3407, # Para reprodutibilidade
fp16 = True, # Treinamento de precisão mista
report_to = "none", # Defina como "wandb" para rastreamento do experimento
)
print("Configuração de treinamento:")
print(f" Tamanho efetivo do lote: {training_config.per_device_train_batch_size * training_config.gradient_accumulation_steps}")
print(f" Total de etapas de treinamento: {training_config.max_steps}")
print(f" Taxa de aprendizado: {training_config.learning_rate}")
Configuração do instrutor
O SFTTrainer (Supervised Fine-Tuning Trainer) lida com toda a complexidade do treinamento:
# Inicializar o treinador
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = Conjuntos de dados,
args = training_config,
)
print("✅ Treinador inicializado")
# Configurar para treinar somente nas respostas do assistente
# Isso é crucial - não queremos que o modelo de aprendizado gere perguntas para o usuário
gpt_oss_kwargs = dict(
instruction_part = "<|start|>user<|message|>",
parte_da_resposta = "<|início|>assistente<|canal|>final<|mensagem|>"
)
trainer = train_on_responses_only(
trainer,
**gpt_oss_kwargs,
)
print("✅ Configurado para treinamento somente com resposta")
Entendendo a configuração do instrutor:
- Integração do SFTTrainer: O instrutor combina vários componentes:
- Seu modelo configurado para LoRA
- O tokenizador para processamento de texto
- Seu conjunto de dados preparado
- Parâmetros de configuração de treinamento
- Treinamento somente de resposta: Isso é fundamental para os modelos de bate-papo. Ao usar
train_on_responses_only, garantimos:- O modelo calcula a perda somente nas respostas do assistente
- Ele não aprende a gerar perguntas para o usuário
- O treinamento é mais eficiente (menos tokens para otimizar)
- O modelo mantém sua capacidade de entender diversas entradas do usuário
- Tags específicas do GPT-OSS: As partes de instrução e resposta devem corresponder exatamente ao que os dados formatados contêm. Essas tags informam ao instrutor onde dividir o que deve ser ignorado (entrada do usuário) e o que deve ser treinado (resposta do assistente).
Verificação da máscara de treinamento
É importante verificar se estamos treinando apenas com as respostas do assistente, e não com as perguntas do usuário:
# Verifique se a máscara de treinamento está correta
print("nVerificando a máscara de treinamento...")
sample = trainer.train_dataset[0]
# Decodifique os rótulos para ver em que estamos treinando
# -100 indica tokens que não estão sendo treinados (mascarados)
tokens_visíveis = []
for token_id, label_id in zip(sample["input_ids"], sample["labels"]):
if label_id != -100:
visible_tokens.append(token_id)
se visible_tokens:
decodificado = tokenizer.decode(visible_tokens)
print(f "Training on: {decoded[:200]}...")
print("✅ Máscara verificada - somente treinamento em respostas")
else:
print("⚠️ Warning: No visible training tokens detected")
O que a verificação da máscara diz a você:
- O rótulo -100: No PyTorch, -100 é um valor especial que diz à função de perda para ignorar esses tokens. É assim que implementamos o treinamento somente com resposta:
- Os tokens de entrada do usuário são rotulados como -100 (ignorados)
- Os tokens de resposta do assistente mantêm seus IDs de token reais (treinados)
- Verificação de tokens visíveis: Ao extrair apenas os tokens não mascarados, podemos ver exatamente com o que o modelo aprenderá. Você deve ver apenas o texto de resposta do assistente, não a pergunta do usuário.
- Por que isso é importante: Sem o mascaramento adequado:
- O modelo pode aprender a gerar perguntas do usuário em vez de respostas
- O treinamento seria menos eficiente (otimizando tokens desnecessários)
- O modelo poderia desenvolver comportamentos indesejados, como ecoar a entrada do usuário
- Dicas de depuração: Se você vir a entrada do usuário no texto decodificado, verifique:
- As strings
instruction_parteresponse_partsão exatamente iguais - A formatação dos Conjuntos de dados inclui todas as tags necessárias
- O tokenizador está aplicando o modelo de bate-papo corretamente
- As strings
Início do processo de treinamento
Com tudo configurado, estamos prontos para começar o treinamento. Vamos monitorar o uso da memória da GPU e acompanhar o progresso do treinamento:
import time
importar torch
# Limpar o cache da GPU antes do treinamento
torch.cuda.empty_cache()
# Registrar o estado inicial da GPU
start_gpu_memory = torch.cuda.max_memory_reserved() / 1024**3
start_time = time.time()
print("="*60)
print("COMEÇANDO O TREINAMENTO")
print("="*60)
print(f "Memória inicial da GPU reservada: {start_gpu_memory:.2f} GB")
print(f "Treinamento para {training_config.max_steps} etapas...")
print("nProgresso do treinamento:")
# Iniciar o treinamento
trainer_stats = trainer.train()
# Calcular as estatísticas do treinamento
training_time = time.time() - start_time
final_gpu_memory = torch.cuda.max_memory_reserved() / 1024**3
memory_used = final_gpu_memory - start_gpu_memory
print("n" + "="*60)
print("TREINAMENTO COMPLETO")
print("="*60)
print(f "Time taken: {training_time/60:.1f} minutes")
print(f "Perda final: {trainer_stats.metrics['train_loss']:.4f}")
print(f "Memória da GPU usada para treinamento: {memory_used:.2f} GB")
print(f "Pico de memória da GPU: {final_gpu_memory:.2f} GB")
print(f "Velocidade de treinamento: {trainer_stats.metrics.get('train_steps_per_second', 0):.2f} steps/second")
Entendendo as métricas de treinamento:
- Gerenciamento de memória da GPU:
- Limpar o cache antes do treinamento libera qualquer memória não utilizada
- O monitoramento do uso da memória ajuda a otimizar os tamanhos dos lotes para execuções futuras
- A diferença entre o início e o fim mostra a sobrecarga real de treinamento
- O pico de memória informa o quão próximo você está dos erros de OOM
- Indicadores de progresso do treinamento:
- Perda: deve diminuir com o tempo. Se atingir um patamar logo no início, sua taxa de aprendizado pode estar muito baixa
- Etapas/segundo: ajuda a estimar o tempo de treinamento para Conjuntos de dados maiores
- Tempo gasto: Em uma GPU T4, espere cerca de 10 a 15 minutos para 60 etapas
- O que observar durante o treinamento:
- Perda diminuindo constantemente (bom)
- Perda saltando de forma errática (taxa de aprendizado muito alta)
- A perda não muda (taxa de aprendizado muito baixa ou problemas de dados)
- Erros de memória (reduzir o tamanho do lote ou o comprimento da sequência)
- Expectativas de desempenho:
- GPU T4: 0,5-1,0 etapas/segundo
- V100: 1,5 a 2,5 etapas/segundo
- A100: 3-5 etapas/segundo
O treinamento deve ser concluído sem erros, e você deve ver a perda diminuir de cerca de 2-3 inicialmente para menos de 1,0 no final.
Testando seu modelo ajustado
Agora vem a parte interessante: testar se o ajuste fino realmente funcionou! Criaremos uma função de teste abrangente e avaliaremos o modelo em várias questões relacionadas ao Python:
from transformers import TextStreamer
def test_model(prompt: str, reasoning_effort: str = "medium", max_length: int = 256):
"""
Teste o modelo ajustado com um determinado prompt.
Args:
prompt: A pergunta ou instrução
reasoning_effort: "low" (baixo), "medium" (médio) ou "high" (alto)
max_length: Máximo de tokens a serem gerados
Retornos:
A resposta gerada
"""
# Criar o formato da mensagem
mensagens = [
{"role": "system" (sistema), "content" (conteúdo): "Você é um assistente especialista em Python"},
{"role": "user" (usuário), "content" (conteúdo): prompt}
]
# Aplicar modelo de bate-papo
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt = True,
return_tensors = "pt",
return_dict = True,
reasoning_effort = reasoning_effort,
).to("cuda")
# Configurar o streaming para saída em tempo real
streamer = TextStreamer(
tokenizer,
skip_prompt=True,
skip_special_tokens=True
)
# Gerar resposta
outputs = model.generate(
**inputs,
max_new_tokens = max_length,
streamer = streamer,
temperatura = 0,7,
top_p = 0,9,
do_sample = True,
)
# Decodificar e retornar a resposta
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
retornar resposta
# Teste sobre vários tópicos do Python
test_questions = [
"O que é um gerador Python e quando devo usá-lo?",
"Como faço para ler um arquivo CSV em Python?",
"Explique async/await em Python com um exemplo simples",
"Qual é a diferença entre uma lista e uma tupla em Python?",
"Como tratar exceções corretamente em Python?",
]
print("="*60)
print("TESTING FINE-TUNED MODEL")
print("="*60)
for i, question in enumerate(test_questions, 1):
print(f"n{'='*60}")
print(f "Question {i}: {question}")
print(f"{'='*60}")
print("Resposta:")
_ = test_model(question, reasoning_effort="medium")
print()

Você deve notar que o modelo agora fornece respostas mais detalhadas e específicas do Python em comparação com o ajuste fino anterior. As respostas devem refletir o estilo da documentação e a profundidade técnica dos seus dados de treinamento.
Teste de diferentes níveis de raciocínio
Vamos testar também como o esforço de raciocínio afeta as respostas:
complex_question = "Write a Python function that finds all prime numbers up to n using the Sieve of Eratosthenes" (Escreva uma função Python que encontre todos os números primos até n usando a peneira de Eratóstenes)
print("="*60)
print("TESTANDO OS NÍVEIS DE ESFORÇO DE RAZÃO")
print("="*60)
for effort in ["low", "medium", "high"]:
print(f"n{'='*40}")
print(f "Reasoning Effort: {effort.upper()}")
print(f"{'='*40}")
_ = test_model(complex_question, reasoning_effort=effort, max_length=300)
print()
Ao executar o código, você verá que “low” fornece uma implementação básica, “medium” fornece um bom equilíbrio entre explicação e código, enquanto “high” inclui explicações detalhadas e otimizações.
Salvando e implantando seu modelo
Após o ajuste fino bem-sucedido, você desejará salvar o modelo para uso futuro. Temos várias opções, dependendo de suas necessidades de implementação:
Salvando localmente
importar os
# Criar diretório para salvar
save_dir = "gpt-oss-python-expert"
os.makedirs(save_dir, exist_ok=True)
print("Salvando o modelo localmente...")
# Opção 1: salvar somente os adaptadores LoRA (pequeno, ~200 MB)
lora_save_dir = f"{save_dir}-lora"
model.save_pretrained(lora_save_dir)
tokenizer.save_pretrained(lora_save_dir)
print(f"✅ Adaptadores LoRA salvos em {lora_save_dir}")
# Verificar o tamanho
lora_size = sum(
os.path.getsize(os.path.join(lora_save_dir, f))
for f in os.listdir(lora_save_dir)
) / (1024**2)
print(f" Tamanho: {lora_size:.1f} MB")
# Opção 2: salvar o modelo mesclado (tamanho completo, ~20 GB)
merged_save_dir = f"{save_dir}-merged"
model.save_pretrained_merged(
merged_save_dir,
tokenizer,
save_method = "merged_16bit" # Opções: "merged_16bit", "mxfp4"
)
print(f"✅ Modelo mesclado salvo em {merged_save_dir}")
Enviando para o Hugging Face Hub
Para facilitar o compartilhamento e a implementação, envie seu modelo para o Hugging Face:
from huggingface_hub import login
# Faça login na Hugging Face (você precisará do seu token)
# Obtenha o token de: https://huggingface.co/settings/tokens
login(token="hf_...") # Substitua pelo seu token
# Empurre os adaptadores LoRA (recomendados para compartilhamento)
nome_do_modelo = "seu-nome-de-usuário/gpt-oss-python-expert-lora"
print(f "Pushing LoRA adapters to {model_name}...")
model.push_to_hub(
nome_do_modelo,
use_auth_token=True,
commit_message="GPT-OSS ajustado na documentação do Python"
)
tokenizer.push_to_hub(
nome_do_modelo,
use_auth_token=True
)
print(f"✅ Modelo disponível em: https://huggingface.co/{model_name}")
# Opcionalmente, envie o modelo mesclado (demora mais)
if False: # Defina como True se quiser enviar o modelo completo
merged_model_name = "seu-nome-de-usuário/gpt-oss-python-expert"
model.push_to_hub_merged(
merged_model_name,
tokenizer,
save_method = "mxfp4", # 4 bits para um tamanho menor
use_auth_token=True
)
Carregamento do modelo ajustado
Veja como carregar seu modelo posteriormente para inferência:
from unsloth import FastLanguageModel
# Carregar do diretório local
model, tokenizer = FastLanguageModel.from_pretrained(
nome_do_modelo = "gpt-oss-python-expert-lora",
max_seq_length = 1024,
dtype = None,
load_in_4bit = True,
)
# Ou carregar do Hugging Face Hub
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "your-username/gpt-oss-python-expert-lora",
max_seq_length = 1024,
dtype = None,
load_in_4bit = True,
)
print("✅ Modelo carregado e pronto para inferência!")
Estratégias de otimização para obter melhores resultados
Aqui estão algumas das estratégias que considero úteis para otimizar o ajuste fino do modelo:
Técnicas de otimização de memória
Ao trabalhar com memória limitada da GPU, essas técnicas podem fazer a diferença entre o sucesso e os erros de OOM:
# 1. Checkpointing de gradiente - troca computação por memória
model.gradient_checkpointing_enable()
# 2. Reduzir o comprimento da sequência se seus dados permitirem
max_seq_length = 512 # Em vez de 1024
# 3. Use tamanhos de lote menores com mais acúmulo
tamanho_do_treinamento_por_dispositivo = 1
gradient_accumulation_steps = 16 # Tamanho do lote ainda efetivo de 16
# 4. Habilite a atenção com eficiência de memória (se suportada)
model.config.use_flash_attention_2 = True
# 5. Limpar o cache regularmente durante o treinamento
importar gc
gc.collect()
torch.cuda.empty_cache()
Práticas recomendadas de treinamento
Por experiência própria, essas práticas levam a melhores resultados de ajuste fino:
- Comece pequeno: teste primeiro com 100 exemplos. Se isso funcionar, aumente a escala gradualmente.
- Monitore as métricas: Se a perda de treinamento cair, mas a perda de validação aumentar, pare antes.
- Misture seus dados: Combine dados específicos do domínio com dados de instruções gerais para evitar o esquecimento catastrófico.
- Programação da taxa de aprendizado: Comece com o padrão 2e-4, mas não tenha medo de fazer experiências. Tenho visto bons resultados com 5e-5 em Conjuntos de dados menores.
- Estratégia de ponto de controle: Salve a cada N etapas para que você possa se recuperar do melhor ponto de verificação:
training_config = SFTConfig(
save_steps = 50,
save_total_limit = 3, # Mantém apenas os 3 melhores pontos de controle
load_best_model_at_end = True,
metric_for_best_model = "loss",
)
Otimizações de velocidade
Para maximizar a velocidade de treinamento:
# Use a compilação do PyTorch 2.0 para um treinamento mais rápido
if hasattr(torch, 'compile'):
model = torch.compile(model)
print("✅ Modelo compilado para treinamento mais rápido")
# Habilite o TF32 nas GPUs Ampere (A100, RTX 30xx)
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
# Use tamanhos de lote maiores se a memória permitir
# Lotes maiores geralmente treinam mais rápido
optimal_batch_size = find_optimal_batch_size(model, max_memory=0.9)
Opções de implantação para produção
Depois que o modelo estiver bem ajustado, você terá várias opções de implementação:
API local rápida com FastAPI
Para uma prototipagem rápida, crie uma API simples:
# salvar como: api.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
importar uvicorn
de unsloth import FastLanguageModel
app = FastAPI()
# Carregar o modelo uma vez na inicialização
model, tokenizer = Nenhum, Nenhum
@app.on_event("startup")
async def load_model():
global model, tokenizer
model, tokenizer = FastLanguageModel.from_pretrained(
"gpt-oss-python-expert-lora",
max_seq_length = 1024,
load_in_4bit = True,
)
class GenerateRequest(BaseModel):
prompt: str
reasoning_effort: str = "medium" (esforço de raciocínio)
max_tokens: int = 256
@app.post("/generate")
async def generate(request: GenerateRequest):
if not model:
raise HTTPException(status_code=503, detail="Modelo não carregado")
messages = [{"role": "user", "content": request.prompt}]
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt = True,
return_tensors = "pt",
reasoning_effort = request.reasoning_effort,
).to("cuda")
outputs = model.generate(
**inputs,
max_new_tokens = request.max_tokens,
temperatura = 0,7,
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return {"response": response}
# Executar com: uvicorn api:app --host 0.0.0.0 --port 8000
Implantação de produção com vLLM
Para o serviço de produção de alto rendimento, o vLLM oferece excelente desempenho:
# Instale o vLLM
pip install vllm
# Sirva seu modelo
python -m vllm.entrypoints.openai.api_server
--modelo gpt-oss-python-expert-merged
--tensor-parallel-size 1
--max-model-len 1024
--dtype float16
Opções de implantação na nuvem
Cada plataforma de nuvem tem suas vantagens:
Pontos de extremidade de inferência de face abraçada
- Configuração mais fácil – basta enviar e implantar
- Excelente para testes e produção em pequena escala
- Escalonamento automático disponível
- Perfeito para implementação sem servidor
- Pague apenas pelo uso real
- Excelente para cargas de trabalho rápidas
- Mais econômico para servir 24 horas por dia, 7 dias por semana
- Controle total sobre o ambiente
- Ideal para aplicativos de alto rendimento
- De nível empresarial com integração total ao AWS
- Monitoramento e registro avançados
- Melhor para implementações de produção em larga escala
Solução de problemas comuns
Mesmo com as otimizações do Unsloth, você pode encontrar alguns problemas. Veja a seguir como resolver os mais comuns:
Erros de falta de memória CUDA
Esse é o problema mais comum ao fazer o ajuste fino de modelos grandes:
# Solução 1: Reduzir o tamanho do lote
training_config = SFTConfig(
per_device_train_batch_size = 1, # Tamanho mínimo do lote
gradient_accumulation_steps = 8, # Compensar com acumulação
)
# Solução 2: Reduzir o comprimento da sequência
max_seq_length = 512 # Em vez de 1024
# Solução 3: usar quantização mais agressiva
model = FastLanguageModel.from_pretrained(
nome_do_modelo = "unsloth/gpt-oss-20b",
load_in_4bit = True,
use_double_quant = True, # Ainda mais economia de memória
)
# Solução 4: habilitar todas as otimizações de memória
use_gradient_checkpointing = "unsloth"
use_flash_attention = True
Diminuir a velocidade de treinamento
Se o treinamento estiver demorando muito:
# Use o conjunto completo de otimização do Unsloth
model = FastLanguageModel.get_peft_model(
model,
use_gradient_checkpointing = "unsloth", # Crítico
lora_dropout = 0, # 0 é mais rápido do que dropout
bias = "none", # "none" é mais rápido do que os bias de treinamento
use_rslora = False, # LoRA padrão é mais rápido
)
# Verifique se você está usando o tipo de dados correto
torch.set_float32_matmul_precision('medium') # Ou 'high'
O modelo não está aprendendo
Se a sua perda não estiver diminuindo:
- Verifique o formato dos dados: Certifique-se de que seus dados correspondam exatamente ao formato GPT-OSS
- Verifique o mascaramento de respostas: Confirme se está treinando somente com respostas
- Ajuste a taxa de aprendizado: Tente 5e-4 ou 1e-4 em vez de 2e-4
- Aumente a qualidade dos dados: Remova exemplos de baixa qualidade
- Adicione mais dados: Mais de 500 exemplos normalmente funcionam melhor do que 100
Saídas inconsistentes
Se o modelo gerar resultados inconsistentes ou de baixa qualidade:
# Use uma temperatura mais baixa para obter resultados mais consistentes
outputs = model.generate(
temperature = 0.3, # Menor = mais consistente
top_p = 0,9,
repetition_penalty = 1.1, # Reduzir a repetição
)
# Ajuste fino para mais etapas
max_steps = 200 # Em vez de 60
# Use filtragem de dados de maior qualidade
min_response_length = 50 # Em vez de 30
Conclusão
Conclusão
O ajuste fino do GPT-OSS é mais rápido e fácil quando você combina a velocidade do Unsloth com dados de treinamento estruturados e de alta qualidade fornecidos por uma das principais empresas de dados de treinamento de IA. O uso das soluções da Bright Data para IA garante que você tenha acesso aos dados confiáveis necessários para um ajuste fino eficaz, para que possa criar modelos de IA personalizados para qualquer caso de uso.
Para explorar mais as estratégias de extração de dados orientadas por IA, considere estes recursos adicionais: