Perceptrón Multicapa (MLP)
La arquitectura que superó las limitaciones del perceptrón simple. Capas de neuronas conectadas que pueden aprender cualquier función continua gracias al backpropagation.
Motivación: más allá del perceptrón
En el módulo anterior, vimos que el perceptrón simple solo puede resolver problemas linealmente separables. La función XOR, los patrones circulares o cualquier frontera curva quedan fuera de su alcance.
La solución, sorprendentemente elegante, consiste en apilar perceptrones en capas. Cada capa transforma el espacio de representación, creando características cada vez más abstractas. Esto es el Perceptrón Multicapa (Multi-Layer Perceptron, MLP), también conocido como red neuronal feedforward (de alimentación directa). Históricamente, la idea de combinar perceptrones en capas se propuso en los años 60, pero no fue hasta 1986, con la publicación del algoritmo de backpropagation por Rumelhart, Hinton y Williams, cuando se demostró que era posible entrenar estas redes de forma eficiente.
Idea fundamental: Un MLP no es simplemente "muchos perceptrones juntos". Es un sistema de transformaciones compuestas: cada capa deforma el espacio de entrada, y las capas sucesivas trabajan sobre representaciones cada vez más útiles para la tarea.
De lineal a no lineal
Un solo perceptrón traza una frontera lineal (una recta en 2D, un plano en 3D). Pero al combinar múltiples perceptrones en una capa oculta, cada uno traza su propia frontera, y la capa de salida combina estas fronteras para crear regiones de decisión arbitrariamente complejas.
La clave de esta capacidad está en las funciones de activación no lineales. Sin ellas, apilar capas lineales seguiría produciendo una transformación lineal (la composición de funciones lineales es lineal). Es la no linealidad la que permite que cada capa «doble» y «deforme» el espacio, creando representaciones internas donde los datos sí son separables. Si quieres profundizar en las funciones de activación, consulta el explorador de funciones de activación.
Arquitectura del MLP
Un MLP está formado por capas de neuronas organizadas secuencialmente. Cada neurona en una capa está conectada a todas las neuronas de la capa siguiente — por eso también se llaman capas fully connected o densas.
Componentes
- Capa de entrada: Recibe los datos crudos. No realiza ningún cálculo — simplemente pasa los valores a la primera capa oculta. Su tamaño viene determinado por el número de características del dato.
- Capas ocultas: Una o más capas intermedias donde ocurre la magia. Cada neurona aplica una transformación lineal (pesos + bias) seguida de una no linealidad (función de activación).
- Capa de salida: Produce el resultado final. Su tamaño y función de activación dependen de la tarea (1 neurona para regresión, N neuronas con softmax para clasificación en N clases).
Todas las conexiones entre neuronas se representan internamente como tensores: los pesos de cada capa son una matriz y los biases un vector. La operación fundamental de cada capa es una multiplicación matricial seguida de una suma y una no linealidad. A esta conexión donde todas las neuronas de una capa se conectan con todas las de la siguiente se le denomina capa densa o fully connected, y es lo que diferencia al MLP de arquitecturas como las CNNs (donde las conexiones son locales) o las RNNs (donde hay conexiones recurrentes).
Notación
Para una red con \(L\) capas (sin contar la entrada), denotamos:
- \(n^{[l]}\) = número de neuronas en la capa \(l\) (donde \(l = 0\) es la entrada).
- \(\mathbf{W}^{[l]} \in \mathbb{R}^{n^{[l]} \times n^{[l-1]}}\) = matriz de pesos de la capa \(l\).
- \(\mathbf{b}^{[l]} \in \mathbb{R}^{n^{[l]}}\) = vector de biases de la capa \(l\).
- \(\mathbf{a}^{[l]}\) = vector de activaciones de la capa \(l\) (con \(\mathbf{a}^{[0]} = \mathbf{x}\)).
Número de parámetros
El número total de parámetros entrenables es la suma de pesos y biases:
Por ejemplo, una red [3, 4, 3, 2] tiene: \(3 \times 4 + 4 + 4 \times 3 + 3 + 3 \times 2 + 2 = 12 + 4 + 12 + 3 + 6 + 2 = 39\) parámetros.
Supongamos una red para clasificar dígitos MNIST (imágenes 28×28 = 784 píxeles, 10 clases). La arquitectura es [784, 256, 128, 10]:
Capa 1 (784 → 256):
Pesos: 784 × 256 = 200.704 | Biases: 256 | Subtotal: 200.960
Capa 2 (256 → 128):
Pesos: 256 × 128 = 32.768 | Biases: 128 | Subtotal: 32.896
Capa 3 (128 → 10):
Pesos: 128 × 10 = 1.280 | Biases: 10 | Subtotal: 1.290
Total: 235.146 parámetros
Nota cómo la primera capa concentra el ~85% de los parámetros. Por eso técnicas como la reducción de dimensionalidad o las redes convolucionales (que comparten pesos) son tan importantes para datos de alta dimensión.
Forward pass (propagación directa)
El forward pass es el proceso de calcular la salida de la red dada una entrada. Se propaga la información capa por capa, de izquierda a derecha.
Para cada capa \(l = 1, 2, \dots, L\):
Esto es simplemente una multiplicación matricial seguida de una suma del bias y una función de activación elemento a elemento. Es lo mismo que hace un perceptrón... ¡pero ahora con matrices! Comprender bien esta operación es fundamental: cada capa densa de tu red ejecuta exactamente este cálculo, y todo el entrenamiento consiste en ajustar las matrices \(\mathbf{W}^{[l]}\) y los vectores \(\mathbf{b}^{[l]}\) para que la composición de todas las capas produzca la salida deseada.
En la práctica: El forward pass se computa con operaciones de álgebra lineal altamente optimizadas (BLAS/cuBLAS). Por eso las GPU, diseñadas para multiplicar matrices en paralelo, son tan eficientes para redes neuronales.
Ejemplo concreto
Para una red [2, 3, 1] con activación sigmoid, el forward pass completo sería:
💻 Forward pass en PyTorch
Así de simple es implementar un MLP con forward pass en PyTorch:
import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self, input_size, hidden_sizes, output_size):
super().__init__()
layers = []
prev = input_size
for h in hidden_sizes:
layers.append(nn.Linear(prev, h))
layers.append(nn.ReLU())
prev = h
layers.append(nn.Linear(prev, output_size))
self.network = nn.Sequential(*layers)
def forward(self, x):
return self.network(x)
# Crear red [784, 256, 128, 10]
model = MLP(784, [256, 128], 10)
# Forward pass
x = torch.randn(32, 784) # batch de 32
output = model(x) # → (32, 10)
print(output.shape)
Cada nn.Linear realiza \(\mathbf{z} = \mathbf{W}\mathbf{x} + \mathbf{b}\),
y nn.ReLU() aplica la activación. PyTorch se encarga automáticamente
del backpropagation gracias a su sistema de autograd.
Backpropagation
El backpropagation (retropropagación) es el algoritmo que permite entrenar un MLP. Es simplemente la regla de la cadena del cálculo diferencial aplicada de forma eficiente a toda la red.
La idea en 3 pasos
- Forward pass: Calcular la salida \(\hat{y}\) y almacenar todas las activaciones intermedias.
- Calcular el error: Comparar \(\hat{y}\) con la etiqueta real \(y\) usando una función de coste \(\mathcal{L}(y, \hat{y})\).
- Backward pass: Propagar el error hacia atrás, calculando el gradiente \(\frac{\partial \mathcal{L}}{\partial w}\) para cada peso, usando la regla de la cadena.
Regla de la cadena
La clave es que cada capa es una composición de funciones. Si \(f = f_L \circ f_{L-1} \circ \dots \circ f_1\), entonces:
Gradientes por capa
Para la capa \(l\), los gradientes son:
Donde \(\delta^{[l]} = \frac{\partial \mathcal{L}}{\partial \mathbf{z}^{[l]}}\) es el error local de la capa \(l\), y \(\odot\) es el producto elemento a elemento (Hadamard).
Actualización de pesos
Con los gradientes calculados, actualizamos los pesos con descenso de gradiente:
Este proceso de actualización iterativa es el descenso del gradiente, que estudiamos en detalle en su propio submódulo. En la práctica existen variantes más sofisticadas (SGD con momentum, Adam, AdamW…) que aceleran la convergencia y se tratan en el módulo de optimizadores y schedulers.
Problema del gradiente desvaneciente: Al propagar gradientes muchas capas hacia atrás, si la derivada de la activación es pequeña (como sigmoid para valores extremos), los gradientes se multiplican por valores < 1 repetidamente y se desvanecen exponencialmente. Esto hace que las primeras capas apenas aprendan, mientras las últimas se actualizan con normalidad. ReLU mitiga parcialmente este problema porque su derivada es exactamente 1 para \(z > 0\), pero puede causar el problema inverso: neuronas muertas (dead neurons) cuando \(z \leq 0\) de forma permanente. Variantes como LeakyReLU y GELU abordan ambos problemas.
Este tema se trata en profundidad en el submódulo de Estabilidad del Entrenamiento, donde verás técnicas avanzadas como batch normalization, gradient clipping, skip connections y estrategias de inicialización de pesos que permiten entrenar redes con decenas o cientos de capas.
Teorema de aproximación universal
El resultado teórico más importante del MLP es el Teorema de Aproximación Universal, demostrado independientemente por Cybenko (1989) y Hornik (1991):
Teorema: Una red neuronal feedforward con una sola capa oculta y un número suficiente de neuronas puede aproximar cualquier función continua en un subconjunto compacto de \(\mathbb{R}^n\) con precisión arbitraria, siempre que la función de activación sea no constante, acotada y continua.
Este teorema es existencial: dice que la red existe, pero no dice que sea fácil de encontrar ni cuántas neuronas serán necesarias. En la práctica, suele ser más eficiente usar redes con múltiples capas más estrechas que una sola capa muy ancha. Esto se conoce como el beneficio de la profundidad (depth) frente a la anchura (width).
Resultados más recientes, como los de Eldan y Shamir (2016), han demostrado formalmente que existen funciones que una red de 3 capas puede representar de forma compacta pero que requerirían un número exponencial de neuronas en una red de 2 capas. Esto justifica teóricamente lo que la práctica ya sugería: la profundidad importa. Sin embargo, añadir profundidad trae sus propios desafíos (gradientes evanescentes, dificultad de optimización), que se abordan con las técnicas del módulo de estabilidad del entrenamiento.
Diseño práctico de un MLP
Diseñar un MLP eficaz es tanto ciencia como arte. Las siguientes son guías prácticas orientativas que funcionan bien como punto de partida, pero no deben tomarse como reglas universales. Cada problema tiene sus particularidades: la cantidad de datos disponible, la complejidad de la tarea, las restricciones de memoria y latencia, e incluso el presupuesto computacional para búsqueda de hiperparámetros influyen en la arquitectura óptima. Lo más importante es experimentar sistemáticamente: empieza con una configuración sencilla, evalúa, y ajusta.
Importante: Estas recomendaciones son heurísticas basadas en la experiencia acumulada de la comunidad, no leyes universales. Un problema concreto puede requerir más o menos capas, neuronas diferentes de las potencias de 2, o combinaciones de activaciones que no aparecen aquí. La búsqueda sistemática de hiperparámetros (que veremos en optimización de hiperparámetros) es siempre recomendable para proyectos serios.
Número de capas
- 1 capa oculta: Suficiente para la mayoría de problemas tabulares simples.
- 2-3 capas ocultas: Permiten representaciones jerárquicas más ricas. Recomendado para problemas complejos.
- 4+ capas: Raramente útil en MLPs clásicos. Para problemas que requieren mucha profundidad, es mejor usar CNNs, RNNs o Transformers.
Número de neuronas por capa
- Regla general: Empezar con un valor entre el número de entradas y salidas.
- Forma de embudo: Reducir gradualmente el número de neuronas (ej: 128 → 64 → 32).
- Forma de reloj de arena: Reducir y luego aumentar (autoencoders).
- Potencias de 2: Usar 32, 64, 128... por eficiencia en GPU.
Funciones de activación recomendadas
La función de activación que usamos en cada capa determina la capacidad expresiva de la red y cómo se propagan los gradientes durante el entrenamiento. En las capas ocultas, la elección estándar hoy en día es ReLU (\(f(z) = \max(0, z)\)) o alguna de sus variantes modernas como GELU (usada en Transformers) o Swish (usada en EfficientNet). ReLU es rápida de computar, no satura para valores positivos y produce gradientes de magnitud constante, lo que facilita el entrenamiento de redes profundas.
En la capa de salida, la activación depende enteramente del tipo de tarea. La siguiente tabla resume las combinaciones estándar. Si quieres explorar el comportamiento de cada función de activación en detalle, visita el explorador de funciones de activación.
| Ubicación | Tarea | Activación |
|---|---|---|
| Capas ocultas | Cualquiera | ReLU (o variantes: LeakyReLU, GELU, Swish) |
| Salida | Regresión | Lineal (identidad) |
| Salida | Clasificación binaria | Sigmoid |
| Salida | Clasificación multiclase | Softmax |
| Salida | Regresión acotada [0,1] | Sigmoid |
| Salida | Regresión acotada [-1,1] | Tanh |
Funciones de coste
La función de coste (o loss function) cuantifica el error entre la predicción del modelo y el valor real. Es la señal que guía todo el proceso de aprendizaje: los gradientes que calculamos en el backward pass son, literalmente, las derivadas parciales de esta función respecto a cada peso.
La elección de la función de coste está íntimamente ligada a la activación de la capa de salida. Por ejemplo, cross-entropy combinada con softmax produce gradientes numéricamente estables y bien comportados, mientras que usar MSE con softmax puede causar gradientes muy pequeños cuando el modelo está «seguro pero equivocado», ralentizando el aprendizaje. Si quieres experimentar con diferentes funciones de pérdida, prueba el explorador de funciones de pérdida.
| Tarea | Función de coste | Fórmula |
|---|---|---|
| Regresión | MSE (Mean Squared Error) | \(\mathcal{L} = \frac{1}{n}\sum(y - \hat{y})^2\) |
| Clasificación binaria | Binary Cross-Entropy | \(\mathcal{L} = -[y\log\hat{y} + (1-y)\log(1-\hat{y})]\) |
| Clasificación multiclase | Categorical Cross-Entropy | \(\mathcal{L} = -\sum_k y_k \log\hat{y}_k\) |
Regla rápida: Si tu salida es continua → MSE. Si es probabilidad binaria → Binary Cross-Entropy. Si es una de \(K\) clases → Categorical Cross-Entropy. En caso de duda, Cross-Entropy suele funcionar mejor que MSE para clasificación porque penaliza más las predicciones seguras pero incorrectas.
Más allá del MLP
El MLP es la base sobre la que se construye todo el deep learning moderno. Cada arquitectura avanzada es, en esencia, un MLP con restricciones estructurales que lo hacen más eficiente para un tipo específico de datos.
Pero es importante entender que estas arquitecturas no reemplazan al MLP: lo combinan con otras estructuras. Un Transformer, por ejemplo, alterna capas de atención con capas MLP (llamadas feed-forward networks) dentro de cada bloque. Una CNN extrae features espaciales con convoluciones, pero luego suele alimentar esas features a capas densas (MLP) para la clasificación final. Incluso las GNNs más sofisticadas usan MLPs como componente interno en sus funciones de paso de mensajes. Dominar el MLP no es solo aprender «lo básico»: es entender el bloque que aparece dentro de prácticamente toda arquitectura de deep learning.
| Arquitectura | Tipo de datos | Innovación clave | Estudiar |
|---|---|---|---|
| CNN | Imágenes, datos espaciales | Pesos compartidos + convoluciones locales | Convolución y fundamentos → |
| RNN / LSTM | Secuencias, texto, series temporales | Conexiones recurrentes + memoria | RNN fundamentos → |
| Transformer | Texto, cualquier secuencia | Atención (atender a todas las posiciones) | Transformers → |
| GNN | Grafos, moléculas | Paso de mensajes entre nodos | Redes de grafos → |
| Autoencoder | Compresión, generación | Bottleneck para representación latente | Autoencoders → |
A pesar de la sofisticación de estas arquitecturas, el MLP sigue siendo la opción más directa y competitiva para datos tabulares (tablas con features numéricos y categóricos). Investigaciones recientes como Gorishniy et al. (2021) han demostrado que MLPs bien ajustados siguen rivalizando con métodos como XGBoost o arquitecturas tabulares especializadas en muchos benchmarks. Si tus datos vienen en forma de tabla, un MLP es casi siempre un excelente punto de partida.
Referencias y lecturas complementarias:
- Rumelhart, Hinton & Williams, «Learning representations by back-propagating errors» (1986) — el paper que popularizó backpropagation
- Cybenko, «Approximation by Superpositions of a Sigmoidal Function» (1989)
- Goodfellow, Bengio & Courville, Deep Learning (2016) — capítulos 6-8 cubren MLPs en profundidad
Siguiente paso: Prueba las demos interactivas para ver el MLP en acción. Configura la arquitectura, ajusta los pesos, observa el forward pass y diseña tu propia red neuronal para exportar como imagen.