O mais recente modelo de IA de peso aberto do Google, o Gemma 3, lançado em março de 2025, oferece um desempenho impressionante que se equipara ao de muitos LLMs proprietários, ao mesmo tempo em que é executado com eficiência em hardware com recursos limitados. Esse avanço na IA de código aberto funciona em várias plataformas, oferecendo aos desenvolvedores de todo o mundo recursos avançados em um formato acessível.
Neste guia, vamos orientá-lo no ajuste fino do Gemma 3 em um conjunto de dados personalizado de respostas a perguntas derivado de avaliações da Trustpilot. Usaremos o Bright Data para extrair as avaliações dos clientes, processá-las em pares estruturados de QA e aproveitar o Unsloth para fazer um ajuste fino eficiente com o mínimo de computação. Ao final, você terá criado um assistente de IA especializado que entende perguntas específicas de um domínio e está pronto para ser hospedado no Hugging Face Hub.
Vamos mergulhar de cabeça!
Entendendo Gemma 3
A família Gemma 3 do Google foi lançada em março de 2025 com quatro tamanhos de peso aberto – parâmetros 1B, 4B, 12B e 27B – todos projetados para serem executados em uma única GPU.
- O modelo 1B é somente de texto com uma janela de contexto de 32K tokens.
- Os modelos 4B, 12B e 27B adicionam entrada multimodal (texto + imagem) e suportam uma janela de 128K tokens.
Na tabela de classificação de preferência humana da LMArena, o Gemma 3-27B-IT pontua à frente de modelos muito maiores, como o Llama 3 405B e o DeepSeek-V3, oferecendo qualidade de última geração sem exigir uma área ocupada por várias GPUs.
Fonte da imagem: Apresentando Gemma 3
Principais recursos dos modelos Gemma 3
Aqui estão alguns recursos notáveis dos modelos Gemma 3:
- A entrada multimodal (texto + imagem) está disponível nos modelos 4B, 12B e 27B.
- Contexto longo –até 128 mil tokens (32 mil no modelo 1B).
- Recursos multilíngues – mais de 35 idiomas suportados prontos para uso; mais de 140 idiomas pré-treinados.
- Quantization-Aware Training – As versões oficiais do QAT reduzem significativamente o uso da memória (em aproximadamente três vezes), mantendo a alta qualidade.
- Chamada de função e saída estruturada – Inclui suporte integrado para automatizar chamadas e receber respostas estruturadas.
- Eficiência – Projetado para ser executado em uma única GPU/TPU ou mesmo em dispositivos de consumo, desde telefones e laptops até estações de trabalho.
- Segurança (ShieldGemma) – Apresenta uma estrutura integrada de filtragem de conteúdo.
Por que fazer o ajuste fino do Gemma 3?
O ajuste fino utiliza um modelo pré-treinado como o Gemma 3 e ensina a ele novos comportamentos para o seu domínio ou tarefa específica, sem o tempo e o custo de um treinamento do zero. Com seu design compacto e, nas variantes 4B+, suporte multimodal, o Gemma 3 é leve, econômico e pode ser ajustado mesmo em hardware com recursos limitados.
Os benefícios do ajuste fino incluem:
- Especialização em domínio – Ajuda o modelo a entender a linguagem específica do setor e a ter um melhor desempenho em tarefas especializadas dentro do seu domínio.
- Aprimoramento do conhecimento – Adiciona fatos e contextos importantes que não faziam parte dos dados de treinamento originais do modelo.
- Refinamento de comportamento – Ajusta a forma como o modelo responde, para que ele corresponda ao tom de sua marca ou ao formato de saída preferido.
- Otimização de recursos – Obtém resultados de alta qualidade usando significativamente menos recursos de computação em comparação com o treinamento de um novo modelo do zero.
Pré-requisitos
Antes de começar este tutorial, verifique se você tem o seguinte:
- O Python 3.9 ou superior está instalado em seu sistema.
- Conhecimento básico de programação em Python.
- Acesso a um ambiente de computação com suporte a GPU (por exemplo, Google Colab, Jupyter Notebook ou Kaggle Notebooks).
- Compreensão dos fundamentos de aprendizado de máquina e modelo de linguagem grande (LLM).
- Experiência no uso de um IDE, como o VS Code ou outro similar.
Você também precisará de credenciais de acesso para serviços externos:
- Uma conta Hugging Face e um token com acesso de gravação.
Crie um token aqui - Uma conta da Bright Data e um token de API.
Registre-se e siga as instruções para gerar um token. - Uma conta OpenAI e uma chave de API.
Obtenha sua chave de API aqui
Certifique-se de que sua conta da OpenAI tenha crédito suficiente. Gerencie o faturamento aqui e acompanhe o uso aqui.
Criação de um conjunto de dados personalizado para ajuste fino
O ajuste fino funciona melhor quando seu conjunto de dados reflete de perto o comportamento que você deseja que o modelo aprenda. Ao criar um conjunto de dados personalizado e adaptado ao seu caso de uso específico, você pode melhorar drasticamente o desempenho do modelo. Lembre-se da regra clássica: “lixo dentro, lixo fora”. É por isso que investir tempo na preparação do conjunto de dados é tão importante.
Um conjunto de dados de alta qualidade deve:
- Corresponder ao seu caso de uso específico – Quanto mais o conjunto de dados estiver alinhado com o aplicativo de destino, mais relevantes serão os resultados do modelo.
- Mantenha uma formatação consistente – Uma estrutura uniforme (como pares de perguntas e respostas) ajuda o modelo a aprender padrões com mais eficiência.
- Inclua exemplos diversos – Uma variedade de cenários ajuda o modelo a se generalizar entre diferentes entradas.
- Seja limpo e livre de erros – A remoção de inconsistências e ruídos evita que o modelo capte comportamentos indesejados.
Começaremos com avaliações brutas como esta:
E transforme-os em pares estruturados de perguntas e respostas como este:
Esse conjunto de dados ensinará o Gemma 3 a extrair insights do feedback do cliente, identificar padrões de sentimento e fornecer recomendações acionáveis.
Etapas de configuração
#1 Instalar bibliotecas: Abra o ambiente do projeto e instale todas as bibliotecas Python necessárias listadas no arquivo requirements.txt. Você pode fazer isso executando o seguinte comando em seu terminal ou notebook:
pip install -r requirements.txt
#2 Configure as variáveis de ambiente: Crie um arquivo .env
no diretório raiz do seu projeto e armazene com segurança suas chaves de API.
OPENAI_API_KEY="your_openai_key_here"
HF_TOKEN="your_hugging_face_token_here"
Etapa 1: Coleta de dados com a Bright Data
A primeira etapa crucial é o fornecimento de dados. Para criar nosso conjunto de dados de ajuste fino, coletaremos dados brutos de avaliação da Trustpilot. Devido às robustas medidas anti-bot da Trustpilot, usaremos a API Trustpilot Scraper da Bright Data. Essa API gerencia com eficiência a rotação de IPs, a resolução de CAPTCHA e o tratamento de conteúdo dinâmico, permitindo a coleta eficiente de avaliações estruturadas em escala, ignorando as complexidades da criação de sua solução de raspagem.
Aqui está um script Python que mostra como usar a API da Bright Data para coletar avaliações, passo a passo:
import time
import json
import requests
from typing import Optional
# --- Configuration ---
API_KEY = "YOUR_API_KEY" # Replace with your Bright Data API key
DATASET_ID = "gd_lm5zmhwd2sni130p" # Replace with your Dataset ID
TARGET_URL = "https://www.trustpilot.com/review/hubspot.com" # Target company page
OUTPUT_FILE = "trustpilot_reviews.json" # Output file name
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
TIMEOUT = 30 # Request timeout in seconds
# --- Functions ---
def trigger_snapshot() -> Optional[str]:
"""Triggers a Bright Data snapshot collection job."""
print(f"Triggering snapshot for: {TARGET_URL}")
try:
resp = requests.post(
"https://api.brightdata.com/datasets/v3/trigger",
headers=HEADERS,
params={"dataset_id": DATASET_ID},
json=[{"url": TARGET_URL}],
timeout=TIMEOUT,
)
resp.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
snapshot_id = resp.json().get("snapshot_id")
print(f"Snapshot triggered successfully. ID: {snapshot_id}")
return snapshot_id
except requests.RequestException as e:
print(f"Error triggering snapshot: {e}")
except json.JSONDecodeError:
print(f"Error decoding trigger response: {resp.text}")
return None
def wait_for_snapshot(snapshot_id: str) -> Optional[list]:
"""Polls the API until snapshot data is ready and returns it."""
check_url = f"https://api.brightdata.com/datasets/v3/snapshot/{snapshot_id}"
print(f"Waiting for snapshot {snapshot_id} to complete...")
while True:
try:
resp = requests.get(
check_url,
headers=HEADERS,
params={"format": "json"},
timeout=TIMEOUT,
)
resp.raise_for_status()
# Check if response is the final data (list) or status info (dict)
if isinstance(resp.json(), list):
print("Snapshot data is ready.")
return resp.json()
else:
pass
except requests.RequestException as e:
print(f"Error checking snapshot status: {e}")
return None # Stop polling on error
except json.JSONDecodeError:
print(f"Error decoding snapshot status response: {resp.text}")
return None # Stop polling on error
print("Data not ready yet. Waiting 30 seconds...")
time.sleep(30)
def save_reviews(reviews: list, output_file: str) -> bool:
"""Saves the collected reviews list to a JSON file."""
try:
with open(output_file, "w", encoding="utf-8") as f:
json.dump(reviews, f, indent=2, ensure_ascii=False)
print(f"Successfully saved {len(reviews)} reviews to {output_file}")
return True
except (IOError, TypeError) as e:
print(f"Error saving reviews to file: {e}")
return False
except Exception as e:
print(f"An unexpected error occurred during saving: {e}")
return False
def main():
"""Main execution flow for collecting Trustpilot reviews."""
print("Starting Trustpilot review collection process...")
snapshot_id = trigger_snapshot()
if not snapshot_id:
print("Failed to trigger snapshot. Exiting.")
return
reviews = wait_for_snapshot(snapshot_id)
if not reviews:
print("Failed to retrieve reviews from snapshot. Exiting.")
return
if not save_reviews(reviews, OUTPUT_FILE):
print("Failed to save the collected reviews.")
else:
print("Review collection process completed.")
if __name__ == "__main__":
main()
Esse script executa as seguintes etapas:
- Autenticação: Ele usa sua
API_KEY
para autenticar com a API da Bright Data por meio do cabeçalho deautorização
. - Acionar coleta: Ele envia uma solicitação POST para disparar um “instantâneo” de coleta de dados para o
TARGET_URL
especificado (a página Trustpilot da HubSpot nesse caso), associado ao seuDATASET_ID
. - Aguardar a conclusão: Ele pesquisa periodicamente a API usando o
snapshot_id
retornado para verificar se a coleta de dados foi concluída. - Recuperar dados: Quando a API indica que os dados estão prontos, o script obtém os dados de revisão no formato JSON.
- Salvar saída: Ele salva a lista coletada de objetos de avaliação em um arquivo JSON estruturado
(trustpilot_reviews.json
).
Cada revisão no arquivo JSON resultante fornece informações detalhadas, como:
{
"review_id": "680af52fb0bab688237f75c5",
"review_date": "2025-04-25T04:36:31.000Z",
"review_rating": 1,
"review_title": "Cancel Auto Renewal Doesn't Work",
"review_content": "I was with Hubspot for almost 3 years...",
"reviewer_name": "Steven Barrett",
"reviewer_location": "AU",
"is_verified_review": false,
"review_date_of_experience": "2025-04-19T00:00:00.000Z",
// Additional fields omitted for brevity
}
Saiba como encontrar os melhores dados para treinamento em LLM com nosso guia: Principais fontes de dados de treinamento em LLM.
Etapa 2: Conversão de JSON em Markdown
Depois de coletar os dados brutos da revisão, a próxima etapa é convertê-los em um formato limpo e legível, adequado para processamento. Usaremos o Markdown, que oferece uma estrutura de texto simples e leve que reduz o ruído durante a tokenização, melhora potencialmente o desempenho do ajuste fino e garante uma separação consistente entre diferentes seções de conteúdo.
Para realizar a conversão, basta executar este script 👉 convert-trustpilot-json-to-markdown.py
Esse script lê os dados JSON da saída da Etapa 1 e gera um arquivo Markdown contendo um resumo estruturado e avaliações individuais dos clientes.
Aqui está um exemplo da estrutura de saída do Markdown:
# HubSpot Review Summary
[Visit Website](https://www.hubspot.com/)
**Overall Rating**: 2.3
**Total Reviews**: 873
**Location**: United States
**Industry**: Electronics & Technology
> HubSpot is a leading growth platform... Grow Better.
---
### Review by Steven Barrett (AU)
- **Posted on**: April 25, 2025
- **Experience Date**: April 19, 2025
- **Rating**: 1
- **Title**: *Cancel Auto Renewal Doesn't Work*
I was with Hubspot for almost 3 years... Avoid.
[View Full Review](https://www.trustpilot.com/reviews/680af52fb0bab688237f75c5)
---
Saiba por que os agentes de IA preferem Markdown a HTML lendo mais em nosso guia.
Etapa 3: fragmentação e processamento do documento
Com o documento Markdown pronto, a próxima etapa crucial é dividi-lo em partes menores e gerenciáveis. Isso é importante porque os modelos de linguagem grande (LLMs) têm limites de tokens de entrada, e o ajuste fino geralmente funciona melhor com exemplos de tamanho adequado. Além disso, o processamento desses pedaços pode melhorar sua clareza e coerência para o modelo.
Usamos o RecursiveCharacterTextSplitter
da LangChain para dividir o arquivo Markdown. Esse método divide recursivamente o texto com base em uma lista de separadores, o que ajuda a manter juntos os pedaços de texto relacionados. Para preservar o contexto que pode se estender pelos pontos de divisão, aplicamos uma sobreposição entre pedaços consecutivos. Para esse processo, usamos um tamanho de bloco de 1.024 caracteres com uma sobreposição de 256 caracteres.
Após a divisão, cada bloco é opcionalmente passado para um LLM (como o GPT-4o) para melhorar sua clareza e coerência gerais, mantendo estritamente o significado original do texto de revisão. Essa etapa de aprimoramento tem como objetivo tornar a estrutura de dados e o conteúdo de cada bloco perfeitamente claros para o processo de ajuste fino subsequente.
Em seguida, cada bloco processado recebe um identificador exclusivo e é armazenado em um formato de arquivo JSON Lines (.jsonl
), preparando-o para o próximo estágio do pipeline.
Aqui está a função Python usando o LLM para melhorar a clareza:
def improve_review_chunk(text: str, client: OpenAI, model: str = "gpt-4o") -> str:
prompt = """Improve this review's clarity while preserving its meaning:
{text}
Return only the improved text without additional commentary."""
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": prompt},
{"role": "user", "content": text}
]
)
return response.choices[0].message.content
Encontre o código completo para essa etapa aqui 👉 split-markdown-into-chunks.py
O resultado é um arquivo JSON Lines em que cada linha representa um trecho de revisão com um identificador exclusivo e o conteúdo da revisão potencialmente aprimorado:
[
{
"id": "f8a3b1c9-e4d5-4f6a-8b7c-2d9e0a1b3c4d", // Unique chunk ID
"review": "# HubSpot Review Summary\\n\\n[Visit Website](https://www.hubspot.com/)...\\n---\\n\\n### Review by Steven Barrett (AU)\\n- **Posted on**: April 25, 2024...\\n- **Rating**: 1\\n- **Title**: *Cancel Auto Renewal Doesn't Work*\\n\\nI was with Hubspot for almost 3 years... [Text continues - may be improved]" // Chunk content (potentially refined)
},
// ... more chunk objects
]
Etapa 4: geração de pares de controle de qualidade
A etapa final de preparação de dados transforma os blocos de revisão processados em pares estruturados de perguntas e respostas (QA) adequados para o ajuste fino de um modelo de linguagem. Usamos o GPT-4o da OpenAI para gerar um par de QA para cada bloco no arquivo .jsonl
criado na Etapa 3.
Para cada bloco, o script chama a API da OpenAI usando um prompt de sistema cuidadosamente projetado:
SYSTEM_PROMPT = """
You are an expert at transforming customer reviews into insightful question–answer pairs. For each review, generate exactly 1 high-quality QA pair.
PURPOSE:
These QA pairs will train a customer service AI to understand feedback patterns about HubSpot products and identify actionable insights.
GUIDELINES FOR QUESTIONS:
- Make questions general and applicable to similar situations
- Phrase from a stakeholder perspective (e.g., "What feature gaps are causing customer frustration?")
- Focus on product features, usability, pricing, or service impact
GUIDELINES FOR ANSWERS:
- Provide analytical responses (3–5 sentences)
- Extract insights without quoting verbatim
- Offer actionable recommendations
- Maintain objectivity and clarity
FORMAT REQUIREMENTS:
- Start with "Q: " followed by your question
- Then "A: " followed by a plain-text answer
"""
O script inclui mecanismos integrados de limitação de taxa e repetição para lidar com interrupções temporárias da API e garantir uma execução estável. Você pode encontrar a implementação completa em generate-qa-pairs.py.
A saída é salva como uma matriz JSON, em que cada objeto contém um par de perguntas e respostas geradas, vinculadas pelo ID do bloco original:
[
{
"id": "82d53a10-9f37-4d03-8d3b-38812e39ecdc",
"question": "How can pricing and customer support issues impact customer satisfaction and retention for HubSpot?",
"answer": "Pricing concerns, particularly when customers feel they are overpaying for services they find unusable or unsupported, can significantly impact customer satisfaction and retention..."
}
// ... more QA pairs
]
Depois de gerado, é altamente recomendável enviar o conjunto de dados de controle de qualidade resultante para o Hugging Face Hub. Isso o torna facilmente acessível para ajuste fino e compartilhamento. Você pode ver um exemplo do conjunto de dados publicado aqui: trustpilot-reviews-qa-dataset.
Ajuste fino da Gemma 3 com Unsloth: Passo a passo
Agora que temos nosso conjunto de dados personalizado de perguntas e respostas preparado, vamos ajustar o modelo Gemma 3. Usaremos o Unsloth, uma biblioteca de código aberto que oferece melhorias significativas de memória e velocidade para o treinamento LoRA/QLoRA em comparação com as implementações padrão do Hugging Face. Essas otimizações tornam os modelos de ajuste fino como o Gemma 3 mais acessíveis em configurações de GPU única, desde que a GPU tenha VRAM suficiente.
Gemma 3 Tamanho | VRAM aproximada necessária* | Plataformas adequadas |
---|---|---|
4B | ~15 GB | Google Colab gratuito (T4), Kaggle (P100 16 GB) |
12B | ≥24 GB | Colab Pro+ (A100/A10), RTX 4090, A40 |
27B | 22-24 GB (com QLoRA de 4 bits, tamanho do lote = 1); caso contrário, ~40 GB | A100 40 GB, H100, Configurações de várias GPUs |
Observação: os requisitos de VRAM podem variar de acordo com o tamanho do lote, o comprimento da sequência e as técnicas específicas de quantização. O requisito para o modelo 27B é com QLoRA de 4 bits e um tamanho de lote pequeno (por exemplo, 1 ou 2); tamanhos de lote maiores ou quantização menos agressiva exigirão muito mais VRAM (~40 GB+).
Para iniciantes, recomenda-se começar com o modelo 4B em um notebook Colab gratuito, pois ele suporta confortavelmente o carregamento, o treinamento e a implementação com o Unsloth. A atualização para os modelos 12B ou 27B só deve ser considerada quando houver acesso a GPUs com maior VRAM ou níveis de nuvem pagos.
Para alterar o tipo de tempo de execução no Google Colab e selecionar uma GPU T4, siga estas etapas:
- Clique no menu Runtime na parte superior.
- Selecione Alterar tipo de tempo de execução.
- Na caixa de diálogo que aparece, em Acelerador de hardware, escolha GPU.
- Clique em Save (Salvar ) para aplicar as alterações.
Etapa 1: Configuração do ambiente
Primeiro, instale as bibliotecas necessárias. Se você estiver em um ambiente Colab ou Jupyter, poderá executar esses comandos diretamente em uma célula de código.
%%capture
!pip install --no-deps unsloth vllm
import sys, re, requests; modules = list(sys.modules.keys())
for x in modules: sys.modules.pop(x) if "PIL" in x or "google" in x else None
!pip install --no-deps bitsandbytes accelerate xformers==0.0.29.post3 peft "trl==0.15.2" triton cut_cross_entropy unsloth_zoo
!pip install sentencepiece protobuf datasets huggingface_hub hf_transfer
# vLLM requirements - vLLM breaks Colab due to reinstalling numpy
f = requests.get("https://raw.githubusercontent.com/vllm-project/vllm/refs/heads/main/requirements/common.txt").content
with open("vllm_requirements.txt", "wb") as file:
file.write(re.sub(rb"(transformers|numpy|xformers)[^\n]{1,}\n", b"", f))
!pip install -r vllm_requirements.txt
Aqui está uma breve explicação dos principais pacotes instalados:
unsloth
: Fornece as principais otimizações para treinamento e carregamento LLM mais rápidos e eficientes em termos de memória usando técnicas como kernels fundidos.peft
: Métodos de ajuste fino com eficiência de parâmetros (como o LoRA). Permite treinar apenas um pequeno número de parâmetros adicionais em vez do modelo completo.trl
: Transformer Reinforcement Learning (Aprendizado por reforço do transformador). Inclui oSFTTrainer
, que simplifica o processo de ajuste fino supervisionado.bitsandbytes
: Permite a quantização de k bits (4 bits e 8 bits), reduzindo drasticamente o espaço de memória do modelo.acelerar
: Biblioteca Hugging Face para executar perfeitamente o treinamento do PyTorch em várias configurações de hardware (GPU única, multi-GPU, etc.).conjuntos de dados
: Biblioteca Hugging Face para carregar, processar e gerenciar conjuntos de dados com eficiência.transformadores
: a biblioteca principal da Hugging Face para modelos pré-treinados, tokenizadores e utilitários.huggingface_hub
: Utilitários para interagir com o Hugging Face Hub (login, download, upload).vllm
(opcional): Um mecanismo de inferência LLM rápido. Pode ser instalado separadamente, se necessário para a implementação.
Etapa 2: Autenticação de rosto de abraço
Você precisará fazer login no Hugging Face Hub a partir do seu ambiente para fazer download do modelo e, possivelmente, carregar o resultado ajustado posteriormente.
import os
from huggingface_hub import login
from google.colab import userdata
hf_token = userdata.get('HF_TOKEN')
if not hf_token:
raise ValueError("Please set your HF_TOKEN environment variable before running.")
try:
login(hf_token)
print("Successfully logged in to Hugging Face Hub.")
except Exception as e:
print(f"Error logging in to Hugging Face Hub: {e}")
No Google Colab, a maneira mais segura de gerenciar o token do Hugging Face é usar a guia “Secrets” (Segredos):
Etapa 3: Carregando o modelo e o tokenizador
Para começar o ajuste fino, carregaremos com eficiência o modelo Gemma 3 ajustado por instruções usando o FastModel
do Unsloth. Para este exemplo, usaremos o modelo unsloth/gemma-3-4b-it
, que é uma versão quantizada de 4 bits otimizada pelo Unsloth para se ajustar às restrições de memória das GPUs Colab típicas.
Confira a coleção Gemma 3 da Unsloth na Hugging Face. Ela inclui modelos nos tamanhos 1B, 4B, 12B e 27B, disponíveis nos formatos GGUF, 4 bits e 16 bits.
from unsloth import FastModel
from unsloth.chat_templates import get_chat_template
import torch # Import torch for checking CUDA
# Ensure CUDA is available
if not torch.cuda.is_available():
raise RuntimeError("CUDA is not available. A GPU is required for this tutorial.")
print(f"CUDA available: {torch.cuda.is_available()}")
print(f"CUDA device name: {torch.cuda.get_device_name(0)}")
model, tokenizer = FastModel.from_pretrained(
model_name="unsloth/gemma-3-4b-it", # Using the 4B instruction-tuned model optimized by Unsloth
max_seq_length=2048, # Set max context length
load_in_4bit=True, # Enable 4-bit quantization
full_finetuning=False, # Use PEFT (LoRA)
token=hf_token, # Pass your Hugging Face token
)
# Apply the correct chat template for Gemma 3
tokenizer = get_chat_template(tokenizer, chat_template="gemma-3")
print("Model and Tokenizer loaded successfully.")
O que está acontecendo neste código:
FastModel.from_pretrained()
: O carregador de modelos otimizado do Unsloth.model_name="unsloth/gemma-3-4b-it"
: Especifica a variante do modelo a ser carregada. Escolhemos a versão 4B com ajuste de instruções(it
), pré-otimizada pelo Unsloth.max_seq_length=2048
: define o número máximo de tokens que o modelo pode processar de uma só vez. Ajuste isso com base no comprimento de seus blocos de dados e na janela de contexto desejada, equilibrando o uso da memória e a capacidade de processar entradas mais longas.load_in_4bit=True
: essencial para treinamento em VRAM limitada. Isso carrega os pesos do modelo em precisão de 4 bits usandobitsandbytes
.full_finetuning=False
: Diz ao Unsloth para preparar o modelo para o ajuste fino PEFT/LoRA, o que significa que somente as camadas do adaptador serão treinadas, e não todos os parâmetros do modelo.get_chat_template(tokenizer, chat_template="gemma-3")
: Envolve o tokenizador para formatar automaticamente os prompts no formato de bate-papo esperado do Gemma 3(<start_of_turn>user\n...\n<end_of_turn><start_of_turn>model\n...\n<end_of_turn>
). Isso é fundamental para ajustar corretamente os modelos de acompanhamento de instruções e garantir que o modelo aprenda a gerar respostas nos turnos de conversação esperados.
Etapa 4: carregamento e preparação do conjunto de dados para treinamento
Carregamos o conjunto de dados que carregamos anteriormente no Hugging Face Hub e, em seguida, o transformamos no formato baseado em bate-papo esperado pelo tokenizador e pelo treinador.
from datasets import load_dataset
from unsloth.chat_templates import standardize_data_formats, train_on_responses_only # train_on_responses_only imported earlier
# 1. Load the dataset from Hugging Face Hub
dataset_name = "triposatt/trustpilot-reviews-qa-dataset" # Replace with your dataset name
dataset = load_dataset(dataset_name, split="train")
print(f"Dataset '{dataset_name}' loaded.")
print(dataset)
# 2. Normalize any odd formats (ensure 'question' and 'answer' fields exist)
dataset = standardize_data_formats(dataset)
print("Dataset standardized.")
# 3. Define a function to format examples into chat template
def formatting_prompts_func(examples):
"""Formats question-answer pairs into Gemma 3 chat template."""
questions = examples["question"]
answers = examples["answer"]
texts = []
for q, a in zip(questions, answers):
# Structure the conversation as a list of roles and content
conv = [
{"role": "user", "content": q},
{"role": "assistant", "content": a},
]
# Apply the chat template
txt = tokenizer.apply_chat_template(
conv,
tokenize=False, # Return string, not token IDs
add_generation_prompt=False # Don't add the model's start tag at the end yet
)
# Gemma 3 tokenizer adds <bos> by default, which the trainer will re-add
# We remove it here to avoid double <bos> tokens
txt = txt.removeprefix(tokenizer.bos_token)
texts.append(txt)
return {"text": texts}
# 4. Apply the formatting function to the dataset
dataset = dataset.map(formatting_prompts_func, batched=True, remove_columns=["question", "answer"])
print("Dataset formatted with chat template.")
print(dataset) # Inspect the new 'text' column
Neste código:
load_dataset()
: Obtém nosso conjunto de dados de Q&A do Hugging Face Hub.standardize_data_formats()
: Garante a consistência dos nomes dos campos em diferentes conjuntos de dados, procurando especificamente por “question” e “answer” nesse caso.formatting_prompts_func()
: Essa função essencial processa lotes de nossos pares de perguntas e respostas. Ela usa o métodotokenizer.apply_chat_template()
para convertê-los em cadeias de caracteres formatadas corretamente para o ajuste fino das instruções do Gemma 3. Esse formato inclui tokens de turno especiais como<start_of_turn>user\n
e<start_of_turn>model\n
, que são essenciais para o modelo entender a estrutura da conversa. Removemos o token<bos>
inicial, pois oSFTTrainer
adiciona o seu próprio token.dataset.map(...)
: Aplica oformatting_prompts_func
a todo o conjunto de dados de forma eficiente, criando uma nova coluna ‘text’ que contém as cadeias de caracteres formatadas e removendo as colunas originais.
Etapa 5: Configuração do LoRA e do instrutor
Agora, definimos as configurações do PEFT (LoRA) e o SFTTrainer
da biblioteca trl
. O LoRA funciona injetando matrizes pequenas e treináveis nas camadas principais do modelo pré-treinado. Somente essas pequenas matrizes adaptadoras são atualizadas durante o ajuste fino, reduzindo drasticamente o número de parâmetros a serem treinados e, portanto, minimizando o uso da memória.
from trl import SFTTrainer, SFTConfig
import torch
# 1. Configure LoRA
model = FastModel.get_peft_model(
model,
r=8, # LoRA rank (a common value) - lower rank means fewer parameters, higher means more expressive
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj", # Attention layers
"gate_proj", "up_proj", "down_proj" # MLP layers
],
# Set True if you want to fine-tune language layers (recommended for text tasks)
# and Attention/MLP modules (where LoRA is applied)
finetune_language_layers=True,
finetune_attention_modules=True,
finetune_mlp_modules=True,
# finetune_vision_layers=False, # Only relevant for multimodal models (12B/27B)
lora_alpha=8, # LoRA scaling factor (often set equal to r)
lora_dropout=0, # Dropout for LoRA layers
bias="none", # Don't train bias terms
use_gradient_checkpointing="unsloth", # Memory optimization
random_state=1000, # Seed for reproducibility
use_rslora=False, # Rank-Stabilized LoRA (optional alternative)
# modules_to_save=["embed_tokens", "lm_head"], # Optional: train embedding/output layers
)
print("Model configured for PEFT (LoRA).")
# 2. Configure the SFTTrainer
# Determine a reasonable max_steps based on dataset size and epochs
# For demonstration, a small number of steps is used (e.g., 30)
# For a real use case, calculate steps = (dataset_size / batch_size / grad_accum) * num_epochs
dataset_size = len(dataset)
per_device_train_batch_size = 2 # Adjust based on your GPU VRAM
gradient_accumulation_steps = 4 # Accumulate gradients to simulate larger batch size (batch_size * grad_accum = 8)
num_train_epochs = 3 # Example: 3 epochs
# Calculate total training steps
total_steps = int((dataset_size / per_device_train_batch_size / gradient_accumulation_steps) * num_train_epochs)
# Ensure max_steps is not 0 if dataset is small or calculation results in < 1 step
max_steps = max(30, total_steps) # Set a minimum or calculate properly
print(f"Calculated total training steps for {num_train_epochs} epochs: {total_steps}. Using max_steps={max_steps}")
sft_config = SFTConfig(
dataset_text_field="text", # The column in our dataset containing the formatted chat text
per_device_train_batch_size=per_device_train_batch_size,
gradient_accumulation_steps=gradient_accumulation_steps,
warmup_steps=max(5, int(max_steps * 0.03)), # Warmup for first few steps (e.g., 3% of total steps)
max_steps=max_steps, # Total number of training steps
learning_rate=2e-4, # Learning rate
logging_steps=max(1, int(max_steps * 0.01)), # Log every 1% of total steps (min 1)
optim="adamw_8bit", # 8-bit AdamW optimizer (memory efficient)
weight_decay=0.01, # L2 regularization
lr_scheduler_type="linear", # Linear learning rate decay
seed=3407, # Random seed
report_to="none", # Disable reporting to platforms like W&B unless needed
output_dir="./results", # Directory to save checkpoints and logs
)
# 3. Build the SFTTrainer instance
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
eval_dataset=None, # Optional: provide a validation dataset
args=sft_config,
)
print("SFTTrainer built.")
# 4. Mask out the input portion for training
# This teaches the model to only generate the assistant’s response
# It prevents the model from just copying the user’s prompt
# Pass the literal prefixes for instruction and response turns from the chat template
trainer = train_on_responses_only(
trainer,
instruction_part="<start_of_turn>user\n", # Literal string before user content
response_part="<start_of_turn>model\n", # Literal string before model content
)
print("Trainer configured to train only on responses.")
Neste código:
FastModel.get_peft_model()
: Configura o modelo carregado para o ajuste fino do LoRA com os parâmetros especificados.r
é a classificação do LoRA, que controla o tamanho das matrizes do adaptador.target_modules
especifica quais camadas do modelo (como atenção e projeções do MLP) receberão esses adaptadores.lora_alpha
é um fator de escala.use_gradient_checkpointing
é uma técnica de economia de memória fornecida pelo Unsloth.SFTConfig()
: Define os hiperparâmetros de treinamento para oSFTTrainer
.per_device_train_batch_size
egradient_accumulation_steps
trabalham juntos para determinar o tamanho efetivo do lote usado para calcular gradientes.max_steps
define o total de iterações de treinamento.learning_rate
,optim
,weight_decay
elr_scheduler_type
controlam o processo de otimização.dataset_text_field
informa ao treinador qual coluna do conjunto de dados contém os exemplos de treinamento formatados.SFTTrainer()
: Instancia o treinador, reunindo o modelo configurado pelo LoRA, o conjunto de dados preparado, o tokenizador e os argumentos de treinamento definidos emSFTConfig
.train_on_responses_only()
: Uma função utilitária (parte dotrl
e compatível com o Unsloth) que modifica o cálculo de perda do treinador. Ela define a perda a ser computada somente nos tokens correspondentes à resposta esperada do modelo(<start_of_turn>model\n...
), ignorando os tokens do prompt do usuário(<start_of_turn>user\n...
). Isso é essencial para ensinar o modelo a gerar respostas relevantes em vez de simplesmente repetir ou completar o prompt de entrada. Fornecemos os prefixos de cadeia exatos usados no modelo de bate-papo para delinear essas seções.
Etapa 6: Treinamento do modelo
Com tudo configurado, podemos iniciar o processo de ajuste fino. O método trainer.train()
lida com o loop de treinamento com base nas configurações fornecidas no SFTConfig
.
# Optional: clear CUDA cache before training
torch.cuda.empty_cache()
print("Starting training...")
# Use mixed precision training for efficiency
# Unsloth automatically handles float16/bf16 based on GPU capabilities and model
with torch.amp.autocast(device_type="cuda", dtype=torch.float16): # Or torch.bfloat16 if supported
trainer.train()
print("Training finished.")
O treinador emitirá atualizações de progresso, incluindo a perda de treinamento. Você deve observar a perda diminuindo ao longo das etapas, o que indica que o modelo está aprendendo com os dados. O tempo total de treinamento dependerá do tamanho do conjunto de dados, do tamanho do modelo, dos hiperparâmetros e da GPU específica usada. Para o nosso conjunto de dados de exemplo e o modelo 4B em uma GPU T4, o treinamento para 200 etapas deve ser concluído com relativa rapidez (por exemplo, menos de 15 a 30 minutos, dependendo da configuração exata e do comprimento dos dados).
Etapa 7: Testar o modelo ajustado (inferência)
Após o treinamento, vamos testar nosso modelo ajustado para ver se ele responde bem às perguntas com base nos dados de avaliação da Trustpilot em que foi treinado. Usaremos o método model.generate
com um TextStreamer
para obter um resultado mais interativo.
from transformers import TextStreamer
# Define some test questions related to the dataset content
questions = [
"What are common issues or complaints mentioned in the reviews?",
"What do customers like most about the product/service?",
"How is the customer support perceived?",
"Are there any recurring themes regarding pricing or value?"
# Add more questions here based on your dataset content
]
# Set up a streamer for real-time output
# skip_prompt=True prevents printing the input prompt again
# skip_special_tokens=True removes chat template tokens from output
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
print("\n--- Testing Fine-Tuned Model ---")
# Iterate through questions and generate answers
for idx, q in enumerate(questions, start=1):
# Build the conversation prompt in the correct Gemma 3 chat format
conv = [{"role": "user", "content": q}]
# Apply the chat template and add the generation prompt token
# add_generation_prompt=True includes the <start_of_turn>model tag
prompt = tokenizer.apply_chat_template(
conv,
add_generation_prompt=True,
tokenize=False
)
# Tokenize the prompt and move to GPU
inputs = tokenizer([prompt], return_tensors="pt", padding=True).to("cuda")
# Display the question
print(f"\n=== Question {idx}: {q}\n")
# Generate the response with streaming
# Pass the tokenized inputs directly to model.generate
_ = model.generate(
**inputs,
streamer=streamer, # Use the streamer for token-by-token output
max_new_tokens=256, # Limit the response length
temperature=0.7, # Control randomness (lower=more deterministic)
top_p=0.95, # Nucleus sampling
top_k=64, # Top-k sampling
use_cache=True, # Use cache for faster generation
# Add stopping criteria if needed, e.g., stopping after <end_of_turn>
# eos_token_id=tokenizer.eos_token_id,
)
# Add a separator after each answer
print("\n" + "="*40)
print("\n--- Testing Complete ---")
Veja as respostas da modelo na imagem abaixo:
Ótimo, está funcionando bem!
Um processo de ajuste fino bem-sucedido significa que o modelo gera respostas mais analíticas e diretamente derivadas do conteúdo da avaliação em que foi ajustado, refletindo o estilo e os insights presentes em seu conjunto de dados personalizado, em vez de respostas genéricas.
Etapa 8: Salvando e impulsionando seu modelo ajustado
Por fim, salve os adaptadores LoRA e o tokenizador ajustados. Você pode salvá-los localmente e também enviá-los para o Hugging Face Hub para facilitar o compartilhamento, o controle de versão e a implantação.
# Define local path and Hub repository ID
new_model_local = "gemma-3-4b-trustpilot-qa-adapter" # Local directory name
new_model_online = "YOUR_HF_USERNAME/gemma-3-4b-trustpilot-qa" # Hub repo name
# 1. Save locally
print(f"Saving model adapter and tokenizer locally to '{new_model_local}'...")
model.save_pretrained(new_model_local)
tokenizer.save_pretrained(new_model_local)
print("Saved locally.")
# 2. Push to Hugging Face Hub
print(f"Pushing model adapter and tokenizer to Hugging Face Hub '{new_model_online}'...")
model.push_to_hub(new_model_online, token=hf_token)
tokenizer.push_to_hub(new_model_online, token=hf_token)
O modelo aperfeiçoado já está disponível no Hugging Face Hub:
Conclusão
Este guia demonstrou uma abordagem de ponta a ponta para o ajuste fino do Gemma 3 do Google para um caso de uso prático: gerar respostas analíticas a partir de avaliações de clientes. Cobrimos todo o fluxo de trabalho, desde a coleta de dados de alta qualidade e específicos do domínio por meio da API de raspagem da Web da Bright Data, estruturando-os em um formato de controle de qualidade usando o processamento com LLM, até o ajuste fino do modelo Gemma 3 4B de forma eficiente usando a biblioteca Unsloth em hardware com recursos limitados.
O resultado é um LLM especializado que é adepto da extração de insights e da interpretação de sentimentos a partir de dados brutos de avaliação, transformando-os em respostas estruturadas e acionáveis. Esse método é altamente adaptável – você pode aplicar esse mesmo fluxo de trabalho para ajustar o Gemma 3 (ou outros LLMs adequados) em vários conjuntos de dados de domínios específicos para criar assistentes de IA adaptados a diferentes necessidades.
Para explorar ainda mais as estratégias de extração de dados orientadas por IA, considere estes recursos adicionais:
- Raspagem da Web com o LLaMA 3
- Raspagem da Web com servidores MCP
- Raspagem com tecnologia de IA com o LLM-Scraper
- ScrapeGraphAI para raspagem da Web do LLM
Para obter mais otimizações de ajuste fino e exemplos usando o Unsloth, confira a Coleção de Notebooks do Unsloth.
Não é necessário cartão de crédito