📚 Módulo 11: Projeto Integrado Final — Ajuste Fino do Qwen2.5-0.5B para Geração de Descrições de Produtos

11.1 Objetivo do Projeto

Treinar um pequeno modelo de linguagem (Qwen2.5-0.5B) para gerar descrições comerciais atrativas e concisas de produtos a partir de nomes e características de produtos. Este projeto integra todos os conceitos aprendidos: carregamento de modelo QLoRA, configuração LoRA, preparação de conjunto de dados, treinamento SFTTrainer, avaliação e salvamento de adaptador.

11.2 Conjunto de Dados de Exemplo

Criar um conjunto de dados sintético de 50 exemplos em formato Alpaca:

import json

dataset = [
    {
        "instruction": "Escreva uma descrição comercial atrativa para um produto tecnológico.",
        "input": "Nome: Drone dobrável 4K. Características: Câmera estabilizada, 30 min de tempo de voo, controle por app, modo de rastreamento automático. Preço: $349,99.",
        "output": "Capture o mundo de cima com nosso Drone dobrável 4K! Leve, potente e pronto para viajar. Desfrute de vídeo ultra nítido graças à sua câmera estabilizada, voe até 30 minutos sem preocupações e controle tudo do seu smartphone. Com modo de rastreamento automático, nunca perca de vista a ação. Tecnologia profissional ao alcance da sua mão. Apenas $349,99."
    },
    {
        "instruction": "Escreva uma descrição comercial atrativa para um produto tecnológico.",
        "input": "Nome: Teclado Mecânico RGB. Características: Interruptores táteis, iluminação personalizável, design ergonômico, anti-ghosting. Preço: $89,99.",
        "output": "Domine cada jogo ou projeto com nosso Teclado Mecânico RGB. Projetado para quem exige precisão e estilo. Interruptores táteis que respondem ao seu toque, iluminação RGB que permite criar seu ambiente ideal e um design ergonômico que protege suas mãos durante longas sessões. Anti-ghosting assegura que cada pressionamento conte. Desempenho e elegância em um único dispositivo. Apenas $89,99."
    },
    # ... 48 exemplos mais
]

# Salvar como JSON
with open("product_descriptions_dataset.json", "w") as f:
    json.dump(dataset, f, indent=2)

11.3 Script Completo de Treinamento

# --- Importações ---
import torch
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments
)
from peft import LoraConfig, get_peft_model, PeftModel
from trl import SFTTrainer
from datasets import Dataset
import json
import wandb

# --- Configuração Inicial ---
wandb.login()  # Opcional

# --- Carregar Conjunto de Dados ---
with open("product_descriptions_dataset.json", "r") as f:
    raw_data = json.load(f)

dataset = Dataset.from_list(raw_data)

# --- Carregar Modelo e Tokenizer com QLoRA ---
model_name = "Qwen/Qwen2.5-0.5B-Instruct"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token  # Requerido para padding

# --- Configurar LoRA ---
lora_config = LoraConfig(
    r=8,
    lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(model, lora_config)
model.print_trainable_parameters()

# --- Função de Formatação ---
def formatting_prompts_func(examples):
    instructions = examples["instruction"]
    inputs = examples["input"]
    outputs = examples["output"]
    texts = []
    for instruction, input_text, output in zip(instructions, inputs, outputs):
        text = f"### Instrução:\n{instruction}\n\n### Entrada:\n{input_text}\n\n### Resposta:\n{output}"
        texts.append(text)
    return texts

# --- Configuração de Treinamento ---
training_args = TrainingArguments(
    output_dir="./product_desc_results",
    num_train_epochs=5,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,
    optim="paged_adamw_8bit",
    save_steps=100,
    logging_steps=25,
    learning_rate=2e-4,
    weight_decay=0.01,
    fp16=True,
    max_grad_norm=0.3,
    warmup_ratio=0.03,
    lr_scheduler_type="cosine",
    report_to="wandb",
    evaluation_strategy="no",  # Pular avaliação para simplificar
    save_total_limit=2,
)

# --- Criar e Executar Trainer ---
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    formatting_func=formatting_prompts_func,
    max_seq_length=512,
    tokenizer=tokenizer,
    packing=False,
)

trainer.train()

# --- Salvar Adaptador ---
model.save_pretrained("./product_desc_adapter")
tokenizer.save_pretrained("./product_desc_adapter")

# --- Opcionalmente Fundir e Salvar Modelo Completo ---
# model = model.merge_and_unload()
# model.save_pretrained("./product_desc_merged")
# tokenizer.save_pretrained("./product_desc_merged")

11.4 Avaliação Qualitativa

Após o treinamento, teste o modelo em novos produtos:

def generate_product_description(product_name, features, price):
    instruction = "Escreva uma descrição comercial atrativa para um produto tecnológico."
    input_text = f"Nome: {product_name}. Características: {features}. Preço: ${price}."
    prompt = f"### Instrução:\n{instruction}\n\n### Entrada:\n{input_text}\n\n### Resposta:\n"

    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    outputs = model.generate(
        **inputs,
        max_new_tokens=200,
        temperature=0.7,
        top_p=0.9,
        do_sample=True
    )
    full_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    response = full_text.split("### Resposta:")[-1].strip()
    return response

# Testar
desc = generate_product_description(
    "Alto-falante Bluetooth à Prova d'Água",
    "20W de potência, 15 horas de bateria, conexão multi-ponto, design portátil",
    "79,99"
)

print(desc)

Saída Esperada (Exemplo):
*"Leve a festa a qualquer lugar com nosso Alto-falante Bluetooth à Prova d'Água. Pura potência de 20W que enche qualquer espaço com som imersivo. Desfrute até 15 horas de música ininterrupta, conecte dois dispositivos simultaneamente e leve-o à praia, piscina ou montanha com seu design resistente e portátil. Diversão sem restrições onde quer que você vá. Apenas $79,99."

Course Info

Course: AI-course3

Language: PT

Lesson: Module11