Introducción: El ritmo de un "Weekend Project"
Bienvenidos a la segunda entrega de mi viaje construyendo Prize, mi propia red neuronal para clasificación de texto.
Antes de entrar en materia, tengo que aclarar que Prize es oficialmente un "proyecto de fin de semana". Como muchos de ustedes, tengo un trabajo diario y responsabilidades, así que solo puedo ponerme la gorra de "Ingeniero de Machine Learning" los sábados y domingos. El progreso será constante, pero a su propio ritmo.
En el post anterior definí la arquitectura. Hoy, les contaré cómo resolví el problema más grande de cualquier proyecto de IA: conseguir los datos.
Fase 1: La limpieza manual
Mi primera fuente de datos es el inventario de una cadena de farmacias con 6,000 ítems. Suena genial, hasta que abres el archivo.
Una red neuronal aprende lo que le enseñas. Si le enseño que una "Coca-Cola" es una medicina, la red fallará. Me di cuenta de que mi inventario estaba "contaminado" con productos que no tienen nada que ver con salud.
Así que me tocó hacer el trabajo sucio. En varias sesiones a lo largo del fin de semana, filtré la lista manualmente. Borré cientos de filas que contenían refrescos, chocolates, detergentes, escobas y araganes.
El resultado final es un archivo limpio: inventarioSoloMedicinas.txt. Es un trabajo tedioso, pero vital. Sin esta limpieza, Prize no tendría oportunidad.
Fase 2: La Fábrica de Datos Sintéticos ($0.08 de inversión)
Aquí vino el reto real. Mientras que de Farmacia tenía miles de ítems, del "Laboratorio Dental" apenas tenía unos 50 ejemplos. Un desbalance total.
Necesitaba convertir esas 50 líneas en 5,500 para igualar a la farmacia. ¿La solución? Usar IA para entrenar IA.
Con la ayuda de Google Gemini, escribí un script en Python que se conecta a la API de OpenRouter (usando modelos Llama 3). La lógica fue sencilla:
- Tomar 5 ejemplos reales de mis servicios dentales.
- Pedirle a la IA que genere 20 variaciones de cómo un usuario pediría esos servicios (con jerga, errores de dedo, diferentes tonos).
- Repetir hasta llegar a la meta.
Aquí está el script:
import requests
import json
import random
import time
import os
# --- CONFIGURACIÓN ---
API_KEY = "XXXXXXXXXX"
OUTPUT_FILE = "dataset_laboratorio_sintetico.txt"
TARGET_COUNT = 5500
BATCH_SIZE = 20 # Cuántas frases pedimos por llamada (no pidas muchas de golpe para no confundir al modelo)
MODEL_ID = "meta-llama/llama-3.1-70b-instruct"
semillas_laboratorio = [
"protesis total superior acrilico",
"reparacion de puente fijo",
"ferula de descarga rigida",
"corona de zirconio sobre implante",
# ... Aquí va el resto de los 50 items ...
]
def generate_variations(seeds):
# Envía un prompt a OpenRouter para generar variaciones basadas en las semillas.
seeds_text = "\n".join([f"- {s}" for s in seeds])
prompt = f"""
Actúa como un generador de datos para entrenar una IA.
Tu tarea es generar {BATCH_SIZE} frases de búsqueda DISTINTAS basadas en los servicios de un laboratorio dental.
Aquí tienes algunos ejemplos reales (semillas):
{seeds_text}
INSTRUCCIONES:
1. Genera {BATCH_SIZE} variaciones nuevas. NO copies las semillas exactamente.
2. Contexto: Usuarios en Venezuela/Latinoamérica buscando estos servicios.
3. Variedad:
- Usa sinónimos (ej: "plancha" en vez de "prótesis", "férula" en vez de "placa").
- Mezcla tonos: Formal ("necesito una cotización..."), Informal ("cuanto cuesta la plancha..."), Directo ("precio corona").
- Incluye ocasionalmente errores leves de ortografía o falta de tildes (como escribe la gente en WhatsApp).
4. FORMATO DE SALIDA: Únicamente una lista JSON pura de strings. Sin texto extra antes ni después.
Ejemplo: ["frase 1", "frase 2", "frase 3"]
"""
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
data = {
"model": MODEL_ID,
"messages": [
{"role": "system", "content": "Eres un asistente útil que responde SOLO en formato JSON válido."},
{"role": "user", "content": prompt}
],
"temperature": 0.8 # Creatividad alta para tener variedad
}
try:
response = requests.post("https://openrouter.ai/api/v1/chat/completions", headers=headers, json=data)
response.raise_for_status() # Lanza error si falla la petición
result = response.json()
content = result['choices'][0]['message']['content']
return json.loads(content)
except Exception as e:
print(f"Error en la llamada a la API: {e}")
return []
def main():
print(f"--- Iniciando Generación para Proyecto Prize ---")
print(f"Meta: {TARGET_COUNT} frases.")
# Contar cuántas llevamos si el archivo ya existe
current_count = 0
if os.path.exists(OUTPUT_FILE):
with open(OUTPUT_FILE, 'r', encoding='utf-8') as f:
current_count = len(f.readlines())
print(f"Progreso actual: {current_count} frases encontradas en {OUTPUT_FILE}")
while current_count < TARGET_COUNT:
# 1. Seleccionar semillas al azar
mis_semillas = random.sample(semillas_laboratorio, k=min(5, len(semillas_laboratorio)))
# 2. Llamar a la API
print(f"Generando lote... (Total actual: {current_count})")
nuevas_frases = generate_variations(mis_semillas)
if not nuevas_frases:
print("Fallo en este lote, reintentando en 5 segundos...")
time.sleep(5)
continue
# 3. Guardar en disco (Append mode 'a')
with open(OUTPUT_FILE, 'a', encoding='utf-8') as f:
for frase in nuevas_frases:
f.write(frase + "\n")
current_count += len(nuevas_frases)
# 4. Pequeña pausa para no saturar la API (Rate Limits)
time.sleep(1)
print(f"\n¡ÉXITO! Generación completada.")
print(f"Archivo guardado en: {OUTPUT_FILE}")
if __name__ == "__main__":
main()
Los Resultados: Datos duros
Dejé el script corriendo en mi laptop mientras hacía otras cosas. Aquí están las estadísticas finales de esta operación de "Ingeniería de Datos":
Tiempo de ejecución: 2 horas.
Volumen generado: 5,500 líneas de texto único y variado.
Costo total de la API: $0.08 USD.
Sí, por menos de 10 centavos de dólar, generé un dataset robusto y equilibrado que me hubiera tomado semanas escribir a mano.
Conclusión
Ahora tengo dos archivos en mi disco duro que valen su peso en oro:
inventarioSoloMedicinas.txt(Datos reales, limpios).dataset_laboratorio_sintetico.txt(Datos generados, variados).
La balanza está equilibrada. Tengo ~11,000 ejemplos de alta calidad listos para ser procesados.
En el próximo fin de semana, haremos la magia matemática: usaremos nuevamente la API de OpenRouter y convertiremos todo este texto a números (embeddings), luego convertiremos los vectores fila en vectores columna (es lo que espera el script network2.py) y finalmente... encenderemos a Prize para su primer entrenamiento.
¡El momento de la verdad!
Ya tenemos los datos. Ahora mira cómo Prize alcanzó el 99.9% de precisión en el post 3.

Top comments (0)