programa
El ajuste fino de los modelos de lenguaje grandes suele implicar lidiar con infraestructuras GPU distribuidas, gestionar fallos en los clústeres y depurar scripts de entrenamiento complejos. Tinker está aquí para resolver eso.
Lanzado por Thinking Machines Lab de Mira Murati el 1 de octubre de 2025, Tinker es una API de entrenamiento que se encarga de toda la complejidad de la infraestructura y te ofrece un control total sobre tus algoritmos y datos. Permite escribir scripts sencillos en Python con cuatro funciones básicas, y Tinker los ejecuta en GPU distribuidas para una amplia gama de modelos de código abierto como Llama 70B o Qwen 235B.
La plataforma utiliza el ajuste fino de adaptación de rango bajo (LoRA) para reducir costes y admite todo, desde el aprendizaje supervisado hasta el aprendizaje por refuerzo. Los equipos de investigación de Princeton, Stanford y Berkeley ya lo utilizan para vuestro trabajo.
En este tutorial, te guiaré a través de la instalación de Tinker, la comprensión de su API y el ajuste de un modelo completo de preguntas y respuestas financieras utilizando el modelo base Qwen3-8B. Si deseas obtener más información sobre el ajuste fino con prácticas prácticas, te recomiendo que consultes este curso sobre el ajuste fino con Llama 3.
¿Qué es Tinker?
Thinking Machines Lab creó Tinker para personas que desean personalizar el modo en que aprenden los modelos sin necesidad de convertirse en expertos en infraestructura.
¿Quién utiliza Tinker?
La plataforma se dirige a tres grupos:
- Investigadores que exploran un nuevo método de entrenamiento
- Programadores que crean productos de IA que requieren un comportamiento personalizado.
- Constructores que desean resultados con calidad de producción sin recursos empresariales.
Todos los grupos comparten una necesidad común: saben lo que quieren que haga su modelo, pero las interfaces de ajuste fino estándar no les proporcionan suficiente control.
Por ejemplo, los equipos académicos podrían utilizarlo para probar algoritmos novedosos. Un laboratorio de química podría querer entrenar modelos basados en patrones de razonamiento específicos del dominio que no se ajustan a las plantillas típicas de ajuste de instrucciones. Una startup que esté desarrollando un bot asesor financiero podría utilizar el modelo para seguir formatos de salida y cadenas de razonamiento específicos.
Todos estos casos de uso tienen en común que necesitan modificar el proceso de entrenamiento en sí, no solo intercambiar conjuntos de datos.
¿Qué hace que Tinker sea diferente?
La mayoría de las plataformas se optimizan para una de dos cosas: facilidad de uso o flexibilidad. El enfoque de Tinker es que estos dos aspectos no tienen por qué entrar en conflicto. La plataforma te ofrece acceso básico a la formación a través de cuatro operaciones principales, pero se encarga de todo lo demás de forma automática:
- Control del bucle de entrenamiento: Escribe tus propias funciones de pérdida, estrategias de acumulación de gradientes o patrones de muestreo.
- Actualizaciones del modelo: Especifica exactamente cómo deben cambiar los pesos durante la optimización.
- Evaluación: Genera resultados o calcula probabilidades en cualquier momento del entrenamiento.
- Gestión estatal: Guarda y reanuda el entrenamiento con control total sobre lo que se conserva.
Tinker expone estas capacidades a través de cuatro primitivas API que aprenderás en la sección práctica.
Esto funciona porque la mayor parte de la complejidad del entrenamiento proviene de la infraestructura, no de los algoritmos en sí mismos. Ejecutar un bucle de entrenamiento personalizado en tu ordenador portátil es muy sencillo. Ejecutarlo en 100 GPU con recuperación de fallos y programación de recursos es difícil. Tinker se encarga de la segunda parte, para que puedas centrarte en la primera.
Dónde encaja Tinker
El ecosistema de herramientas de IA se divide, a grandes rasgos, en tres capas: proveedores de nube que ofrecen GPU sin procesar, plataformas gestionadas que ejecutan flujos de trabajo predefinidos y marcos que ayudan a crear sistemas de entrenamiento desde cero. Tinker se sitúa entre las plataformas gestionadas y los marcos de trabajo. Obtienes más control que con plataformas como Hugging Face AutoTrain, pero menos trabajo de infraestructura que al configurar un clúster personalizado en Google Cloud Platform (GCP).
Precios y créditos de Tinker
Tinker utiliza precios transparentes por token que varían según el tamaño del modelo y el tipo de operación. El entrenamiento de Qwen3-8B cuesta 0,40 dólares por cada millón de tokens. La buena noticia: los nuevos usuarios reciben 150 dólares en créditos cuando se les elimina de la lista de espera, lo que cubre con creces varias sesiones de formación experimental como la de este tutorial.

Fuente: Laboratorio de máquinas pensantes
Ejemplo de Tinker: Ajuste de Qwen3-8B para preguntas y respuestas financieras
En esta sección, te guiaré a través del proceso de creación de un flujo de trabajo completo de ajuste fino utilizando la API de Tinker. Ajustaremos Qwen3-8B con datos de preguntas y respuestas financieras utilizando LoRA, aprendiendo las cuatro primitivas API básicas mientras vemos las mejores prácticas de producción en acción.
Para obtener una descripción más detallada del modelo, no te pierdas este artículo sobre Qwen3.
Nota importante: Tinker se encuentra actualmente en fase beta con acceso mediante lista de espera. Si tienes acceso, puedes seguir las instrucciones y ejecutar el código. Si estás en lista de espera, puedes seguir leyendo para comprender lo sencillo que es el proceso de ajuste con Tinker. Cuando obtengas acceso, estarás listo para empezar de inmediato.
Resultados del entrenamiento y selección de puntos de control
Veamos primero los resultados del entrenamiento. Comprender lo que sucedió te ayuda a detectar estos patrones cuando ejecutas tus propios trabajos de ajuste.
Ajustamos Qwen3-8B en el conjunto de datos FinCoT, un conjunto de datos de razonamiento financiero en cadena de pensamiento, utilizando LoRA con rango 32. Después de filtrar las secuencias que no superaban los 10 000 tokens, obtuvimos 7244 ejemplos de entrenamiento y 500 ejemplos de validación con los que trabajar.
El entrenamiento se llevó a cabo durante 904 iteraciones a lo largo de cuatro épocas, lo que llevó aproximadamente tres horas, mientras que Tinker se encargó de toda la coordinación distribuida de las GPU.
Una época es una pasada completa por todo el conjunto de datos de entrenamiento. Dado que había 226 lotes por época (7244 ejemplos divididos por un tamaño de lote de 32), las cuatro épocas sumaron un total de 904 iteraciones.
Análisis de los resultados de la formación
El siguiente gráfico programa tres métricas: pérdida de entrenamiento (azul), pérdida de validación (rojo) y tasa de aprendizaje (verde).

El entrenamiento inicial (iteraciones 0-400) parece saludable: tanto las pérdidas de entrenamiento como las de validación caen drásticamente, pasando de más de 1,4 a menos de 0,8 y manteniéndose a una distancia de aproximadamente 0,1 entre sí.
Este periodo demuestra que el modelo está aprendiendo eficazmente patrones generalizables en los datos. La tasa de aprendizaje (línea verde) aumenta gradualmente durante las primeras 200 iteraciones, a medida que el calentamiento estabiliza el entrenamiento.
Alrededor de la iteración 400-600, las cosas cambian. La pérdida de entrenamiento sigue disminuyendo, pero la pérdida de validación se estabiliza en torno a 0,75-0,8, ampliando la diferencia a 0,13 a medida que el modelo comienza a sobreajustarse. Entre las iteraciones 600 y 900, la divergencia se hace evidente: la pérdida de entrenamiento cae en picado hasta 0,39, mientras que la pérdida de validación aumenta hasta 0,8, lo que demuestra que el modelo ha pasado de aprender patrones de razonamiento a memorizar ejemplos.
Este patrón es habitual cuando se ajustan los modelos de razonamiento. Si te interesa saber cómo se desarrollan las capacidades de razonamiento durante el entrenamiento, no dudes en consultar este tutorial sobre el ajuste fino de DeepSeek R1, que explora dinámicas similares.
El modelo guardado en el punto de control 400 ofrece la mejor generalización, con una pérdida de entrenamiento de 0,68, una pérdida de validación de 0,73 y una pequeña diferencia de solo 0,05. A pesar de tener una pérdida de entrenamiento mayor que el punto de control final, maneja mejor las preguntas nuevas. La pérdida de entrenamiento más baja rara vez produce el mejor modelo, por lo que las métricas de validación son importantes.
Lo verificaremos más adelante comparando el modelo en el punto de control 400 con el modelo base utilizando un juez LLM. Por ahora, veamos paso a paso cómo crear este flujo de trabajo de ajuste fino.
Paso 1: Instala Tinker y configura tu entorno.
Nota: Los siguientes pasos desglosan un script de unas 300 líneas. Dado que las explicaciones se entrelazan con fragmentos de código, te recomendamos que abras el script completo en una nueva pestaña mientras sigues las instrucciones para tener una visión completa.
Necesitarás Python 3.11 o posterior y una conexión a Internet estable. Dado que Tinker se encarga del entrenamiento de la GPU en sus servidores, es más importante tener una buena conexión que un ordenador potente.
Empieza por iniciar sesión en la consola de Tinker y crear una clave API. Guarda esta clave en un archivo .env en el directorio de tu proyecto:
TINKER_API_KEY=your_key_here
Una vez configurada, la consola Tinker se convierte en tu centro neurálgico para supervisar las sesiones de entrenamiento y gestionar los puntos de control.

La consola Tinker programa todas tus carreras de entrenamiento con detalles clave: identificadores únicos de carrera, modelo base, rango LoRA y horas de la última solicitud. Desde esta interfaz puedes buscar, filtrar y gestionar múltiples experimentos.
Instala los paquetes necesarios. Tinker tiene dependencias entre pares que no se instalan automáticamente, por lo que debes especificarlas explícitamente:
pip install tinker transformers torch datasets python-dotenv numpy
Los paquetes transformers y torch proporcionan operaciones de tokenización y tensores que Tinker utiliza internamente. La biblioteca datasets se encarga de cargar los datos de entrenamiento desde Hugging Face.
Ahora configura tus importaciones y una función auxiliar de reintento. Al realizar la formación en servidores remotos, es posible que aparezcan errores de conexión temporales que no significan necesariamente que haya algún problema. Tu trabajo de formación sigue funcionando correctamente en la infraestructura de Tinker. Este envoltorio de reintento gestiona automáticamente esos fallos transitorios:
import time
import numpy as np
from dotenv import load_dotenv
from datasets import load_dataset
import tinker
from tinker import types
def with_retry(future, max_attempts=3, delay=5):
"""Simple retry logic for API futures"""
for attempt in range(max_attempts):
try:
return future.result()
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(delay)
Esta función toma un objeto futuro (un objeto que representa una operación que aún no ha finalizado) de la API de Tinker e intenta obtener el resultado hasta tres veces, esperando cinco segundos entre cada intento. La mayoría de los fallos temporales se resuelven dentro de este intervalo.
Paso 2: Inicializa ServiceClient y carga el conjunto de datos.
ServiceClient es tu punto de entrada a Tinker. Busca los modelos disponibles y gestiona la autenticación:
load_dotenv()
service_client = tinker.ServiceClient()
No lo usarás mucho después de esta configuración inicial. Su principal función es crear clientes de entrenamiento y muestreo para modelos específicos.
Ahora es el momento de cargar el conjunto de datos FinCoT. Contiene preguntas financieras acompañadas de cadenas de razonamiento paso a paso y respuestas finales. A diferencia de los simples conjuntos de datos de preguntas y respuestas, FinCoT enseña al modelo a mostrar su trabajo antes de dar respuestas, una habilidad importante para las aplicaciones de asesoramiento financiero, en las que los usuarios necesitan comprender el razonamiento que hay detrás de las recomendaciones.
dataset = load_dataset("TheFinAI/FinCoT")

El conjunto de datos incluye una división SFT (ajuste fino supervisado) con cadenas de razonamiento completas y una división RL para el aprendizaje por refuerzo. Utiliza la división SFT para el entrenamiento y la muestra de la división RL para la validación, a fin de garantizar que el modelo se generalice a diferentes ejemplos.
train_data_raw = dataset["SFT"] # 7,686 examples with reasoning chains
val_data_raw = dataset["RL"].shuffle(seed=42).select(range(500)) # 500 validation examples
print(f"Loaded {len(train_data_raw)} training examples")
print(f"Loaded {len(val_data_raw)} validation examples")
Paso 3: Crea tu cliente de entrenamiento LoRA
Ahora crea un cliente de entrenamiento LoRA para Qwen3-8B:
training_client = service_client.create_lora_training_client(
base_model="Qwen/Qwen3-8B",
rank=32
)
Analicemos lo que está sucediendo aquí:
- Qwen/Qwen3-8B: Este es un modelo con 8000 millones de parámetros, lo suficientemente potente para tareas reales, pero aún así eficiente para entrenar. Es un buen lugar para aprender. Puedes ver todos los modelos disponibles en la gama de modelos de Tinker.
- LoRA (adaptación de rango bajo): En lugar de actualizar los 8000 millones de parámetros, LoRA entrena pequeñas capas «adaptadoras» que modifican el comportamiento del modelo base. Piensa en ello como si se tratara de añadir lentes personalizadas a una cámara, en lugar de reconstruir toda la cámara. El modelo base permanece congelado en los servidores de Tinker; solo estás entrenando estos pequeños adaptadores.
- rank=32: Esto controla cuántos parámetros entrenables contienen tus adaptadores LoRA. Un rango de 32 funciona bien para conjuntos de datos con entre 5000 y 10 000 ejemplos. Los conjuntos de datos más grandes pueden necesitar rank=64 o rank=128 para una mayor capacidad de adaptación.
Para profundizar en el funcionamiento matemático de LoRA y en cómo ajustar los parámetros de rango, consulta la introducción a LoRA de Tinker o la guía completa de DataCamp sobre cómo dominar la adaptación de rango bajo.
Los modelos lingüísticos no trabajan directamente con palabras. Trabajan con «tokens», que representan fragmentos de texto. Un tokenizador divide el texto en estas partes. Por ejemplo, «financiero» podría convertirse en un solo token, mientras que «comprensión» podría dividirse en «compr» y «ensión».
El tokenizador convierte el texto en números (ID de tokens) que el modelo puede procesar. Al obtener el tokenizador del cliente de entrenamiento, te aseguras de utilizar exactamente la misma tokenización que espera Qwen3-8B.
tokenizer = training_client.get_tokenizer()
Paso 4: Transformar los datos al formato de Tinker.
Tinker necesita tus datos de entrenamiento en un formato llamado types.Datum. Este formato separa la entrada del modelo de la configuración de la función de pérdida, lo que te permite controlar con precisión qué tokens contribuyen al entrenamiento.
Al ajustar los modelos lingüísticos, normalmente se quiere que el modelo aprenda a generar buenas respuestas, no a memorizar las preguntas. Para ello, asigna diferentes pesos a las distintas partes de la entrada:
- Fichas de respuesta (la pregunta): Peso = 0,0 (no aprendas de estos)
- Fichas de finalización (la respuesta): Peso = 1,0 (aprende de esto)
Construyamos la función de transformación de datos paso a paso. Comienza con la firma de la función:
def prepare_datum(example, max_length=10000):
Da formato a la conversación utilizando la plantilla de chat de Qwen3.
Los diferentes modelos esperan diferentes formatos para las conversaciones de varios turnos. Qwen3 utiliza tokens especiales como <|im_start|> y <|im_end|> para marcar los límites de los mensajes. Divide cada mensaje en partes de «observación» (encabezados de roles) y partes de «acción» (contenido real):
# Qwen3 chat format - split into observation and action parts
user_ob = "<|im_start|>user\n"
user_ac = f"{example['Question']}<|im_end|>"
assistant_ob = "\n<|im_start|>assistant\n"
# Include both reasoning process AND final answer
assistant_ac = f"{example['Reasoning_process']}\n\nFinal Answer: {example['Final_response']}<|im_end|>"
El conjunto de datos FinCoT proporciona un razonamiento estructurado, por lo que concatenamos los campos « Reasoning_process » y « Final_response ». Esto enseña al modelo a mostrar su trabajo antes de responder.
Tokeniza cada parte por separado.
Para programar dónde deben aplicarse las ponderaciones, cada una de las partes del mensaje debe tokenizarse por separado.
# Tokenize each part separately to track weight boundaries
user_ob_tokens = tokenizer.encode(user_ob, add_special_tokens=False)
user_ac_tokens = tokenizer.encode(user_ac, add_special_tokens=False)
assistant_ob_tokens = tokenizer.encode(assistant_ob, add_special_tokens=False)
assistant_ac_tokens = tokenizer.encode(assistant_ac, add_special_tokens=False)
# Combine all tokens
all_tokens = user_ob_tokens + user_ac_tokens + assistant_ob_tokens + assistant_ac_tokens
Filtrar secuencias que son demasiado largas
Algunos ejemplos de FinCoT incluyen cadenas de razonamiento extremadamente detalladas que superan la ventana de contexto cómoda de Qwen3-8B. Aunque el modelo admite técnicamente 32 768 tokens, las secuencias muy largas pueden provocar inestabilidad en el entrenamiento:
# Check if sequence exceeds max length
if len(all_tokens) > max_length:
return None # Skip this example
Filtrar los ejemplos por encima de 10 000 tokens elimina solo entre el 5 % y el 6 % de los datos, al tiempo que mejora significativamente la fiabilidad del entrenamiento.
Asigna pesos para controlar lo que aprende el modelo.
Establece los pesos en 0,0 para la pregunta (para que el modelo no aprenda a predecir preguntas) y en 1,0 para la respuesta (para que aprenda a generar buenas respuestas):
# Weights: only train on assistant's answer (action part)
weights = np.array(
[0.0] * len(user_ob_tokens) +
[0.0] * len(user_ac_tokens) +
[0.0] * len(assistant_ob_tokens) +
[1.0] * len(assistant_ac_tokens)
)
Fichas de cambio y pesos para la predicción de la siguiente ficha
Los modelos lingüísticos predicen el siguiente token de una secuencia. Cuando el modelo ve los tokens 0-9, debe predecir el token 10. Por lo tanto, tus objetivos son siempre los datos introducidos desplazados una posición hacia adelante:
# CRITICAL: Shift tokens AND weights for next-token prediction
input_tokens_model = all_tokens[:-1]
target_tokens = all_tokens[1:]
weights_shifted = weights[1:]
Es importante tener en cuenta que debes desplazar los pesos junto con las fichas. Si no cambias los pesos, el cálculo de la pérdida estará desalineado: el modelo se entrenaría para predecir el primer token de respuesta, pero el peso en esa posición seguiría siendo 0,0 desde el prompt. Este error de alineación puede impedir que el modelo aprenda correctamente.
Devuelve los datos formateados.
Por último, la función prepare_datum() devuelve los datos formateados, listos para su procesamiento.
return types.Datum(
model_input=types.ModelInput.from_ints(tokens=input_tokens_model),
loss_fn_inputs=dict(weights=weights_shifted, target_tokens=target_tokens),
)
Al aplicar la función definida a los subconjuntos de entrenamiento y validación sin procesar, ahora podemos procesar todo el conjunto de datos:
# Process and filter training data
training_data_raw = [prepare_datum(example) for example in train_data_raw]
training_data = [d for d in training_data_raw if d is not None]
skipped = len(train_data_raw) - len(training_data)
print(f"Processed {len(training_data)} examples (skipped {skipped} too-long sequences)")
# Output: Processed 7,244 examples (skipped 442 too-long sequences)
# Process validation data
val_data_raw_processed = [prepare_datum(example) for example in val_data_raw]
val_data = [d for d in val_data_raw_processed if d is not None]
print(f"Validation set: {len(val_data)} examples")
Paso 5: Entrena el modelo con el bucle de entrenamiento.
El bucle de entrenamiento utiliza dos de las primitivas API principales de Tinker: forward_backward y optim_step. Construyámoslo paso a paso con seguimiento de validación para detectar el sobreajuste a tiempo.
Define una función de pérdida de validación.
En primer lugar, define una función de pérdida de validación. A diferencia de la pérdida de entrenamiento, la pérdida de validación indica cómo generaliza el modelo a datos no vistos. Esto es fundamental para detectar el sobreajuste.
def compute_validation_loss(val_data, batch_size=100):
"""Compute loss on validation set (forward only, no backward)"""
batch_indices = np.random.choice(
len(val_data), size=min(batch_size, len(val_data)), replace=False
)
batch = [val_data[i] for i in batch_indices]
# Forward pass only (no backward!)
fwd_future = training_client.forward(batch, loss_fn="cross_entropy")
fwd_result = with_retry(fwd_future)
# Calculate per-token loss
loss_sum = fwd_result.metrics["loss:sum"]
total_completion_tokens = sum(
np.sum(np.array(val_data[i].loss_fn_inputs["weights"].data) > 0)
for i in batch_indices
)
return loss_sum / total_completion_tokens if total_completion_tokens > 0 else 0
El método « forward() » calcula la pérdida sin calcular gradientes, lo que lo hace eficiente para la evaluación en la que solo necesitas medir el rendimiento, sin actualizar los pesos. El cálculo de la pérdida de validación añade una sobrecarga mínima (solo es una pasada hacia adelante, sin cálculo de gradientes), pero proporciona una alerta temprana cuando la diferencia entre la pérdida de entrenamiento y la de validación supera 0,2, lo que indica que el modelo está memorizando en lugar de aprendiendo.
Configurar los parámetros de entrenamiento
Necesitamos definir el número de épocas, así como la tasa de aprendizaje. LoRA requiere tasas de aprendizaje significativamente más altas que el ajuste fino completo, normalmente entre 10 y 20 veces mayores.
# Training configuration
n_samples = len(training_data) # 7,244
n_epochs = 4 # More epochs for smaller dataset
batch_size = 32
# Calculate optimal LR for Qwen3-8B with LoRA
# Formula: base_lr * lora_multiplier * (2000 / hidden_size) ** exponent
# For Qwen: base=5e-5, multiplier=10, hidden_size=4096, exponent=0.0775
learning_rate = 5e-5 * 10.0 * (2000 / 4096) ** 0.0775 # ≈ 4.7e-4
La fórmula tiene en cuenta el tamaño del modelo (Qwen3-8B tiene un tamaño oculto de 4096) y utiliza exponentes determinados empíricamente que varían según la familia de modelos, tal y como se documenta en el libro de recetas de Tinker. Para los modelos Qwen, el exponente es 0,0775, mientras que los modelos Llama utilizan 0,781. En nuestro caso, esto produce una tasa de aprendizaje de alrededor de 4,7e-4, lo que proporciona una convergencia estable sin explosiones de gradiente.
Configurar el calentamiento de la tasa de aprendizaje
Comenzar con la tasa de aprendizaje completa puede provocar explosiones de gradiente al inicio del entrenamiento, cuando el modelo aún no se ha adaptado a la tarea. El calentamiento aumenta gradualmente la tasa de aprendizaje desde casi cero hasta el valor objetivo durante las primeras 200 iteraciones. Esto se puede ver claramente en la visualización del entrenamiento anterior, donde la tasa de aprendizaje (línea verde) aumenta suavemente durante las primeras 200 iteraciones.
# Warmup configuration
warmup_steps = 200
# Calculate iterations from epochs
num_iterations = n_epochs * (n_samples // batch_size) # 4 * 226 = 904
checkpoint_interval = 200
validation_interval = 50
Iniciar el ciclo de entrenamiento
Después de inicializar las listas para realizar un seguimiento de las pérdidas, podemos comenzar el bucle de entrenamiento:
losses = []
per_token_losses = []
val_losses = []
for iteration in range(num_iterations):
# Sample random batch
batch_indices = np.random.choice(len(training_data), size=batch_size, replace=False)
batch = [training_data[i] for i in batch_indices]
# Apply learning rate warmup
if iteration < warmup_steps:
current_lr = learning_rate * (iteration + 1) / warmup_steps
else:
current_lr = learning_rate
La lógica de calentamiento aumenta gradualmente la tasa de aprendizaje durante las primeras 200 iteraciones, lo que evita la inestabilidad inicial.
Llama a las primitivas de entrenamiento básicas de Tinker.
En cada iteración, las primitivas básicas de Tinker se invocan de la siguiente manera:
# API Primitive 1: forward_backward - compute gradients
fwdbwd_future = training_client.forward_backward(batch, loss_fn="cross_entropy")
# API Primitive 2: optim_step - update parameters
optim_future = training_client.optim_step(types.AdamParams(learning_rate=current_lr))
# Wait for results with retry logic
fwdbwd_result = with_retry(fwdbwd_future)
optim_result = with_retry(optim_future)
forward_backward calcula los gradientes determinando el grado de error de las predicciones del modelo y en qué dirección ajustar cada parámetro. Utiliza la pérdida de entropía cruzada, que mide en qué medida la distribución de probabilidad prevista por el modelo coincide con los siguientes tokens reales. El optimizador de entrenamiento ( optim_step ) actualiza los parámetros del modelo basándose en esos gradientes utilizando el optimizador Adam.
Observa cómo ambos métodos devuelven objetos «futuros» inmediatamente. Esto permite a Tinker procesar múltiples operaciones simultáneamente, lo que acelera el proceso. Al llamar a with_retry(future), se espera a que finalice esa operación específica.
Realizar un seguimiento y mostrar las métricas de pérdidas mediante un programa.
Calculamos las pérdidas totales y por token y las añadimos a las listas respectivas:
# Track loss
loss_sum = fwdbwd_result.metrics["loss:sum"]
total_completion_tokens = sum(
np.sum(np.array(training_data[i].loss_fn_inputs["weights"].data) > 0)
for i in batch_indices
)
per_token_loss = loss_sum / total_completion_tokens
losses.append(loss_sum)
per_token_losses.append(per_token_loss)
if iteration % 10 == 0:
warmup_indicator = "🔥" if iteration < warmup_steps else ""
print(f"{warmup_indicator} Iteration {iteration} | Train: {per_token_loss:.4f} | LR: {current_lr:.6f}")
La pérdida por token normaliza la pérdida según el número de tokens de respuesta, lo que facilita su interpretación en diferentes tamaños de lotes. Una buena sesión de entrenamiento suele comenzar con una pérdida por token de entre 1,2 y 1,5, y disminuye por debajo de 0,8 en el punto de control 400. Si tu pérdida comienza por encima de 2,0 o no disminuye después de 50 iteraciones, comprueba la preparación de los datos y la tasa de aprendizaje.
Añadir comprobaciones de validación periódicas y puntos de control.
Guardar los pesos cada 200 iteraciones crea instantáneas que puedes comparar. Como se ha visto en los resultados del entrenamiento anteriores, el punto de control 400 generaliza mejor que el punto de control final, a pesar de la mayor pérdida de entrenamiento. Guarda siempre varios puntos de control y evalúalos con datos retenidos. Los puntos de control persisten en los servidores de Tinker una vez que finaliza tu script.
# Compute validation loss periodically
if iteration % validation_interval == 0:
val_loss = compute_validation_loss(val_data)
val_losses.append((iteration, val_loss))
gap = val_loss - per_token_loss
print(f" 📊 Iteration {iteration} | Train: {per_token_loss:.4f} | Val: {val_loss:.4f} | Gap: {gap:+.4f}")
# Checkpoint every 200 iterations
if iteration > 0 and iteration % checkpoint_interval == 0:
training_client.save_weights_for_sampler(name=f"fincot-checkpoint-{iteration}")

Tinker organiza los puntos de control en dos categorías: puntos de control de estado completo (para reanudar el entrenamiento si se interrumpe) y pesos de muestreo (puntos de control ligeros para la inferencia). Observa que checkpoint-400 aparece en ambos formatos, lo que facilita tanto continuar con el entrenamiento como implementar el modelo.
Si tu entrenamiento se interrumpe, puedes reanudarlo desde cualquier punto de control guardado utilizando los pesos de estado completo, lo que te permite continuar el entrenamiento sin tener que empezar de nuevo.
Para obtener una referencia completa de la API sobre estos métodos, consulta la documentación de formación y muestreo de Tinker.
Paso 6: Guarda tu modelo ajustado
Una vez completado el entrenamiento, guarda los pesos finales del modelo:
sampling_client = training_client.save_weights_and_get_sampling_client(
name="financial-qa-qwen3-8b-lora"
)
Los pesos se guardan en los servidores de Tinker, no en tu ordenador. Los adaptadores LoRA suelen requerir solo decenas de megabytes. Tinker puede exportarlos opcionalmente para su uso con proveedores de inferencia externos si deseas implementar el modelo fuera de su infraestructura.
Paso 7: Prueba el modelo con preguntas de muestra.
Por último, prueba tu modelo ajustado utilizando la primitiva API sample. Primero, configura los parámetros de muestreo:
sampling_params = types.SamplingParams(
max_tokens=200,
temperature=0.7,
top_p=0.9,
stop_sequences=["<|im_end|>"]
)
Explicaremos estos parámetros:
max_tokens: Longitud máxima de la respuesta generada (200 tokens equivalen aproximadamente a 150 palabras).temperature: Controla la aleatoriedad en la generación. Los valores más bajos (0,1-0,5) hacen que el resultado sea más concreto y determinista; los valores más altos (0,7-1,0) permiten una mayor creatividad. Un valor de 0,7 equilibra la coherencia con la variedad.top_p(muestreo del núcleo): Solo considera el conjunto más pequeño de tokens cuya probabilidad acumulativa supera este valor. 0,9 significa «considerar solo los tokens que, en conjunto, representan el 90 % de la masa de probabilidad». Esto evita que el modelo elija tokens muy improbables.stop_sequences: Indica al modelo que deje de generar cuando llegue a esta secuencia.<|im_end|>es el marcador de fin de secuencia.
Generar respuestas para preguntas de prueba:
questions = [
"What are the main risks associated with investing in stocks?",
"How does diversification help reduce portfolio risk?"
]
for question in questions:
model_input = types.ModelInput.from_ints(tokenizer.encode(question))
response = with_retry(
sampling_client.sample(
prompt=model_input,
num_samples=1,
sampling_params=sampling_params
)
)
answer = tokenizer.decode(response.sequences[0].tokens)
print(f"Q: {question}")
print(f"A: {answer}\n")
A continuación se muestran dos ejemplos de resultados del modelo ajustado (truncados a efectos ilustrativos):
Q: What are the main risks associated with investing in stocks?
A: Investing in stocks carries several key risks. Market risk represents the potential for stock prices to decline due to economic downturns or market volatility. Company-specific risk arises from poor management decisions, competitive pressures, or operational failures that affect individual stocks. Liquidity risk occurs when you cannot sell shares quickly without significantly impacting the price. Additionally, investors face inflation risk, where returns may not keep pace with rising prices...
Q: How does diversification help reduce portfolio risk?
A: Diversification reduces portfolio risk by spreading investments across different asset classes, sectors, and geographic regions. When you hold multiple uncorrelated assets, losses in one investment can be offset by gains in others. For example, if technology stocks decline but healthcare stocks rise, a diversified portfolio experiences less volatility than one concentrated in technology alone. The key principle is that different assets respond differently to market conditions...
El objeto de respuesta contiene una lista de sequences, ya que puedes solicitar varias muestras por cada solicitud. Accede a los tokens a través de sequences[0].tokens y decodifícalos de nuevo a texto utilizando el mismo tokenizador.
Ahora has demostrado correctamente las cuatro primitivas API básicas:
forward_backward: Calcula los gradientes a partir de tus datos de entrenamiento.optim_step: Actualizar los parámetros del modelo con el optimizador Adam.save_weights_and_get_sampling_client: Persiste en tu modelo ajustadosample: Generar predicciones a partir del modelo ajustado
Este mismo patrón de flujo de trabajo se extiende a paradigmas de entrenamiento más avanzados. Para el aprendizaje por refuerzo o la optimización de preferencias, se utilizarían las mismas primitivas, pero con diferentes funciones de pérdida y formatos de datos. Si te interesa alinear los modelos con las preferencias humanas, echa un vistazo a las técnicas de ajuste de preferencias, que se basan en los mismos fundamentos. El libro de recetas de Tinker proporciona ejemplos para estos escenarios avanzados.
Pero, ¿cómo sabes si el ajuste realmente mejoró el rendimiento? En lugar de basarte en impresiones subjetivas, necesitas una evaluación rigurosa. En la siguiente sección, verás los resultados de una evaluación LLM-as-judge que compara el modelo ajustado con el Qwen3-8B base en 10 preguntas financieras diversas.
Evaluación del modelo ajustado con un juez LLM
Para medir el rendimiento de forma objetiva, utilizamos LLM como juez: GPT-4o evaluó checkpoint-400 frente a la base Qwen3-8B en 10 preguntas financieras diversas, puntuando cada respuesta en cuanto a precisión, claridad, exhaustividad y terminología financiera.
El modelo perfeccionado obtuvo una puntuación de 8,5/10, frente al 6,5 del modelo básico, y ganó las 10 comparaciones. Los veredictos revelan el motivo: ofrece explicaciones completas con un razonamiento seguro y estructurado, mientras que la base se cubre con expresiones como «creo» y «quizás», dejando los pensamientos incompletos.
Esto confirma el mejor rendimiento real del checkpoint-400. La menor pérdida de entrenamiento en la iteración 900 nos habría proporcionado memorización, no razonamiento.
Consulta el script comparativo y los resultados detallados para realizar evaluaciones similares en tus modelos.
Conclusión
Ya has visto cómo Tinker simplifica el ajuste fino sin sacrificar el control. Cuatro primitivas API básicas, forward_backward, optim_step, save_weights_and_get_sampling_client y sample, te proporcionan los componentes básicos para crear flujos de trabajo de formación personalizados, mientras que Tinker se encarga de la infraestructura distribuida.
El modelo financiero de preguntas y respuestas que hemos creado muestra las mejores prácticas de producción: el seguimiento de la validación detectó el sobreajuste en una fase temprana, el punto de control 400 superó al modelo final al centrarse en la generalización, y la evaluación LLM-as-judge confirmó una mejora de 2 puntos con respecto al modelo base. Estos patrones se aplican tanto si estás ajustando modelos de parámetros 8B como 70B.
La plataforma está en fase beta con acceso mediante lista de espera, pero los conceptos que has aprendido se transfieren directamente una vez que obtienes acceso. Las ejecuciones de entrenamiento que realicé para este tutorial (incluidas las fallidas y las experimentales) costaron 150 créditos iniciales, por lo que con los créditos iniciales puedes ejecutar este tutorial seis veces más experimentos adicionales, lo que te da mucho margen para aprender y repetir tus propios proyectos de ajuste.
¿Qué sigue? Prueba a ajustar los datos específicos de tu dominio, experimenta con diferentes rangos LoRA y tasas de aprendizaje, o explora paradigmas de entrenamiento avanzados, como el aprendizaje por refuerzo a partir de comentarios humanos. El libro de recetas de Tinker ofrece ejemplos para estos casos. Empieza a pensar ahora mismo en tu caso de uso: ¿qué comportamiento quieres que aprenda tu modelo?
Si deseas desarrollar un conjunto de habilidades más amplio en el entrenamiento y ajuste de los últimos modelos de IA para producción, no te pierdas el programa de Ingeniero asociado de IA para programadores.

Soy un creador de contenidos de ciencia de datos con más de 2 años de experiencia y uno de los mayores seguidores en Medium. Me gusta escribir artículos detallados sobre IA y ML con un estilo un poco sarcastıc, porque hay que hacer algo para que sean un poco menos aburridos. He publicado más de 130 artículos y un curso DataCamp, y estoy preparando otro. Mi contenido ha sido visto por más de 5 millones de ojos, 20.000 de los cuales se convirtieron en seguidores tanto en Medium como en LinkedIn.


