1. Importaciones y Configuración¶
In [ ]:
import tensorflow as tf
import tensorflow_datasets as tfds
import matplotlib.pyplot as plt
import numpy as np
import math
# Configuración global
VALIDATION_SPLIT = 0.10
BATCH_SIZE = 32
EPOCHS = 10
IMAGE_SHAPE = (28, 28, 1)
NUM_CLASSES = 10
2. Clase para Gestión de Datos (Single Responsibility Principle)¶
In [ ]:
class MNISTDataManager:
"""Gestiona la carga, preparación y normalización de datos MNIST"""
def __init__(self, validation_split=0.10):
self.validation_split = validation_split
self.datos = None
self.metadatos = None
self.nombres_clases = None
def cargar_datos(self):
"""Carga el dataset MNIST"""
self.datos, self.metadatos = tfds.load(
"mnist",
as_supervised=True,
with_info=True
)
self.nombres_clases = self.metadatos.features["label"].names
return self
@staticmethod
def normalizar(imagenes, etiquetas):
"""Normaliza imágenes de 0-255 a 0-1"""
imagenes = tf.cast(imagenes, tf.float32) / 255.0
return imagenes, etiquetas
def preparar_datasets(self, batch_size=32):
"""Prepara y divide los datasets"""
num_train = self.metadatos.splits['train'].num_examples
num_val = int(num_train * self.validation_split)
num_train_final = num_train - num_val
# División de datos
train_raw = self.datos["train"].take(num_train_final)
val_raw = self.datos["train"].skip(num_train_final).take(num_val)
test_raw = self.datos["test"]
# Normalización y optimización
train_ds = (train_raw
.map(self.normalizar)
.cache()
.shuffle(num_train_final)
.batch(batch_size)
.repeat())
val_ds = (val_raw
.map(self.normalizar)
.cache()
.batch(batch_size))
test_ds = (test_raw
.map(self.normalizar)
.cache()
.batch(batch_size))
print(f"Ejemplos de entrenamiento: {num_train_final}")
print(f"Ejemplos de validación: {num_val}")
print(f"Ejemplos de prueba: {self.metadatos.splits['test'].num_examples}")
return train_ds, val_ds, test_ds, num_train_final, num_val
3. Clase para Construcción del Modelo (Open/Closed Principle)¶
In [ ]:
class ModelBuilder:
"""Constructor de modelos de clasificación"""
@staticmethod
def crear_modelo_clasificacion(input_shape, num_classes, hidden_units=[50, 50]):
"""Crea un modelo de clasificación con capas configurables"""
modelo = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=input_shape)
])
# Capas ocultas configurables
for units in hidden_units:
modelo.add(tf.keras.layers.Dense(units, activation='relu'))
# Capa de salida
modelo.add(tf.keras.layers.Dense(num_classes, activation='softmax'))
return modelo
@staticmethod
def compilar_modelo(modelo, optimizer='adam'):
"""Compila el modelo con configuración estándar"""
modelo.compile(
optimizer=optimizer,
loss=tf.keras.losses.SparseCategoricalCrossentropy(),
metrics=['accuracy']
)
return modelo
4. Clase para Visualización (Single Responsibility Principle)¶
In [ ]:
class Visualizador:
"""Gestiona todas las visualizaciones del modelo"""
def __init__(self, nombres_clases):
self.nombres_clases = nombres_clases
@staticmethod
def mostrar_imagen_simple(imagen):
"""Muestra una imagen individual"""
plt.figure()
plt.imshow(imagen, cmap=plt.cm.binary)
plt.colorbar()
plt.show()
@staticmethod
def mostrar_grid_imagenes(dataset, num_imagenes=25):
"""Muestra un grid de imágenes"""
filas = columnas = int(math.sqrt(num_imagenes))
plt.figure(figsize=(10, 10))
for i, (imagen, _) in enumerate(dataset.take(num_imagenes)):
plt.subplot(filas, columnas, i + 1)
plt.imshow(imagen, cmap=plt.cm.binary)
plt.axis('off')
plt.tight_layout()
plt.show()
def visualizar_predicciones(self, imagenes, etiquetas_reales, predicciones, num_imagenes=25):
"""Visualiza predicciones con etiquetas correctas/incorrectas"""
filas = 5
columnas = 5
plt.figure(figsize=(2 * 2 * columnas, 2 * filas))
for i in range(min(num_imagenes, len(imagenes))):
# Imagen
plt.subplot(filas, 2 * columnas, 2 * i + 1)
self._graficar_imagen(i, predicciones, etiquetas_reales, imagenes)
# Gráfico de barras
plt.subplot(filas, 2 * columnas, 2 * i + 2)
self._graficar_barras(i, predicciones, etiquetas_reales)
plt.tight_layout()
plt.show()
def _graficar_imagen(self, idx, predicciones, etiquetas_reales, imagenes):
"""Grafica una imagen con su predicción"""
pred = predicciones[idx]
etiqueta_real = etiquetas_reales[idx]
img = imagenes[idx]
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.imshow(img[..., 0], cmap=plt.cm.binary)
etiqueta_pred = np.argmax(pred)
color = 'blue' if etiqueta_pred == etiqueta_real else 'red'
plt.xlabel(
f"{self.nombres_clases[etiqueta_pred]} {100*np.max(pred):.0f}% ({self.nombres_clases[etiqueta_real]})",
color=color
)
def _graficar_barras(self, idx, predicciones, etiquetas_reales):
"""Grafica barras de probabilidad"""
pred = predicciones[idx]
etiqueta_real = etiquetas_reales[idx]
plt.grid(False)
plt.xticks(range(10))
plt.yticks([])
grafica = plt.bar(range(10), pred, color="#777777")
plt.ylim([0, 1])
etiqueta_pred = np.argmax(pred)
grafica[etiqueta_pred].set_color('red')
grafica[etiqueta_real].set_color('blue')
5. Ejecución Principal¶
In [ ]:
# Cargar y preparar datos
data_manager = MNISTDataManager(validation_split=VALIDATION_SPLIT)
data_manager.cargar_datos()
train_ds, val_ds, test_ds, num_train, num_val = data_manager.preparar_datasets(BATCH_SIZE)
In [ ]:
# Visualizar datos
visualizador = Visualizador(data_manager.nombres_clases)
# Mostrar una imagen
for imagen, _ in train_ds.take(1):
visualizador.mostrar_imagen_simple(imagen[0])
break
In [ ]:
# Mostrar grid de imágenes
visualizador.mostrar_grid_imagenes(train_ds.unbatch(), num_imagenes=25)
In [ ]:
# Crear y compilar modelo
modelo = ModelBuilder.crear_modelo_clasificacion(
input_shape=IMAGE_SHAPE,
num_classes=NUM_CLASSES,
hidden_units=[50, 50]
)
modelo = ModelBuilder.compilar_modelo(modelo)
modelo.summary()
In [ ]:
# Entrenar modelo
history = modelo.fit(
train_ds,
epochs=EPOCHS,
steps_per_epoch=math.ceil(num_train / BATCH_SIZE),
validation_data=val_ds,
validation_steps=math.ceil(num_val / BATCH_SIZE)
)
In [ ]:
# Evaluar y visualizar predicciones
for imagenes_test, etiquetas_test in test_ds.take(1):
imagenes_test = imagenes_test.numpy()
etiquetas_test = etiquetas_test.numpy()
predicciones = modelo.predict(imagenes_test)
visualizador.visualizar_predicciones(
imagenes_test,
etiquetas_test,
predicciones,
num_imagenes=25
)