build-small-hackathon/sofia-qwen2.5-7b
Sofía — Qwen2.5-7B-Instruct fine-tuneado para una compañera de voz de una nena de 3 años
TL;DR (EN): QLoRA fine-tune of Qwen/Qwen2.5-7B-Instruct (merged, full precision weights) that teaches the base model to consistently play "Sofía" — a warm, short-sentence Spanish-speaking companion for a ~3 year old — to present curated content (<contenido>) verbatim instead of paraphrasing/inventing it, and to gently refuse/redirect unsafe topics. Built for the Build Small Hackathon (track Backyard AI, mérito Well-Tuned). Powers the `sofia-educational-companion` Space.
Qué es
Este modelo es el "pegamento conversacional" de Sofía, una compañera educativa por voz para una niña de ~3 años (proyecto Lumi, Build Small Hackathon, track Backyard AI). El LLM nunca es la fuente de hechos: todo el contenido (cuentos, actividades, números) vive curado en content/ y se le inyecta al modelo entre <contenido>...</contenido>. Este fine-tune entrena estilo y seguridad, no conocimiento.
Objetivos del fine-tune:
- Mantener siempre la persona "Sofía": cálida, frases muy cortas, español rioplatense, una pregunta por turno.
- Presentar el contenido curado verbatim (sin parafrasear ni inventar), incluso cuando se le pide un cuento/actividad que no existe en
content/. - Rechazar o redirigir con cariño temas no aptos para una niña de 3 años, generalizando más allá de los términos exactos de
safety/blocklist.txt(defensa en profundidad).
Datos de entrenamiento
finetune/build_dataset.py genera finetune/dataset.jsonl: 196 ejemplos en formato chat (mismo SYSTEM_PROMPT y los mismos bloques <contenido>/<nota> que construye llm/engine.py en producción), mezclando:
- saludos y charla / persona,
- entrega de actividades curadas (contar, formas, colores, animales), x3 frases cada una,
- cambio de color de Sofía (intent
sofia_color), - cuentos curados,
- ~62 rechazos/redirecciones de temas no aptos que no repiten los términos exactos de la blocklist.
Entrenamiento
QLoRA sobre Qwen/Qwen2.5-7B-Instruct, corrido en Modal (GPU A10G):
- Carga en 4-bit NF4 (
bnb_4bit_use_double_quant=True), cómputo en bf16. - LoRA
r=16,alpha=32,target_modules=["q_proj", "k_proj", "v_proj", "o_proj"]. - 3 épocas, batch size 2, gradient accumulation 4, learning rate 2e-4.
- 72 steps, ~408s en A10G. Loss 2.51 → 0.14.
- El adapter se mergeó (
merge_and_unload) y se publicó en este repo como pesos completos en safetensors.
Código: finetune/train_modal.py y finetune/merge_lora.py en el repo del proyecto.
Evaluación (smoke test)
finetune/smoke_test_modal.py corrió 5 turnos representativos sobre este modelo ya mergeado: saludo, entrega de <contenido> de conteo, una pregunta sobre un arma, miedo a los monstruos, y un pedido de cuento inventado ("un dragón que come autos"). Resultado: saluda en persona, repite el <contenido> verbatim (a diferencia de alternativas probadas como Nemotron-mini/Nemotron-3-nano, que parafraseaban o inventaban), rechaza el tema del arma sin engancharse, redirige el miedo con calidez, y ante el cuento inventado dice que no lo tiene y ofrece una alternativa curada en vez de inventar una.
Cómo usarlo
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL_ID = "build-small-hackathon/sofia-qwen2.5-7b"
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID, torch_dtype=torch.bfloat16
).to("cuda")
# SYSTEM_PROMPT exacto y bloques <contenido>/<nota>: ver llm/engine.py
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": "Hola Sofía"},
]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
inputs = tokenizer(text, return_tensors="pt").to("cuda")
output = model.generate(
**inputs, max_new_tokens=120, do_sample=True, temperature=0.6
)Limitaciones
- Entrenado para un caso de uso muy acotado: compañera de juego para una niña de 3 años, en español rioplatense, con contenido inyectado por el sistema (
<contenido>/<nota>/<contexto>). Fuera de ese contexto se comporta básicamente como el Qwen2.5-7B-Instruct base. - No agrega conocimiento factual nuevo: por diseño, los hechos siguen viviendo en
content/, nunca en los pesos. - Dataset chico (196 ejemplos), curado a mano para un hackathon — no es un benchmark de seguridad general.
Licencia
Apache 2.0, igual que el modelo base Qwen/Qwen2.5-7B-Instruct.
