Inicialización de Pesos
Por qué los valores iniciales de los pesos determinan si tu red aprenderá, y cómo Xavier, He y otras técnicas resuelven el problema de la propagación de la varianza.
❓ ¿Por qué importa la inicialización?
Antes de que el entrenamiento comience, los pesos de una red neuronal deben tener algún valor inicial. Esta decisión, aparentemente trivial, tiene consecuencias profundas en si la red podrá aprender y con qué velocidad lo hará.
La inicialización de pesos es un problema que se agravó enormemente con la llegada del deep learning. En redes poco profundas (1-2 capas ocultas), una inicialización aleatoria genérica suele funcionar razonablemente bien. Pero a medida que las redes se hacen más profundas — 10, 50, 100 o incluso 1000 capas — las señales que se propagan a través de la red (tanto en el forward como en el backward pass) pasan por multiplicaciones sucesivas. Si esas multiplicaciones no están cuidadosamente calibradas, la señal se amplifica hasta el infinito o se desvanece hasta cero, haciendo el entrenamiento imposible. Este fenómeno está íntimamente relacionado con los problemas de vanishing y exploding gradients que estudiaremos en el submódulo de Estabilidad del Entrenamiento.
Recuerda que durante el forward pass, cada capa computa:
Si los pesos \mathbf{W} no se eligen cuidadosamente, las activaciones \mathbf{a}^{(l)} pueden colapsar a cero, saturarse, o explotar — todo antes de dar un solo paso de optimización.
Idea central: una buena inicialización debe mantener la varianza de las activaciones estable a lo largo de las capas en el forward, y la varianza de los gradientes estable en el backward. Si cualquiera de las dos crece o decrece exponencialmente, el entrenamiento será imposible o muy lento.
🚫 Inicialización a cero (y a constante)
Todo ceros: W = 0
Parece intuitivo empezar con todos los pesos a cero. Pero es la peor elección posible:
Todas las neuronas de una capa producen exactamente la misma salida (solo depende del bias, que suele inicializarse a cero también). Esto significa:
- Todas las neuronas reciben el mismo gradiente durante backpropagation.
- Todas se actualizan de la misma forma.
- Tras cualquier número de épocas, todas siguen siendo idénticas.
Problema de simetría: con pesos a cero (o cualquier constante), la red tiene n neuronas por capa pero se comporta como si tuviera solo una. Todas las neuronas son copias idénticas que nunca se diferencian. Esto se conoce como el symmetry problem y destruye completamente la capacidad expresiva de la red.
Todo unos: W = 1
Igual de problemático. Todas las neuronas computan lo mismo, y además las activaciones crecen descontroladamente al pasar por las capas (cada capa multiplica por la suma de todas las entradas), causando saturación en funciones como sigmoid/tanh y activaciones enormes con ReLU.
Cualquier constante: W = c
El mismo problema de simetría aplica a cualquier valor constante. El problema no es el valor específico, es que todos los pesos son iguales.
Conclusión: necesitamos que los pesos iniciales sean diferentes entre sí para romper la simetría. La solución natural es inicializarlos aleatoriamente.
🎲 Inicialización aleatoria naïf
La primera idea es inicializar los pesos con valores aleatorios de una distribución normal o uniforme. Pero, ¿con qué varianza?
Varianza demasiado grande
Si usamos W \sim \mathcal{N}(0, 1) (varianza = 1), las pre-activaciones z = \mathbf{W}\mathbf{a} tienen varianza:
Con n_{\text{in}} = 256 neuronas, la varianza de las pre-activaciones es 256 veces mayor que la de las activaciones de la capa anterior. En una red con 10 capas de 256 neuronas, las pre-activaciones de la última capa tendrían varianza 256^{10} \approx 10^{24}. Las activaciones saturan inmediatamente (sigmoid/tanh) o explotan (ReLU).
Varianza demasiado pequeña
Si usamos W \sim \mathcal{N}(0, 0.001), las activaciones se comprimen hacia cero progresivamente. Después de pocas capas, todas las activaciones son prácticamente cero y los gradientes se desvanecen.
Ejemplo: propagación de la varianza
Para entender el problema formalmente, consideremos una capa lineal (sin activación) con n_{\text{in}} entradas:
Esto asume que los pesos y las activaciones son independientes con media cero (razonable para sigmoid centrada y tanh). La varianza se multiplica por n_\text{in} \cdot \sigma_w^2 en cada capa. Para que se mantenga estable, necesitamos:
¡Esta es la idea clave! En vez de elegir una varianza arbitraria, debemos escalarla según el número de neuronas. Este es exactamente el principio detrás de las inicializaciones Xavier y He que veremos a continuación.
🧪 Simulador: propagación de activaciones según la inicialización
Observa cómo la distribución de activaciones cambia a lo largo de las capas según el método de inicialización. Con inicialización naïf (σ²=1), las activaciones explotan o colapsan. Con inicializaciones adecuadas, se mantienen estables.
Caso de uso: ¿Quieres ver estas diferencias con datos reales? En el caso práctico con FashionMNIST entrenamos un MLP con las 6 estrategias de inicialización y comparamos curvas de loss, accuracy y distribución de activaciones.
📐 Inicialización Xavier / Glorot
En 2010, Xavier Glorot y Yoshua Bengio publicaron "Understanding the difficulty of training deep feedforward neural networks" (AISTATS 2010), donde formalizaron el problema de la propagación de la varianza y propusieron una solución elegante.
Motivación: preservar la varianza en ambos sentidos
Vimos que para mantener la varianza de las activaciones estable en el forward pass, necesitamos:
Pero durante el backward pass, los gradientes se propagan en dirección opuesta, y la condición análoga para preservar la varianza de los gradientes es:
Estas dos condiciones son incompatibles cuando n_\text{in} \neq n_\text{out}. Glorot y Bengio propusieron un compromiso: usar la media armónica de ambas:
Derivación paso a paso
Consideremos una capa con n_\text{in} entradas y n_\text{out} salidas. Para una neurona j:
Asumimos que:
- w_{ji} y a_i son independientes.
- Ambos tienen media cero: \mathbb{E}[w] = 0, \mathbb{E}[a] = 0.
- Los pesos son i.i.d. con varianza \sigma_w^2.
- Las activaciones son i.i.d. con varianza \sigma_a^2.
Entonces, para el producto de dos variables independientes con media cero:
Y como la suma de n_\text{in} términos i.i.d.:
Para que la varianza se preserve en el forward (\text{Var}(z) = \text{Var}(a)), necesitamos n_\text{in} \cdot \sigma_w^2 = 1.
Análogamente, durante backpropagation el gradiente \frac{\partial \mathcal{L}}{\partial \mathbf{a}^{(l-1)}} se propaga como:
Esta suma tiene n_\text{out} términos, así que para preservar la varianza del gradiente necesitamos n_\text{out} \cdot \sigma_w^2 = 1.
Tomando la media:
Distribuciones
La inicialización Xavier puede implementarse con dos distribuciones:
| Distribución | Fórmula | Nota |
|---|---|---|
| Normal | W \sim \mathcal{N}\!\left(0,\; \frac{2}{n_\text{in} + n_\text{out}}\right) | La más común |
| Uniforme | W \sim \mathcal{U}\!\left(-\sqrt{\frac{6}{n_\text{in}+n_\text{out}}},\; +\sqrt{\frac{6}{n_\text{in}+n_\text{out}}}\right) | Usa \text{Var}(\mathcal{U}(-a,a)) = \frac{a^2}{3} |
¿Cuándo usar Xavier?
Xavier está diseñada para activaciones simétricas con derivada ≈ 1 alrededor de cero:
- ✅ tanh — la activación original del paper
- ✅ sigmoid (con cuidado, ya que su media no es cero)
- ✅ Lineal / Softmax (última capa)
- ⚠️ ReLU — NO es la elección óptima (la mitad de las neuronas producen 0, lo que reduce la varianza efectiva a la mitad). Para ReLU, usa He.
Análisis gráfico
La derivación de Xavier asume que la función de activación es lineal en la zona donde opera. Para tanh, esto es cierto cuando las pre-activaciones son pequeñas (la zona lineal alrededor de cero). Justamente, una buena inicialización mantiene las pre-activaciones en esta zona:
🧪 Visualización: distribución de activaciones con Xavier
Compara cómo se distribuyen las activaciones a lo largo de las capas con inicialización Xavier vs. naïf. Con tanh, Xavier mantiene las activaciones en la zona lineal.
🔥 Inicialización He / Kaiming
En 2015, Kaiming He, Xiangyu Zhang, Shaoqing Ren y Jian Sun publicaron "Delving Deep into Rectifiers" (arXiv:1502.01852), extendiendo el análisis de Xavier para activaciones ReLU. Hoy, He (Kaiming) es la inicialización estándar para redes con ReLU y sus variantes.
¿Por qué Xavier falla con ReLU?
ReLU elimina todas las activaciones negativas: \text{ReLU}(z) = \max(0, z). Esto significa que, en promedio, la mitad de las neuronas producen cero. Si partimos del análisis de Xavier:
El factor \frac{1}{2} proviene de que ReLU pone a cero exactamente la mitad de las entradas (asumiendo entrada simétrica alrededor de cero). Esto significa que con Xavier, la varianza se divide por 2 en cada capa:
Con Xavier y ReLU, las activaciones decaen exponencialmente. Una red con 10 capas perdería un factor de 2^{10} = 1024 en varianza. Los gradientes sufren el problema inverso y también se desvanecen.
Derivación de He
Partimos del forward pass de la capa l:
Calculamos la varianza (pesos i.i.d. con media cero, independientes de las activaciones):
Nota clave: con ReLU, las activaciones a = \text{ReLU}(z) no tienen media cero. Por eso usamos el segundo momento \mathbb{E}[a^2] en lugar de la varianza.
Si z \sim \mathcal{N}(0, \sigma_z^2), entonces \text{ReLU}(z) tiene:
Sustituyendo:
Para que \text{Var}(z^{(l)}) = \text{Var}(z^{(l-1)}):
Fórmula de He
La diferencia con Xavier es simplemente el factor 2 en el numerador (Xavier tiene \frac{2}{n_\text{in}+n_\text{out}}, y cuando n_\text{in} = n_\text{out} esto se reduce a \frac{1}{n_\text{in}} — exactamente la mitad de He). Este factor compensa las neuronas muertas de ReLU.
Aunque la diferencia parezca un simple factor de 2, su impacto en la práctica es enorme. Con Xavier y ReLU, la varianza de las activaciones se divide por 2 en cada capa. En una red con 20 capas, las activaciones de la última capa tendrían una varianza 2^{20} \approx 10^6 veces menor que las de la primera. Los gradientes sufrirían el mismo decaimiento en dirección contraria, haciendo que las capas iniciales fueran esencialmente «inentrenables». La inicialización He corrige este desequilibrio y es la razón por la que redes profundas con ReLU pueden entrenarse de forma efectiva.
fan_in vs. fan_out
Al igual que Xavier, He puede derivarse para el forward (usando n_\text{in}) o para el backward (usando n_\text{out}):
| Modo | Varianza | Uso |
|---|---|---|
| fan_in (default) | \frac{2}{n_\text{in}} | Preserva la magnitud de las activaciones en el forward. La opción estándar. |
| fan_out | \frac{2}{n_\text{out}} | Preserva la magnitud de los gradientes en el backward. Útil para redes muy profundas. |
Distribuciones
| Distribución | Fórmula (fan_in) |
|---|---|
| Kaiming Normal | W \sim \mathcal{N}\!\left(0,\; \frac{2}{n_\text{in}}\right) |
| Kaiming Uniform | W \sim \mathcal{U}\!\left(-\sqrt{\frac{6}{n_\text{in}}},\; +\sqrt{\frac{6}{n_\text{in}}}\right) |
Variantes para Leaky ReLU
Para Leaky ReLU con pendiente negativa \alpha, la fracción de varianza preservada no es \frac{1}{2} sino:
Con \alpha = 0 (ReLU estándar), se recupera \frac{2}{n_\text{in}}. Con \alpha = 1 (lineal), se obtiene \frac{1}{n_\text{in}}, que es Xavier.
¿Cuándo usar He?
He/Kaiming es la elección por defecto para:
- ✅ ReLU — la activación para la que fue diseñada
- ✅ Leaky ReLU / PReLU / ELU — con ajuste del factor α
- ✅ GELU / SiLU (Swish) — empíricamente funciona bien
- ✅ CNNs — casi siempre usan ReLU → He es el estándar
Regla práctica: si tu red usa ReLU (o variantes), usa He. Si usa tanh o sigmoid, usa Xavier. En la duda, He suele funcionar bien en ambos casos.
🧪 Comparación: He vs Xavier con ReLU profundo
Observa cómo con ReLU, Xavier pierde varianza en cada capa mientras He la mantiene estable. El gráfico muestra la desviación estándar de las activaciones por capa.
⚖️ Xavier vs. He: resumen comparativo
| Aspecto | Xavier / Glorot | He / Kaiming |
|---|---|---|
| Año | 2010 | 2015 |
| Varianza | \frac{2}{n_\text{in} + n_\text{out}} | \frac{2}{n_\text{in}} |
| Asunción | Activación lineal alrededor de 0 | ReLU (mitad de neuronas = 0) |
| Activaciones ideales | tanh, sigmoid, lineal | ReLU, Leaky ReLU, ELU, GELU |
| Default en | TensorFlow/Keras (Dense) | PyTorch (Linear, Conv2d) |
| Preserva varianza de | Forward + backward (compromiso) | Forward (fan_in) o backward (fan_out) |
🧬 Inicialización LeCun
Antes de Xavier y He, Yann LeCun propuso en 1998 (Efficient BackProp — LeCun et al., 1998) una inicialización basada únicamente en n_\text{in}:
Es idéntica a Xavier cuando n_\text{in} = n_\text{out}, y puede verse como un caso especial de He con un factor \frac{1}{2}. Actualmente se recomienda para redes con activaciones SELU (Self-Normalizing Neural Networks, Klambauer et al., 2017), donde las propiedades auto-normalizantes de SELU requieren pesos con esta varianza específica.
Uso recomendado: redes con activación SELU. Si usas SELU + LeCun + AlphaDropout, la red se auto-normaliza sin necesidad de Batch Normalization.
📏 Inicialización ortogonal
La inicialización ortogonal (Saxe et al., 2014 — arXiv:1312.6120) inicializa la matriz de pesos como una matriz ortogonal (o semi-ortogonal si no es cuadrada). Esto se logra generando una matriz aleatoria y aplicando descomposición QR o SVD:
donde g es un factor de ganancia que depende de la activación.
¿Por qué funciona?
Una matriz ortogonal \mathbf{W} satisface \mathbf{W}^T \mathbf{W} = \mathbf{I}. Esto significa que:
- Todos sus valores singulares son 1.
- Preserva exactamente la norma de los vectores que multiplica: \|\mathbf{W}\mathbf{x}\| = \|\mathbf{x}\|.
- Los gradientes tampoco se amplifican ni se reducen.
Ventaja principal: la inicialización ortogonal garantiza que la norma de los gradientes se preserva (para redes lineales). En la práctica, funciona especialmente bien para RNNs, donde la multiplicación repetida por la misma matriz de pesos hace que los problemas de vanishing/exploding gradients sean más severos.
Tabla de ganancias
| Activación | Ganancia g |
|---|---|
| Lineal / identidad | 1.0 |
| sigmoid | 1.0 |
| tanh | 5/3 ≈ 1.6667 |
| ReLU | \sqrt{2} \approx 1.4142 |
| Leaky ReLU (α) | \sqrt{\frac{2}{1 + \alpha^2}} |
| SELU | 3/4 = 0.75 |
🔬 LSUV: inicialización data-driven
Layer-Sequential Unit-Variance (Mishkin & Matas, 2016 — arXiv:1511.06422) es un método que no se basa en fórmulas teóricas sino en datos reales. El proceso:
- Inicializar cada capa con matrices ortogonales.
- Para cada capa (secuencialmente), pasar un mini-batch por la red.
- Medir la varianza de la salida de esa capa.
- Re-escalar los pesos dividiendo por \sqrt{\text{Var}_\text{observada}}.
- Repetir hasta que la varianza sea ≈ 1.
Ventaja: no asume nada sobre la función de activación ni la arquitectura. Funciona con cualquier red. Desventaja: requiere un forward pass con datos reales antes del entrenamiento, y no está implementada de forma nativa en los frameworks.
🔧 Fixup y otras inicializaciones para redes residuales
Las redes residuales (ResNets) presentan un desafío particular: la conexión residual y = x + F(x) acumula varianza con cada bloque. Con L bloques:
Fixup (Zhang et al., 2019 — arXiv:1901.09321) propone escalar la última capa de cada bloque residual por \frac{1}{\sqrt{L}} o inicializarla a cero, de modo que al inicio del entrenamiento la red se comporta como la identidad:
Otras técnicas para ResNets incluyen:
- Zero-init residual: inicializar la última BatchNorm de cada bloque con \gamma = 0, efecto similar a Fixup.
- ReZero (Bachlechner et al., 2021): usar un escalar aprendible \alpha inicializado a 0 en cada conexión residual.
🌌 Inicialización sparse
La inicialización sparse (Martens, 2010) fija la mayoría de los pesos a cero y solo inicializa un número fijo de conexiones por neurona (típicamente 15). Los pesos no-cero se inicializan con varianza adecuada. Esto:
- Garantiza que cada neurona recibe un número fijo de señales de entrada, independientemente del ancho de la capa.
- Evita la dependencia de la varianza con n_\text{in}.
- Se ha usado principalmente en redes tipo DBN y autoencoders.
📊 Tabla comparativa completa
Resumen de todas las inicializaciones discutidas:
| Método | Varianza / regla | Activaciones ideales | Caso de uso |
|---|---|---|---|
| Xavier / Glorot | \frac{2}{n_\text{in}+n_\text{out}} | tanh, sigmoid, lineal | MLPs con tanh, redes clásicas |
| He / Kaiming | \frac{2}{n_\text{in}} | ReLU, Leaky ReLU, ELU, GELU | MLPs, CNNs (estándar moderno) |
| LeCun | \frac{1}{n_\text{in}} | SELU | Self-Normalizing Networks |
| Ortogonal | QR/SVD + ganancia | Cualquiera | RNNs, redes muy profundas |
| LSUV | Data-driven (varianza=1) | Cualquiera | Arquitecturas complejas |
| Fixup / ReZero | Escala \frac{1}{\sqrt{L}} o 0 | Cualquiera | ResNets (sin BatchNorm) |
| Sparse | K conexiones/neurona | Cualquiera | Autoencoders, DBNs |
Regla práctica simplificada:
- ¿Usas ReLU? → He (Kaiming)
- ¿Usas tanh/sigmoid? → Xavier (Glorot)
- ¿Usas SELU? → LeCun
- ¿Es una RNN? → Ortogonal
- ¿Nada funciona? → LSUV
🧪 Explorador: distribución de pesos en un MLP de 3 capas
Visualiza los histogramas de pesos de un MLP de tres capas (2048 \to 1024 \to 512) inicializado con diferentes técnicas. Observa cómo las técnicas naïf producen distribuciones demasiado anchas o estrechas, mientras que Xavier y He ajustan la varianza al tamaño de cada capa.
🔶 Inicializaciones en PyTorch
PyTorch proporciona todas las inicializaciones a través de
torch.nn.init. Por defecto, las capas Linear y Conv2d
usan Kaiming Uniform.
import torch
import torch.nn as nn
# Xavier Normal
nn.init.xavier_normal_(layer.weight)
# Xavier Uniform
nn.init.xavier_uniform_(layer.weight)
# En un modelo completo
class MLP(nn.Module):
def __init__(self, in_features, hidden, out_features):
super().__init__()
self.fc1 = nn.Linear(in_features, hidden)
self.fc2 = nn.Linear(hidden, hidden)
self.fc3 = nn.Linear(hidden, out_features)
# Aplicar Xavier a todas las capas
for m in self.modules():
if isinstance(m, nn.Linear):
nn.init.xavier_normal_(m.weight)
nn.init.zeros_(m.bias)
def forward(self, x):
x = torch.tanh(self.fc1(x)) # tanh — ideal para Xavier
x = torch.tanh(self.fc2(x))
return self.fc3(x)
import torch.nn as nn
# Kaiming Normal (fan_in, ReLU)
nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')
# Kaiming Uniform (fan_in, ReLU)
nn.init.kaiming_uniform_(layer.weight, mode='fan_in', nonlinearity='relu')
# Para Leaky ReLU
nn.init.kaiming_normal_(layer.weight, mode='fan_in',
nonlinearity='leaky_relu') # usa a=0.01
# En un modelo CNN
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 64, 3, padding=1)
self.conv2 = nn.Conv2d(64, 128, 3, padding=1)
self.fc = nn.Linear(128 * 8 * 8, 10)
# He/Kaiming para capas con ReLU
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out',
nonlinearity='relu')
if m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight)
nn.init.zeros_(m.bias)
def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.relu(self.conv2(x))
x = x.view(x.size(0), -1)
return self.fc(x)
import torch.nn as nn
# No existe nn.init.lecun_*, pero es equivalente a:
# Normal: std = 1 / sqrt(fan_in)
fan_in = layer.weight.shape[1]
nn.init.normal_(layer.weight, mean=0, std=(1.0 / fan_in) ** 0.5)
# O manualmente usando kaiming con nonlinearity='linear':
nn.init.kaiming_normal_(layer.weight, mode='fan_in',
nonlinearity='linear')
# Modelo con SELU
class SelfNormMLP(nn.Module):
def __init__(self, in_f, hidden, out_f):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_f, hidden),
nn.SELU(),
nn.AlphaDropout(0.05),
nn.Linear(hidden, hidden),
nn.SELU(),
nn.AlphaDropout(0.05),
nn.Linear(hidden, out_f),
)
# LeCun init para SELU
for m in self.modules():
if isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight, mode='fan_in',
nonlinearity='linear')
nn.init.zeros_(m.bias)
def forward(self, x):
return self.net(x)
import torch.nn as nn
# Ortogonal con ganancia para ReLU
nn.init.orthogonal_(layer.weight, gain=nn.init.calculate_gain('relu'))
# Ganancias predefinidas
gain_relu = nn.init.calculate_gain('relu') # √2
gain_tanh = nn.init.calculate_gain('tanh') # 5/3
gain_lrelu = nn.init.calculate_gain('leaky_relu', param=0.2)
# Ejemplo para RNN
class CustomRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super().__init__()
self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
# Ortogonal para recurrencia (crucial en RNNs)
for name, param in self.rnn.named_parameters():
if 'weight_hh' in name:
nn.init.orthogonal_(param)
elif 'weight_ih' in name:
nn.init.xavier_uniform_(param)
elif 'bias' in name:
nn.init.zeros_(param)
def forward(self, x):
return self.rnn(x)
import torch
import torch.nn as nn
def check_activation_stats(model, input_shape=(64, 784)):
"""Registra la media y varianza de activaciones por capa."""
activations = {}
def hook_fn(name):
def hook(module, input, output):
activations[name] = output.detach()
return hook
hooks = []
for name, layer in model.named_modules():
if isinstance(layer, (nn.Linear, nn.Conv2d)):
hooks.append(layer.register_forward_hook(hook_fn(name)))
# Forward con datos aleatorios
x = torch.randn(*input_shape)
with torch.no_grad():
model(x)
# Imprimir estadísticas
for name, act in activations.items():
print(f"{name:20s} | media={act.mean():.4f} | "
f"std={act.std():.4f} | "
f"% ceros={100*(act==0).float().mean():.1f}%")
# Limpiar hooks
for h in hooks:
h.remove()
# Uso:
# model = MLP(784, 256, 10)
# check_activation_stats(model)
🟠 Inicializaciones en TensorFlow / Keras
En TensorFlow/Keras, las inicializaciones se pasan como el argumento
kernel_initializer de cada capa. Por defecto, Dense usa
Glorot Uniform (Xavier Uniform).
import tensorflow as tf
from tensorflow import keras
from keras import layers
# Glorot/Xavier Normal
init_glorot_n = keras.initializers.GlorotNormal()
# Glorot/Xavier Uniform (DEFAULT para Dense)
init_glorot_u = keras.initializers.GlorotUniform()
model = keras.Sequential([
layers.Dense(256, activation='tanh',
kernel_initializer='glorot_normal', # string
bias_initializer='zeros'),
layers.Dense(256, activation='tanh',
kernel_initializer=init_glorot_n), # objeto
layers.Dense(10, activation='softmax',
kernel_initializer='glorot_uniform'),
])
# Nota: Dense() sin kernel_initializer ya usa GlorotUniform
model_default = keras.Sequential([
layers.Dense(256, activation='tanh'), # Glorot Uniform implícito
layers.Dense(10, activation='softmax'),
])
import tensorflow as tf
from tensorflow import keras
from keras import layers
# He Normal
init_he_n = keras.initializers.HeNormal()
# He Uniform
init_he_u = keras.initializers.HeUniform()
# Modelo CNN con He
model = keras.Sequential([
layers.Conv2D(64, 3, padding='same', activation='relu',
kernel_initializer='he_normal'),
layers.BatchNormalization(),
layers.Conv2D(128, 3, padding='same', activation='relu',
kernel_initializer='he_normal'),
layers.BatchNormalization(),
layers.GlobalAveragePooling2D(),
layers.Dense(10, activation='softmax',
kernel_initializer='glorot_uniform'),
])
# Con Leaky ReLU (usar VarianceScaling directamente)
init_leaky = keras.initializers.VarianceScaling(
scale=2.0,
mode='fan_in',
distribution='truncated_normal'
)
model_leaky = keras.Sequential([
layers.Dense(256, kernel_initializer=init_leaky),
layers.LeakyReLU(0.2),
layers.Dense(10, activation='softmax'),
])
from tensorflow import keras
from keras import layers
# LeCun Normal
init_lecun_n = keras.initializers.LecunNormal()
# LeCun Uniform
init_lecun_u = keras.initializers.LecunUniform()
# Modelo SELU auto-normalizante
model = keras.Sequential([
layers.Dense(256, activation='selu',
kernel_initializer='lecun_normal'),
layers.AlphaDropout(0.05),
layers.Dense(256, activation='selu',
kernel_initializer='lecun_normal'),
layers.AlphaDropout(0.05),
layers.Dense(10, activation='softmax'),
])
from tensorflow import keras
from keras import layers
# Ortogonal con ganancia
init_ortho = keras.initializers.Orthogonal(gain=1.0)
# Para RNN
model = keras.Sequential([
layers.SimpleRNN(128, return_sequences=True,
kernel_initializer='glorot_uniform',
recurrent_initializer='orthogonal'), # default!
layers.SimpleRNN(64,
kernel_initializer='glorot_uniform',
recurrent_initializer=keras.initializers.Orthogonal(
gain=1.0
)),
layers.Dense(10, activation='softmax'),
])
# Nota: las capas RNN de Keras ya usan ortogonal por defecto
# para recurrent_initializer. ¡No necesitas cambiarlo!
from tensorflow import keras
# VarianceScaling es la base de todas las demás:
# GlorotUniform = VarianceScaling(scale=1, mode='fan_avg', dist='uniform')
# GlorotNormal = VarianceScaling(scale=1, mode='fan_avg', dist='truncated_normal')
# HeNormal = VarianceScaling(scale=2, mode='fan_in', dist='truncated_normal')
# HeUniform = VarianceScaling(scale=2, mode='fan_in', dist='uniform')
# LecunNormal = VarianceScaling(scale=1, mode='fan_in', dist='truncated_normal')
# LecunUniform = VarianceScaling(scale=1, mode='fan_in', dist='uniform')
# Personalizado: He para Leaky ReLU con α=0.2
alpha = 0.2
custom_init = keras.initializers.VarianceScaling(
scale=2.0 / (1 + alpha**2), # ≈ 1.923
mode='fan_in',
distribution='truncated_normal',
seed=42
)
# Parámetros:
# scale: factor multiplicativo de la varianza
# mode: 'fan_in', 'fan_out', o 'fan_avg'
# distribution: 'truncated_normal', 'untruncated_normal', o 'uniform'
📋 Resumen: correspondencias entre frameworks
| Inicialización | PyTorch (nn.init) |
TensorFlow/Keras |
|---|---|---|
| Xavier Normal | xavier_normal_(w) |
GlorotNormal() |
| Xavier Uniform | xavier_uniform_(w) |
GlorotUniform() ← default Dense |
| He Normal | kaiming_normal_(w) |
HeNormal() |
| He Uniform | kaiming_uniform_(w) ← default |
HeUniform() |
| LeCun Normal | kaiming_normal_(w, nonlinearity='linear') |
LecunNormal() |
| Ortogonal | orthogonal_(w, gain=g) |
Orthogonal(gain=g) |
| Ceros | zeros_(w) |
Zeros() |
| Normal genérica | normal_(w, mean, std) |
RandomNormal(mean, stddev) |
Atención a los defaults:
- PyTorch:
LinearyConv2dusan Kaiming Uniform (He) por defecto — ✅ buena elección para ReLU. - TensorFlow/Keras:
DenseyConv2Dusan Glorot Uniform (Xavier) por defecto — ⚠️ subóptimo para ReLU, considera cambiar ahe_normal.