volver al blog

Temperatura, top-p y top-k: domar la aleatoriedad del modelo

llmsamplinginferencia

El mismo prompt, dos respuestas distintas

Lanzas la misma petición dos veces y el modelo contesta cosas diferentes. La primera reacción es pensar que algo falla. No falla: es que un LLM no elige la siguiente palabra, la sortea. En cada paso calcula una probabilidad para cada token posible y saca uno de esa lotería. Quien controla las reglas de la lotería eres tú, con tres parámetros que la mayoría deja en su valor por defecto sin saber qué hacen: temperatura, top-p y top-k.

Entender esas tres perillas es la diferencia entre un modelo que inventa cuando necesitas precisión y uno que repite lo mismo cuando le pides ideas. No es ajuste fino de experto; es higiene básica de cualquiera que sirva un LLM en producción.

De logits a probabilidades

Antes de elegir, el modelo produce un vector de puntuaciones crudas, una por cada token de su vocabulario: los logits. Esos números no suman uno ni son probabilidades todavía. La función softmax los convierte en una distribución: exponencia cada logit y lo normaliza para que el total sea uno. El token con mayor logit se lleva la mayor probabilidad, pero el resto también recibe su parte.

La clave está en ese paso de exponenciar, porque es justo donde entra la temperatura. Todo lo demás (top-k, top-p) actúa después, recortando la lista de candidatos antes de tirar el dado.

Temperatura: cuánto arriesga el modelo

La temperatura divide los logits antes del softmax. Con temperatura baja, las diferencias entre logits se agrandan: el token favorito se dispara y el modelo casi siempre elige lo más probable. Con temperatura alta, las diferencias se aplanan y tokens improbables ganan opciones reales de salir.

import numpy as np

logits = np.array([2.0, 1.0, 0.1])  # tres tokens candidatos

def softmax_con_temperatura(logits, t):
    z = logits / t                  # la temperatura escala los logits
    e = np.exp(z - z.max())         # -max por estabilidad numérica
    return e / e.sum()

print(softmax_con_temperatura(logits, 0.5))  # [0.87 0.12 0.02] -> conservador
print(softmax_con_temperatura(logits, 1.0))  # [0.66 0.24 0.10] -> por defecto
print(softmax_con_temperatura(logits, 1.5))  # [0.55 0.28 0.16] -> arriesgado

Fíjate en la primera columna: a temperatura 0,5 el token favorito acapara el 87 % de la probabilidad; a 1,5 baja al 55 % y los demás respiran. Temperatura 0 es el caso límite: elegir siempre el token más probable, sin azar. Ahí el modelo se vuelve determinista y repetible, que es justo lo que quieres cuando extraes un JSON o clasificas un ticket.

Top-k y top-p: recortar la lista antes de sortear

La temperatura reparte probabilidad, pero no impide que un token absurdo con una probabilidad diminuta acabe saliendo de vez en cuando. Para eso están los métodos de truncado, que descartan la cola larga de candidatos malos antes del sorteo.

Top-k se queda con los k tokens más probables y tira el resto. Sencillo, pero rígido: k = 40 puede ser demasiado generoso cuando solo hay una continuación sensata y demasiado corto cuando hay muchas igual de válidas.

Top-p (o nucleus sampling) es más listo: ordena los tokens por probabilidad y se queda con los que suman, acumulados, hasta p. Con p = 0,9, el modelo considera el conjunto mínimo de tokens que cubre el 90 % de la probabilidad. Si un token domina, la lista se queda en dos o tres; si hay muchos plausibles, se ensancha sola. Se adapta al contexto, y por eso es hoy el método por defecto en casi todas las APIs.

ParámetroQué haceSube el valor →Rango típico
TemperaturaEscala los logitsMás aleatorio y creativo0 – 1,2
Top-kDeja los k mejoresMás candidatos, más ruido20 – 100
Top-pDeja el núcleo del p %Más candidatos, más ruido0,8 – 0,95

No los toques todos a la vez

El error más común es mover temperatura y top-p al mismo tiempo buscando “más creatividad” y acabar con salidas incoherentes. Los dos regulan lo mismo desde ángulos distintos, así que interactúan de forma poco intuitiva. La regla práctica que a mí me funciona: fija uno y mueve el otro.

Para tareas donde importa el acierto (extracción de datos, clasificación, llamadas a herramientas, código), baja la temperatura a cerca de cero y olvídate de top-p. Quieres el token más probable, no una interpretación artística del esquema. Para tareas donde importa la variedad (ideas, borradores, títulos alternativos), sube la temperatura a un rango de 0,7 a 1,0 y deja top-p en torno a 0,9. Y si generas varias opciones de una tirada, súbela un poco más: preferible tres candidatos distintos que tres clones.

# Determinista: para JSON, clasificación, tool calling
resp = client.chat.completions.create(
    model="gpt-4o",
    messages=mensajes,
    temperature=0,        # sin azar; misma entrada, misma salida
)

# Creativo: para brainstorming, variantes de copy
resp = client.chat.completions.create(
    model="gpt-4o",
    messages=mensajes,
    temperature=0.9,      # arriesga
    top_p=0.9,            # pero sin candidatos absurdos
    n=3,                  # tres alternativas de una vez
)

La letra pequeña: determinista no es garantía

Temperatura 0 acerca la salida a lo determinista, pero no siempre la clava al cien por cien. En hardware paralelo el orden de las operaciones en coma flotante puede variar entre ejecuciones, y una diferencia mínima en los logits basta para que, en un empate, salga otro token. Si necesitas reproducibilidad estricta para depurar o para tests, fija también la semilla (seed) cuando la API la exponga y registra la respuesta del modelo. No te fíes de que “temperatura 0” signifique “idéntico siempre”.

Cómo empezar mañana

Antes de tocar el prompt por enésima vez, comprueba con qué parámetros de muestreo estás llamando al modelo. Si los tienes por defecto, muy probablemente estás pidiendo creatividad a una tarea que necesita precisión, o al revés.

Separa tus llamadas en dos perfiles y trátalos distinto: uno determinista a temperatura cero para todo lo que deba ser fiable y verificable, y otro creativo con temperatura alta y top-p sobre 0,9 para lo que deba ser variado. Cambia un parámetro cada vez y mide el efecto sobre casos reales, no sobre una impresión. La aleatoriedad de un LLM no es un defecto que aguantar; es una perilla, y ahora sabes en qué dirección girarla.