Entrenar un pequeño modelo de lenguaje (Qwen2.5-0.5B) para generar descripciones comerciales atractivas y concisas de productos a partir de nombres y características de productos. Este proyecto integra todos los conceptos aprendidos: carga de modelo QLoRA, configuración LoRA, preparación de conjunto de datos, entrenamiento SFTTrainer, evaluación y guardado de adaptador.
Crear un conjunto de datos sintético de 50 ejemplos en formato Alpaca:
import json
dataset = [
{
"instruction": "Escribe una descripción comercial atractiva para un producto tecnológico.",
"input": "Nombre: Drone plegable 4K. Características: Cámara estabilizada, 30 min de tiempo de vuelo, control por app, modo de seguimiento automático. Precio: $349.99.",
"output": "¡Captura el mundo desde arriba con nuestro Drone plegable 4K! Ligero, potente y listo para viajar. Disfruta de video ultra nítido gracias a su cámara estabilizada, vuela hasta 30 minutos sin preocupaciones y controla todo desde tu smartphone. Con modo de seguimiento automático, nunca pierdas de vista la acción. Tecnología profesional al alcance de tu mano. Solo $349.99."
},
{
"instruction": "Escribe una descripción comercial atractiva para un producto tecnológico.",
"input": "Nombre: Teclado Mecánico RGB. Características: Interruptores táctiles, iluminación personalizable, diseño ergonómico, anti-ghosting. Precio: $89.99.",
"output": "Domina cada juego o proyecto con nuestro Teclado Mecánico RGB. Diseñado para quienes exigen precisión y estilo. Interruptores táctiles que responden a tu toque, iluminación RGB que te permite crear tu ambiente ideal y un diseño ergonómico que protege tus manos durante largas sesiones. Anti-ghosting asegura que cada pulsación cuente. Rendimiento y elegancia en un solo dispositivo. Solo $89.99."
},
# ... 48 ejemplos más
]
# Guardar como JSON
with open("product_descriptions_dataset.json", "w") as f:
json.dump(dataset, f, indent=2)
# --- Importaciones ---
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
# --- Configuración Inicial ---
wandb.login() # Opcional
# --- Cargar Conjunto de Datos ---
with open("product_descriptions_dataset.json", "r") as f:
raw_data = json.load(f)
dataset = Dataset.from_list(raw_data)
# --- Cargar Modelo y Tokenizer con 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()
# --- Función de Formateo ---
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"### Instrucción:\n{instruction}\n\n### Entrada:\n{input_text}\n\n### Respuesta:\n{output}"
texts.append(text)
return texts
# --- Configuración de Entrenamiento ---
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", # Saltar evaluación para simplificar
save_total_limit=2,
)
# --- Crear y Ejecutar 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()
# --- Guardar Adaptador ---
model.save_pretrained("./product_desc_adapter")
tokenizer.save_pretrained("./product_desc_adapter")
# --- Opcionalmente Fusionar y Guardar Modelo Completo ---
# model = model.merge_and_unload()
# model.save_pretrained("./product_desc_merged")
# tokenizer.save_pretrained("./product_desc_merged")
Después del entrenamiento, prueba el modelo en nuevos productos:
def generate_product_description(product_name, features, price):
instruction = "Escribe una descripción comercial atractiva para un producto tecnológico."
input_text = f"Nombre: {product_name}. Características: {features}. Precio: ${price}."
prompt = f"### Instrucción:\n{instruction}\n\n### Entrada:\n{input_text}\n\n### Respuesta:\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("### Respuesta:")[-1].strip()
return response
# Probar
desc = generate_product_description(
"Altavoz Bluetooth Impermeable",
"20W de potencia, 15 horas de batería, conexión multi-punto, diseño portátil",
"79.99"
)
print(desc)
Salida Esperada (Ejemplo):
*"Lleva la fiesta a cualquier lugar con nuestro Altavoz Bluetooth Impermeable. Pura potencia de 20W que llena cualquier espacio con sonido inmersivo. Disfruta hasta 15 horas de música ininterrumpida, conecta dos dispositivos simultáneamente y llévalo a la playa, piscina o montaña con su diseño resistente y portátil. Diversión sin restricciones dondequiera que vayas. Solo $79.99."