Saltar al contenido

# deep-learning-en-python-y-tensorflow

Deep Learning en Python y Tensorflow

Volver a la portada del curso
Lectura (Markdown)

Técnicas de Entrenamiento

Sección 5 · Entrenamiento y Evaluación de Modelos

Técnicas de entrenamiento

Definir una red con Keras toma diez líneas. Que aprenda bien depende de decisiones que no se ven en el summary: qué pérdida minimizar, con qué optimizador y tasa de aprendizaje, y cuándo detenerse. Aquí ves cada una con las cifras reales del clasificador de prendas de PyShop.

La pérdida: cuánto se equivoca la red

Entrenar es ajustar los pesos para que una función de pérdida baje. Para clasificar prendas, el curso usa la entropía cruzada (sparse_categorical_crossentropy). Si la red da probabilidad $p$ a la clase correcta, la pérdida de ese ejemplo es:

$$L = -\ln p$$

Si el MLP da 0.968 al botín correcto, la pérdida es 0.0325. Si diera 0.1, sería 2.3026. Una red que aún no sabe nada reparte 0.1 entre las 10 clases, así que su pérdida ronda 2.30; en la primera época del MLP la pérdida media ya baja a 0.5291. La pérdida que reporta Keras es el promedio sobre los ejemplos.

Descenso de gradiente y retropropagación

El gradiente indica cómo cambia la pérdida si se mueve un poco cada peso. El descenso de gradiente da un paso en la dirección contraria:

$$w \leftarrow w - \eta \, \frac{\partial L}{\partial w}$$

donde $\eta$ es la tasa de aprendizaje. La retropropagación es el método que calcula todas esas derivadas de una vez: recorre la red de la salida a la entrada y reparte la culpa del error entre las capas. TensorFlow lo hace solo; tú eliges la pérdida y el optimizador.

Lote y época

No se calcula el gradiente con las 60,000 fotos a la vez. Se toman lotes (64 fotos en el curso) y se da un paso por lote. Una época es una pasada completa por los datos. Con validation_split=0.1 quedan 54,000 fotos para entrenar, es decir, 844 pasos por época. Lotes más grandes dan gradientes más estables pero menos pasos por época.

Optimizadores y tasa de aprendizaje

El mismo MLP (Flatten → Dense(128) → Dense(10)), con la misma semilla y 5 épocas, cambiando solo el optimizador:

OptimizadorTasaPérdida en la época 1val_accuracy final
SGD0.010.90650.8385
SGD0.10.59670.874
Adam0.0010.52910.8758
Adam0.12.00760.3583

SGD con tasa 0.01 avanza despacio. Adam ajusta el paso de cada peso según el historial de sus gradientes y, con su tasa por defecto de 0.001, gana sin tocar nada. Con tasa 0.1 los pasos son tan grandes que la red salta de un lado a otro y se queda en 0.3583. Si la pérdida no baja desde la primera época, la tasa de aprendizaje es la primera sospechosa.

Sobreajuste

Un modelo sobreajusta cuando memoriza los ejemplos de entrenamiento en lugar de aprender patrones. Para provocarlo, el curso entrena un MLP grande (dos capas de 512) con solo 3000 fotos durante 40 épocas, y lo valida con 2000 fotos que no ve al entrenar:

Épocaaccuracy (entrenamiento)val_accuracy
100.92330.824
200.94930.8165
400.9830.8365

La accuracy de entrenamiento sigue subiendo, pero la de validación no. La señal más clara está en la pérdida de validación: llega a su mínimo de 0.4603 en la época 6 y después sube hasta 0.8487. Desde la época 6, cada época extra empeora la red ante fotos nuevas.

Dropout y EarlyStopping

Dropout apaga al azar una fracción de las neuronas en cada paso de entrenamiento, y así ninguna neurona puede depender de otra en particular. Con Dropout(0.5) tras cada capa de 512, la accuracy de entrenamiento en la época 40 baja a 0.898, pero la de validación sube a 0.854 y la pérdida de validación termina en 0.5175 en lugar de 0.8487.

EarlyStopping es un callback: una función que Keras llama al final de cada época. Vigila la pérdida de validación y detiene el entrenamiento cuando deja de mejorar:

import keras
from keras import layers
from datos import cargar_moda, semillas
(x_tr, y_tr), (x_te, y_te) = cargar_moda()
semillas(42)
modelo = keras.Sequential([keras.Input(shape=(28, 28)), layers.Flatten(),
    layers.Dense(512, activation="relu"), layers.Dense(512, activation="relu"),
    layers.Dense(10, activation="softmax")])
modelo.compile(optimizer="adam", metrics=["accuracy"],
               loss="sparse_categorical_crossentropy")
alto = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)
h = modelo.fit(x_tr[:3000], y_tr[:3000], epochs=40, batch_size=64, verbose=0,
               validation_data=(x_te[:2000], y_te[:2000]), callbacks=[alto])
print(len(h.history["loss"]), round(min(h.history["val_loss"]), 4))
print(round(modelo.evaluate(x_te[:2000], y_te[:2000], verbose=0)[1], 4))

Salida:

9 0.4603
0.842

Por defecto vigila val_loss. Con patience=3 espera tres épocas sin mejora después del mínimo de la época 6 y se detiene en la 9, en lugar de seguir hasta la 40. Con restore_best_weights=True recupera los pesos de la época 6: la accuracy de validación queda en 0.842. Sin esa opción, te quedarías con los pesos de la última época, ya peores.

Otro callback útil, ModelCheckpoint con save_best_only=True, guarda en disco el mejor modelo durante el entrenamiento.

Trampas comunes

  • Mirar solo la accuracy de entrenamiento. El 0.983 del ejemplo es memoria, no aprendizaje. Decide siempre con la validación.
  • Ajustar con el conjunto de prueba. El test se usa una vez, al final; si lo usas para decidir, deja de ser una medida honesta.
  • Una tasa demasiado alta. Adam con 0.1 se estanca en 0.3583.
  • Olvidar restore_best_weights. EarlyStopping detiene el entrenamiento, pero no vuelve al mejor punto si no se lo pides.
  • Comparar corridas sin semilla. Llama a semillas(42) antes de crear cada modelo, o las diferencias pueden ser azar.

Para entrenar un modelo nuevo: empieza con Adam y su tasa por defecto, reserva validación, grafica las dos curvas de pérdida y agrega EarlyStopping con restore_best_weights=True. El notebook tecnicas-de-entrenamiento reproduce todos estos experimentos.

Recursos