← Todo el material
Curso Avanzado 23 de agosto de 2026

Transformers — Módulo 9: decodificación — de los logits al texto (temperature, top-p, beam)

Noveno módulo del Curso de Transformers: cómo el modelo elige el token concreto que escribe. Greedy y sampling, temperature, top-k y top-p (nucleus), beam search y repetition penalty. Código real filtrando logits, la tabla de qué estrategia usar según la tarea y el embudo de logits a token.

☰ Índice del contenido

En el módulo 8 alineamos el modelo con RLHF: ya responde con criterio y buenas maneras. Pero hay un eslabón que dimos por sentado desde el principio y que nadie te muestra: cuando el modelo termina de pensar, no escribe un token — escribe una distribución de probabilidad sobre TODO su vocabulario. Cincuenta mil números que dicen “creo que el próximo token es este con 12%, este otro con 8%, este con 0,3%…”. Alguien tiene que elegir uno solo. Ese alguien es el decodificador, y la estrategia que use cambia por completo si el modelo suena creativo, repetitivo, robótico o brillante. Hoy abrimos esa caja.

La tesis del módulo

El modelo no decide qué palabra escribir: decide qué tan probable es cada palabra. La decisión final la toma la estrategia de decodificación, que vive fuera del modelo. El mismo modelo, con los mismos pesos, puede sonar aburrido o genial según cómo elijas el token — y eso lo controlas tú.

Del último vector a los logits

Recordemos dónde quedamos. El Transformer procesa la secuencia y, en la última capa, produce para la posición actual un vector de d_model números. Ese vector se multiplica por una matriz final (la de salida) y da un logit por cada token del vocabulario: un puntaje crudo, sin normalizar, que puede ser negativo o enorme.

Los logits (puntajes crudos) no son probabilidades todavía. Para volverlos probabilidades se aplica softmax (función que convierte una lista de números en porcentajes que suman 100%): exagera las diferencias y garantiza que todo sume 1.

             vector final del token
                     │
                     ▼   matriz de salida
  logits:  [ 3.2,  1.1,  -0.5,  ...,  2.8 ]  (50000 nums)
                     │
                     ▼   softmax
  probs:   [ 0.12, 0.02, 0.001, ..., 0.09 ]  (suman 1.0)

Ahora tenemos una probabilidad por token. La pregunta del módulo es simple de enunciar y sorprendentemente profunda: ¿cuál de estos 50.000 elegimos?

Estrategia 1: greedy — siempre el más probable

La opción obvia: elegir el token con la probabilidad más alta. Se llama greedy (codicioso, “agarra lo mejor ahora mismo”). Es rápido y determinista — con el mismo prompt siempre sale lo mismo.

import numpy as np

def greedy(logits):
    # logits: vector de puntajes crudos, uno por token del vocabulario
    return int(np.argmax(logits))   # el índice del más alto

El problema es doble. Primero, greedy es miope: elegir el mejor token AHORA no garantiza la mejor frase completa. La palabra más probable en la posición 3 puede acorralar al modelo en una frase mediocre en la posición 7. Segundo, greedy es aburrido y repetitivo: al no haber azar, el modelo cae en bucles (“el gato y el gato y el gato…”) y suena robótico. Para un chat, greedy casi nunca es lo que quieres.

Estrategia 2: sampling — tirar el dado con las probabilidades

La alternativa es sampling (muestreo): en vez de agarrar siempre el máximo, tiramos un dado cargado con las probabilidades del modelo. Si “gato” tiene 12% y “perro” 8%, “gato” sale más seguido, pero “perro” también aparece a veces. Eso introduce variedad — y la variedad es lo que hace que un modelo suene humano.

El sampling puro tiene un peligro: la cola larga. Entre 50.000 tokens hay miles con probabilidad diminuta (0,001%) pero que, sumados, tienen chance real de salir. Y un token absurdo elegido por mala suerte puede descarrilar toda la respuesta. Necesitamos controlar cuánto azar permitimos. Ahí entran las tres perillas del oficio.

Perilla 1: temperature — subir o bajar el azar

La temperature (temperatura) escala los logits antes del softmax: divide cada logit por un número T. Es la perilla maestra del azar.

  • T baja (0.2): las diferencias entre logits se agrandan → el softmax se vuelve muy picudo → casi siempre gana el más probable. Conservador, predecible, casi greedy.
  • T = 1.0: no cambia nada, las probabilidades originales del modelo.
  • T alta (1.5): las diferencias se achican → la distribución se aplana → tokens raros ganan chance. Creativo, sorprendente, a veces incoherente.
  logits crudos → dividir por T → softmax

  T = 0.3 (picudo):    ▁▁█▁▁▁▁   casi siempre el mismo
  T = 1.0 (original):  ▁▂▆▃▂▁▁   la distribución real
  T = 1.8 (plano):     ▃▄▅▄▄▃▃   casi lotería

Ojo: temperature no es un dial de “inteligencia”. Subirla no hace al modelo más listo, solo más azaroso. Pasado cierto punto solo agrega ruido y errores.

Perilla 2: top-k — cortar a los k mejores

top-k sampling limita el sorteo a los k tokens más probables y descarta el resto. Con k=50, el modelo solo puede elegir entre sus 50 mejores candidatos; los otros 49.950 quedan fuera aunque juntos sumaran algo. Simple y efectivo para matar la cola larga.

Su debilidad: k es fijo y el contexto no. A veces el modelo está segurísimo (solo 2 tokens tienen sentido) y k=50 mete 48 tokens de basura. Otras veces hay 200 continuaciones válidas y k=50 corta buenas opciones. El corte rígido no se adapta.

Perilla 3: top-p (nucleus) — cortar por masa de probabilidad

top-p sampling, también llamado nucleus sampling (muestreo por núcleo), arregla eso. En vez de un número fijo de tokens, toma los tokens más probables hasta acumular una masa de probabilidad p (por ejemplo 0.9 = 90%). Si el modelo está seguro, el 90% se junta con 2 tokens y se queda con 2. Si está dudoso, necesita 40 tokens para llegar al 90% y se queda con 40. El corte se adapta solo a la confianza del modelo — por eso top-p es hoy el estándar para chat.

  probs ordenadas de mayor a menor, corte p = 0.90

  ┌────┬────┬────┬────┬────┬────┬─────────────┐
  │0.40│0.25│0.15│0.08│0.05│... │  descartado │
  └────┴────┴────┴────┴────┴────┴─────────────┘
   \........ suma ≥ 0.90 ........./
      el "núcleo": solo de aquí se muestrea

Perilla 4: repetition penalty — castigar lo ya dicho

Aun con todo lo anterior, los modelos tienden a repetirse. La repetition penalty (penalización por repetición) baja el logit de los tokens que ya aparecieron en el texto, haciéndolos menos probables. Un valor típico es 1.1–1.3: suficiente para romper bucles sin volver antinatural al modelo (que a veces debe repetir, como en “sí, sí, sí”).

El código real: temperature + top-p sobre logits

Así se ve el embudo completo en Python, paso a paso y comentado:

import numpy as np

def softmax(x):
    x = x - np.max(x)            # estabilidad numérica
    e = np.exp(x)
    return e / e.sum()

def sample_top_p(logits, temperature=0.8, top_p=0.9):
    # 1) temperature: escala los logits (T baja = conservador)
    logits = logits / temperature

    # 2) softmax: logits crudos -> probabilidades
    probs = softmax(logits)

    # 3) ordenar de mayor a menor y acumular
    orden = np.argsort(probs)[::-1]        # índices, mejor primero
    probs_ord = probs[orden]
    acum = np.cumsum(probs_ord)

    # 4) quedarse con el núcleo: hasta acumular top_p
    corte = np.searchsorted(acum, top_p) + 1
    nucleo_idx = orden[:corte]
    nucleo_p = probs_ord[:corte]
    nucleo_p = nucleo_p / nucleo_p.sum()   # renormalizar a 1

    # 5) tirar el dado cargado solo dentro del núcleo
    return int(np.random.choice(nucleo_idx, p=nucleo_p))

# Ejemplo: 50k logits crudos que salieron del modelo
logits = np.random.randn(50000)
token_id = sample_top_p(logits, temperature=0.8, top_p=0.9)

Fíjate en el orden: temperature primero (reforma la distribución), top-p después (recorta la cola), muestreo al final. Ese es el pipeline que corre por dentro cada vez que ChatGPT o Claude escriben una palabra.

Estrategia 3: beam search — para tareas deterministas

Todo lo anterior elige un token a la vez, sin vuelta atrás. beam search (búsqueda por haces) hace otra cosa: mantiene vivas las b mejores secuencias parciales a la vez (los “haces”), las extiende todas, y se queda con las b mejores combinaciones acumuladas. Al final elige la secuencia completa con mayor probabilidad total, no la que fue mejor token a token.

Eso lo vuelve ideal para tareas donde hay una respuesta correcta y quieres la más probable de verdad: traducción, resumen fiel, corrección gramatical. Su costo: es más lento (explora b caminos), y para chat creativo produce texto plano y aburrido — porque optimiza probabilidad, no interés. Por eso el chat usa sampling y la traducción usa beam.

El embudo completo: de logits a token

  START
    │
    ▼
  ┌──────────────────────────────┐
  │ logits crudos (50000 nums)   │
  └──────────────────────────────┘
    │
    ▼   dividir por temperature
  ┌──────────────────────────────┐
  │ logits escalados + softmax   │
  └──────────────────────────────┘
    │
    ▼   filtrar cola: top-k o top-p
  ┌──────────────────────────────┐
  │ solo el núcleo de candidatos │
  └──────────────────────────────┘
    │
    ▼   muestrear (dado cargado)
  ┌──────────────────────────────┐
  │ 1 token elegido              │
  └──────────────────────────────┘
    │
    ▼
  END  (y vuelve para el siguiente token)

¿Cuál uso? La tabla de decisión

TareaEstrategiaAjustes típicos
Chat generaltop-p + temperatureT ≈ 0.7, p ≈ 0.9
Escritura creativatop-p + temperature altaT ≈ 1.0–1.2, p ≈ 0.95
Generar códigogreedy o T muy bajaT ≈ 0.0–0.2
Respuesta factual / QAT bajaT ≈ 0.1–0.3
Traducciónbeam searchb ≈ 4–8
Datos estructurados (JSON)greedyT = 0

La regla mental: cuando hay una respuesta correcta, baja el azar (código, datos, traducción). Cuando quieres variedad, súbelo (chat, creatividad). Todo lo demás es afinar.

Cuidado: T = 0 no es “sampling con temperatura cero” (eso sería dividir por cero). Las librerías lo interpretan como greedy: sáltate el sorteo y agarra el máximo. Cuando pides “respuestas deterministas” en una API, esto es lo que pasa por debajo.

Errores comunes

  • Subir temperature esperando más inteligencia. Solo agregas azar. Para tareas exactas, bájala; no la subas.
  • Usar greedy para chat. Determinista pero repetitivo y miope; el resultado suena a robot atascado.
  • Combinar top-k y top-p sin pensar. Se pueden apilar, pero para chat top-p solo suele bastar. Dos filtros mal calibrados recortan demasiado.
  • Esperar reproducibilidad con sampling. Si hay azar (T > 0 y sin semilla fija), la misma pregunta da respuestas distintas. Para reproducir, fija la semilla o usa greedy.
  • Usar beam search para creatividad. Optimiza probabilidad total → texto correcto pero soso. Beam es para traducir, no para inventar.

La frase para llevarse

“El modelo te entrega 50.000 probabilidades; el token que ves es una decisión tuya disfrazada de decisión del modelo. Temperature dosifica el azar, top-p recorta la cola, y beam busca la mejor frase entera: elegir bien la perilla es la mitad de la calidad.”

Qué viene

Módulo 10: contexto largo, KV cache y FlashAttention. Ya sabes cómo se elige cada token — pero generar el token número mil es mucho más caro que el primero. Veremos por qué la atención cuesta O(n²), cómo el KV cache evita recomputar todo en cada paso, y por qué FlashAttention cambió las reglas de la memoria.

Material relacionado

TransformersLLMDecodificaciónSamplingInferencia

Preguntas sobre este curso

Regístrate gratis para hacer preguntas y guardar tu progreso.

✉ Entérate de lo nuevo

Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.