¿Por qué necesitamos probabilidad en deep learning?

El machine learning es, en esencia, la matemática de la incertidumbre. Un modelo de lenguaje no produce una sola palabra siguiente — produce una distribución de probabilidad sobre cada palabra de su vocabulario, y muestreamos de esa distribución para generar texto. Un clasificador no declara "esto es un gato" — asigna probabilidades como $[0.92, 0.05, 0.03]$ entre las clases, y elegimos la más probable. Incluso durante el entrenamiento, la función de pérdida que guía el aprendizaje (cross-entropy) se define completamente en términos de probabilidad: mide qué tan bien la distribución predicha por el modelo se ajusta a la verdadera.

Las distribuciones de probabilidad no son solo salidas — están entretejidas en la estructura de cómo se construyen y optimizan los modelos. La función softmax convierte logits en bruto en una distribución de probabilidad válida. Los esquemas de inicialización de pesos (Glorot, He) muestrean valores iniciales de parámetros de distribuciones cuidadosamente elegidas. Los modelos de difusión generan imágenes añadiendo y eliminando sistemáticamente ruido gaussiano. Y QLoRA usa la cuantización NF4 con la CDF inversa de la distribución normal para colocar niveles de cuantización donde los pesos del modelo son más densos.

Entonces, ¿qué es exactamente una distribución de probabilidad? ¿Cómo la describimos matemáticamente? ¿Y cómo calculamos cantidades útiles a partir de ella — como la probabilidad de que un valor caiga en algún rango, o el valor por debajo del cual se encuentra cierta fracción de los resultados? Este artículo construye esos fundamentos. Comenzaremos con la distinción entre distribuciones discretas y continuas, desarrollaremos las herramientas fundamentales (PDF, CDF y la función cuantil), y nos centraremos en profundidad en la distribución normal — la distribución más importante en deep learning.

Distribuciones discretas vs continuas

Antes de sumergirnos en fórmulas, necesitamos distinguir dos tipos fundamentalmente diferentes de variables aleatorias, porque requieren herramientas matemáticas distintas.

Una distribución discreta tiene un conjunto finito (o infinito numerable) de resultados posibles. Lanzar un dado da uno de $\{1, 2, 3, 4, 5, 6\}$. La predicción del siguiente token en un modelo de lenguaje selecciona de un vocabulario de, digamos, 50,000 tokens. En estos casos, podemos asignar una probabilidad específica a cada resultado: $P(X = 3) = \frac{1}{6}$ para un dado justo, o $P( ext{next token} = ext{"the"}) = 0.12$ para un modelo de lenguaje. Esta asignación de probabilidades a resultados individuales se llama función de masa de probabilidad (PMF) .

Una distribución continua tiene resultados sobre un rango de valores reales — una infinidad no numerable de valores posibles. Los valores de los pesos en una red neuronal son continuos. El ruido añadido durante la difusión es continuo. Un pronóstico de temperatura es continuo. Aquí es donde las cosas se vuelven conceptualmente complicadas: ¿cuál es la probabilidad de que un peso sea exactamente $0.314159265...$, con infinitos decimales coincidentes? Es cero. No aproximadamente cero — exactamente cero. Hay una cantidad no numerable de números reales posibles, y si cada uno tuviera incluso una pequeña probabilidad positiva, el total excedería 1 (violando el axioma de que todas las probabilidades deben sumar 1). Así que $P(X = ext{any specific value}) = 0$ para variables aleatorias continuas.

Esto significa que no podemos usar una PMF para distribuciones continuas. Necesitamos una herramienta diferente: una que nos diga dónde es probable que se agrupen los valores sin asignar probabilidad a ningún punto individual. Esa herramienta es la función de densidad de probabilidad.

La función de densidad de probabilidad (PDF)

Una función de densidad de probabilidad (PDF) , escrita $f(x)$, describe la probabilidad relativa de que una variable aleatoria continua tome un valor dado. La palabra clave es densidad — así como la densidad de masa indica cuánta masa está empaquetada en una unidad de volumen, la densidad de probabilidad indica cuánta probabilidad está empaquetada en una unidad de $x$. Una densidad alta en un punto significa que es probable que los valores se agrupen cerca de él. Una densidad baja significa que es poco probable encontrar valores allí.

Pero aquí está la idea clave que confunde a muchos estudiantes: $f(x)$ no es una probabilidad. No puedes decir "la probabilidad de $X = 3$ es $f(3)$." Las probabilidades provienen de las áreas bajo la curva . Para obtener una probabilidad real, debes integrar la PDF sobre un intervalo:

$$P(a \leq X \leq b) = \int_a^b f(x) \, dx$$

Desglosemos esto.

$P(a \leq X \leq b)$ es la probabilidad de que la variable aleatoria $X$ tome un valor entre $a$ y $b$. Esto sí es una probabilidad propiamente dicha — un número entre 0 y 1.

$\int_a^b f(x) \, dx$ es el área bajo la curva $f(x)$ entre $a$ y $b$. Geométricamente, estás sombreando la región entre la curva de la PDF y el eje $x$ desde $a$ hasta $b$, y calculando su área. Cuanto más ancho sea el intervalo o más alta la PDF en esa región, mayor será la probabilidad.

Ahora tracemos el comportamiento en los límites. A medida que el intervalo se reduce a un solo punto ($a = b$), el ancho de la región tiende a cero, y por lo tanto el área tiende a cero: $\int_a^a f(x) \, dx = 0$. Esto confirma lo que dijimos antes — $P(X = ext{exactly } c) = 0$ para cualquier distribución continua. No importa cuán alta sea la PDF en un punto, una región de ancho cero tiene área cero. En el otro extremo, integrar sobre toda la recta real debe dar 1, porque la probabilidad total de que la variable aleatoria tome algún valor es segura:

$$\int_{-\infty}^{\infty} f(x) \, dx = 1$$

Esto nos lleva a las dos propiedades que toda PDF válida debe satisfacer:

  • $f(x) \geq 0$ for all $x$ — la densidad no puede ser negativa. Una probabilidad negativa no tiene sentido.
  • $\int_{-\infty}^{\infty} f(x) \, dx = 1$ — la probabilidad total en todos los valores posibles debe ser igual a 1. Algo debe ocurrir.
📌 $f(x)$ PUEDE ser mayor que 1. Esto sorprende a muchas personas, pero recuerda: $f(x)$ es una densidad, no una probabilidad. Una distribución muy estrecha y puntiaguda puede tener $f(x) > 1$ cerca de su pico, siempre y cuando el área total bajo la curva siga siendo exactamente 1. Por ejemplo, una distribución uniforme en $[0, 0.5]$ tiene $f(x) = 2$ en todo su soporte — una densidad de 2, pero el área total es $2 imes 0.5 = 1$. Perfectamente válido.

El gráfico a continuación muestra la PDF de la distribución normal estándar (la campana de Gauss). La región sombreada entre $x = -1$ y $x = 1$ representa $P(-1 \leq X \leq 1)$ — aproximadamente el 68% del área total. Observa cómo la densidad alcanza su máximo en $x = 0$ y decae simétricamente en ambas direcciones.

import math, json, js

def norm_pdf(x, mu=0, sigma=1):
    """PDF of the normal distribution."""
    coeff = 1.0 / (sigma * math.sqrt(2 * math.pi))
    exponent = -((x - mu) ** 2) / (2 * sigma ** 2)
    return coeff * math.exp(exponent)

# Generate x values from -4 to 4
x_vals = [i * 0.05 for i in range(-80, 81)]  # -4.0 to 4.0 in steps of 0.05

# Full PDF curve
y_full = [norm_pdf(x) for x in x_vals]

# Shaded region: PDF between -1 and 1, zero elsewhere
y_shaded = [norm_pdf(x) if -1 <= x <= 1 else 0 for x in x_vals]

plot_data = [
    {
        "title": "Standard Normal PDF with P(-1 ≤ X ≤ 1) ≈ 0.683 shaded",
        "x_label": "x",
        "y_label": "f(x)",
        "x_data": x_vals,
        "lines": [
            {"label": "N(0,1) PDF", "data": y_full, "color": "#3b82f6"},
            {"label": "P(-1 ≤ X ≤ 1)", "data": y_shaded, "color": "#93c5fd", "fill": True}
        ]
    }
]
js.window.py_plot_data = json.dumps(plot_data)

La distribución normal (gaussiana)

La distribución normal es la distribución más importante en estadística y deep learning. Su curva en forma de campana aparece en todas partes: en la distribución de errores de medición, en los valores iniciales de los pesos de redes neuronales, en el ruido añadido por los modelos de difusión, y en la distribución empírica de los pesos de modelos preentrenados (razón por la cual la NF4 quantization funciona tan bien). Comprender su fórmula es esencial porque es el bloque fundamental de tantas técnicas que encontrarás.

La distribución normal está parametrizada por dos valores: la media $\mu$ y la desviación estándar $\sigma$. Su PDF es:

$$f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left(-\frac{(x - \mu)^2}{2\sigma^2}\right)$$

Esta fórmula parece intimidante, pero cada parte tiene un propósito claro. Desglosémoslas una por una.

$\mu$ (mu) — la media. Este es el centro de la campana: el valor donde se ubica el pico. Desplazar $\mu$ a la izquierda o derecha desliza toda la distribución a lo largo del eje $x$ sin cambiar su forma. En deep learning, cuando decimos que los pesos se inicializan desde $\mathcal{N}(0, \sigma)$, el $\mu = 0$ significa que los pesos iniciales están centrados alrededor de cero.

$\sigma$ (sigma) — la desviación estándar. Esto controla el ancho de la campana. Un $\sigma$ pequeño significa que los valores se agrupan estrechamente alrededor de la media (una campana alta y estrecha), mientras que un $\sigma$ grande significa que se dispersan ampliamente (una campana baja y ancha). La varianza $\sigma^2$ aparece en la fórmula como la unidad natural de dispersión.

$\frac{1}{\sigma\sqrt{2\pi}}$ — la constante de normalización. Este factor asegura que el área total bajo la curva sea exactamente 1 (lo que la convierte en una PDF válida). Sin él, cambiar $\sigma$ cambiaría el área total. Cuando $\sigma$ es pequeño, la campana es estrecha pero aún debe tener área 1, así que este factor la hace alta. Cuando $\sigma$ es grande, la campana es ancha, así que este factor la hace baja. Es puramente un término contable que normaliza la probabilidad total a 1.

$-\frac{(x - \mu)^2}{2\sigma^2}$ — el exponente. Este es el corazón de la fórmula. Es una distancia al cuadrado desde la media, escalada por la varianza, con un signo negativo. $(x - \mu)^2$ mide qué tan lejos está $x$ del centro — siempre es no negativo y simétrico (estar 5 unidades por encima de la media se trata igual que 5 unidades por debajo). Dividir por $2\sigma^2$ normaliza esta distancia por la dispersión: una desviación de 1 en una distribución con $\sigma = 0.5$ es significativa (exponente = $-2$), pero en una distribución con $\sigma = 10$ es despreciable (exponente = $-0.005$). El signo negativo significa que los puntos lejanos de $\mu$ obtienen un exponente muy negativo, lo que lleva a $\exp(\cdot)$ hacia cero.

Ahora tracemos el comportamiento en los límites:

  • En $x = \mu$: el exponente es $-\frac{(\mu - \mu)^2}{2\sigma^2} = 0$, así que $\exp(0) = 1$, y $f(\mu) = \frac{1}{\sigma\sqrt{2\pi}}$. Este es el pico de la campana. Observa que la altura del pico es inversamente proporcional a $\sigma$ — una distribución más concentrada tiene un pico más alto.
  • Cuando $|x - \mu| o \infty$: el exponente tiende a $-\infty$, así que $f(x) o 0$. Las colas de la campana decaen hacia cero, pero nunca llegan a alcanzarlo — la distribución normal tiene soporte infinito (está definida sobre todos los números reales), aunque los valores lejanos de la media son astronómicamente improbables.
  • Cuando $\sigma o 0$: la distribución se vuelve infinitamente alta e infinitamente estrecha, concentrando toda la probabilidad en un solo punto $\mu$. En el límite, esto se aproxima a una función delta de Dirac — un "pico" de altura infinita pero ancho cero que aún tiene área 1.
  • Cuando $\sigma o \infty$: la distribución se aplana hacia cero en todas partes. La campana se vuelve tan ancha que la densidad en cualquier punto dado es despreciable, aproximándose a una distribución uniforme sobre toda la recta real.

Una regla práctica útil es la regla 68-95-99.7 : aproximadamente el 68% de los valores extraídos de una distribución normal caen dentro de $1\sigma$ de la media, el 95% dentro de $2\sigma$, y el 99.7% dentro de $3\sigma$. Esto te dice que valores a más de $3\sigma$ de la media son extremadamente raros (aproximadamente 3 de cada 1,000).

La distribución normal estándar es el caso especial con $\mu = 0$ y $\sigma = 1$, escrita $\mathcal{N}(0, 1)$. Sirve como la distribución de referencia — cualquier distribución normal $\mathcal{N}(\mu, \sigma^2)$ puede convertirse a ella mediante la sustitución $z = \frac{x - \mu}{\sigma}$, un proceso llamado estandarización .

¿Por qué esta distribución es tan importante para el deep learning?

  • Inicialización de pesos: esquemas como Glorot (Xavier) y He/Kaiming muestrean pesos iniciales de distribuciones normales con $\sigma$ cuidadosamente elegidos para mantener un flujo de gradientes estable a través de redes profundas.
  • Distribuciones de pesos preentrenados: empíricamente, los pesos de redes neuronales entrenadas siguen distribuciones aproximadamente normales. Esta observación es lo que hace funcionar la cuantización NF4 — colocar niveles de cuantización en los cuantiles de una distribución normal coincide con donde realmente están los pesos.
  • Modelos de difusión: el proceso directo en la generación de imágenes basada en difusión añade progresivamente ruido gaussiano a una imagen hasta que se convierte en ruido puro de $\mathcal{N}(0, I)$. El proceso inverso aprende a eliminar el ruido paso a paso.
  • El teorema del límite central: la suma de muchas variables aleatorias independientes tiende hacia una distribución normal, independientemente de las distribuciones originales. El deep learning está lleno de sumas — sumas ponderadas de entradas, promedios sobre lotes, gradientes acumulados — por lo que las distribuciones normales surgen naturalmente.

El gráfico a continuación superpone tres PDFs normales para ilustrar cómo $\mu$ y $\sigma$ afectan la forma. $\mathcal{N}(0, 1)$ es la campana estándar. $\mathcal{N}(0, 0.5)$ es más alta y estrecha ($\sigma$ más pequeño concentra la probabilidad). $\mathcal{N}(2, 1)$ tiene la misma forma que la normal estándar pero está desplazada a la derecha ($\mu$ diferente).

import math, json, js

def norm_pdf(x, mu=0, sigma=1):
    coeff = 1.0 / (sigma * math.sqrt(2 * math.pi))
    exponent = -((x - mu) ** 2) / (2 * sigma ** 2)
    return coeff * math.exp(exponent)

x_vals = [i * 0.05 for i in range(-80, 101)]  # -4.0 to 5.0

y_standard = [norm_pdf(x, 0, 1) for x in x_vals]
y_narrow   = [norm_pdf(x, 0, 0.5) for x in x_vals]
y_shifted  = [norm_pdf(x, 2, 1) for x in x_vals]

plot_data = [
    {
        "title": "Normal PDFs with different μ and σ",
        "x_label": "x",
        "y_label": "f(x)",
        "x_data": x_vals,
        "lines": [
            {"label": "N(0, 1)",   "data": y_standard, "color": "#3b82f6"},
            {"label": "N(0, 0.5)", "data": y_narrow,   "color": "#f59e0b"},
            {"label": "N(2, 1)",   "data": y_shifted,   "color": "#10b981"}
        ]
    }
]
js.window.py_plot_data = json.dumps(plot_data)
💡 Observa que $\mathcal{N}(0, 0.5)$ alcanza un pico por encima de $f(x) = 0.7$ — muy por encima de 1 si $\sigma$ fuera aún más pequeño. Este es un ejemplo concreto de una PDF que toma valores mayores que 1 y es perfectamente válida. La densidad es alta porque la probabilidad está concentrada en una banda muy estrecha, pero el área total sigue siendo exactamente 1.

La función de distribución acumulativa (CDF)

La PDF nos dice dónde la probabilidad es densa, pero a menudo necesitamos responder una pregunta diferente: ¿cuál es la probabilidad de que $X$ sea menor o igual a algún valor $x$? Por ejemplo, ¿qué fracción de los pesos de una red neuronal caen por debajo de 0.5? ¿Cuál es la probabilidad de que una muestra aleatoria de $\mathcal{N}(0, 1)$ sea negativa? La función de distribución acumulativa (CDF) responde exactamente a esto.

$$F(x) = P(X \leq x) = \int_{-\infty}^{x} f(t) \, dt$$

Desglosemos esto.

$F(x)$ es la CDF evaluada en el punto $x$. Proporciona la probabilidad total acumulada desde $-\infty$ hasta $x$ — el área bajo la curva de la PDF a la izquierda de $x$.

$\int_{-\infty}^{x} f(t) \, dt$ es la integral de la PDF desde menos infinito hasta $x$. Usamos $t$ como la variable muda de integración (en lugar de $x$) para distinguir la variable sobre la que se integra del límite superior fijo $x$. A medida que $x$ aumenta, acumulamos más área, por lo que $F(x)$ crece — por eso se llama función de distribución "acumulativa".

La CDF tiene varias propiedades importantes:

  • Monótonamente no decreciente: $F(x)$ solo puede subir o mantenerse plana, nunca disminuir. Una vez que la probabilidad se ha acumulado, no puede des-acumularse.
  • $\lim_{x o -\infty} F(x) = 0$: a medida que nos movemos infinitamente hacia la izquierda, no se ha acumulado ninguna probabilidad aún.
  • $\lim_{x o \infty} F(x) = 1$: a medida que nos movemos infinitamente hacia la derecha, toda la probabilidad se ha acumulado.
  • $F(x) \in [0, 1]$: la CDF siempre es una probabilidad válida.

Para la distribución normal estándar $\mathcal{N}(0, 1)$, la CDF se denota tradicionalmente $\Phi(x)$:

$$\Phi(x) = \frac{1}{\sqrt{2\pi}} \int_{-\infty}^{x} e^{-t^2/2} \, dt$$

Esta integral no tiene solución en forma cerrada — no existe ninguna combinación de funciones elementales que sea igual a $\Phi(x)$. Debemos calcularla numéricamente. Afortunadamente, $\Phi(x)$ se puede expresar usando la función de error $ ext{erf}(x)$, que está disponible en la mayoría de los lenguajes de programación:

$$\Phi(x) = \frac{1}{2}\left[1 + ext{erf}\!\left(\frac{x}{\sqrt{2}}\right)\right]$$

Algunos valores clave que vale la pena memorizar:

  • $\Phi(0) = 0.5$ — exactamente la mitad de la distribución se encuentra por debajo de la media. Por simetría, exactamente la mitad se encuentra por encima.
  • $\Phi(1.96) \approx 0.975$ — el 97.5% de la distribución se encuentra por debajo de $x = 1.96$. Esta es la base del famoso intervalo de confianza del 95%: el 95% central de la distribución se encuentra entre $-1.96$ y $+1.96$.
  • $\Phi(-1.96) \approx 0.025$ — solo el 2.5% de la distribución se encuentra por debajo de $x = -1.96$, la imagen especular del valor anterior.

El gráfico a continuación muestra $\Phi(x)$ — la característica curva en forma de S (similar a una sigmoide). Comienza cerca de 0 en el extremo izquierdo, pasa por 0.5 en $x = 0$, y se aproxima a 1 en el extremo derecho. La parte más empinada de la curva está en $x = 0$, donde la PDF (la derivada de la CDF) es más alta.

import math, json, js

def norm_cdf(x):
    """CDF of the standard normal distribution using math.erf."""
    return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))

x_vals = [i * 0.05 for i in range(-80, 81)]  # -4.0 to 4.0
y_cdf = [norm_cdf(x) for x in x_vals]

# Horizontal reference lines at key probabilities
y_half = [0.5 for _ in x_vals]
y_025 = [0.025 for _ in x_vals]
y_975 = [0.975 for _ in x_vals]

plot_data = [
    {
        "title": "Standard Normal CDF Φ(x)",
        "x_label": "x",
        "y_label": "Φ(x)",
        "x_data": x_vals,
        "lines": [
            {"label": "Φ(x)", "data": y_cdf, "color": "#8b5cf6"},
            {"label": "Φ = 0.5 (at x = 0)", "data": y_half, "color": "#9ca3af"},
            {"label": "Φ = 0.025 (at x ≈ -1.96)", "data": y_025, "color": "#f87171"},
            {"label": "Φ = 0.975 (at x ≈ 1.96)", "data": y_975, "color": "#f87171"}
        ]
    }
]
js.window.py_plot_data = json.dumps(plot_data)

El siguiente código calcula la CDF en varios valores importantes de $x$ y los muestra como una tabla. Estos valores aparecen repetidamente en estadística y machine learning — vale la pena tener intuición sobre ellos.

import math, json, js

def norm_cdf(x):
    return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))

x_values = [-3.0, -2.0, -1.96, -1.0, 0.0, 1.0, 1.96, 2.0, 3.0]

rows = []
for x in x_values:
    cdf_val = norm_cdf(x)
    interpretation = ""
    if x == 0.0:
        interpretation = "Mean: exactly half below"
    elif abs(x) == 1.0:
        interpretation = f"{'Below' if x < 0 else 'Above'} 1σ"
    elif abs(x - 1.96) < 0.01 or abs(x + 1.96) < 0.01:
        interpretation = "95% confidence boundary"
    elif abs(x) == 2.0:
        interpretation = f"{'Below' if x < 0 else 'Above'} 2σ"
    elif abs(x) == 3.0:
        interpretation = f"{'Below' if x < 0 else 'Above'} 3σ"
    rows.append([f"{x:+.2f}", f"{cdf_val:.6f}", interpretation])

js.window.py_table_data = json.dumps({
    "headers": ["x", "Φ(x)", "Interpretation"],
    "rows": rows
})

print("Key insight: Φ(x) + Φ(-x) = 1 for all x (by symmetry)")
print(f"Verify: Φ(1.96) + Φ(-1.96) = {norm_cdf(1.96):.6f} + {norm_cdf(-1.96):.6f} = {norm_cdf(1.96) + norm_cdf(-1.96):.6f}")
💡 La CDF también nos da probabilidades para rangos. Como $P(a \leq X \leq b) = \Phi(b) - \Phi(a)$, podemos verificar la regla 68-95-99.7: $\Phi(1) - \Phi(-1) \approx 0.8413 - 0.1587 = 0.6827$, o aproximadamente el 68.3% dentro de $\pm 1\sigma$. Para $\pm 2\sigma$: $\Phi(2) - \Phi(-2) \approx 0.9772 - 0.0228 = 0.9545$, o 95.4%.

La función cuantil (CDF inversa)

La CDF responde "¿qué fracción de la distribución se encuentra por debajo de $x$?" Pero a veces necesitamos la pregunta inversa: "¿en qué valor de $x$ una fracción dada $p$ de la distribución se encuentra por debajo?" Por ejemplo, ¿qué valor separa el 97.5% inferior del 2.5% superior? Esto es lo que proporciona la función cuantil (también llamada CDF inversa o función de punto porcentual ).

Para la normal estándar, la función cuantil se escribe $\Phi^{-1}(p)$. Dada una probabilidad $p \in (0, 1)$, devuelve el $z$-score donde $\Phi(z) = p$ — el punto donde exactamente una fracción $p$ de la distribución se encuentra a su izquierda.

Algunos ejemplos clave:

  • $\Phi^{-1}(0.5) = 0$ — la mediana. La mitad de la distribución se encuentra por debajo de 0, lo cual tiene sentido porque la normal estándar es simétrica respecto a 0.
  • $\Phi^{-1}(0.975) \approx 1.96$ — el percentil 97.5. Este es el valor por encima del cual solo se encuentra el 2.5% de la distribución. Es el límite derecho del intervalo de confianza del 95%.
  • $\Phi^{-1}(0.025) \approx -1.96$ — el percentil 2.5. Solo el 2.5% de la distribución se encuentra por debajo de este valor. Es el límite izquierdo del intervalo de confianza del 95%.

Ahora el comportamiento en los límites:

  • $\Phi^{-1}(0) = -\infty$: ningún valor finito captura el 0% de la distribución. Tendrías que ir infinitamente hacia la izquierda para tener cero probabilidad acumulada.
  • $\Phi^{-1}(1) = +\infty$: necesitarías incluir toda la recta real infinita para capturar el 100% de la distribución.
  • $\Phi^{-1}(0.5) = 0$: la normal estándar es simétrica respecto a 0, por lo que la mediana es igual a la media.

Esta función tiene una aplicación directa e importante en deep learning. QLoRA usa la cuantización NF4 con la función cuantil para diseñar un tipo de datos de 4 bits optimizado para pesos con distribución normal. La idea: dividir el rango de probabilidad $[0, 1]$ en $2^b = 16$ intervalos de igual probabilidad y colocar cada nivel de cuantización en el cuantil del punto medio de su intervalo:

$$q_i = \Phi^{-1}\!\left(\frac{2i + 1}{2 \cdot 2^b}\right)$$

Esto coloca niveles densamente cerca de $\mu = 0$ (donde la mayoría de los pesos se agrupan) y escasamente en las colas (donde existen pocos pesos), minimizando el error de cuantización bajo la suposición de que los pesos están aproximadamente distribuidos normalmente.

El gráfico a continuación muestra la función cuantil — una curva S inversa. Es una imagen especular de la CDF: donde la CDF mapea valores de $x$ a probabilidades, la función cuantil mapea probabilidades de vuelta a valores de $x$. Observa cómo es empinada cerca de $p = 0$ y $p = 1$ (un pequeño cambio en la probabilidad corresponde a un gran cambio en $x$ en las colas) y relativamente plana cerca de $p = 0.5$ (las probabilidades se acumulan rápidamente cerca del centro).

import math, json, js

def norm_ppf(p):
    """Approximate inverse CDF of standard normal (Abramowitz & Stegun)."""
    if p <= 0:
        return -4.0
    if p >= 1:
        return 4.0
    if p > 0.5:
        return -norm_ppf(1.0 - p)
    t = math.sqrt(-2.0 * math.log(p))
    c0, c1, c2 = 2.515517, 0.802853, 0.010328
    d1, d2, d3 = 1.432788, 0.189269, 0.001308
    return -(t - (c0 + c1*t + c2*t*t) / (1 + d1*t + d2*t*t + d3*t*t*t))

# Probabilities from 0.01 to 0.99
p_vals = [i * 0.005 for i in range(2, 199)]  # 0.01 to 0.99
z_vals = [norm_ppf(p) for p in p_vals]

plot_data = [
    {
        "title": "Standard Normal Quantile Function Φ⁻¹(p)",
        "x_label": "Probability p",
        "y_label": "z-score (Φ⁻¹(p))",
        "x_data": p_vals,
        "lines": [
            {"label": "Φ⁻¹(p)", "data": z_vals, "color": "#ef4444"}
        ]
    }
]
js.window.py_plot_data = json.dumps(plot_data)

El siguiente código calcula valores cuantiles en probabilidades clave y muestra cómo NF4 los usa para colocar sus 16 niveles de cuantización:

import math, json, js

def norm_ppf(p):
    """Approximate inverse CDF of standard normal (Abramowitz & Stegun)."""
    if p <= 0: return -4.0
    if p >= 1: return 4.0
    if p > 0.5:
        return -norm_ppf(1.0 - p)
    t = math.sqrt(-2.0 * math.log(p))
    c0, c1, c2 = 2.515517, 0.802853, 0.010328
    d1, d2, d3 = 1.432788, 0.189269, 0.001308
    return -(t - (c0 + c1*t + c2*t*t) / (1 + d1*t + d2*t*t + d3*t*t*t))

# Key quantile values
rows_key = []
for p in [0.025, 0.05, 0.10, 0.25, 0.50, 0.75, 0.90, 0.95, 0.975]:
    z = norm_ppf(p)
    rows_key.append([f"{p:.3f}", f"{z:+.4f}", f"{p*100:.1f}th percentile"])

# NF4 levels
rows_nf4 = []
for i in range(16):
    p = (2 * i + 1) / 32
    z = norm_ppf(p)
    rows_nf4.append([str(i), f"{p:.5f}", f"{z:+.4f}"])

# Combine into two tables displayed sequentially
js.window.py_table_data = json.dumps({
    "headers": ["Probability p", "Φ⁻¹(p)", "Meaning"],
    "rows": rows_key
})

print("
NF4 quantization levels (16 levels for 4-bit):")
print(f"{'Level':>5} {'Prob p':>10} {'Φ⁻¹(p)':>10}")
print("-" * 28)
for row in rows_nf4:
    print(f"{row[0]:>5} {row[1]:>10} {row[2]:>10}")
💡 La función cuantil es el puente matemático entre "intervalos de igual probabilidad" y "dónde colocar los niveles de cuantización". Debido a que la distribución normal concentra su masa cerca de la media, los intervalos de igual probabilidad se traducen en niveles estrechamente espaciados cerca de cero y niveles ampliamente espaciados en las colas — exactamente el diseño que minimiza el error de cuantización para pesos con distribución normal.

Conectando todo

La PDF, la CDF y la función cuantil forman una cadena, cada una derivada de la anterior:

  • PDF $f(x)$: "¿Qué tan densa es la probabilidad en este punto?" Este es el punto de partida. Te indica dónde es probable que los valores se agrupen, pero sus valores son densidades, no probabilidades.
  • CDF $F(x) = \int_{-\infty}^{x} f(t) \, dt$: "¿Cuánta probabilidad total se encuentra a la izquierda de este punto?" Se obtiene integrando la PDF. Convierte información de densidad en probabilidades acumulativas.
  • Quantile $F^{-1}(p)$: "¿En qué punto se ha acumulado esta cantidad de probabilidad?" Se obtiene invirtiendo la CDF. Convierte probabilidades de vuelta en valores en la escala original.

Estas tres funciones son diferentes vistas de la misma distribución subyacente. La CDF es la integral de la PDF. La PDF es la derivada de la CDF. La función cuantil es la inversa funcional de la CDF. Conocer cualquiera de ellas determina completamente las otras dos.

El gráfico a continuación muestra las tres funciones para la distribución normal estándar en un solo gráfico. La PDF azul alcanza su pico en $x = 0$ con densidad $\approx 0.399$. La CDF morada pasa por 0.5 en $x = 0$ (la mitad de la probabilidad acumulada). Y la función cuantil roja, graficada en un eje conceptual diferente, muestra el mapeo inverso.

import math, json, js

def norm_pdf(x, mu=0, sigma=1):
    coeff = 1.0 / (sigma * math.sqrt(2 * math.pi))
    exponent = -((x - mu) ** 2) / (2 * sigma ** 2)
    return coeff * math.exp(exponent)

def norm_cdf(x):
    return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))

def norm_ppf(p):
    if p <= 0: return -4.0
    if p >= 1: return 4.0
    if p > 0.5:
        return -norm_ppf(1.0 - p)
    t = math.sqrt(-2.0 * math.log(p))
    c0, c1, c2 = 2.515517, 0.802853, 0.010328
    d1, d2, d3 = 1.432788, 0.189269, 0.001308
    return -(t - (c0 + c1*t + c2*t*t) / (1 + d1*t + d2*t*t + d3*t*t*t))

x_vals = [i * 0.05 for i in range(-80, 81)]  # -4.0 to 4.0

y_pdf = [norm_pdf(x) for x in x_vals]
y_cdf = [norm_cdf(x) for x in x_vals]

# For the quantile function, we plot it on the same x-axis
# by treating x as a probability input (only valid for 0 < x < 1 region)
# Instead, we plot the inverse: for each x, show ppf(cdf(x)) = x (identity check)
# Better: show all three as separate sub-plots

# We use two charts: one for PDF + CDF (same x-axis), one for quantile function

plot_data = [
    {
        "title": "PDF and CDF of the Standard Normal Distribution",
        "x_label": "x",
        "y_label": "Value",
        "x_data": x_vals,
        "lines": [
            {"label": "PDF f(x)", "data": y_pdf, "color": "#3b82f6"},
            {"label": "CDF Φ(x)", "data": y_cdf, "color": "#8b5cf6"}
        ]
    },
    {
        "title": "Quantile Function Φ⁻¹(p)",
        "x_label": "Probability p",
        "y_label": "z-score",
        "x_data": [i * 0.005 for i in range(2, 199)],
        "lines": [
            {"label": "Φ⁻¹(p)", "data": [norm_ppf(i * 0.005) for i in range(2, 199)], "color": "#ef4444"}
        ]
    }
]
js.window.py_plot_data = json.dumps(plot_data)

La tabla a continuación muestra la correspondencia entre las tres funciones en puntos clave, haciendo concreta la cadena de relaciones:

import math, json, js

def norm_pdf(x, mu=0, sigma=1):
    coeff = 1.0 / (sigma * math.sqrt(2 * math.pi))
    exponent = -((x - mu) ** 2) / (2 * sigma ** 2)
    return coeff * math.exp(exponent)

def norm_cdf(x):
    return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))

def norm_ppf(p):
    if p <= 0: return -4.0
    if p >= 1: return 4.0
    if p > 0.5:
        return -norm_ppf(1.0 - p)
    t = math.sqrt(-2.0 * math.log(p))
    c0, c1, c2 = 2.515517, 0.802853, 0.010328
    d1, d2, d3 = 1.432788, 0.189269, 0.001308
    return -(t - (c0 + c1*t + c2*t*t) / (1 + d1*t + d2*t*t + d3*t*t*t))

x_values = [-3.0, -1.96, -1.0, 0.0, 1.0, 1.96, 3.0]
rows = []
for x in x_values:
    f_x = norm_pdf(x)
    phi_x = norm_cdf(x)
    # Verify round-trip: ppf(cdf(x)) should equal x
    roundtrip = norm_ppf(phi_x)
    rows.append([
        f"{x:+.2f}",
        f"{f_x:.4f}",
        f"{phi_x:.4f}",
        f"{roundtrip:+.4f}"
    ])

js.window.py_table_data = json.dumps({
    "headers": ["x", "PDF f(x)", "CDF Φ(x)", "Φ⁻¹(Φ(x)) ≈ x"],
    "rows": rows
})

print("The last column verifies the round-trip: applying Φ⁻¹ to Φ(x) recovers x.")
print("Small discrepancies are due to the rational approximation used for Φ⁻¹.")
💡 La propiedad de ida y vuelta $\Phi^{-1}(\Phi(x)) = x$ es lo que hace que estas funciones sean inversas. En la práctica, la aproximación racional para $\Phi^{-1}$ introduce errores diminutos (del orden de $10^{-4}$), pero para aplicaciones como la cuantización NF4 estos errores son despreciables — los niveles de cuantización no necesitan ser exactos con precisión de milésimas.

Quiz

Pon a prueba tu comprensión de las distribuciones de probabilidad, PDFs, CDFs y la distribución normal.

Si una función de densidad de probabilidad tiene f(x) = 2.5 en algún punto x, ¿es una PDF válida?

¿Cuál es Φ(0) para la distribución normal estándar N(0, 1)?

¿A qué es aproximadamente igual Φ⁻¹(0.975) para la normal estándar?

¿Por qué QLoRA usa la función cuantil Φ⁻¹ para calcular los niveles de cuantización NF4?