MLP multiclase
Antes de tocar el código
Dos clases pueden estar entrelazadas de manera que ninguna línea las divida. Las neuronas ocultas transforman primero el espacio para volver separable el problema.
Pregunta esencial: ¿Qué puede aprender una capa oculta que una recta nunca podrá separar?
Haz una predicción antes de ejecutar el notebook. Al final volverás a ella y tendrás que decir qué evidencia la confirmó, la corrigió o la dejó abierta.
Mapa de esta clase: Cómo una MLP dobla el espacio. La figura no es decorativa; úsala para explicar el mecanismo con tus propias palabras.
🎯 Qué vas a aprender y construir
Resolver clasificación no lineal con capas densas, activaciones y regularización.
Práctica propia de esta clase: Comparar una frontera lineal con una MLP y relacionar las activaciones ocultas con las regiones de decisión.
Es la clase 03 de 31 del programa y pertenece a 🟢 el módulo 1, Fundamentos: de la derivada a la primera red. Llegas desde Perceptrón con PyTorch y lo que hagas aquí lo da por supuesto CNN para visión.
Trabajarás con el dataset dry_bean (UCI, licencia: CC BY 4.0), y tendrás que superar la línea base Regresión logística multinomial y Random Forest, decidiendo con la métrica macro_f1 medida sobre validation. Nivel fundamentos, unas 4 horas de dedicación.
Lo que conviene traer resuelto de las clases anteriores: Python básico, NumPy, álgebra lineal elemental.
Al terminar deberías ser capaz de:
- Resolver clasificación no lineal con capas densas, activaciones y regularización.
- Preparar y auditar el dataset real dry_bean sin fuga de datos.
- Entrenar y evaluar red multicapa para relaciones no lineales.
- Comparar contra la línea base: Regresión logística multinomial y Random Forest.
- Interpretar intervalos de confianza, errores y limitaciones.
Una idea que conviene desmontar
Más capas no garantizan una mejor solución; añaden capacidad, costo y nuevas formas de sobreajustar.
🧠 Comprender antes de entrenar
Esta sección es la explicación completa del tema. No hace falta abrir otro archivo para entender lo que viene después: aquí está la idea, la matemática que la sostiene y sus límites. (El mismo texto vive en theory.md, que es la fuente desde la que se genera esta guía, junto con la bibliografía del final.)
De qué trata
Este laboratorio estudia red multicapa para relaciones no lineales usando dry_bean, un dataset público real procedente de UCI.
Un clasificador lineal solo puede trazar hiperplanos: falla cuando las clases se entrelazan de forma no lineal. El perceptrón multicapa (MLP) resuelve esto apilando capas de neuronas separadas por funciones de activación no lineales. La clave conceptual es que sin esas no linealidades, componer varias capas lineales sería inútil —el producto de matrices sigue siendo una matriz, es decir, otro modelo lineal—. La activación (aquí ReLU) es lo que permite que cada capa doble y pliegue el espacio de representación, de modo que clases inseparables en el espacio original se vuelvan separables en el espacio aprendido.
El problema —clasificar 13.611 granos en siete variedades a partir de 16 atributos de forma— es genuinamente multiclase y no lineal, ideal para observar cómo una capa oculta supera a la regresión logística multinomial. El laboratorio también introduce la regularización (dropout, weight decay) como respuesta al mayor riesgo de sobreajuste que trae la capacidad adicional.
La matemática, paso a paso
Una red de una capa oculta calcula su predicción en dos etapas. Primero proyecta la entrada a un espacio oculto y aplica una no linealidad; luego proyecta ese espacio oculto a los logits de las clases:
h = ReLU(x·W₁ + b₁) con ReLU(a) = max(0, a)
logits = h·W₂ + b₂
La función ReLU (Rectified Linear Unit) es engañosamente simple: deja pasar los valores positivos y anula los negativos. Su derivada es 1 para a > 0 y 0 para a < 0, lo que la hace barata de calcular y, sobre todo, evita el problema del desvanecimiento del gradiente que sufren la sigmoide y la tanh, cuyas derivadas se aproximan a 0 en sus extremos y frenan el aprendizaje en redes profundas. El "codo" no lineal en a = 0 es lo que aporta la capacidad expresiva: cada neurona ReLU introduce un pliegue lineal por tramos, y su combinación aproxima superficies de decisión arbitrariamente complejas.
Este poder no es una intuición vaga sino un resultado formal: el teorema de aproximación universal (Cybenko 1989 para sigmoides; Hornik 1991 para activaciones generales) demuestra que una red con una sola capa oculta y suficientes neuronas puede aproximar cualquier función continua sobre un conjunto compacto con el error que se desee. El teorema garantiza la existencia de los pesos, no que el descenso de gradiente los encuentre fácilmente; en la práctica, apilar más capas suele ser más eficiente en parámetros que ensanchar una sola.
Para clasificación multiclase, los logits se convierten en una distribución de probabilidad con softmax, que normaliza exponenciales para que sumen 1:
softmax(z)ₖ = e^(zₖ) / Σⱼ e^(zⱼ)
y se entrena minimizando la entropía cruzada categórica, L = −(1/N) Σᵢ ln( p_{i, yᵢ} ), donde p_{i, yᵢ} es la probabilidad que el modelo asigna a la clase verdadera del ejemplo i. En PyTorch, CrossEntropyLoss fusiona softmax y log-verosimilitud de forma numéricamente estable, por lo que la última capa entrega logits crudos. Los gradientes fluyen hacia atrás por retropropagación: ∂L/∂W₂ se calcula directamente y, por la regla de la cadena a través de la ReLU, el error se propaga a la capa oculta (∂L/∂W₁), donde la máscara de la ReLU bloquea el gradiente en las neuronas que estaban inactivas.
La formulación debe conectarse con cuatro elementos: representación de entrada, función del modelo, función de pérdida y regla de actualización. El notebook muestra las dimensiones de los tensores y conserva la misma implementación que el script de terminal.
Las ecuaciones de la retropropagación en esta red
Con dos capas, la retropropagación cabe en cuatro líneas y conviene tenerlas escritas, porque son el esqueleto de todo lo que viene después. Llamando a = x·W₁ + b₁ a la preactivación oculta, h = ReLU(a), z = h·W₂ + b₂ a los logits y p = softmax(z):
δ² = p − Y, ∂L/∂W₂ = hᵀ·δ² / N, ∂L/∂b₂ = Σ_filas δ² / N,
δ¹ = (δ²·W₂ᵀ) ⊙ 𝟙[a > 0], ∂L/∂W₁ = xᵀ·δ¹ / N, ∂L/∂b₁ = Σ_filas δ¹ / N,
donde ⊙ es el producto elemento a elemento y 𝟙[a > 0] la máscara de la ReLU. Vale la pena leer la segunda línea despacio: el error de la capa de salida viaja hacia atrás multiplicado por W₂ᵀ —la misma matriz del paso hacia adelante, transpuesta— y luego se apaga en las posiciones donde la neurona estaba inactiva. Una neurona que no participó en la predicción tampoco recibe corrección.
Que δ² = p − Y no es evidente, y es el mismo regalo que aparecía en la clase 01. Derivando la entropía cruzada categórica respecto de los logits, el término del softmax ∂pₖ/∂z_j = pₖ·(δ_kj − p_j) se combina con ∂L/∂pₖ = −y_k/pₖ y todo se simplifica a p − y. Softmax con entropía cruzada, igual que sigmoide con entropía cruzada binaria, están emparejadas para que el gradiente sea el error puro.
Sobre el softmax hay una propiedad que se usa en toda implementación seria: es invariante a desplazamientos, softmax(z + c) = softmax(z) para cualquier constante c. Restar el máximo, softmax(z − max z), no cambia el resultado y garantiza que el mayor exponente sea e⁰ = 1, evitando el desbordamiento de e^z con logits grandes. Es lo que CrossEntropyLoss hace internamente, y la razón de que la última capa deba devolver logits crudos.
Cuántos parámetros hay y cómo inicializarlos
Contar los parámetros es inmediato y conviene hacerlo antes de entrenar. Cada capa densa de m entradas y n salidas aporta m·n pesos más n sesgos, así que para una pila de anchuras (H₁, H₂, …) entre d características y C clases:
|θ| = (d·H₁ + H₁) + (H₁·H₂ + H₂) + … + (H_L·C + C).
El modelo tabular de este repositorio usa por defecto dos capas ocultas de 128 y 64 unidades. Con las 16 características de forma del dataset y sus 7 variedades, la cuenta es 16·128 + 128 + 128·64 + 64 + 64·7 + 7 = 10 887 parámetros. Frente a los 13 611 granos del conjunto completo, la red tiene casi un parámetro por ejemplo: es exactamente la situación en la que memorizar es una estrategia disponible, y la que justifica el dropout y el weight decay que se estudian en la clase 20.
La inicialización no es un detalle. Si todos los pesos se ponen a cero, todas las neuronas ocultas calculan lo mismo, reciben el mismo gradiente y siguen siendo idénticas para siempre: la red se comporta como si tuviera una sola neurona oculta. Es el problema de simetría, y por eso los pesos se inicializan al azar. Pero la escala de ese azar importa: si la varianza es alta, las preactivaciones crecen capa a capa y saturan; si es baja, se encogen y la señal se apaga.
La receta que usan las redes con ReLU es la de He: muestrear W de una normal de varianza 2/fan_in, donde fan_in es el número de entradas de la capa. El factor 2 compensa exactamente que la ReLU anula la mitad de las activaciones y por tanto reduce la varianza a la mitad. Para activaciones simétricas como tanh, la inicialización de Glorot usa 2/(fan_in + fan_out), que equilibra la propagación en ambos sentidos. Es la primera aparición de una idea que la clase 19 desarrolla: gran parte del arte del entrenamiento consiste en mantener la varianza de las activaciones y de los gradientes dentro de un rango sano de extremo a extremo de la red.
De ahí sale también el fallo característico de la ReLU: si una neurona recibe una actualización que deja su preactivación negativa para todos los ejemplos, su gradiente es cero permanentemente y no vuelve a aprender nunca. Es la ReLU muerta, y su causa habitual es una tasa de aprendizaje demasiado alta. Las variantes Leaky ReLU y GELU, que se comparan en la clase 18, existen precisamente para dejar pasar algo de gradiente en la zona negativa.
Por qué la profundidad gana a la anchura
El teorema de aproximación universal garantiza que basta una capa oculta, pero no dice cuántas neuronas hacen falta, y ahí está la trampa: para muchas funciones ese número crece exponencialmente. Contar regiones lineales lo hace concreto. Una red ReLU es una función lineal a trozos, y el número de regiones en que divide el espacio de entrada mide su capacidad de plegar la frontera de decisión. Una red de una capa con H neuronas sobre d entradas genera del orden de O(H^d) regiones; una red profunda de L capas con H neuronas cada una alcanza del orden de Ω((H/d)^(d(L−1))·H^d), es decir, exponencial en la profundidad y solo polinómico en la anchura.
La lectura práctica es que añadir una capa multiplica la capacidad expresiva mucho más barato que ensanchar la existente, y es la justificación de que este laboratorio compare profundidad frente a anchura en vez de dar por buena la primera arquitectura que funcione. La contrapartida —redes profundas más difíciles de optimizar por gradientes que se desvanecen— es lo que motivará las conexiones residuales y la normalización en rutas posteriores.
La pregunta que deberías poder responder al terminar: ¿La complejidad adicional supera de forma estable a la línea base?
Qué se mide y con qué se decide
La clase reporta accuracy, balanced_accuracy, macro_precision, macro_recall, macro_f1. De todas ellas, la que decide qué modelo se conserva es macro_f1, y se mide siempre sobre validation: es la única forma de que test siga siendo una estimación honesta de lo que pasará con datos nuevos.
📓 Los tres cuadernos
La clase se puede recorrer en Jupyter y trae tres cuadernos con papeles distintos. Los tres siguen el mismo camino —descargar el dataset real, auditar la partición, entrenar, sellar el experimento y evaluar test una vez—; lo que cambia es qué te toca escribir a ti:
| Cuaderno | Qué trae | Cuándo usarlo |
|---|---|---|
📓 notebook.ipynb |
El recorrido de referencia: 25 celdas (9 de código) con todo el código escrito y ejecutable, intercalado con las explicaciones. No trae ejercicios. | Para leer y ejecutar de principio a fin. |
✏️ notebook_student.ipynb |
El mismo recorrido más 5 ejercicios evaluables (40 celdas en total). Las celdas de ejercicio están marcadas con # YOUR CODE HERE y debajo de cada una hay una comprobación. |
Para practicar. |
✅ notebook_solution.ipynb |
Los mismos ejercicios resueltos, marcados con # SOLUCIÓN DE REFERENCIA. Cada solución se ejecuta en la integración continua, así que se sabe que pasa. |
Para contrastar después de intentarlo. |
Qué se practica en los ejercicios
Cinco de ellos cubren el contrato experimental común: auditar la partición, decidir con validation, compararse con la línea base, sellar antes de abrir test y dejar el plan por escrito. Se resuelven con Python estándar —sin descargar el dataset ni entrenar—, así que se corrigen en segundos y sin GPU, y cada uno está parametrizado con los valores de este laboratorio: su métrica de selección, su línea base y su experimento propio.
Cómo abrirlos
Los cuadernos necesitan el extra notebooks, que instala Jupyter junto con el paquete:
pip install -e ".[dev,notebooks]"
jupyter lab labs/02_mlp_nonlinear/notebook.ipynb
También se abren desde VS Code —con la extensión de Jupyter— haciendo doble clic en el archivo, o desde la interfaz clásica con jupyter notebook. El primer arranque descarga el dataset real desde su proveedor, así que la primera ejecución tarda más y requiere conexión.
Si prefieres ejecutar sin abrir un cuaderno, train.py hace exactamente lo mismo desde la terminal, y la sección de comandos de arriba explica cada opción.
🖥️ Los comandos, explicados
Todo el laboratorio se maneja con una sola herramienta de terminal, neural-labs, que se instala junto con el paquete (pip install -e ".[dev,notebooks]"). Cada subcomando hace una cosa del protocolo, y por eso se pueden ejecutar por separado: preparar datos, auditar la partición, entrenar, repetir con varias semillas.
La forma general es siempre la misma:
neural-labs <subcomando> --lab <identificador> [opciones]
| Opción | Valor por defecto | Valores | Qué hace y cuándo cambiarla |
|---|---|---|---|
--lab |
02_mlp_nonlinear |
obligatorio | Qué laboratorio se ejecuta. Solo acepta los identificadores del catálogo. |
--quick |
desactivado | — | Usa una fracción real del dataset y pocas épocas. Sirve para comprobar la instalación, no para concluir nada sobre el modelo. |
--split-seed N |
42 |
entero | Semilla que decide qué ejemplo cae en qué partición. Se mantiene fija al comparar modelos. |
--training-seed N |
42 |
entero | Semilla de la inicialización de pesos y del barajado de lotes. Es la que se varía para medir cuánta diferencia es simple azar. |
--config |
baseline |
baseline · improved |
Cuál de las dos configuraciones del laboratorio se usa. |
--device |
auto |
auto · cpu · cuda · mps |
Dónde entrenar. auto elige GPU si está disponible y cae a CPU si no. |
--training-seeds A B C |
41 42 43 |
enteros | Solo en benchmark: la lista de semillas de entrenamiento que se repiten. |
--output-dir |
runs |
ruta | Dónde se escribe el directorio de la ejecución. |
El script del laboratorio
labs/02_mlp_nonlinear/train.py no es un programa distinto: fija el --lab y delega en la misma herramienta, de modo que estas dos líneas hacen exactamente lo mismo.
python labs/02_mlp_nonlinear/train.py --quick
neural-labs train --lab 02_mlp_nonlinear --quick
Lo mismo desde Python
Si prefieres trabajar en un cuaderno o llamar al laboratorio desde tu propio código, la misma ejecución se lanza así. La función devuelve un objeto con el directorio de la ejecución, las métricas y el historial ya cargados:
from neural_labs.experiments import run_lab
resultado = run_lab(
"02_mlp_nonlinear",
quick=True, # False para la ejecución completa
config_name="baseline",
split_seed=42, # fija la partición
training_seed=43, # varía la inicialización
)
print(resultado.run_dir) # dónde quedaron los archivos
print(resultado.metrics) # el diccionario de métricas finales
Y para preparar el dataset sin entrenar —útil para inspeccionarlo antes—:
from neural_labs.datasets import prepare_dataset
datos = prepare_dataset("02_mlp_nonlinear", quick=True, seed=42)
print(datos.summary) # tamaño de cada partición y metadatos de la fuente
🪜 Laboratorio guiado
Cada paso dice qué ocurre por dentro, por qué se hace en ese orden y cómo comprobar que salió bien. El orden no es una convención de estilo: es el que ejecuta el código, y alterarlo invalida el resultado.
Paso 1 — Traer el dataset real y partirlo
Qué ocurre. Descarga dry_bean desde su proveedor y construye las tres particiones —train, validation y test— con la semilla de partición que le pases.
Por qué. La partición se fija antes de entrenar y su semilla (--split-seed) es independiente de la del entrenamiento (--training-seed). Si ambas fueran la misma, no podrías distinguir si un cambio en el resultado viene de haber repartido los datos de otro modo o de haber inicializado los pesos de otro modo.
neural-labs dataset --lab 02_mlp_nonlinear --quick --split-seed 42
Cómo sabes que salió bien. El comando termina sin error y deja el dataset en caché. Si no hay red, falla: no se rellena con datos inventados (data/dataset.yaml declara fallback_to_generated_data: false).
Paso 2 — Comprobar que las particiones no se tocan
Qué ocurre. Recorre los identificadores de las tres particiones y verifica que ninguno aparece en dos de ellas.
Por qué. Una sola fila compartida entre train y test basta para que la métrica final quede inflada. Es el error más caro del oficio porque no produce ningún síntoma visible: el modelo simplemente «parece» mejor de lo que es.
neural-labs audit --lab 02_mlp_nonlinear --quick --split-seed 42
Cómo sabes que salió bien. La auditoría reporta cero solapamientos. Si reporta alguno, no sigas: el resto del laboratorio no significaría nada.
Paso 3 — Mirar los datos antes de modelarlos
Qué ocurre. Genera el informe de calidad (valores faltantes, balance de clases, rangos) y el de deriva entre particiones.
Por qué. Un desbalance fuerte o una diferencia de distribución entre train y test cambia qué métrica tiene sentido leer. Descubrirlo después de entrenar obliga a repetirlo todo.
neural-labs quality --lab 02_mlp_nonlinear --quick --split-seed 42
Cómo sabes que salió bien. Obtienes data_quality.json y drift_report.json; ábrelos antes de decidir la configuración.
Paso 4 — Estudiar la teoría del laboratorio
Qué ocurre. Leer theory.md: la idea central, el desarrollo matemático, los riesgos de interpretación y la bibliografía de la que sale todo eso.
Por qué. Sin esto, el entrenamiento es una caja que devuelve números. La teoría es lo que te permite decidir qué mirar y reconocer cuándo un resultado es sospechoso.
Cómo sabes que salió bien. Puedes responder, con tus palabras, qué calcula el modelo y por qué esa arquitectura encaja con la tarea multiclass_classification.
Paso 5 — Entrenar y seleccionar con validation
Qué ocurre. El entrenamiento recorre las épocas midiendo en validation después de cada una, y conserva el checkpoint con el mejor valor de macro_f1.
Por qué. El conjunto de validación existe para tomar decisiones —arquitectura, hiperparámetros, cuándo parar—. Si esas decisiones se tomaran mirando test, test dejaría de ser una estimación de lo que pasará con datos nuevos y pasaría a ser parte del entrenamiento.
python labs/02_mlp_nonlinear/train.py --quick
# o, con control explícito de las dos semillas:
neural-labs train --lab 02_mlp_nonlinear --config baseline --split-seed 42 --training-seed 43
Cómo sabes que salió bien. En runs/02_mlp_nonlinear/<ejecución>/ aparecen history.csv y best_model.pt; la métrica de validación mejora respecto de la primera época.
Paso 6 — Compararte con la línea base
Qué ocurre. El repositorio entrena por su cuenta Regresión logística multinomial y Random Forest y guarda su resultado, primero sobre validation y —solo al final— sobre test.
Por qué. Una métrica sola no dice si el modelo aporta algo. Puede que un método mucho más simple llegue igual de lejos, y entonces la complejidad añadida no está justificada. Esta comparación es la que convierte un número en un argumento.
Cómo sabes que salió bien. Comparas metrics.json con baseline_metrics.json. Si tu modelo no supera la línea base, el resultado del laboratorio es exactamente ese, y hay que reportarlo.
Paso 7 — El sellado: experiment.lock.json
Qué ocurre. Antes de tocar test, el código escribe un archivo que fija el laboratorio, las dos semillas, la configuración, la métrica de selección, el checkpoint elegido y el hash del dataset.
Por qué. Es la frontera del experimento. A partir de ahí, cualquier ajuste que hagas mirando test queda a la vista: el sello dice qué habías decidido antes de ver el resultado final. Sin ese archivo, nadie —incluido tú dentro de un mes— puede distinguir una predicción de una racionalización.
Cómo sabes que salió bien. El archivo existe y su contenido coincide con lo que creías haber ejecutado.
Paso 8 — Evaluar test una sola vez y medir la incertidumbre
Qué ocurre. Con el checkpoint congelado se evalúa test, se calculan intervalos de confianza por bootstrap y se desglosan las métricas por subgrupo.
Por qué. Un número puntual esconde cuánto podría moverse. Los intervalos dicen si la diferencia con la línea base es real o cabe dentro del ruido; el desglose por subgrupo revela si el promedio está tapando un grupo donde el modelo funciona mucho peor.
Cómo sabes que salió bien. Tienes metrics.json, confidence_intervals.json y subgroup_metrics.json, y puedes decir la magnitud de la mejora y su incertidumbre.
Paso 9 — Repetir con varias semillas de entrenamiento
Qué ocurre. Se repite el entrenamiento manteniendo fija la partición y cambiando solo la semilla de entrenamiento.
Por qué. Dos ejecuciones idénticas salvo por la inicialización pueden diferir bastante. Si no mides esa dispersión, corres el riesgo de celebrar una mejora que era una semilla afortunada.
neural-labs benchmark --lab 02_mlp_nonlinear --quick --split-seed 42 --training-seeds 41 42 43
Cómo sabes que salió bien. Obtienes media y dispersión entre semillas, no un único número.
Paso 10 — Documentar y cerrar
Qué ocurre. Cada ejecución deja model_card.md y report.md; el plan de experimentos vive en experiments.md y la rúbrica en assessment.md.
Por qué. Un resultado sin su contexto —qué datos, qué decisiones, qué límites— no es reutilizable. La model card es lo que permite que otra persona sepa cuándo no debería usar tu modelo.
Cómo sabes que salió bien. Completaste la tabla multi-semilla de experiments.md y respondiste las preguntas de assessment.md.
🔍 Cómo leer lo que produce la ejecución
Cada ejecución escribe su propio directorio con nombre único, de modo que dos corridas nunca se pisan. Esto es lo que encontrarás dentro:
| Archivo | Qué contiene y qué mirar |
|---|---|
config.yaml · environment.json |
La configuración exacta y el entorno (versiones, dispositivo) de la ejecución. |
dataset_manifest.json · dataset_card.md |
Procedencia, licencia, hash y tamaño de cada partición. |
data_quality.json · drift_report.json |
Calidad de los datos y diferencias de distribución entre particiones. |
baseline_validation_metrics.json |
La línea base medida en validation, antes de entrenar. |
history.csv · history.png |
Pérdida y métricas época a época: aquí se ve si el entrenamiento converge o sobreajusta. |
experiment.lock.json |
El sello del experimento, escrito antes de tocar test. |
metrics.json |
El resultado final en test, más tiempo, dispositivo y número de parámetros. |
baseline_metrics.json |
La línea base en test, calculada después de tu evaluación final. |
best_model.pt · last_model.pt |
El checkpoint elegido por validación y el último, para poder compararlos. |
confidence_intervals.json |
Intervalos por bootstrap: cuánto podría moverse cada métrica. |
subgroup_metrics.json |
El mismo resultado desglosado por subgrupo, para ver qué esconde el promedio. |
predictions.csv |
Predicción por ejemplo, para analizar los errores uno a uno. |
confusion_matrix.png |
Qué clases se confunden entre sí. |
model_spec.json · inference_contract.json |
Qué entrada espera el modelo y qué devuelve: lo que necesita quien lo despliegue. |
model_card.md · report.md |
La ficha del modelo y el informe legible de la ejecución. |
⚠️ Dónde suele perderse la gente
--quickno es una versión pequeña del resultado, es una prueba de que todo corre. En esta ruta recorta a 1024 ejemplos de entrenamiento · 256 de validación · 256 de test · 2 épocas. Sirve para comprobar la instalación y la descarga; cualquier conclusión sobre el modelo exige la ejecución completa.- Cambiar algo después de ver
testinvalida la comparación. Si al mirar el resultado final se te ocurre una mejora, la ruta correcta es volver avalidation, decidir allí, y sellar de nuevo. - Las dos semillas no son intercambiables.
--split-seedcambia qué datos caen en cada partición;--training-seedcambia cómo se inicializa y baraja el entrenamiento. Para comparar modelos se fija la primera y se varía la segunda. - Límite declarado de este dataset. 13.611 granos de siete variedades reales y 16 atributos de forma.
Riesgos al interpretar los resultados
13.611 granos de siete variedades reales y 16 atributos de forma.
El dataset refleja su proceso de recolección y no representa automáticamente otros períodos, países o poblaciones. Una asociación predictiva no demuestra causalidad.
✅ Antes de darlo por terminado
La clase está aprobada cuando se cumplen estos criterios:
- [ ] cero solapamiento entre train, validation y test
- [ ] selección basada únicamente en validation
- [ ] métricas finales acompañadas por incertidumbre
- [ ] conclusiones que distinguen evidencia de suposición
Y cuando tienes estos entregables:
- [ ] notebook ejecutado
- [ ] reporte experimental
- [ ] model card
- [ ] comparación con línea base
- [ ] respuesta a preguntas críticas
El plan experimental con la tabla que hay que completar está en experiments.md, y las preguntas con su rúbrica, en assessment.md. Ambos documentos se abren desde la barra de navegación de arriba.
Para ir más lejos
- Cambia una decisión experimental y justifícala con el resultado en
validation, no con la intuición. - Analiza los errores por clase o por segmento: casi siempre se concentran en un subconjunto reconocible.
- Compara costo, precisión y latencia; el mejor modelo no siempre es el que gana por décimas.
- Documenta sesgos, limitaciones y usos para los que no recomendarías este modelo.
📚 Fuentes
La teoría de arriba no es original de este repositorio: se apoya en la literatura de referencia del área y en los papers originales de cada arquitectura. Estas son las obras concretas, y lo que aporta cada una:
Las referencias apuntan a las obras; no se reproduce su contenido, la redacción es original.
- Goodfellow, Bengio & Courville — Deep Learning (MIT Press 2016), cap. 6 — redes hacia adelante, ReLU, softmax y retropropagación.
- Géron — Hands-On Machine Learning (3.ª ed., O'Reilly 2022), cap. 10 — diseño e implementación de MLP y regularización.
- Prince — Understanding Deep Learning (MIT Press 2024), cap. 3–4 — redes superficiales y profundas con activaciones lineales por tramos.
- He et al. (2015), Delving Deep into Rectifiers, ICCV — la inicialización de varianza 2/fan_in para redes con ReLU.
- Glorot & Bengio (2010), Understanding the difficulty of training deep feedforward neural networks, AISTATS — la inicialización que equilibra la varianza en ambos sentidos.
- Montúfar et al. (2014), On the Number of Linear Regions of Deep Neural Networks, NeurIPS — el conteo de regiones lineales que cuantifica la ventaja de la profundidad.
- Cybenko (1989), Approximation by superpositions of a sigmoidal function, Math. Control Signals Systems — teorema de aproximación universal para sigmoides.
- Hornik (1991), Approximation capabilities of multilayer feedforward networks, Neural Networks — generalización del teorema a activaciones arbitrarias.
- Fuente del dataset: https://archive.ics.uci.edu/dataset/602/dry+bean+dataset — Dry Bean (UCI Machine Learning Repository, CC BY 4.0); procedencia, versión y SHA-256 en el registro de fuentes, entrada
uci-dry-bean— esta clase la usa para resolver una clasificación no lineal de siete variedades de grano con capas densas, activaciones y regularización. - Consulte
docs/experiment-protocol.md,docs/reproducibility.mdydocs/ethics-and-licenses.md.
Los archivos de este laboratorio
Todo lo que necesitas está en esta carpeta. Cada enlace abre el archivo directamente:
| Archivo | Qué es |
|---|---|
📄 README.md |
Esta guía. |
🧠 theory.md |
La teoría completa con su bibliografía; es la fuente del apartado teórico de arriba. |
🔬 experiments.md |
El plan experimental y la tabla multi-semilla que hay que completar. |
📝 assessment.md |
Las preguntas de evaluación y la rúbrica con la que se corrigen. |
🧑🏫 instructor-guide.md |
La apertura, los tiempos y las intervenciones sugeridas para facilitar esta clase. |
📓 notebook.ipynb |
El recorrido completo con todo el código escrito y ejecutable. |
✏️ notebook_student.ipynb |
El mismo recorrido con las celdas de ejercicio vacías. |
✅ notebook_solution.ipynb |
Los ejercicios resueltos, para contrastar. |
🖥️ train.py |
El mismo entrenamiento desde la terminal, sin abrir un cuaderno. |
🎛️ configs/baseline.yaml |
Épocas, lote, tasa de aprendizaje y qué recorta --quick. |
🎚️ configs/improved.yaml |
La configuración ampliada que se compara contra la base. |
🗄️ data/dataset.yaml |
Fuente, licencia, política de partición y límites del dataset. |
🧾 lesson.yaml |
Nivel, prerrequisitos, resultados de aprendizaje y criterios. |
🖥️ index.html |
Esta misma clase como página autocontenida, para leerla sin conexión. |
Y fuera de la carpeta, tres referencias que esta guía usa: el catálogo configs/labs.yaml —de donde salen el objetivo, la línea base y las métricas—, el código src/neural_labs/experiments.py —que define el orden de los pasos y los archivos que escribe cada ejecución— y docs/experiment-protocol.md, con la regla general del protocolo.
Los datasets se descargan de su proveedor original y conservan su licencia; este repositorio no los redistribuye ni sustituye una descarga fallida por datos generados.
🔬 Experimentos
Plan de experimentos — MLP multiclase
Qué significa «experimentar» aquí
Entrenar un modelo y mirar el número que sale no es un experimento: es una observación. Un experimento empieza por una afirmación que podría resultar falsa, sigue fijando todo lo que no se está poniendo a prueba, y termina midiendo si la afirmación se sostiene cuando se repite.
La diferencia importa porque el entrenamiento de una red tiene azar por dentro: la inicialización de los pesos y el orden en que se barajan los lotes cambian el resultado aunque no cambies nada más. Si comparas una sola corrida contra otra sola corrida, no sabes si la diferencia viene de tu idea o de la semilla. Por eso todo lo que sigue está organizado para separar la señal (el efecto de lo que cambiaste) del ruido (la variabilidad propia del entrenamiento).
La hipótesis de este laboratorio
Resolver clasificación no lineal con capas densas, activaciones y regularización.
Formulada como algo que se puede refutar: el modelo de este laboratorio supera a Regresión logística multinomial y Random Forest en macro_f1, y la diferencia es mayor que la dispersión entre semillas.
Qué la haría falsa —y esto también es un resultado que hay que reportar—:
- El modelo no supera a Regresión logística multinomial y Random Forest.
- Lo supera, pero por menos de lo que varía el propio modelo al cambiar la semilla de entrenamiento.
- Lo supera solo con una semilla concreta y no con las demás.
- Lo supera a costa de un tiempo o un tamaño que el problema no justifica.
El experimento propio de esta clase
Además de la comparación con la línea base, aquí interesa una pregunta específica: Comparar profundidad, activación y dropout. Es la comparación que da sentido al tema de este laboratorio; la de la línea base solo dice si el modelo sirve, mientras que esta dice qué parte del diseño es la que aporta.
Qué se varía y qué se mantiene fijo
Un experimento es interpretable cuando cambia una cosa a la vez. Esta tabla dice qué papel juega cada elemento y, sobre todo, por qué:
| Elemento | En este experimento | Por qué |
|---|---|---|
Partición de los datos (--split-seed) |
Fija en 42 | Si cambiara entre corridas, no podrías saber si la diferencia viene de tu modelo o de que le tocaron datos distintos. |
Semilla de entrenamiento (--training-seed) |
Varía: 41, 42, 43 | Es exactamente el ruido que quieres medir. Sin varias semillas no tienes con qué comparar la mejora. |
Configuración (--config) |
Varía: baseline y improved |
Es la intervención bajo estudio: lo único que estás poniendo a prueba. |
| Dataset y preprocesamiento | Fijos | Ajustar la normalización o el vocabulario con otros datos cambiaría el problema, no el modelo. |
| Presupuesto de épocas y criterio de parada | Fijos dentro de cada configuración | Dar más épocas a una variante que a otra es comparar dos cosas distintas. |
| Hardware y versiones | Registrados en environment.json |
No siempre se pueden fijar, pero sí dejar anotados: explican diferencias de tiempo y, a veces, de resultado. |
El conjunto test |
Intacto hasta el final | Se abre una sola vez, después de que macro_f1 sobre validation haya elegido al ganador. |
Las dos configuraciones que vas a comparar
El laboratorio trae dos configuraciones ya escritas. Estos son los parámetros en los que difieren —el resto es idéntico, que es justamente lo que hace legible la comparación—:
| Parámetro | baseline.yaml |
improved.yaml |
Qué controla |
|---|---|---|---|
| Épocas | 20 |
50 |
Cuántas pasadas completas sobre train. |
| Tasa de aprendizaje | 0.001 |
0.0005 |
Cuánto se mueve cada peso en la dirección del gradiente. |
| Paciencia | 5 |
8 |
Épocas sin mejorar en validation antes de detener el entrenamiento. |
| Precisión mixta | no | sí | Usar float16 donde se puede: acelera en GPU, no cambia el protocolo. |
| Procesos de carga | 0 |
2 |
Procesos que preparan los lotes en paralelo. |
Elegir entre ellas es una decisión de validation. Si improved gana en validación, se queda; si no, la configuración base es la respuesta y hay que decirlo así.
Cómo ejecutar la serie
El comando benchmark hace exactamente esto: repite el entrenamiento manteniendo la partición fija y cambiando solo la semilla de entrenamiento.
neural-labs benchmark --lab 02_mlp_nonlinear \
--config baseline --split-seed 42 --training-seeds 41 42 43
neural-labs benchmark --lab 02_mlp_nonlinear \
--config improved --split-seed 42 --training-seeds 41 42 43
Por dentro, benchmark no hace magia: llama a run_lab una vez por semilla con la misma partición y resume los resultados. Desde Python es literalmente eso:
from neural_labs.experiments import run_lab
from neural_labs.benchmarking import summarize_benchmark
registros = []
for semilla in (41, 42, 43):
resultado = run_lab(
"02_mlp_nonlinear",
config_name="baseline",
split_seed=42, # la partición NO cambia
training_seed=semilla, # solo cambia la inicialización
)
registros.append({"training_seed": semilla, "metrics": resultado.metrics})
print(summarize_benchmark(registros)) # media y dispersión entre semillas
Empieza con --quick para comprobar que la serie corre entera; los números que se reportan salen de la ejecución completa.
La tabla que debes completar
| Variante | Semilla | Métrica en validation |
Métrica en test |
Tiempo | Parámetros | Observación |
|---|---|---|---|---|---|---|
| baseline | 41 | |||||
| baseline | 42 | |||||
| baseline | 43 | |||||
| improved | 41 | |||||
| improved | 42 | |||||
| improved | 43 |
De dónde sale cada columna, para que no haya que adivinarlo:
- Métrica en
validation: el mejor valor demacro_f1durante el entrenamiento; está enhistory.csvy resumido enmetrics.json. - Métrica en
test: el valor final, enmetrics.json, ya con el modelo congelado. - Tiempo:
wall_time_secondsdemetrics.json. - Parámetros:
parametersdemetrics.json; es la medida honesta del tamaño del modelo. - Observación: lo que viste y los números no dicen —una curva que se dispara, una clase que concentra los errores, una corrida que no convergió—.
Cómo decidir con esos números
- Compara medias, pero decide con la dispersión. Calcula media y desviación entre las tres semillas de cada variante. Una mejora de 0,3 puntos entre variantes cuya dispersión interna es de 1,2 puntos no es una mejora: es ruido con buena suerte.
- Si los rangos se solapan, dilo. «No se observó una diferencia distinguible del ruido con tres semillas» es una conclusión legítima y mucho más útil que un número inventado de confianza.
- Decide con
validation. La columna detestsirve para reportar el resultado final una vez, no para elegir la variante ganadora. - El costo forma parte del resultado. Si
improvedgana por poco y cuesta el triple de tiempo, la conclusión honesta menciona ambas cosas. - Vuelve a la línea base. La comparación contra Regresión logística multinomial y Random Forest es la que dice si todo el aparato de la red neuronal estaba justificado para este problema.
Qué debe decir tu conclusión
Una conclusión completa contiene cinco cosas, y se puede escribir en un párrafo:
- Magnitud: cuánto mejoró, en qué métrica y sobre qué conjunto.
- Incertidumbre: cuánto varió entre semillas, y si la mejora sobrevive a esa variación.
- Costo: tiempo, memoria o tamaño adicional que hubo que pagar.
- Errores: dónde falla el modelo, no solo cuánto acierta.
- Condiciones: en qué circunstancias no esperarías que este resultado se repitiera.
Errores de diseño que invalidan el experimento
- Cambiar dos cosas a la vez. Si tocas la arquitectura y la tasa de aprendizaje en la misma corrida, el resultado no atribuye el efecto a ninguna de las dos.
- Comparar con particiones distintas. Es el error más frecuente y el más difícil de detectar después: parece una mejora del modelo y es un reparto distinto de los datos.
- Quedarse con la mejor semilla. Elegir la corrida más favorable y reportar solo esa es seleccionar el ruido. Se reportan todas.
- Ajustar mirando
test. En cuanto una decisión se toma con el resultado detest, ese conjunto deja de estimar el rendimiento con datos nuevos. - No registrar el entorno. Sin versiones ni hardware, una diferencia de tiempo o de resultado se vuelve inexplicable meses después.
📝 Evaluación
Evaluación — MLP multiclase
Cómo se evalúa esta clase
No se evalúa el número final. Un modelo con una métrica alta obtenida mirando test, o sin compararse con nada, vale menos que uno modesto cuyo resultado se puede auditar. Lo que se califica es el proceso: si las particiones están limpias, si la decisión se tomó donde debía, si la conclusión distingue lo que se midió de lo que se supone.
Evidencias que debes entregar
| Evidencia | Dónde vive | Por qué se pide |
|---|---|---|
| Dataset preparado y auditado | salida de neural-labs audit |
Es la única prueba de que el resultado no está contaminado por una fuga entre particiones. |
| Notebook ejecutado sin celdas omitidas | notebook.ipynb |
Una celda saltada suele ser justo la que rompía el argumento. |
| Comparación contra Regresión logística multinomial y Random Forest | metrics.json y baseline_metrics.json |
Sin línea base, una métrica no dice si el modelo aporta algo. |
| Al menos tres semillas de entrenamiento, o la justificación de por qué no | salida de benchmark |
Una sola corrida no permite separar mejora de azar. |
| Análisis de errores y limitaciones | tu reporte | Saber dónde falla vale más que saber cuánto acierta. |
| Model card actualizada | model_card.md |
Es lo que permite a otra persona saber cuándo no debería usar tu modelo. |
Las preguntas, y qué se busca en tu respuesta
No se corrige la longitud de la respuesta, sino si demuestra comprensión. Debajo de cada pregunta está lo que una buena respuesta debería contener.
1. ¿Qué puede aprender una capa oculta que una recta nunca podrá separar?
Qué se busca: Comienza con la predicción que hiciste al abrir la clase y contrástala con una gráfica, una medida o una observación concreta de tu ejecución.
2. Explica con tus palabras: h=ReLU(xW1+b1); logits=hW2+b2.
Qué se busca: Una buena respuesta conecta cuatro cosas —cómo se representa la entrada, qué calcula el modelo, qué mide la función de pérdida y cómo se actualizan los pesos— en vez de repetir la definición del libro. Si puedes explicarlo sin la fórmula delante, lo entendiste.
3. ¿Qué información del dataset solo puede usarse durante el entrenamiento?
Qué se busca: Se espera que nombres casos concretos: las estadísticas de normalización, el vocabulario, la selección de variables, los umbrales. Todo eso se ajusta solo con train; calcularlo sobre el conjunto completo es una fuga silenciosa que infla el resultado sin dar ningún aviso.
4. ¿Por qué Regresión logística multinomial y Random Forest es una comparación razonable para este problema?
Qué se busca: Una buena respuesta explica qué captura la línea base y qué no, y por qué superarla —o no superarla— es informativo aquí. Si la línea base ya resuelve el problema, la conclusión correcta es que la red no estaba justificada.
5. ¿La complejidad adicional supera de forma estable a la línea base?
Qué se busca: Esta es la pregunta propia del tema. Responde con evidencia de tu ejecución —predicciones, matriz de confusión, curvas, artefactos del directorio de la corrida—, no con una impresión general.
6. ¿Qué aprendiste al realizar esta práctica: Comparar una frontera lineal con una MLP y relacionar las activaciones ocultas con las regiones de decisión.?
Qué se busca: No basta con relatar los pasos. Explica qué cambió, por qué cambió y qué resultado te haría rechazar tu interpretación inicial.
7. Refuta con evidencia esta idea frecuente: «Más capas no garantizan una mejor solución; añaden capacidad, costo y nuevas formas de sobreajustar.»
Qué se busca: Se busca una refutación situada en esta materia, apoyada en el mecanismo estudiado y en los resultados obtenidos, no una negación genérica.
8. ¿Qué te dice macro_f1 que no te dirían las otras métricas?
Qué se busca: Cada métrica pondera distinto los errores. Se espera que expliques por qué esa es la que decide aquí y en qué situación sería una mala elección.
9. ¿Qué cambiarías antes de usar este modelo fuera del laboratorio?
Qué se busca: Aquí se evalúa el criterio, no la técnica: licencias y condiciones de uso de dry_bean, representatividad de la población, calibración de las probabilidades, vigilancia de la deriva, desempeño por subgrupo y supervisión humana. Un «funcionaría bien» sin condiciones se corrige como respuesta incompleta.
La rúbrica, explicada
| Criterio | Insuficiente | Adecuado | Excelente | Peso |
|---|---|---|---|---|
| Integridad de los datos | mezcla particiones o no puede demostrar que no lo hizo | las tres particiones están separadas y auditadas | además documenta hashes, política de partición y justifica la estrategia elegida | 20 % |
| Implementación | no ejecuta o produce resultados irreproducibles | entrena y evalúa siguiendo el protocolo | código claro y reutilizable, con las decisiones explicadas | 20 % |
| Diseño experimental | un solo resultado aislado | comparación controlada contra la línea base | varias semillas, dispersión reportada y variables controladas explícitas | 20 % |
| Análisis | repite las métricas sin interpretarlas | interpreta los errores y su distribución | identifica sesgos, límites y costo, y distingue evidencia de suposición | 25 % |
| Comunicación | incompleta o sin contexto | reporte entendible y model card presente | conclusiones verificables por un tercero a partir de los artefactos | 15 % |
La diferencia entre adecuado y excelente casi nunca está en la métrica: está en si el trabajo permite que otra persona llegue a la misma conclusión con los artefactos que dejaste. Un resultado peor, bien medido y bien explicado, se califica por encima de uno mejor que no se puede auditar.
La aprobación exige al menos 70 % y cero errores críticos de fuga de datos. La fuga es eliminatoria por sí sola porque invalida todas las demás cifras del trabajo, por buenas que parezcan.
Autoevaluación antes de entregar
- [ ] Puedo explicar la idea central de la clase a alguien que no la cursó, sin leer el código.
- [ ] Sé qué decisión tomé en cada paso y con qué evidencia la tomé.
- [ ] Miré
testuna sola vez, después de que existieraexperiment.lock.json. - [ ] Mi conclusión dice magnitud, incertidumbre, costo, errores y condiciones.
-
[ ] Puedo señalar al menos una limitación real de mi resultado.
-
[ ] cero solapamiento entre train, validation y test
- [ ] selección basada únicamente en validation
- [ ] métricas finales acompañadas por incertidumbre
- [ ] conclusiones que distinguen evidencia de suposición
🧑🏫 Guía docente
Guía docente — Clase 03: MLP multiclase
Módulo 1: Fundamentos · Esta guía acompaña a quien facilita la clase; no es un guion que deba leerse literalmente.
La conversación que abre la clase
Dos clases pueden estar entrelazadas de manera que ninguna línea las divida. Las neuronas ocultas transforman primero el espacio para volver separable el problema.
Plantee primero esta pregunta y permita que el grupo formule una predicción antes de mostrar código:
¿Qué puede aprender una capa oculta que una recta nunca podrá separar?
Qué debe quedar comprendido
- Resolver clasificación no lineal con capas densas, activaciones y regularización.
- Preparar y auditar el dataset real dry_bean sin fuga de datos.
- Entrenar y evaluar red multicapa para relaciones no lineales.
- Comparar contra la línea base: Regresión logística multinomial y Random Forest.
- Interpretar intervalos de confianza, errores y limitaciones.
Secuencia sugerida
| Momento | Duración | Acción docente | Evidencia del estudiante |
|---|---|---|---|
| Activación | 10 min | Presentar el caso inicial y recoger predicciones. | Explica qué espera observar y por qué. |
| Construcción | 25 min | Conectar intuición, representación y matemática. | Dibuja o relata el mecanismo con sus propias palabras. |
| Demostración | 25 min | Recorrer el mapa visual y ejecutar el ejemplo mínimo. | Interpreta cada transición sin limitarse a describir la figura. |
| Investigación | 35 min | Guiar la práctica propia de esta clase. | Contrasta su predicción con una medida o gráfica. |
| Cierre | 15 min | Volver a la pregunta esencial y discutir límites. | Entrega una conclusión breve con evidencia y una duda abierta. |
Práctica propia de esta materia
Comparar una frontera lineal con una MLP y relacionar las activaciones ocultas con las regiones de decisión.
La figura es un punto de entrada. Pida al estudiante que explique qué cambia entre los tres pasos y qué resultado observable invalidaría su explicación.
Dificultad conceptual que conviene provocar
Idea engañosa: Más capas no garantizan una mejor solución; añaden capacidad, costo y nuevas formas de sobreajustar.
No la corrija inmediatamente. Solicite un ejemplo o contraejemplo, ejecute una comparación y recién entonces reconstruya la explicación con el grupo.
Preguntas para acompañar sin resolver
- ¿Qué observación concreta respalda esa afirmación?
- ¿Qué variable está cambiando y cuáles permanecen controladas?
- ¿La gráfica muestra el mecanismo o solamente una correlación?
- ¿En qué caso dejaría de ser válida esta conclusión?
Cierre verificable
La clase termina cuando el estudiante puede responder la pregunta esencial, interpretar su visualización y señalar al menos una limitación. Una métrica aislada no reemplaza ninguna de esas tres evidencias.