Phase 01 - Lesson 09
Teoría de la Información
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
La teoría de la información mide la sorpresa. Las funciones de pérdida se construyen sobre ella.
Tipo: Learn Lenguaje: Python Requisitos previos: Fase 1, Lección 06 (Probabilidad) Tiempo: ~60 minutos
Objetivos de Aprendizaje
- Calcular entropía, entropía cruzada y divergencia KL desde cero y explicar la relación entre ellas
- Derivar por que minimizar la pérdida de entropía cruzada equivale a maximizar la log-verosimilitud
- Calcular la información mutua entre features y un objetivo para clasificar la importancia de las features
- Explicar la perplejidad como el tamaño efectivo de vocabulario del que elige un modelo de lenguaje
El Problema
Llamas a CrossEntropyLoss() en cada modelo de clasificación que entrenas. Ves "perplejidad" en cada artículo de modelo de lenguaje. Lees sobre divergencia KL en VAEs, destilación y RLHF. Estos no son conceptos inconexos. Son todos la misma idea usando sombreros distintos.
La teoría de la información te da el lenguaje para razonar sobre incertidumbre, compresión y predicción. Claude Shannon la inventó en 1948 para resolver problemas de comunicación. Resulta que entrenar una red neuronal es un problema de comunicación: el modelo intenta transmitir la etiqueta correcta a través de un canal ruidoso de pesos aprendidos.
Esta lección construye cada fórmula desde cero para que veas de donde vienen y por que funcionan.
El Concepto
Contenido de Información (Sorpresa)
Cuando ocurre algo improbable, lleva más información. Una moneda que cae cara? No sorprendente. Ganar la lotería? Muy sorprendente.
El contenido de información de un evento con probabilidad p es:
I(x) = -log(p(x))
Usar log base 2 te da bits. Usar log natural te da nats. Misma idea, distintas unidades.
Event Probability Surprise (bits)
Fair coin heads 0.5 1.0
Rolling a 6 0.167 2.58
1-in-1000 event 0.001 9.97
Certain event 1.0 0.0
Los eventos ciertos llevan cero información. Ya sabias que ocurrirían.
Entropía (Sorpresa Promedio)
La entropía es la sorpresa esperada a lo largo de todos los resultados posibles de una distribución.
H(P) = -sum( p(x) * log(p(x)) ) for all x
Una moneda justa tiene entropía máxima para una variable binaria: 1 bit. Una moneda sesgada (99% cara) tiene baja entropía: 0,08 bits. Ya sabes lo que va a pasar, así que cada lanzamiento te dice casi nada.
Fair coin: H = -(0.5 * log2(0.5) + 0.5 * log2(0.5)) = 1.0 bit
Biased coin: H = -(0.99 * log2(0.99) + 0.01 * log2(0.01)) = 0.08 bits
La entropía mide la incertidumbre irreducible en una distribución. No puedes comprimir por debajo de ella.
Entropía Cruzada (La Función de Pérdida que Usas Cada Día)
La entropía cruzada mide la sorpresa promedio cuando usas la distribución Q para codificar eventos que en realidad provienen de la distribución P.
H(P, Q) = -sum( p(x) * log(q(x)) ) for all x
P es la distribución verdadera (las etiquetas). Q son las predicciones de tu modelo. Si Q coincide con P perfectamente, la entropía cruzada es igual a la entropía. Cualquier discrepancia la hace mayor.
En clasificación, P es un vector one-hot (la clase verdadera tiene probabilidad 1, todo lo demas 0). Esto simplifica la entropía cruzada a:
H(P, Q) = -log(q(true_class))
Esa es la fórmula completa de la pérdida de entropía cruzada para clasificación. Maximiza la probabilidad predicha de la clase correcta.
Divergencia KL (Distancia Entre Distribuciones)
La divergencia KL mide cuanta sorpresa extra obtienes al usar Q en lugar de P.
D_KL(P || Q) = sum( p(x) * log(p(x) / q(x)) ) for all x
= H(P, Q) - H(P)
La entropía cruzada es entropía más divergencia KL. Como la entropía de la distribución verdadera es constante durante el entrenamiento, minimizar la entropía cruzada es lo mismo que minimizar la divergencia KL. Estas empujando la distribución de tu modelo hacia la distribución verdadera.
La divergencia KL no es simétrica: D_KL(P || Q) != D_KL(Q || P). No es una métrica de distancia verdadera.
Información Mutua
La información mutua mide cuanto te dice conocer una variable sobre otra.
I(X; Y) = H(X) - H(X|Y)
= H(X) + H(Y) - H(X, Y)
Si X e Y son independientes, la información mutua es cero. Conocer una no te dice nada sobre la otra. Si están perfectamente correlacionadas, la información mutua es igual a la entropía de cualquiera de las variables.
En la selección de features, una alta información mutua entre una feature y el objetivo significa que la feature es útil. Una baja información mutua significa que es ruido.
Entropía Condicional
H(Y|X) mide cuanta incertidumbre permanece sobre Y después de observar X.
H(Y|X) = H(X,Y) - H(X)
Dos extremos:
- Si X determina completamente Y, entonces H(Y|X) = 0. Conocer X elimina toda la incertidumbre sobre Y. Ejemplo: X = temperatura en Celsius, Y = temperatura en Fahrenheit.
- Si X no te dice nada sobre Y, entonces H(Y|X) = H(Y). Conocer X no reduce tu incertidumbre en absoluto. Ejemplo: X = lanzamiento de moneda, Y = clima de mañana.
La entropía condicional es siempre no negativa y nunca excede H(Y):
0 <= H(Y|X) <= H(Y)
En machine learning, la entropía condicional aparece en los árboles de decisión. En cada división, el algoritmo elige la feature X que minimiza H(Y|X) -- la feature que elimina la mayor incertidumbre sobre la etiqueta Y.
Entropía Conjunta
H(X,Y) es la entropía de la distribución conjunta de X e Y juntos.
H(X,Y) = -sum sum p(x,y) * log(p(x,y)) for all x, y
Propiedad clave:
H(X,Y) <= H(X) + H(Y)
La igualdad se cumple cuando X e Y son independientes. Si comparten información, la entropía conjunta es menor que la suma de las entropias individuales. La entropía "faltante" es exactamente la información mutua.
graph TD
subgraph "Information Venn Diagram"
direction LR
HX["H(X)"]
HY["H(Y)"]
MI["I(X;Y)<br/>Mutual<br/>Information"]
HXgY["H(X|Y)<br/>= H(X) - I(X;Y)"]
HYgX["H(Y|X)<br/>= H(Y) - I(X;Y)"]
HXY["H(X,Y) = H(X) + H(Y) - I(X;Y)"]
end
HXgY --- MI
MI --- HYgX
HX -.- HXgY
HX -.- MI
HY -.- MI
HY -.- HYgX
HXY -.- HXgY
HXY -.- MI
HXY -.- HYgX
Las relaciones:
- H(X,Y) = H(X) + H(Y|X) = H(Y) + H(X|Y)
- I(X;Y) = H(X) - H(X|Y) = H(Y) - H(Y|X)
- H(X,Y) = H(X) + H(Y) - I(X;Y)
Información Mutua (Inmersión Profunda)
La información mutua I(X;Y) cuantifica cuanto reduce conocer una variable la incertidumbre sobre la otra.
I(X;Y) = H(X) - H(X|Y)
= H(Y) - H(Y|X)
= H(X) + H(Y) - H(X,Y)
= sum sum p(x,y) * log(p(x,y) / (p(x) * p(y)))
Propiedades:
- I(X;Y) >= 0 siempre. Nunca pierdes información al observar algo.
- I(X;Y) = 0 si y solo si X e Y son independientes.
- I(X;Y) = I(Y;X). Es simétrica, a diferencia de la divergencia KL.
- I(X;X) = H(X). Una variable comparte toda su información consigo misma.
Información mutua para selección de features. En ML, quieres features que sean informativas sobre el objetivo. La información mutua te da una forma fundamentada de clasificar features:
- Para cada feature X_i, calcula I(X_i; Y) donde Y es la variable objetivo.
- Clasifica las features por su puntaje de MI.
- Conserva las top k features.
Esto funciona para cualquier relación entre feature y objetivo -- lineal, no lineal, monótona o no. La correlación solo capta relaciones lineales. La MI capta todo.
| Method | Detects | Computational cost | Handles categorical? |
|---|---|---|---|
| Pearson correlation | Relaciones lineales | O(n) | No |
| Spearman correlation | Relaciones monótonas | O(n log n) | No |
| Mutual information | Cualquier dependencia estadística | O(n log n) con binning | Si |
Label Smoothing y Entropía Cruzada
La clasificación estándar usa objetivos duros: [0, 0, 1, 0]. La clase verdadera recibe probabilidad 1, todo lo demas recibe 0. El label smoothing los reemplaza con objetivos suaves:
soft_target = (1 - epsilon) * hard_target + epsilon / num_classes
Con epsilon = 0.1 y 4 clases:
- Hard target: [0, 0, 1, 0]
- Soft target: [0.025, 0.025, 0.925, 0.025]
Desde una perspectiva de teoría de la información, el label smoothing aumenta la entropía de la distribución objetivo. Los objetivos one-hot duros tienen entropía 0 -- no hay incertidumbre. Los objetivos suaves tienen entropía positiva.
Por que ayuda esto:
- Impide que el modelo lleve los logits a valores extremos (se necesitarian logits infinitos para coincidir perfectamente con un objetivo one-hot bajo entropía cruzada)
- Actúa como regularización: el modelo no puede tener 100% de confianza
- Mejora la calibración: las probabilidades predichas reflejan mejor la incertidumbre verdadera
- Reduce la brecha entre el comportamiento de entrenamiento e inferencia
La pérdida de entropía cruzada con label smoothing se convierte en:
L = (1 - epsilon) * CE(hard_target, prediction) + epsilon * H_uniform(prediction)
El segundo término penaliza las predicciones que están lejos de lo uniforme -- una regularización directa sobre la confianza.
Por que la Entropía Cruzada es LA Pérdida de Clasificación
Tres perspectivas, la misma conclusión.
Visión de la teoría de la información. La entropía cruzada mide cuantos bits desperdicias al usar la distribución de tu modelo en lugar de la distribución verdadera. Minimizarla hace que tu modelo sea el codificador más eficiente de la realidad.
Visión de máxima verosimilitud. Para N muestras de entrenamiento con clases verdaderas y_i:
Likelihood = product( q(y_i) )
Log-likelihood = sum( log(q(y_i)) )
Negative log-likelihood = -sum( log(q(y_i)) )
Esa última línea es la pérdida de entropía cruzada. Minimizar la entropía cruzada = maximizar la verosimilitud de los datos de entrenamiento bajo tu modelo.
Visión del gradiente. El gradiente de la entropía cruzada respecto a los logits es simplemente (predicted - true). Limpio, estable y rápido de calcular. Por eso combina perfectamente con softmax.
Bits vs Nats
La única diferencia es la base del log.
log base 2 -> bits (information theory tradition)
log base e -> nats (machine learning convention)
log base 10 -> hartleys (rarely used)
1 nat = 1/ln(2) bits = 1.4427 bits. PyTorch y TensorFlow usan log natural (nats) por defecto.
Perplejidad
La perplejidad es el exponencial de la entropía cruzada. Te dice el número efectivo de opciones igualmente probables entre las que el modelo está indeciso.
Perplexity = 2^H(P,Q) (if using bits)
Perplexity = e^H(P,Q) (if using nats)
Un modelo de lenguaje con perplejidad 50 está, en promedio, tan confundido como si tuviera que elegir uniformemente entre 50 posibles tokens siguientes. Menor es mejor.
GPT-2 alcanzó una perplejidad de ~30 en benchmarks comunes. Los modelos modernos están en un solo dígito para dominios bien representados.
Construye
Paso 1: Contenido de información y entropía
import math
def information_content(p, base=2):
if p <= 0 or p > 1:
return float('inf') if p <= 0 else 0.0
return -math.log(p) / math.log(base)
def entropy(probs, base=2):
return sum(
p * information_content(p, base)
for p in probs if p > 0
)
fair_coin = [0.5, 0.5]
biased_coin = [0.99, 0.01]
fair_die = [1/6] * 6
print(f"Fair coin entropy: {entropy(fair_coin):.4f} bits")
print(f"Biased coin entropy: {entropy(biased_coin):.4f} bits")
print(f"Fair die entropy: {entropy(fair_die):.4f} bits")
Paso 2: Entropía cruzada y divergencia KL
def cross_entropy(p, q, base=2):
total = 0.0
for pi, qi in zip(p, q):
if pi > 0:
if qi <= 0:
return float('inf')
total += pi * (-math.log(qi) / math.log(base))
return total
def kl_divergence(p, q, base=2):
return cross_entropy(p, q, base) - entropy(p, base)
true_dist = [0.7, 0.2, 0.1]
good_model = [0.6, 0.25, 0.15]
bad_model = [0.1, 0.1, 0.8]
print(f"Entropy of true dist: {entropy(true_dist):.4f} bits")
print(f"CE (good model): {cross_entropy(true_dist, good_model):.4f} bits")
print(f"CE (bad model): {cross_entropy(true_dist, bad_model):.4f} bits")
print(f"KL divergence (good): {kl_divergence(true_dist, good_model):.4f} bits")
print(f"KL divergence (bad): {kl_divergence(true_dist, bad_model):.4f} bits")
Paso 3: Entropía cruzada como pérdida de clasificación
def softmax(logits):
max_logit = max(logits)
exps = [math.exp(z - max_logit) for z in logits]
total = sum(exps)
return [e / total for e in exps]
def cross_entropy_loss(true_class, logits):
probs = softmax(logits)
return -math.log(probs[true_class])
logits = [2.0, 1.0, 0.1]
true_class = 0
probs = softmax(logits)
loss = cross_entropy_loss(true_class, logits)
print(f"Logits: {logits}")
print(f"Softmax: {[f'{p:.4f}' for p in probs]}")
print(f"True class: {true_class}")
print(f"Loss: {loss:.4f} nats")
print(f"Perplexity: {math.exp(loss):.2f}")
Paso 4: La entropía cruzada es igual a la log-verosimilitud negativa
import random
random.seed(42)
n_samples = 1000
n_classes = 3
true_labels = [random.randint(0, n_classes - 1) for _ in range(n_samples)]
model_logits = [[random.gauss(0, 1) for _ in range(n_classes)] for _ in range(n_samples)]
ce_loss = sum(
cross_entropy_loss(label, logits)
for label, logits in zip(true_labels, model_logits)
) / n_samples
nll = -sum(
math.log(softmax(logits)[label])
for label, logits in zip(true_labels, model_logits)
) / n_samples
print(f"Cross-entropy loss: {ce_loss:.6f}")
print(f"Negative log-likelihood: {nll:.6f}")
print(f"Difference: {abs(ce_loss - nll):.2e}")
Paso 5: Información mutua
def mutual_information(joint_probs, base=2):
rows = len(joint_probs)
cols = len(joint_probs[0])
margin_x = [sum(joint_probs[i][j] for j in range(cols)) for i in range(rows)]
margin_y = [sum(joint_probs[i][j] for i in range(rows)) for j in range(cols)]
mi = 0.0
for i in range(rows):
for j in range(cols):
pxy = joint_probs[i][j]
if pxy > 0:
mi += pxy * math.log(pxy / (margin_x[i] * margin_y[j])) / math.log(base)
return mi
independent = [[0.25, 0.25], [0.25, 0.25]]
dependent = [[0.45, 0.05], [0.05, 0.45]]
print(f"MI (independent): {mutual_information(independent):.4f} bits")
print(f"MI (dependent): {mutual_information(dependent):.4f} bits")
Usalo
Los mismos conceptos usando NumPy, tal como los usaras en la practica:
import numpy as np
def np_entropy(p):
p = np.asarray(p, dtype=float)
mask = p > 0
result = np.zeros_like(p)
result[mask] = p[mask] * np.log(p[mask])
return -result.sum()
def np_cross_entropy(p, q):
p, q = np.asarray(p, dtype=float), np.asarray(q, dtype=float)
mask = p > 0
return -(p[mask] * np.log(q[mask])).sum()
def np_kl_divergence(p, q):
return np_cross_entropy(p, q) - np_entropy(p)
true = np.array([0.7, 0.2, 0.1])
pred = np.array([0.6, 0.25, 0.15])
print(f"Entropy: {np_entropy(true):.4f} nats")
print(f"Cross-ent: {np_cross_entropy(true, pred):.4f} nats")
print(f"KL div: {np_kl_divergence(true, pred):.4f} nats")
Construiste desde cero lo que torch.nn.CrossEntropyLoss() hace internamente. Ahora sabes por que la pérdida baja durante el entrenamiento: la distribución predicha de tu modelo se está acercando a la distribución verdadera, medida en nats de información desperdiciada.
Ejercicios
Calcula la entropía del alfabeto inglés asumiendo distribución uniforme (26 letras). Luego estímala usando frecuencias reales de letras. ¿Cuál es mayor y por qué?
Un modelo genera logits [5.0, 2.0, 0.5] para una muestra con clase verdadera 1. Calcula la pérdida de entropía cruzada a mano, luego verifica con tu función
cross_entropy_loss. ¿Qué logits darían pérdida cero?Muestra que la divergencia KL no es simétrica. Elige dos distribuciones P y Q y calcula D_KL(P || Q) y D_KL(Q || P). Explica por que difieren.
Construye una función que calcule la perplejidad para una secuencia de predicciones de tokens. Dada una lista de pares (true_token_index, predicted_logits), devuelve la perplejidad de la secuencia.
Términos Clave
| Term | What people say | What it actually means |
|---|---|---|
| Information content | "Sorpresa" | El número de bits (o nats) necesarios para codificar un evento: -log(p) |
| Entropy | "Aleatoriedad" | La sorpresa promedio a lo largo de todos los resultados de una distribución. Mide la incertidumbre irreducible. |
| Cross-entropy | "La función de pérdida" | Sorpresa promedio al usar la distribución del modelo Q para codificar eventos de la distribución verdadera P. |
| KL divergence | "Distancia entre distribuciones" | Bits extra desperdiciados al usar Q en lugar de P. Igual a la entropía cruzada menos la entropía. No simétrica. |
| Mutual information | "Que tan relacionados están X e Y" | Reducción en la incertidumbre sobre X al conocer Y. Cero significa independiente. |
| Softmax | "Convertir logits en probabilidades" | Exponencia y normaliza. Mapea cualquier vector de valores reales a una distribución de probabilidad válida. |
| Perplexity | "Que tan confundido esta el modelo" | Exponencial de la entropía cruzada. El tamaño efectivo de vocabulario del que elige el modelo en cada paso. |
| Bits | "La unidad de Shannon" | Información medida con log base 2. Un bit resuelve un lanzamiento de moneda justa. |
| Nats | "La unidad del ML" | Información medida con log natural. Usada por PyTorch y TensorFlow por defecto. |
| Negative log-likelihood | "Pérdida NLL" | Idéntica a la pérdida de entropía cruzada para etiquetas one-hot. Minimizarla maximiza la probabilidad de predicciones correctas. |
Lectura Adicional
- Shannon 1948: A Mathematical Theory of Communication - el artículo original, todavía legible
- Visual Information Theory (Chris Olah) - la mejor explicación visual de entropía y divergencia KL
- PyTorch CrossEntropyLoss docs - como el framework implementa lo que acabas de construir