Menu iconMenu icon
Superhéroe de Aprendizaje Profundo e IA

Capítulo 3: Aprendizaje profundo con Keras

3.2 Construcción de modelos secuenciales y funcionales con Keras

Keras ofrece dos enfoques principales para la construcción de modelos de redes neuronales: la API Sequential y la API Funcional. La API Sequential proporciona un método sencillo para construir modelos apilando capas en una secuencia lineal.

Este enfoque es ideal para arquitecturas simples de "feed-forward" donde cada capa tiene un único tensor de entrada y un único tensor de salida. Por otro lado, la API Funcional ofrece mayor flexibilidad y potencia, permitiendo la creación de arquitecturas de modelo más complejas.

Con la API Funcional, los desarrolladores pueden diseñar modelos con múltiples entradas y salidas, implementar capas compartidas y construir estructuras avanzadas como redes residuales o modelos con caminos ramificados. Esta versatilidad hace que la API Funcional sea especialmente adecuada para desarrollar modelos de aprendizaje profundo sofisticados que van más allá de las arquitecturas lineales simples.

3.2.1 Construcción de modelos con la API Sequential

La API Sequential es la forma más simple y directa de definir un modelo en Keras. Es especialmente adecuada para modelos donde las capas siguen una secuencia lineal desde la entrada hasta la salida, sin ramificaciones complejas ni fusión de rutas de datos.

Esto la convierte en una elección ideal para principiantes o para la construcción de arquitecturas de redes neuronales relativamente simples. Vamos a profundizar en el proceso de construcción de una red neuronal básica usando la API Sequential, explorando cada paso en detalle.

Creación de una red neuronal simple de alimentación directa (feedforward)

En este ejemplo completo, recorreremos la creación de una red neuronal diseñada para una tarea clásica de aprendizaje automático: la clasificación de dígitos escritos a mano del conjunto de datos MNIST. El conjunto de datos MNIST es una gran base de datos de dígitos escritos a mano que se usa comúnmente para entrenar varios sistemas de procesamiento de imágenes. Nuestra red estará estructurada de la siguiente manera:

  • Una capa Flatten: Esta capa inicial cumple un propósito crucial. Transforma la entrada, que consiste en imágenes de 28x28 píxeles, en un vector unidimensional. Esta transformación es necesaria porque las capas densas subsiguientes esperan una entrada en forma de un arreglo 1D. Básicamente, "desenrolla" la imagen 2D en una sola línea de píxeles.
  • Dos capas Dense con activación ReLU: Estas son capas completamente conectadas, lo que significa que cada neurona en estas capas está conectada a todas las neuronas en las capas anterior y posterior. La función de activación Rectified Linear Unit (ReLU) se aplica para introducir no linealidad en el modelo, lo que le permite aprender patrones complejos. ReLU se elige por su eficiencia computacional y su capacidad para mitigar el problema del gradiente de desvanecimiento en redes profundas.
  • Una capa Dense final con activación softmax: Esta capa de salida está específicamente diseñada para la clasificación multiclase. Contiene 10 neuronas, una para cada dígito (0-9). La función de activación softmax asegura que la salida de estas neuronas sume 1, proporcionando efectivamente una distribución de probabilidad sobre las 10 posibles clases de dígitos.

Esta arquitectura, aunque simple, es lo suficientemente potente como para lograr una alta precisión en el conjunto de datos MNIST, demostrando la efectividad de incluso las estructuras básicas de redes neuronales cuando se aplican a problemas bien definidos.

Ejemplo: Construcción de un modelo secuencial

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

# Load the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Normalize the input data
X_train, X_test = X_train / 255.0, X_test / 255.0

# Convert labels to one-hot encoding
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

# Define a Sequential model
model = Sequential([
    Flatten(input_shape=(28, 28)),  # Flatten the 28x28 input into a 1D vector
    Dense(256, activation='relu'),  # First hidden layer with 256 units and ReLU activation
    Dropout(0.3),                   # Dropout layer to prevent overfitting
    Dense(128, activation='relu'),  # Second hidden layer with 128 units and ReLU activation
    Dropout(0.2),                   # Another dropout layer
    Dense(64, activation='relu'),   # Third hidden layer with 64 units and ReLU activation
    Dense(10, activation='softmax') # Output layer for 10 classes (digits 0-9)
])

# Compile the model
model.compile(optimizer='adam', 
              loss='categorical_crossentropy', 
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=64, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Explicación del desglose del código:

  • Importaciones: Importamos las bibliotecas necesarias, incluyendo numpy para operaciones numéricas, matplotlib para gráficos y varios módulos de Keras para construir y entrenar la red neuronal.
  • Preparación de datos:
    • El conjunto de datos MNIST se carga utilizando mnist.load_data().
    • Los datos de entrada (X_train y X_test) se normalizan dividiendo por 255 para escalar los valores de los píxeles entre 0 y 1.
    • Las etiquetas (y_train y y_test) se convierten al formato codificado one-hot usando to_categorical().
  • Arquitectura del modelo:
    • Se crea un modelo Sequential con múltiples capas:
    • Capa Flatten para convertir la entrada 2D (28x28) en 1D.
    • Tres capas Dense con activación ReLU (256, 128 y 64 unidades).
    • Dos capas Dropout (tasas de abandono del 30% y 20%) para prevenir el sobreajuste.
    • Capa de salida Dense con 10 unidades y activación softmax para la clasificación multiclase.
  • Compilación del modelo:
    • Se utiliza el optimizador Adam.
    • Se elige crossentropy categórica como la función de pérdida para la clasificación multiclase.
    • La precisión se establece como la métrica a monitorear durante el entrenamiento.
  • Callbacks:
    • Se utiliza ModelCheckpoint para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  • Entrenamiento del modelo:
    • El modelo se entrena durante un máximo de 30 épocas con un tamaño de lote de 64.
    • El 20% de los datos de entrenamiento se utiliza para la validación.
    • Se aplican callbacks durante el entrenamiento.
  • Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  • Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) para los conjuntos de entrenamiento y validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este ejemplo proporciona un enfoque completo para construir, entrenar y evaluar una red neuronal para el conjunto de datos MNIST. Incluye características adicionales como dropout para la regularización, callbacks para optimizar el entrenamiento y visualizaciones para comprender mejor el rendimiento del modelo.

Entrenamiento y evaluación del modelo secuencial

Después de definir la arquitectura del modelo, pasamos a los pasos cruciales de entrenar y evaluar el modelo. Este proceso involucra dos funciones clave:

  1. La función fit(): Se utiliza para entrenar el modelo en nuestro conjunto de datos preparado. Durante el entrenamiento, el modelo aprende a mapear entradas a salidas ajustando sus parámetros internos (pesos y sesgos) en función de los datos de entrenamiento. La función fit() toma varios argumentos importantes:
    • X_train y y_train: Las características de entrada y las etiquetas correspondientes del conjunto de datos de entrenamiento
    • epochs: El número de veces que el modelo iterará sobre todo el conjunto de datos de entrenamiento
    • batch_size: El número de muestras procesadas antes de que se actualice el modelo
    • validation_data: Un conjunto de datos separado que se utiliza para evaluar el rendimiento del modelo durante el entrenamiento
  2. La función evaluate(): Después del entrenamiento, utilizamos esta función para evaluar el rendimiento del modelo en el conjunto de datos de prueba. Este paso es crucial ya que nos brinda una estimación imparcial de qué tan bien generaliza nuestro modelo a datos no vistos. La función evaluate() generalmente devuelve dos valores:
    • test_loss: Una medida del error del modelo en el conjunto de prueba
    • test_accuracy: La proporción de predicciones correctas realizadas por el modelo en el conjunto de prueba

Al utilizar estas funciones en conjunto, podemos entrenar nuestro modelo en los datos de entrenamiento y luego evaluar su efectividad en datos de prueba previamente no vistos, lo que nos brinda una comprensión completa del rendimiento y la capacidad de generalización de nuestro modelo.

Ejemplo: Entrenamiento y evaluación del modelo secuencial

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train / 255.0, X_test / 255.0  # Normalize pixel values
y_train, y_test = to_categorical(y_train), to_categorical(y_test)  # One-hot encode labels

# Define the model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=32, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model on the test data
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Este código demuestra el proceso de construir, entrenar y evaluar un modelo secuencial utilizando Keras para el conjunto de datos MNIST.

Desglose de los componentes principales:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluidos los componentes de TensorFlow/Keras.
    • El conjunto de datos MNIST se carga y preprocesa:
      • Las imágenes se normalizan dividiendo los valores de los píxeles por 255.
      • Las etiquetas se codifican en formato one-hot.
  2. Definición del modelo:
    • Se crea un modelo secuencial con las siguientes capas:
      • Capa Flatten para convertir la entrada 2D en 1D.
      • Dos capas Dense con activación ReLU (128 y 64 unidades).
      • Capa de salida Dense con activación softmax para 10 clases.
  3. Compilación del modelo:
    • El modelo se compila usando el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
  4. Callbacks:
    • ModelCheckpoint se utiliza para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  5. Entrenamiento del modelo:
    • El modelo se entrena durante 30 épocas con un tamaño de lote de 32.
    • El 20% de los datos de entrenamiento se utiliza para validación.
  6. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  7. Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) tanto para el conjunto de entrenamiento como para el de validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este código proporciona un ejemplo completo del flujo de trabajo de machine learning para la clasificación de imágenes utilizando una arquitectura básica de red neuronal.

3.2.2 Construcción de modelos con la API funcional

La API funcional en Keras es una herramienta poderosa y flexible diseñada para construir arquitecturas de redes neuronales complejas. A diferencia de la API secuencial, que se limita a apilar capas de manera lineal, la API funcional permite la creación de estructuras de modelos más sofisticadas. A continuación, se explica más detalladamente sus capacidades:

  1. Conexiones no lineales entre capas: Con la API funcional, puedes definir modelos en los que las capas se conectan de manera no secuencial, lo que te permite crear bifurcaciones, conexiones de salto o incluso conexiones circulares entre capas, habilitando la construcción de topologías de red más complejas.
  2. Múltiples entradas y salidas: La API admite modelos con múltiples tensores de entrada y salida, lo cual es especialmente útil para tareas que requieren procesar diferentes tipos de datos simultáneamente o generar múltiples predicciones a partir de una sola entrada.
  3. Capas compartidas: Puedes reutilizar instancias de capas en diferentes partes de tu modelo. Esto es crucial para implementar arquitecturas como redes siamesas, donde se aplica un procesamiento idéntico a múltiples entradas.
  4. Conexiones residuales: La API funcional facilita la implementación de conexiones residuales, un componente clave de las redes profundas residuales (ResNets). Estas conexiones permiten que la información salte una o más capas, lo que ayuda a mitigar el problema del gradiente que desaparece en redes muy profundas.
  5. Composición de modelos: Puedes tratar modelos instanciados como capas y usarlos para construir modelos más grandes y complejos. Esta modularidad permite la creación de arquitecturas altamente sofisticadas combinando submodelos más simples.
  6. Capas personalizadas: La API funcional se integra perfectamente con capas definidas de forma personalizada, lo que te da la flexibilidad de incorporar operaciones especializadas en la arquitectura de tu modelo.
  7. Modelos tipo gráfico: Para tareas que requieren procesar datos estructurados en gráficos, como el análisis de redes sociales o la predicción de propiedades de moléculas, la API funcional te permite construir modelos que puedan manejar tales estructuras de datos complejas.

Estas características hacen que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Creación de un modelo con múltiples entradas y salidas

Exploremos una aplicación más avanzada de la API funcional creando un modelo con múltiples entradas y salidas. Este enfoque es particularmente útil para tareas complejas que requieren procesar diversos tipos de datos o generar múltiples predicciones de manera simultánea. Consideremos un escenario en el que estamos desarrollando una red de análisis de imágenes sofisticada. Esta red está diseñada para extraer dos piezas de información distintas de una sola imagen de entrada: la categoría del objeto representado y su color predominante.

Para lograr esto, diseñaremos un modelo con una base compartida que se ramifica en dos capas de salida separadas. La base compartida se encargará de extraer características generales de la imagen, mientras que las capas de salida especializadas se centrarán en predecir la categoría del objeto y su color, respectivamente. Esta arquitectura demuestra la flexibilidad de la API funcional, permitiéndonos crear modelos que puedan realizar múltiples tareas relacionadas de manera eficiente.

Por ejemplo, la predicción de categoría podría implicar clasificar el objeto en clases predefinidas (como coche, perro, silla), mientras que la predicción del color podría identificar el color primario (como rojo, azul, verde) del objeto. Al usar dos capas de salida separadas, podemos optimizar cada tarea de predicción de manera independiente, utilizando diferentes funciones de pérdida o métricas para cada salida.

Este enfoque multi-salida no solo muestra la versatilidad de la API funcional, sino que también ilustra cómo podemos diseñar modelos que imiten la percepción humana, donde múltiples atributos de un objeto se procesan e identifican simultáneamente. Tales modelos tienen aplicaciones prácticas en diversos campos, incluyendo la visión por computadora, la robótica y los sistemas de control de calidad automatizados en la manufactura.

Ejemplo: Construcción de un modelo con múltiples salidas utilizando la API funcional

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

# Generate synthetic data
def generate_data(num_samples=1000):
    images = np.random.rand(num_samples, 64, 64, 3)
    categories = np.random.randint(0, 10, num_samples)
    colors = np.random.randint(0, 3, num_samples)
    return images, categories, colors

# Prepare data
X, y_category, y_color = generate_data(5000)
y_category = to_categorical(y_category, 10)
y_color = to_categorical(y_color, 3)

# Split data
X_train, X_test, y_category_train, y_category_test, y_color_train, y_color_test = train_test_split(
    X, y_category, y_color, test_size=0.2, random_state=42
)

# Define the input layer
input_layer = Input(shape=(64, 64, 3))  # Input shape is 64x64 RGB image

# Convolutional layers
x = Conv2D(32, (3, 3), activation='relu')(input_layer)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D((2, 2))(x)

# Flatten the output
x = Flatten()(x)

# Add shared dense layers
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)

# Define the first output for object category
category_output = Dense(10, activation='softmax', name='category_output')(x)

# Define the second output for object color
color_output = Dense(3, activation='softmax', name='color_output')(x)

# Create the model with multiple outputs
model = Model(inputs=input_layer, outputs=[category_output, color_output])

# Compile the model with different loss functions for each output
model.compile(optimizer='adam',
              loss={'category_output': 'categorical_crossentropy', 
                    'color_output': 'categorical_crossentropy'},
              loss_weights={'category_output': 1.0, 'color_output': 0.5},
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Train the model
history = model.fit(
    X_train, 
    {'category_output': y_category_train, 'color_output': y_color_train},
    validation_data=(X_test, {'category_output': y_category_test, 'color_output': y_color_test}),
    epochs=10,
    batch_size=32
)

# Evaluate the model
test_loss, category_loss, color_loss, category_acc, color_acc = model.evaluate(
    X_test, 
    {'category_output': y_category_test, 'color_output': y_color_test}
)
print(f"Test category accuracy: {category_acc:.4f}")
print(f"Test color accuracy: {color_acc:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['category_output_accuracy'], label='Category Accuracy')
plt.plot(history.history['color_output_accuracy'], label='Color Accuracy')
plt.plot(history.history['val_category_output_accuracy'], label='Val Category Accuracy')
plt.plot(history.history['val_color_output_accuracy'], label='Val Color Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['category_output_loss'], label='Category Loss')
plt.plot(history.history['color_output_loss'], label='Color Loss')
plt.plot(history.history['val_category_output_loss'], label='Val Category Loss')
plt.plot(history.history['val_color_output_loss'], label='Val Color Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
sample_image = X_test[0:1]
predictions = model.predict(sample_image)
predicted_category = np.argmax(predictions[0])
predicted_color = np.argmax(predictions[1])

print(f"Predicted category: {predicted_category}")
print(f"Predicted color: {predicted_color}")

# Display the sample image
plt.imshow(sample_image[0])
plt.title(f"Category: {predicted_category}, Color: {predicted_color}")
plt.axis('off')
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluyendo NumPy para operaciones numéricas, Matplotlib para visualización y varios módulos de Keras para construir y entrenar el modelo.
    • Se define una función generate_data() para crear datos sintéticos para nuestra tarea de clasificación con múltiples salidas.
    • Se generan 5000 muestras de imágenes RGB de 64x64 junto con etiquetas correspondientes de categoría (10 clases) y color (3 clases).
    • Las etiquetas se codifican en formato one-hot usando to_categorical().
    • Los datos se dividen en conjuntos de entrenamiento y prueba utilizando train_test_split().
  2. Arquitectura del modelo:
    • Se define una capa de entrada para imágenes RGB de 64x64.
    • Se añaden capas convolucionales (Conv2D) y capas de max pooling para extraer características de las imágenes.
    • La salida se aplana y pasa por dos capas densas (128 y 64 unidades) con activación ReLU.
    • Se definen dos capas de salida separadas:
      • Salida de categoría: 10 unidades con activación softmax para clasificar en 10 categorías.
      • Salida de color: 3 unidades con activación softmax para clasificar en 3 colores.
    • El modelo se crea usando la API funcional, especificando la entrada y múltiples salidas.
  3. Compilación del modelo:
    • El modelo se compila con el optimizador Adam.
    • Se usa la pérdida categorical crossentropy para ambas salidas.
    • Se especifican los pesos de la pérdida (1.0 para la categoría, 0.5 para el color) para equilibrar la importancia de cada tarea.
    • Se define la precisión como la métrica para ambas salidas.
  4. Entrenamiento del modelo:
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 32.
    • Se proporcionan los datos de entrenamiento y validación como diccionarios que asignan nombres de salida a sus datos respectivos.
  5. Evaluación del modelo:
    • El modelo se evalúa en el conjunto de prueba, imprimiendo la precisión para las predicciones de categoría y color.
  6. Visualización:
    • Se grafica el historial de entrenamiento, mostrando precisión y pérdida para ambas salidas a lo largo de las épocas.
    • Se utiliza una imagen de prueba del conjunto de prueba para hacer predicciones.
    • La imagen de prueba se muestra junto con su categoría y color predichos.

Este ejemplo demuestra un escenario realista de una tarea de clasificación con múltiples salidas, incluyendo la generación de datos, creación de modelo, entrenamiento, evaluación y visualización de los resultados. Muestra la flexibilidad de la API funcional de Keras para crear arquitecturas de modelos complejas con múltiples salidas y cómo manejar estos modelos a lo largo del flujo de trabajo de machine learning.

Capas compartidas y conexiones residuales

La API funcional en Keras ofrece la característica poderosa de capas compartidas, lo que permite reutilizar instancias de capas en diferentes partes de un modelo. Esta capacidad es particularmente valiosa para implementar arquitecturas avanzadas como las redes siamesas y las redes residuales. Las redes siamesas, comúnmente utilizadas en tareas de reconocimiento facial, usan procesamiento idéntico en múltiples entradas para comparar su similitud. Por otro lado, las redes residuales, ejemplificadas por arquitecturas como ResNet, utilizan conexiones de salto para permitir que la información evite una o más capas, lo que facilita el entrenamiento de redes muy profundas.

El concepto de capas compartidas se extiende más allá de estas arquitecturas específicas. Es una herramienta fundamental para crear modelos con compartición de pesos, lo que puede ser crucial en varios escenarios. Por ejemplo, en tareas de procesamiento de lenguaje natural como los sistemas de preguntas y respuestas, las capas compartidas pueden procesar tanto la pregunta como el contexto con el mismo conjunto de pesos, asegurando una extracción de características consistente. De manera similar, en el aprendizaje multimodal, donde se necesitan procesar entradas de diferentes fuentes (por ejemplo, imagen y texto), las capas compartidas pueden crear un espacio de representación común para estas entradas diversas.

Además, la flexibilidad de la API funcional permite la creación de topologías de modelos complejas que van más allá de las estructuras secuenciales simples. Esto incluye modelos con múltiples entradas o salidas, modelos con caminos ramificados e incluso modelos que incorporan bucles de retroalimentación. Tal versatilidad hace que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Ejemplo: Uso de capas compartidas en la API funcional

import numpy as np
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.utils import plot_model
from tensorflow.keras.datasets import mnist

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
y_train = np.eye(10)[y_train]
y_test = np.eye(10)[y_test]

# Define two inputs
input_a = Input(shape=(784,), name='input_a')
input_b = Input(shape=(784,), name='input_b')

# Define a shared dense layer
shared_dense = Dense(64, activation='relu', name='shared_dense')

# Apply the shared layer to both inputs
processed_a = shared_dense(input_a)
processed_b = shared_dense(input_b)

# Concatenate the processed inputs
concatenated = Concatenate(name='concatenate')([processed_a, processed_b])

# Add more layers
x = Dense(32, activation='relu', name='dense_1')(concatenated)
x = Dense(16, activation='relu', name='dense_2')(x)

# Add a final output layer
output = Dense(10, activation='softmax', name='output')(x)

# Create the model with shared layers
model = Model(inputs=[input_a, input_b], outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display the model summary
model.summary()

# Visualize the model architecture
plot_model(model, to_file='model_architecture.png', show_shapes=True, show_layer_names=True)

# Train the model
history = model.fit(
    [x_train, x_train],  # Use the same input twice for demonstration
    y_train,
    epochs=10,
    batch_size=128,
    validation_split=0.2,
    verbose=1
)

# Evaluate the model
test_loss, test_accuracy = model.evaluate([x_test, x_test], y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Make predictions
sample_input = x_test[:5]
predictions = model.predict([sample_input, sample_input])
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

Desglose completo del código:

  • Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se aplanan y normalizan, y las etiquetas se convierten a formato one-hot.
  • Arquitectura del modelo:
    • Se definen dos capas de entrada (input_a y input_b), ambas aceptan vectores de 784 dimensiones (imágenes aplanadas de 28x28).
    • Se crea una capa densa compartida con 64 unidades y activación ReLU.
    • La capa compartida se aplica a ambas entradas, demostrando la compartición de pesos.
    • Las entradas procesadas se concatenan usando la capa Concatenate.
    • Se añaden dos capas densas más (32 y 16 unidades) para un procesamiento adicional.
    • La capa de salida final tiene 10 unidades con activación softmax para la clasificación multiclase.
  • Creación y compilación del modelo:
    • El modelo se crea usando la API funcional, especificando múltiples entradas y una salida.
    • El modelo se compila con el optimizador Adam, la función de pérdida categorical crossentropy y la métrica de precisión.
  • Visualización del modelo:
    • Se llama a model.summary() para mostrar un resumen textual de la arquitectura del modelo.
    • Se usa plot_model() para generar una representación visual de la arquitectura del modelo.
  • Entrenamiento del modelo:
    • El modelo se entrena usando el método fit().
    • Para fines demostrativos, se usa la misma entrada (x_train) dos veces para simular dos entradas diferentes.
    • El entrenamiento se realiza durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  • Evaluación y predicción del modelo:
    • El modelo se evalúa en el conjunto de prueba para obtener la precisión.
    • Se hacen predicciones de muestra usando las primeras 5 imágenes de prueba.
    • Se imprimen las clases predichas para demostrar la salida del modelo.

Este ejemplo muestra un flujo de trabajo completo, incluyendo la preparación de datos, la creación de un modelo con capas compartidas, el entrenamiento, la evaluación y la realización de predicciones. Ilustra la flexibilidad de la API funcional para crear arquitecturas de modelos complejas con componentes compartidos y múltiples entradas.

Combinando las APIs Sequential y Funcional

La flexibilidad de Keras permite la integración sin problemas de las APIs Sequential y Funcional, lo que permite la creación de arquitecturas de modelos altamente personalizables y complejas. Esta poderosa combinación ofrece a los desarrolladores la capacidad de aprovechar la simplicidad de la API Sequential para pilas de capas sencillas mientras se utiliza la versatilidad de la API Funcional para diseños de modelos más intrincados.

Al combinar estas APIs, puedes crear modelos híbridos que se beneficien de ambos enfoques. Por ejemplo, podrías usar la API Sequential para definir rápidamente una serie de capas para la extracción de características, y luego emplear la API Funcional para introducir caminos ramificados, múltiples entradas o salidas, o capas compartidas. Este enfoque es especialmente útil cuando se trabaja con aprendizaje transferido, donde los modelos Sequential preentrenados pueden incorporarse en arquitecturas más complejas.

Además, esta combinación permite la integración fácil de capas personalizadas, conexiones de salto, e incluso la implementación de arquitecturas avanzadas como redes residuales o mecanismos de atención. La capacidad de mezclar y combinar estas APIs proporciona un alto grado de flexibilidad, lo que facilita experimentar con nuevos diseños de modelos y adaptarse a requisitos específicos del problema sin sacrificar la naturaleza intuitiva de la construcción de modelos en Keras.

Ejemplo: Combinando modelos Sequential y Funcional

import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Dense, Flatten
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# Build a Sequential model
sequential_model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu', name='sequential_dense')
])

# Define an input using the Functional API
input_layer = Input(shape=(28, 28))

# Pass the input through the Sequential model
x = sequential_model(input_layer)

# Add more layers using the Functional API
x = Dense(64, activation='relu', name='functional_dense_1')(x)
output = Dense(10, activation='softmax', name='output')(x)

# Create the final model
model = Model(inputs=input_layer, outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display model summary
model.summary()

# Train the model
history = model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2, verbose=1)

# Evaluate the model
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions on a sample
sample = x_test[:5]
predictions = model.predict(sample)
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

# Visualize sample predictions
plt.figure(figsize=(15, 3))
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(sample[i].reshape(28, 28), cmap='gray')
    plt.title(f"Predicted: {predicted_classes[i]}")
    plt.axis('off')
plt.tight_layout()
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras, así como NumPy y Matplotlib para la manipulación y visualización de datos.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se normalizan y las etiquetas se convierten a formato one-hot.
  2. Arquitectura del modelo:
    • Se crea un modelo Sequential con una capa Flatten y una capa Dense.
    • Se define una capa de entrada usando la API funcional.
    • El modelo Sequential se aplica a la capa de entrada.
    • Se añaden capas Dense adicionales utilizando la API funcional.
    • El modelo final se crea especificando las capas de entrada y salida.
  3. Compilación y entrenamiento del modelo:
    • El modelo se compila con el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
    • Se muestra el resumen del modelo para ver la arquitectura.
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  4. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  5. Visualización del historial de entrenamiento:
    • Se trazan las curvas de precisión de entrenamiento y validación a lo largo de las épocas.
    • Se trazan las curvas de pérdida de entrenamiento y validación a lo largo de las épocas.
  6. Predicciones:
    • Se hacen predicciones sobre una muestra de 5 imágenes de prueba.
    • Se imprimen las clases predichas.
  7. Visualización de predicciones de muestra:
    • Se muestran las 5 imágenes de muestra junto con sus clases predichas.

Este ejemplo demuestra un flujo de trabajo completo que combina las APIs Sequential y Funcional en Keras. Incluye la preparación de datos, la creación del modelo, el entrenamiento, la evaluación y la visualización de resultados. El código muestra cómo aprovechar ambas APIs para crear una arquitectura de modelo flexible, entrenarla con datos reales y analizar su rendimiento.

3.2 Construcción de modelos secuenciales y funcionales con Keras

Keras ofrece dos enfoques principales para la construcción de modelos de redes neuronales: la API Sequential y la API Funcional. La API Sequential proporciona un método sencillo para construir modelos apilando capas en una secuencia lineal.

Este enfoque es ideal para arquitecturas simples de "feed-forward" donde cada capa tiene un único tensor de entrada y un único tensor de salida. Por otro lado, la API Funcional ofrece mayor flexibilidad y potencia, permitiendo la creación de arquitecturas de modelo más complejas.

Con la API Funcional, los desarrolladores pueden diseñar modelos con múltiples entradas y salidas, implementar capas compartidas y construir estructuras avanzadas como redes residuales o modelos con caminos ramificados. Esta versatilidad hace que la API Funcional sea especialmente adecuada para desarrollar modelos de aprendizaje profundo sofisticados que van más allá de las arquitecturas lineales simples.

3.2.1 Construcción de modelos con la API Sequential

La API Sequential es la forma más simple y directa de definir un modelo en Keras. Es especialmente adecuada para modelos donde las capas siguen una secuencia lineal desde la entrada hasta la salida, sin ramificaciones complejas ni fusión de rutas de datos.

Esto la convierte en una elección ideal para principiantes o para la construcción de arquitecturas de redes neuronales relativamente simples. Vamos a profundizar en el proceso de construcción de una red neuronal básica usando la API Sequential, explorando cada paso en detalle.

Creación de una red neuronal simple de alimentación directa (feedforward)

En este ejemplo completo, recorreremos la creación de una red neuronal diseñada para una tarea clásica de aprendizaje automático: la clasificación de dígitos escritos a mano del conjunto de datos MNIST. El conjunto de datos MNIST es una gran base de datos de dígitos escritos a mano que se usa comúnmente para entrenar varios sistemas de procesamiento de imágenes. Nuestra red estará estructurada de la siguiente manera:

  • Una capa Flatten: Esta capa inicial cumple un propósito crucial. Transforma la entrada, que consiste en imágenes de 28x28 píxeles, en un vector unidimensional. Esta transformación es necesaria porque las capas densas subsiguientes esperan una entrada en forma de un arreglo 1D. Básicamente, "desenrolla" la imagen 2D en una sola línea de píxeles.
  • Dos capas Dense con activación ReLU: Estas son capas completamente conectadas, lo que significa que cada neurona en estas capas está conectada a todas las neuronas en las capas anterior y posterior. La función de activación Rectified Linear Unit (ReLU) se aplica para introducir no linealidad en el modelo, lo que le permite aprender patrones complejos. ReLU se elige por su eficiencia computacional y su capacidad para mitigar el problema del gradiente de desvanecimiento en redes profundas.
  • Una capa Dense final con activación softmax: Esta capa de salida está específicamente diseñada para la clasificación multiclase. Contiene 10 neuronas, una para cada dígito (0-9). La función de activación softmax asegura que la salida de estas neuronas sume 1, proporcionando efectivamente una distribución de probabilidad sobre las 10 posibles clases de dígitos.

Esta arquitectura, aunque simple, es lo suficientemente potente como para lograr una alta precisión en el conjunto de datos MNIST, demostrando la efectividad de incluso las estructuras básicas de redes neuronales cuando se aplican a problemas bien definidos.

Ejemplo: Construcción de un modelo secuencial

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

# Load the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Normalize the input data
X_train, X_test = X_train / 255.0, X_test / 255.0

# Convert labels to one-hot encoding
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

# Define a Sequential model
model = Sequential([
    Flatten(input_shape=(28, 28)),  # Flatten the 28x28 input into a 1D vector
    Dense(256, activation='relu'),  # First hidden layer with 256 units and ReLU activation
    Dropout(0.3),                   # Dropout layer to prevent overfitting
    Dense(128, activation='relu'),  # Second hidden layer with 128 units and ReLU activation
    Dropout(0.2),                   # Another dropout layer
    Dense(64, activation='relu'),   # Third hidden layer with 64 units and ReLU activation
    Dense(10, activation='softmax') # Output layer for 10 classes (digits 0-9)
])

# Compile the model
model.compile(optimizer='adam', 
              loss='categorical_crossentropy', 
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=64, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Explicación del desglose del código:

  • Importaciones: Importamos las bibliotecas necesarias, incluyendo numpy para operaciones numéricas, matplotlib para gráficos y varios módulos de Keras para construir y entrenar la red neuronal.
  • Preparación de datos:
    • El conjunto de datos MNIST se carga utilizando mnist.load_data().
    • Los datos de entrada (X_train y X_test) se normalizan dividiendo por 255 para escalar los valores de los píxeles entre 0 y 1.
    • Las etiquetas (y_train y y_test) se convierten al formato codificado one-hot usando to_categorical().
  • Arquitectura del modelo:
    • Se crea un modelo Sequential con múltiples capas:
    • Capa Flatten para convertir la entrada 2D (28x28) en 1D.
    • Tres capas Dense con activación ReLU (256, 128 y 64 unidades).
    • Dos capas Dropout (tasas de abandono del 30% y 20%) para prevenir el sobreajuste.
    • Capa de salida Dense con 10 unidades y activación softmax para la clasificación multiclase.
  • Compilación del modelo:
    • Se utiliza el optimizador Adam.
    • Se elige crossentropy categórica como la función de pérdida para la clasificación multiclase.
    • La precisión se establece como la métrica a monitorear durante el entrenamiento.
  • Callbacks:
    • Se utiliza ModelCheckpoint para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  • Entrenamiento del modelo:
    • El modelo se entrena durante un máximo de 30 épocas con un tamaño de lote de 64.
    • El 20% de los datos de entrenamiento se utiliza para la validación.
    • Se aplican callbacks durante el entrenamiento.
  • Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  • Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) para los conjuntos de entrenamiento y validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este ejemplo proporciona un enfoque completo para construir, entrenar y evaluar una red neuronal para el conjunto de datos MNIST. Incluye características adicionales como dropout para la regularización, callbacks para optimizar el entrenamiento y visualizaciones para comprender mejor el rendimiento del modelo.

Entrenamiento y evaluación del modelo secuencial

Después de definir la arquitectura del modelo, pasamos a los pasos cruciales de entrenar y evaluar el modelo. Este proceso involucra dos funciones clave:

  1. La función fit(): Se utiliza para entrenar el modelo en nuestro conjunto de datos preparado. Durante el entrenamiento, el modelo aprende a mapear entradas a salidas ajustando sus parámetros internos (pesos y sesgos) en función de los datos de entrenamiento. La función fit() toma varios argumentos importantes:
    • X_train y y_train: Las características de entrada y las etiquetas correspondientes del conjunto de datos de entrenamiento
    • epochs: El número de veces que el modelo iterará sobre todo el conjunto de datos de entrenamiento
    • batch_size: El número de muestras procesadas antes de que se actualice el modelo
    • validation_data: Un conjunto de datos separado que se utiliza para evaluar el rendimiento del modelo durante el entrenamiento
  2. La función evaluate(): Después del entrenamiento, utilizamos esta función para evaluar el rendimiento del modelo en el conjunto de datos de prueba. Este paso es crucial ya que nos brinda una estimación imparcial de qué tan bien generaliza nuestro modelo a datos no vistos. La función evaluate() generalmente devuelve dos valores:
    • test_loss: Una medida del error del modelo en el conjunto de prueba
    • test_accuracy: La proporción de predicciones correctas realizadas por el modelo en el conjunto de prueba

Al utilizar estas funciones en conjunto, podemos entrenar nuestro modelo en los datos de entrenamiento y luego evaluar su efectividad en datos de prueba previamente no vistos, lo que nos brinda una comprensión completa del rendimiento y la capacidad de generalización de nuestro modelo.

Ejemplo: Entrenamiento y evaluación del modelo secuencial

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train / 255.0, X_test / 255.0  # Normalize pixel values
y_train, y_test = to_categorical(y_train), to_categorical(y_test)  # One-hot encode labels

# Define the model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=32, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model on the test data
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Este código demuestra el proceso de construir, entrenar y evaluar un modelo secuencial utilizando Keras para el conjunto de datos MNIST.

Desglose de los componentes principales:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluidos los componentes de TensorFlow/Keras.
    • El conjunto de datos MNIST se carga y preprocesa:
      • Las imágenes se normalizan dividiendo los valores de los píxeles por 255.
      • Las etiquetas se codifican en formato one-hot.
  2. Definición del modelo:
    • Se crea un modelo secuencial con las siguientes capas:
      • Capa Flatten para convertir la entrada 2D en 1D.
      • Dos capas Dense con activación ReLU (128 y 64 unidades).
      • Capa de salida Dense con activación softmax para 10 clases.
  3. Compilación del modelo:
    • El modelo se compila usando el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
  4. Callbacks:
    • ModelCheckpoint se utiliza para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  5. Entrenamiento del modelo:
    • El modelo se entrena durante 30 épocas con un tamaño de lote de 32.
    • El 20% de los datos de entrenamiento se utiliza para validación.
  6. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  7. Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) tanto para el conjunto de entrenamiento como para el de validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este código proporciona un ejemplo completo del flujo de trabajo de machine learning para la clasificación de imágenes utilizando una arquitectura básica de red neuronal.

3.2.2 Construcción de modelos con la API funcional

La API funcional en Keras es una herramienta poderosa y flexible diseñada para construir arquitecturas de redes neuronales complejas. A diferencia de la API secuencial, que se limita a apilar capas de manera lineal, la API funcional permite la creación de estructuras de modelos más sofisticadas. A continuación, se explica más detalladamente sus capacidades:

  1. Conexiones no lineales entre capas: Con la API funcional, puedes definir modelos en los que las capas se conectan de manera no secuencial, lo que te permite crear bifurcaciones, conexiones de salto o incluso conexiones circulares entre capas, habilitando la construcción de topologías de red más complejas.
  2. Múltiples entradas y salidas: La API admite modelos con múltiples tensores de entrada y salida, lo cual es especialmente útil para tareas que requieren procesar diferentes tipos de datos simultáneamente o generar múltiples predicciones a partir de una sola entrada.
  3. Capas compartidas: Puedes reutilizar instancias de capas en diferentes partes de tu modelo. Esto es crucial para implementar arquitecturas como redes siamesas, donde se aplica un procesamiento idéntico a múltiples entradas.
  4. Conexiones residuales: La API funcional facilita la implementación de conexiones residuales, un componente clave de las redes profundas residuales (ResNets). Estas conexiones permiten que la información salte una o más capas, lo que ayuda a mitigar el problema del gradiente que desaparece en redes muy profundas.
  5. Composición de modelos: Puedes tratar modelos instanciados como capas y usarlos para construir modelos más grandes y complejos. Esta modularidad permite la creación de arquitecturas altamente sofisticadas combinando submodelos más simples.
  6. Capas personalizadas: La API funcional se integra perfectamente con capas definidas de forma personalizada, lo que te da la flexibilidad de incorporar operaciones especializadas en la arquitectura de tu modelo.
  7. Modelos tipo gráfico: Para tareas que requieren procesar datos estructurados en gráficos, como el análisis de redes sociales o la predicción de propiedades de moléculas, la API funcional te permite construir modelos que puedan manejar tales estructuras de datos complejas.

Estas características hacen que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Creación de un modelo con múltiples entradas y salidas

Exploremos una aplicación más avanzada de la API funcional creando un modelo con múltiples entradas y salidas. Este enfoque es particularmente útil para tareas complejas que requieren procesar diversos tipos de datos o generar múltiples predicciones de manera simultánea. Consideremos un escenario en el que estamos desarrollando una red de análisis de imágenes sofisticada. Esta red está diseñada para extraer dos piezas de información distintas de una sola imagen de entrada: la categoría del objeto representado y su color predominante.

Para lograr esto, diseñaremos un modelo con una base compartida que se ramifica en dos capas de salida separadas. La base compartida se encargará de extraer características generales de la imagen, mientras que las capas de salida especializadas se centrarán en predecir la categoría del objeto y su color, respectivamente. Esta arquitectura demuestra la flexibilidad de la API funcional, permitiéndonos crear modelos que puedan realizar múltiples tareas relacionadas de manera eficiente.

Por ejemplo, la predicción de categoría podría implicar clasificar el objeto en clases predefinidas (como coche, perro, silla), mientras que la predicción del color podría identificar el color primario (como rojo, azul, verde) del objeto. Al usar dos capas de salida separadas, podemos optimizar cada tarea de predicción de manera independiente, utilizando diferentes funciones de pérdida o métricas para cada salida.

Este enfoque multi-salida no solo muestra la versatilidad de la API funcional, sino que también ilustra cómo podemos diseñar modelos que imiten la percepción humana, donde múltiples atributos de un objeto se procesan e identifican simultáneamente. Tales modelos tienen aplicaciones prácticas en diversos campos, incluyendo la visión por computadora, la robótica y los sistemas de control de calidad automatizados en la manufactura.

Ejemplo: Construcción de un modelo con múltiples salidas utilizando la API funcional

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

# Generate synthetic data
def generate_data(num_samples=1000):
    images = np.random.rand(num_samples, 64, 64, 3)
    categories = np.random.randint(0, 10, num_samples)
    colors = np.random.randint(0, 3, num_samples)
    return images, categories, colors

# Prepare data
X, y_category, y_color = generate_data(5000)
y_category = to_categorical(y_category, 10)
y_color = to_categorical(y_color, 3)

# Split data
X_train, X_test, y_category_train, y_category_test, y_color_train, y_color_test = train_test_split(
    X, y_category, y_color, test_size=0.2, random_state=42
)

# Define the input layer
input_layer = Input(shape=(64, 64, 3))  # Input shape is 64x64 RGB image

# Convolutional layers
x = Conv2D(32, (3, 3), activation='relu')(input_layer)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D((2, 2))(x)

# Flatten the output
x = Flatten()(x)

# Add shared dense layers
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)

# Define the first output for object category
category_output = Dense(10, activation='softmax', name='category_output')(x)

# Define the second output for object color
color_output = Dense(3, activation='softmax', name='color_output')(x)

# Create the model with multiple outputs
model = Model(inputs=input_layer, outputs=[category_output, color_output])

# Compile the model with different loss functions for each output
model.compile(optimizer='adam',
              loss={'category_output': 'categorical_crossentropy', 
                    'color_output': 'categorical_crossentropy'},
              loss_weights={'category_output': 1.0, 'color_output': 0.5},
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Train the model
history = model.fit(
    X_train, 
    {'category_output': y_category_train, 'color_output': y_color_train},
    validation_data=(X_test, {'category_output': y_category_test, 'color_output': y_color_test}),
    epochs=10,
    batch_size=32
)

# Evaluate the model
test_loss, category_loss, color_loss, category_acc, color_acc = model.evaluate(
    X_test, 
    {'category_output': y_category_test, 'color_output': y_color_test}
)
print(f"Test category accuracy: {category_acc:.4f}")
print(f"Test color accuracy: {color_acc:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['category_output_accuracy'], label='Category Accuracy')
plt.plot(history.history['color_output_accuracy'], label='Color Accuracy')
plt.plot(history.history['val_category_output_accuracy'], label='Val Category Accuracy')
plt.plot(history.history['val_color_output_accuracy'], label='Val Color Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['category_output_loss'], label='Category Loss')
plt.plot(history.history['color_output_loss'], label='Color Loss')
plt.plot(history.history['val_category_output_loss'], label='Val Category Loss')
plt.plot(history.history['val_color_output_loss'], label='Val Color Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
sample_image = X_test[0:1]
predictions = model.predict(sample_image)
predicted_category = np.argmax(predictions[0])
predicted_color = np.argmax(predictions[1])

print(f"Predicted category: {predicted_category}")
print(f"Predicted color: {predicted_color}")

# Display the sample image
plt.imshow(sample_image[0])
plt.title(f"Category: {predicted_category}, Color: {predicted_color}")
plt.axis('off')
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluyendo NumPy para operaciones numéricas, Matplotlib para visualización y varios módulos de Keras para construir y entrenar el modelo.
    • Se define una función generate_data() para crear datos sintéticos para nuestra tarea de clasificación con múltiples salidas.
    • Se generan 5000 muestras de imágenes RGB de 64x64 junto con etiquetas correspondientes de categoría (10 clases) y color (3 clases).
    • Las etiquetas se codifican en formato one-hot usando to_categorical().
    • Los datos se dividen en conjuntos de entrenamiento y prueba utilizando train_test_split().
  2. Arquitectura del modelo:
    • Se define una capa de entrada para imágenes RGB de 64x64.
    • Se añaden capas convolucionales (Conv2D) y capas de max pooling para extraer características de las imágenes.
    • La salida se aplana y pasa por dos capas densas (128 y 64 unidades) con activación ReLU.
    • Se definen dos capas de salida separadas:
      • Salida de categoría: 10 unidades con activación softmax para clasificar en 10 categorías.
      • Salida de color: 3 unidades con activación softmax para clasificar en 3 colores.
    • El modelo se crea usando la API funcional, especificando la entrada y múltiples salidas.
  3. Compilación del modelo:
    • El modelo se compila con el optimizador Adam.
    • Se usa la pérdida categorical crossentropy para ambas salidas.
    • Se especifican los pesos de la pérdida (1.0 para la categoría, 0.5 para el color) para equilibrar la importancia de cada tarea.
    • Se define la precisión como la métrica para ambas salidas.
  4. Entrenamiento del modelo:
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 32.
    • Se proporcionan los datos de entrenamiento y validación como diccionarios que asignan nombres de salida a sus datos respectivos.
  5. Evaluación del modelo:
    • El modelo se evalúa en el conjunto de prueba, imprimiendo la precisión para las predicciones de categoría y color.
  6. Visualización:
    • Se grafica el historial de entrenamiento, mostrando precisión y pérdida para ambas salidas a lo largo de las épocas.
    • Se utiliza una imagen de prueba del conjunto de prueba para hacer predicciones.
    • La imagen de prueba se muestra junto con su categoría y color predichos.

Este ejemplo demuestra un escenario realista de una tarea de clasificación con múltiples salidas, incluyendo la generación de datos, creación de modelo, entrenamiento, evaluación y visualización de los resultados. Muestra la flexibilidad de la API funcional de Keras para crear arquitecturas de modelos complejas con múltiples salidas y cómo manejar estos modelos a lo largo del flujo de trabajo de machine learning.

Capas compartidas y conexiones residuales

La API funcional en Keras ofrece la característica poderosa de capas compartidas, lo que permite reutilizar instancias de capas en diferentes partes de un modelo. Esta capacidad es particularmente valiosa para implementar arquitecturas avanzadas como las redes siamesas y las redes residuales. Las redes siamesas, comúnmente utilizadas en tareas de reconocimiento facial, usan procesamiento idéntico en múltiples entradas para comparar su similitud. Por otro lado, las redes residuales, ejemplificadas por arquitecturas como ResNet, utilizan conexiones de salto para permitir que la información evite una o más capas, lo que facilita el entrenamiento de redes muy profundas.

El concepto de capas compartidas se extiende más allá de estas arquitecturas específicas. Es una herramienta fundamental para crear modelos con compartición de pesos, lo que puede ser crucial en varios escenarios. Por ejemplo, en tareas de procesamiento de lenguaje natural como los sistemas de preguntas y respuestas, las capas compartidas pueden procesar tanto la pregunta como el contexto con el mismo conjunto de pesos, asegurando una extracción de características consistente. De manera similar, en el aprendizaje multimodal, donde se necesitan procesar entradas de diferentes fuentes (por ejemplo, imagen y texto), las capas compartidas pueden crear un espacio de representación común para estas entradas diversas.

Además, la flexibilidad de la API funcional permite la creación de topologías de modelos complejas que van más allá de las estructuras secuenciales simples. Esto incluye modelos con múltiples entradas o salidas, modelos con caminos ramificados e incluso modelos que incorporan bucles de retroalimentación. Tal versatilidad hace que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Ejemplo: Uso de capas compartidas en la API funcional

import numpy as np
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.utils import plot_model
from tensorflow.keras.datasets import mnist

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
y_train = np.eye(10)[y_train]
y_test = np.eye(10)[y_test]

# Define two inputs
input_a = Input(shape=(784,), name='input_a')
input_b = Input(shape=(784,), name='input_b')

# Define a shared dense layer
shared_dense = Dense(64, activation='relu', name='shared_dense')

# Apply the shared layer to both inputs
processed_a = shared_dense(input_a)
processed_b = shared_dense(input_b)

# Concatenate the processed inputs
concatenated = Concatenate(name='concatenate')([processed_a, processed_b])

# Add more layers
x = Dense(32, activation='relu', name='dense_1')(concatenated)
x = Dense(16, activation='relu', name='dense_2')(x)

# Add a final output layer
output = Dense(10, activation='softmax', name='output')(x)

# Create the model with shared layers
model = Model(inputs=[input_a, input_b], outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display the model summary
model.summary()

# Visualize the model architecture
plot_model(model, to_file='model_architecture.png', show_shapes=True, show_layer_names=True)

# Train the model
history = model.fit(
    [x_train, x_train],  # Use the same input twice for demonstration
    y_train,
    epochs=10,
    batch_size=128,
    validation_split=0.2,
    verbose=1
)

# Evaluate the model
test_loss, test_accuracy = model.evaluate([x_test, x_test], y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Make predictions
sample_input = x_test[:5]
predictions = model.predict([sample_input, sample_input])
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

Desglose completo del código:

  • Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se aplanan y normalizan, y las etiquetas se convierten a formato one-hot.
  • Arquitectura del modelo:
    • Se definen dos capas de entrada (input_a y input_b), ambas aceptan vectores de 784 dimensiones (imágenes aplanadas de 28x28).
    • Se crea una capa densa compartida con 64 unidades y activación ReLU.
    • La capa compartida se aplica a ambas entradas, demostrando la compartición de pesos.
    • Las entradas procesadas se concatenan usando la capa Concatenate.
    • Se añaden dos capas densas más (32 y 16 unidades) para un procesamiento adicional.
    • La capa de salida final tiene 10 unidades con activación softmax para la clasificación multiclase.
  • Creación y compilación del modelo:
    • El modelo se crea usando la API funcional, especificando múltiples entradas y una salida.
    • El modelo se compila con el optimizador Adam, la función de pérdida categorical crossentropy y la métrica de precisión.
  • Visualización del modelo:
    • Se llama a model.summary() para mostrar un resumen textual de la arquitectura del modelo.
    • Se usa plot_model() para generar una representación visual de la arquitectura del modelo.
  • Entrenamiento del modelo:
    • El modelo se entrena usando el método fit().
    • Para fines demostrativos, se usa la misma entrada (x_train) dos veces para simular dos entradas diferentes.
    • El entrenamiento se realiza durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  • Evaluación y predicción del modelo:
    • El modelo se evalúa en el conjunto de prueba para obtener la precisión.
    • Se hacen predicciones de muestra usando las primeras 5 imágenes de prueba.
    • Se imprimen las clases predichas para demostrar la salida del modelo.

Este ejemplo muestra un flujo de trabajo completo, incluyendo la preparación de datos, la creación de un modelo con capas compartidas, el entrenamiento, la evaluación y la realización de predicciones. Ilustra la flexibilidad de la API funcional para crear arquitecturas de modelos complejas con componentes compartidos y múltiples entradas.

Combinando las APIs Sequential y Funcional

La flexibilidad de Keras permite la integración sin problemas de las APIs Sequential y Funcional, lo que permite la creación de arquitecturas de modelos altamente personalizables y complejas. Esta poderosa combinación ofrece a los desarrolladores la capacidad de aprovechar la simplicidad de la API Sequential para pilas de capas sencillas mientras se utiliza la versatilidad de la API Funcional para diseños de modelos más intrincados.

Al combinar estas APIs, puedes crear modelos híbridos que se beneficien de ambos enfoques. Por ejemplo, podrías usar la API Sequential para definir rápidamente una serie de capas para la extracción de características, y luego emplear la API Funcional para introducir caminos ramificados, múltiples entradas o salidas, o capas compartidas. Este enfoque es especialmente útil cuando se trabaja con aprendizaje transferido, donde los modelos Sequential preentrenados pueden incorporarse en arquitecturas más complejas.

Además, esta combinación permite la integración fácil de capas personalizadas, conexiones de salto, e incluso la implementación de arquitecturas avanzadas como redes residuales o mecanismos de atención. La capacidad de mezclar y combinar estas APIs proporciona un alto grado de flexibilidad, lo que facilita experimentar con nuevos diseños de modelos y adaptarse a requisitos específicos del problema sin sacrificar la naturaleza intuitiva de la construcción de modelos en Keras.

Ejemplo: Combinando modelos Sequential y Funcional

import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Dense, Flatten
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# Build a Sequential model
sequential_model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu', name='sequential_dense')
])

# Define an input using the Functional API
input_layer = Input(shape=(28, 28))

# Pass the input through the Sequential model
x = sequential_model(input_layer)

# Add more layers using the Functional API
x = Dense(64, activation='relu', name='functional_dense_1')(x)
output = Dense(10, activation='softmax', name='output')(x)

# Create the final model
model = Model(inputs=input_layer, outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display model summary
model.summary()

# Train the model
history = model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2, verbose=1)

# Evaluate the model
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions on a sample
sample = x_test[:5]
predictions = model.predict(sample)
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

# Visualize sample predictions
plt.figure(figsize=(15, 3))
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(sample[i].reshape(28, 28), cmap='gray')
    plt.title(f"Predicted: {predicted_classes[i]}")
    plt.axis('off')
plt.tight_layout()
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras, así como NumPy y Matplotlib para la manipulación y visualización de datos.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se normalizan y las etiquetas se convierten a formato one-hot.
  2. Arquitectura del modelo:
    • Se crea un modelo Sequential con una capa Flatten y una capa Dense.
    • Se define una capa de entrada usando la API funcional.
    • El modelo Sequential se aplica a la capa de entrada.
    • Se añaden capas Dense adicionales utilizando la API funcional.
    • El modelo final se crea especificando las capas de entrada y salida.
  3. Compilación y entrenamiento del modelo:
    • El modelo se compila con el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
    • Se muestra el resumen del modelo para ver la arquitectura.
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  4. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  5. Visualización del historial de entrenamiento:
    • Se trazan las curvas de precisión de entrenamiento y validación a lo largo de las épocas.
    • Se trazan las curvas de pérdida de entrenamiento y validación a lo largo de las épocas.
  6. Predicciones:
    • Se hacen predicciones sobre una muestra de 5 imágenes de prueba.
    • Se imprimen las clases predichas.
  7. Visualización de predicciones de muestra:
    • Se muestran las 5 imágenes de muestra junto con sus clases predichas.

Este ejemplo demuestra un flujo de trabajo completo que combina las APIs Sequential y Funcional en Keras. Incluye la preparación de datos, la creación del modelo, el entrenamiento, la evaluación y la visualización de resultados. El código muestra cómo aprovechar ambas APIs para crear una arquitectura de modelo flexible, entrenarla con datos reales y analizar su rendimiento.

3.2 Construcción de modelos secuenciales y funcionales con Keras

Keras ofrece dos enfoques principales para la construcción de modelos de redes neuronales: la API Sequential y la API Funcional. La API Sequential proporciona un método sencillo para construir modelos apilando capas en una secuencia lineal.

Este enfoque es ideal para arquitecturas simples de "feed-forward" donde cada capa tiene un único tensor de entrada y un único tensor de salida. Por otro lado, la API Funcional ofrece mayor flexibilidad y potencia, permitiendo la creación de arquitecturas de modelo más complejas.

Con la API Funcional, los desarrolladores pueden diseñar modelos con múltiples entradas y salidas, implementar capas compartidas y construir estructuras avanzadas como redes residuales o modelos con caminos ramificados. Esta versatilidad hace que la API Funcional sea especialmente adecuada para desarrollar modelos de aprendizaje profundo sofisticados que van más allá de las arquitecturas lineales simples.

3.2.1 Construcción de modelos con la API Sequential

La API Sequential es la forma más simple y directa de definir un modelo en Keras. Es especialmente adecuada para modelos donde las capas siguen una secuencia lineal desde la entrada hasta la salida, sin ramificaciones complejas ni fusión de rutas de datos.

Esto la convierte en una elección ideal para principiantes o para la construcción de arquitecturas de redes neuronales relativamente simples. Vamos a profundizar en el proceso de construcción de una red neuronal básica usando la API Sequential, explorando cada paso en detalle.

Creación de una red neuronal simple de alimentación directa (feedforward)

En este ejemplo completo, recorreremos la creación de una red neuronal diseñada para una tarea clásica de aprendizaje automático: la clasificación de dígitos escritos a mano del conjunto de datos MNIST. El conjunto de datos MNIST es una gran base de datos de dígitos escritos a mano que se usa comúnmente para entrenar varios sistemas de procesamiento de imágenes. Nuestra red estará estructurada de la siguiente manera:

  • Una capa Flatten: Esta capa inicial cumple un propósito crucial. Transforma la entrada, que consiste en imágenes de 28x28 píxeles, en un vector unidimensional. Esta transformación es necesaria porque las capas densas subsiguientes esperan una entrada en forma de un arreglo 1D. Básicamente, "desenrolla" la imagen 2D en una sola línea de píxeles.
  • Dos capas Dense con activación ReLU: Estas son capas completamente conectadas, lo que significa que cada neurona en estas capas está conectada a todas las neuronas en las capas anterior y posterior. La función de activación Rectified Linear Unit (ReLU) se aplica para introducir no linealidad en el modelo, lo que le permite aprender patrones complejos. ReLU se elige por su eficiencia computacional y su capacidad para mitigar el problema del gradiente de desvanecimiento en redes profundas.
  • Una capa Dense final con activación softmax: Esta capa de salida está específicamente diseñada para la clasificación multiclase. Contiene 10 neuronas, una para cada dígito (0-9). La función de activación softmax asegura que la salida de estas neuronas sume 1, proporcionando efectivamente una distribución de probabilidad sobre las 10 posibles clases de dígitos.

Esta arquitectura, aunque simple, es lo suficientemente potente como para lograr una alta precisión en el conjunto de datos MNIST, demostrando la efectividad de incluso las estructuras básicas de redes neuronales cuando se aplican a problemas bien definidos.

Ejemplo: Construcción de un modelo secuencial

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

# Load the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Normalize the input data
X_train, X_test = X_train / 255.0, X_test / 255.0

# Convert labels to one-hot encoding
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

# Define a Sequential model
model = Sequential([
    Flatten(input_shape=(28, 28)),  # Flatten the 28x28 input into a 1D vector
    Dense(256, activation='relu'),  # First hidden layer with 256 units and ReLU activation
    Dropout(0.3),                   # Dropout layer to prevent overfitting
    Dense(128, activation='relu'),  # Second hidden layer with 128 units and ReLU activation
    Dropout(0.2),                   # Another dropout layer
    Dense(64, activation='relu'),   # Third hidden layer with 64 units and ReLU activation
    Dense(10, activation='softmax') # Output layer for 10 classes (digits 0-9)
])

# Compile the model
model.compile(optimizer='adam', 
              loss='categorical_crossentropy', 
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=64, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Explicación del desglose del código:

  • Importaciones: Importamos las bibliotecas necesarias, incluyendo numpy para operaciones numéricas, matplotlib para gráficos y varios módulos de Keras para construir y entrenar la red neuronal.
  • Preparación de datos:
    • El conjunto de datos MNIST se carga utilizando mnist.load_data().
    • Los datos de entrada (X_train y X_test) se normalizan dividiendo por 255 para escalar los valores de los píxeles entre 0 y 1.
    • Las etiquetas (y_train y y_test) se convierten al formato codificado one-hot usando to_categorical().
  • Arquitectura del modelo:
    • Se crea un modelo Sequential con múltiples capas:
    • Capa Flatten para convertir la entrada 2D (28x28) en 1D.
    • Tres capas Dense con activación ReLU (256, 128 y 64 unidades).
    • Dos capas Dropout (tasas de abandono del 30% y 20%) para prevenir el sobreajuste.
    • Capa de salida Dense con 10 unidades y activación softmax para la clasificación multiclase.
  • Compilación del modelo:
    • Se utiliza el optimizador Adam.
    • Se elige crossentropy categórica como la función de pérdida para la clasificación multiclase.
    • La precisión se establece como la métrica a monitorear durante el entrenamiento.
  • Callbacks:
    • Se utiliza ModelCheckpoint para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  • Entrenamiento del modelo:
    • El modelo se entrena durante un máximo de 30 épocas con un tamaño de lote de 64.
    • El 20% de los datos de entrenamiento se utiliza para la validación.
    • Se aplican callbacks durante el entrenamiento.
  • Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  • Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) para los conjuntos de entrenamiento y validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este ejemplo proporciona un enfoque completo para construir, entrenar y evaluar una red neuronal para el conjunto de datos MNIST. Incluye características adicionales como dropout para la regularización, callbacks para optimizar el entrenamiento y visualizaciones para comprender mejor el rendimiento del modelo.

Entrenamiento y evaluación del modelo secuencial

Después de definir la arquitectura del modelo, pasamos a los pasos cruciales de entrenar y evaluar el modelo. Este proceso involucra dos funciones clave:

  1. La función fit(): Se utiliza para entrenar el modelo en nuestro conjunto de datos preparado. Durante el entrenamiento, el modelo aprende a mapear entradas a salidas ajustando sus parámetros internos (pesos y sesgos) en función de los datos de entrenamiento. La función fit() toma varios argumentos importantes:
    • X_train y y_train: Las características de entrada y las etiquetas correspondientes del conjunto de datos de entrenamiento
    • epochs: El número de veces que el modelo iterará sobre todo el conjunto de datos de entrenamiento
    • batch_size: El número de muestras procesadas antes de que se actualice el modelo
    • validation_data: Un conjunto de datos separado que se utiliza para evaluar el rendimiento del modelo durante el entrenamiento
  2. La función evaluate(): Después del entrenamiento, utilizamos esta función para evaluar el rendimiento del modelo en el conjunto de datos de prueba. Este paso es crucial ya que nos brinda una estimación imparcial de qué tan bien generaliza nuestro modelo a datos no vistos. La función evaluate() generalmente devuelve dos valores:
    • test_loss: Una medida del error del modelo en el conjunto de prueba
    • test_accuracy: La proporción de predicciones correctas realizadas por el modelo en el conjunto de prueba

Al utilizar estas funciones en conjunto, podemos entrenar nuestro modelo en los datos de entrenamiento y luego evaluar su efectividad en datos de prueba previamente no vistos, lo que nos brinda una comprensión completa del rendimiento y la capacidad de generalización de nuestro modelo.

Ejemplo: Entrenamiento y evaluación del modelo secuencial

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train / 255.0, X_test / 255.0  # Normalize pixel values
y_train, y_test = to_categorical(y_train), to_categorical(y_test)  # One-hot encode labels

# Define the model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=32, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model on the test data
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Este código demuestra el proceso de construir, entrenar y evaluar un modelo secuencial utilizando Keras para el conjunto de datos MNIST.

Desglose de los componentes principales:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluidos los componentes de TensorFlow/Keras.
    • El conjunto de datos MNIST se carga y preprocesa:
      • Las imágenes se normalizan dividiendo los valores de los píxeles por 255.
      • Las etiquetas se codifican en formato one-hot.
  2. Definición del modelo:
    • Se crea un modelo secuencial con las siguientes capas:
      • Capa Flatten para convertir la entrada 2D en 1D.
      • Dos capas Dense con activación ReLU (128 y 64 unidades).
      • Capa de salida Dense con activación softmax para 10 clases.
  3. Compilación del modelo:
    • El modelo se compila usando el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
  4. Callbacks:
    • ModelCheckpoint se utiliza para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  5. Entrenamiento del modelo:
    • El modelo se entrena durante 30 épocas con un tamaño de lote de 32.
    • El 20% de los datos de entrenamiento se utiliza para validación.
  6. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  7. Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) tanto para el conjunto de entrenamiento como para el de validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este código proporciona un ejemplo completo del flujo de trabajo de machine learning para la clasificación de imágenes utilizando una arquitectura básica de red neuronal.

3.2.2 Construcción de modelos con la API funcional

La API funcional en Keras es una herramienta poderosa y flexible diseñada para construir arquitecturas de redes neuronales complejas. A diferencia de la API secuencial, que se limita a apilar capas de manera lineal, la API funcional permite la creación de estructuras de modelos más sofisticadas. A continuación, se explica más detalladamente sus capacidades:

  1. Conexiones no lineales entre capas: Con la API funcional, puedes definir modelos en los que las capas se conectan de manera no secuencial, lo que te permite crear bifurcaciones, conexiones de salto o incluso conexiones circulares entre capas, habilitando la construcción de topologías de red más complejas.
  2. Múltiples entradas y salidas: La API admite modelos con múltiples tensores de entrada y salida, lo cual es especialmente útil para tareas que requieren procesar diferentes tipos de datos simultáneamente o generar múltiples predicciones a partir de una sola entrada.
  3. Capas compartidas: Puedes reutilizar instancias de capas en diferentes partes de tu modelo. Esto es crucial para implementar arquitecturas como redes siamesas, donde se aplica un procesamiento idéntico a múltiples entradas.
  4. Conexiones residuales: La API funcional facilita la implementación de conexiones residuales, un componente clave de las redes profundas residuales (ResNets). Estas conexiones permiten que la información salte una o más capas, lo que ayuda a mitigar el problema del gradiente que desaparece en redes muy profundas.
  5. Composición de modelos: Puedes tratar modelos instanciados como capas y usarlos para construir modelos más grandes y complejos. Esta modularidad permite la creación de arquitecturas altamente sofisticadas combinando submodelos más simples.
  6. Capas personalizadas: La API funcional se integra perfectamente con capas definidas de forma personalizada, lo que te da la flexibilidad de incorporar operaciones especializadas en la arquitectura de tu modelo.
  7. Modelos tipo gráfico: Para tareas que requieren procesar datos estructurados en gráficos, como el análisis de redes sociales o la predicción de propiedades de moléculas, la API funcional te permite construir modelos que puedan manejar tales estructuras de datos complejas.

Estas características hacen que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Creación de un modelo con múltiples entradas y salidas

Exploremos una aplicación más avanzada de la API funcional creando un modelo con múltiples entradas y salidas. Este enfoque es particularmente útil para tareas complejas que requieren procesar diversos tipos de datos o generar múltiples predicciones de manera simultánea. Consideremos un escenario en el que estamos desarrollando una red de análisis de imágenes sofisticada. Esta red está diseñada para extraer dos piezas de información distintas de una sola imagen de entrada: la categoría del objeto representado y su color predominante.

Para lograr esto, diseñaremos un modelo con una base compartida que se ramifica en dos capas de salida separadas. La base compartida se encargará de extraer características generales de la imagen, mientras que las capas de salida especializadas se centrarán en predecir la categoría del objeto y su color, respectivamente. Esta arquitectura demuestra la flexibilidad de la API funcional, permitiéndonos crear modelos que puedan realizar múltiples tareas relacionadas de manera eficiente.

Por ejemplo, la predicción de categoría podría implicar clasificar el objeto en clases predefinidas (como coche, perro, silla), mientras que la predicción del color podría identificar el color primario (como rojo, azul, verde) del objeto. Al usar dos capas de salida separadas, podemos optimizar cada tarea de predicción de manera independiente, utilizando diferentes funciones de pérdida o métricas para cada salida.

Este enfoque multi-salida no solo muestra la versatilidad de la API funcional, sino que también ilustra cómo podemos diseñar modelos que imiten la percepción humana, donde múltiples atributos de un objeto se procesan e identifican simultáneamente. Tales modelos tienen aplicaciones prácticas en diversos campos, incluyendo la visión por computadora, la robótica y los sistemas de control de calidad automatizados en la manufactura.

Ejemplo: Construcción de un modelo con múltiples salidas utilizando la API funcional

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

# Generate synthetic data
def generate_data(num_samples=1000):
    images = np.random.rand(num_samples, 64, 64, 3)
    categories = np.random.randint(0, 10, num_samples)
    colors = np.random.randint(0, 3, num_samples)
    return images, categories, colors

# Prepare data
X, y_category, y_color = generate_data(5000)
y_category = to_categorical(y_category, 10)
y_color = to_categorical(y_color, 3)

# Split data
X_train, X_test, y_category_train, y_category_test, y_color_train, y_color_test = train_test_split(
    X, y_category, y_color, test_size=0.2, random_state=42
)

# Define the input layer
input_layer = Input(shape=(64, 64, 3))  # Input shape is 64x64 RGB image

# Convolutional layers
x = Conv2D(32, (3, 3), activation='relu')(input_layer)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D((2, 2))(x)

# Flatten the output
x = Flatten()(x)

# Add shared dense layers
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)

# Define the first output for object category
category_output = Dense(10, activation='softmax', name='category_output')(x)

# Define the second output for object color
color_output = Dense(3, activation='softmax', name='color_output')(x)

# Create the model with multiple outputs
model = Model(inputs=input_layer, outputs=[category_output, color_output])

# Compile the model with different loss functions for each output
model.compile(optimizer='adam',
              loss={'category_output': 'categorical_crossentropy', 
                    'color_output': 'categorical_crossentropy'},
              loss_weights={'category_output': 1.0, 'color_output': 0.5},
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Train the model
history = model.fit(
    X_train, 
    {'category_output': y_category_train, 'color_output': y_color_train},
    validation_data=(X_test, {'category_output': y_category_test, 'color_output': y_color_test}),
    epochs=10,
    batch_size=32
)

# Evaluate the model
test_loss, category_loss, color_loss, category_acc, color_acc = model.evaluate(
    X_test, 
    {'category_output': y_category_test, 'color_output': y_color_test}
)
print(f"Test category accuracy: {category_acc:.4f}")
print(f"Test color accuracy: {color_acc:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['category_output_accuracy'], label='Category Accuracy')
plt.plot(history.history['color_output_accuracy'], label='Color Accuracy')
plt.plot(history.history['val_category_output_accuracy'], label='Val Category Accuracy')
plt.plot(history.history['val_color_output_accuracy'], label='Val Color Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['category_output_loss'], label='Category Loss')
plt.plot(history.history['color_output_loss'], label='Color Loss')
plt.plot(history.history['val_category_output_loss'], label='Val Category Loss')
plt.plot(history.history['val_color_output_loss'], label='Val Color Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
sample_image = X_test[0:1]
predictions = model.predict(sample_image)
predicted_category = np.argmax(predictions[0])
predicted_color = np.argmax(predictions[1])

print(f"Predicted category: {predicted_category}")
print(f"Predicted color: {predicted_color}")

# Display the sample image
plt.imshow(sample_image[0])
plt.title(f"Category: {predicted_category}, Color: {predicted_color}")
plt.axis('off')
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluyendo NumPy para operaciones numéricas, Matplotlib para visualización y varios módulos de Keras para construir y entrenar el modelo.
    • Se define una función generate_data() para crear datos sintéticos para nuestra tarea de clasificación con múltiples salidas.
    • Se generan 5000 muestras de imágenes RGB de 64x64 junto con etiquetas correspondientes de categoría (10 clases) y color (3 clases).
    • Las etiquetas se codifican en formato one-hot usando to_categorical().
    • Los datos se dividen en conjuntos de entrenamiento y prueba utilizando train_test_split().
  2. Arquitectura del modelo:
    • Se define una capa de entrada para imágenes RGB de 64x64.
    • Se añaden capas convolucionales (Conv2D) y capas de max pooling para extraer características de las imágenes.
    • La salida se aplana y pasa por dos capas densas (128 y 64 unidades) con activación ReLU.
    • Se definen dos capas de salida separadas:
      • Salida de categoría: 10 unidades con activación softmax para clasificar en 10 categorías.
      • Salida de color: 3 unidades con activación softmax para clasificar en 3 colores.
    • El modelo se crea usando la API funcional, especificando la entrada y múltiples salidas.
  3. Compilación del modelo:
    • El modelo se compila con el optimizador Adam.
    • Se usa la pérdida categorical crossentropy para ambas salidas.
    • Se especifican los pesos de la pérdida (1.0 para la categoría, 0.5 para el color) para equilibrar la importancia de cada tarea.
    • Se define la precisión como la métrica para ambas salidas.
  4. Entrenamiento del modelo:
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 32.
    • Se proporcionan los datos de entrenamiento y validación como diccionarios que asignan nombres de salida a sus datos respectivos.
  5. Evaluación del modelo:
    • El modelo se evalúa en el conjunto de prueba, imprimiendo la precisión para las predicciones de categoría y color.
  6. Visualización:
    • Se grafica el historial de entrenamiento, mostrando precisión y pérdida para ambas salidas a lo largo de las épocas.
    • Se utiliza una imagen de prueba del conjunto de prueba para hacer predicciones.
    • La imagen de prueba se muestra junto con su categoría y color predichos.

Este ejemplo demuestra un escenario realista de una tarea de clasificación con múltiples salidas, incluyendo la generación de datos, creación de modelo, entrenamiento, evaluación y visualización de los resultados. Muestra la flexibilidad de la API funcional de Keras para crear arquitecturas de modelos complejas con múltiples salidas y cómo manejar estos modelos a lo largo del flujo de trabajo de machine learning.

Capas compartidas y conexiones residuales

La API funcional en Keras ofrece la característica poderosa de capas compartidas, lo que permite reutilizar instancias de capas en diferentes partes de un modelo. Esta capacidad es particularmente valiosa para implementar arquitecturas avanzadas como las redes siamesas y las redes residuales. Las redes siamesas, comúnmente utilizadas en tareas de reconocimiento facial, usan procesamiento idéntico en múltiples entradas para comparar su similitud. Por otro lado, las redes residuales, ejemplificadas por arquitecturas como ResNet, utilizan conexiones de salto para permitir que la información evite una o más capas, lo que facilita el entrenamiento de redes muy profundas.

El concepto de capas compartidas se extiende más allá de estas arquitecturas específicas. Es una herramienta fundamental para crear modelos con compartición de pesos, lo que puede ser crucial en varios escenarios. Por ejemplo, en tareas de procesamiento de lenguaje natural como los sistemas de preguntas y respuestas, las capas compartidas pueden procesar tanto la pregunta como el contexto con el mismo conjunto de pesos, asegurando una extracción de características consistente. De manera similar, en el aprendizaje multimodal, donde se necesitan procesar entradas de diferentes fuentes (por ejemplo, imagen y texto), las capas compartidas pueden crear un espacio de representación común para estas entradas diversas.

Además, la flexibilidad de la API funcional permite la creación de topologías de modelos complejas que van más allá de las estructuras secuenciales simples. Esto incluye modelos con múltiples entradas o salidas, modelos con caminos ramificados e incluso modelos que incorporan bucles de retroalimentación. Tal versatilidad hace que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Ejemplo: Uso de capas compartidas en la API funcional

import numpy as np
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.utils import plot_model
from tensorflow.keras.datasets import mnist

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
y_train = np.eye(10)[y_train]
y_test = np.eye(10)[y_test]

# Define two inputs
input_a = Input(shape=(784,), name='input_a')
input_b = Input(shape=(784,), name='input_b')

# Define a shared dense layer
shared_dense = Dense(64, activation='relu', name='shared_dense')

# Apply the shared layer to both inputs
processed_a = shared_dense(input_a)
processed_b = shared_dense(input_b)

# Concatenate the processed inputs
concatenated = Concatenate(name='concatenate')([processed_a, processed_b])

# Add more layers
x = Dense(32, activation='relu', name='dense_1')(concatenated)
x = Dense(16, activation='relu', name='dense_2')(x)

# Add a final output layer
output = Dense(10, activation='softmax', name='output')(x)

# Create the model with shared layers
model = Model(inputs=[input_a, input_b], outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display the model summary
model.summary()

# Visualize the model architecture
plot_model(model, to_file='model_architecture.png', show_shapes=True, show_layer_names=True)

# Train the model
history = model.fit(
    [x_train, x_train],  # Use the same input twice for demonstration
    y_train,
    epochs=10,
    batch_size=128,
    validation_split=0.2,
    verbose=1
)

# Evaluate the model
test_loss, test_accuracy = model.evaluate([x_test, x_test], y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Make predictions
sample_input = x_test[:5]
predictions = model.predict([sample_input, sample_input])
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

Desglose completo del código:

  • Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se aplanan y normalizan, y las etiquetas se convierten a formato one-hot.
  • Arquitectura del modelo:
    • Se definen dos capas de entrada (input_a y input_b), ambas aceptan vectores de 784 dimensiones (imágenes aplanadas de 28x28).
    • Se crea una capa densa compartida con 64 unidades y activación ReLU.
    • La capa compartida se aplica a ambas entradas, demostrando la compartición de pesos.
    • Las entradas procesadas se concatenan usando la capa Concatenate.
    • Se añaden dos capas densas más (32 y 16 unidades) para un procesamiento adicional.
    • La capa de salida final tiene 10 unidades con activación softmax para la clasificación multiclase.
  • Creación y compilación del modelo:
    • El modelo se crea usando la API funcional, especificando múltiples entradas y una salida.
    • El modelo se compila con el optimizador Adam, la función de pérdida categorical crossentropy y la métrica de precisión.
  • Visualización del modelo:
    • Se llama a model.summary() para mostrar un resumen textual de la arquitectura del modelo.
    • Se usa plot_model() para generar una representación visual de la arquitectura del modelo.
  • Entrenamiento del modelo:
    • El modelo se entrena usando el método fit().
    • Para fines demostrativos, se usa la misma entrada (x_train) dos veces para simular dos entradas diferentes.
    • El entrenamiento se realiza durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  • Evaluación y predicción del modelo:
    • El modelo se evalúa en el conjunto de prueba para obtener la precisión.
    • Se hacen predicciones de muestra usando las primeras 5 imágenes de prueba.
    • Se imprimen las clases predichas para demostrar la salida del modelo.

Este ejemplo muestra un flujo de trabajo completo, incluyendo la preparación de datos, la creación de un modelo con capas compartidas, el entrenamiento, la evaluación y la realización de predicciones. Ilustra la flexibilidad de la API funcional para crear arquitecturas de modelos complejas con componentes compartidos y múltiples entradas.

Combinando las APIs Sequential y Funcional

La flexibilidad de Keras permite la integración sin problemas de las APIs Sequential y Funcional, lo que permite la creación de arquitecturas de modelos altamente personalizables y complejas. Esta poderosa combinación ofrece a los desarrolladores la capacidad de aprovechar la simplicidad de la API Sequential para pilas de capas sencillas mientras se utiliza la versatilidad de la API Funcional para diseños de modelos más intrincados.

Al combinar estas APIs, puedes crear modelos híbridos que se beneficien de ambos enfoques. Por ejemplo, podrías usar la API Sequential para definir rápidamente una serie de capas para la extracción de características, y luego emplear la API Funcional para introducir caminos ramificados, múltiples entradas o salidas, o capas compartidas. Este enfoque es especialmente útil cuando se trabaja con aprendizaje transferido, donde los modelos Sequential preentrenados pueden incorporarse en arquitecturas más complejas.

Además, esta combinación permite la integración fácil de capas personalizadas, conexiones de salto, e incluso la implementación de arquitecturas avanzadas como redes residuales o mecanismos de atención. La capacidad de mezclar y combinar estas APIs proporciona un alto grado de flexibilidad, lo que facilita experimentar con nuevos diseños de modelos y adaptarse a requisitos específicos del problema sin sacrificar la naturaleza intuitiva de la construcción de modelos en Keras.

Ejemplo: Combinando modelos Sequential y Funcional

import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Dense, Flatten
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# Build a Sequential model
sequential_model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu', name='sequential_dense')
])

# Define an input using the Functional API
input_layer = Input(shape=(28, 28))

# Pass the input through the Sequential model
x = sequential_model(input_layer)

# Add more layers using the Functional API
x = Dense(64, activation='relu', name='functional_dense_1')(x)
output = Dense(10, activation='softmax', name='output')(x)

# Create the final model
model = Model(inputs=input_layer, outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display model summary
model.summary()

# Train the model
history = model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2, verbose=1)

# Evaluate the model
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions on a sample
sample = x_test[:5]
predictions = model.predict(sample)
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

# Visualize sample predictions
plt.figure(figsize=(15, 3))
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(sample[i].reshape(28, 28), cmap='gray')
    plt.title(f"Predicted: {predicted_classes[i]}")
    plt.axis('off')
plt.tight_layout()
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras, así como NumPy y Matplotlib para la manipulación y visualización de datos.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se normalizan y las etiquetas se convierten a formato one-hot.
  2. Arquitectura del modelo:
    • Se crea un modelo Sequential con una capa Flatten y una capa Dense.
    • Se define una capa de entrada usando la API funcional.
    • El modelo Sequential se aplica a la capa de entrada.
    • Se añaden capas Dense adicionales utilizando la API funcional.
    • El modelo final se crea especificando las capas de entrada y salida.
  3. Compilación y entrenamiento del modelo:
    • El modelo se compila con el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
    • Se muestra el resumen del modelo para ver la arquitectura.
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  4. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  5. Visualización del historial de entrenamiento:
    • Se trazan las curvas de precisión de entrenamiento y validación a lo largo de las épocas.
    • Se trazan las curvas de pérdida de entrenamiento y validación a lo largo de las épocas.
  6. Predicciones:
    • Se hacen predicciones sobre una muestra de 5 imágenes de prueba.
    • Se imprimen las clases predichas.
  7. Visualización de predicciones de muestra:
    • Se muestran las 5 imágenes de muestra junto con sus clases predichas.

Este ejemplo demuestra un flujo de trabajo completo que combina las APIs Sequential y Funcional en Keras. Incluye la preparación de datos, la creación del modelo, el entrenamiento, la evaluación y la visualización de resultados. El código muestra cómo aprovechar ambas APIs para crear una arquitectura de modelo flexible, entrenarla con datos reales y analizar su rendimiento.

3.2 Construcción de modelos secuenciales y funcionales con Keras

Keras ofrece dos enfoques principales para la construcción de modelos de redes neuronales: la API Sequential y la API Funcional. La API Sequential proporciona un método sencillo para construir modelos apilando capas en una secuencia lineal.

Este enfoque es ideal para arquitecturas simples de "feed-forward" donde cada capa tiene un único tensor de entrada y un único tensor de salida. Por otro lado, la API Funcional ofrece mayor flexibilidad y potencia, permitiendo la creación de arquitecturas de modelo más complejas.

Con la API Funcional, los desarrolladores pueden diseñar modelos con múltiples entradas y salidas, implementar capas compartidas y construir estructuras avanzadas como redes residuales o modelos con caminos ramificados. Esta versatilidad hace que la API Funcional sea especialmente adecuada para desarrollar modelos de aprendizaje profundo sofisticados que van más allá de las arquitecturas lineales simples.

3.2.1 Construcción de modelos con la API Sequential

La API Sequential es la forma más simple y directa de definir un modelo en Keras. Es especialmente adecuada para modelos donde las capas siguen una secuencia lineal desde la entrada hasta la salida, sin ramificaciones complejas ni fusión de rutas de datos.

Esto la convierte en una elección ideal para principiantes o para la construcción de arquitecturas de redes neuronales relativamente simples. Vamos a profundizar en el proceso de construcción de una red neuronal básica usando la API Sequential, explorando cada paso en detalle.

Creación de una red neuronal simple de alimentación directa (feedforward)

En este ejemplo completo, recorreremos la creación de una red neuronal diseñada para una tarea clásica de aprendizaje automático: la clasificación de dígitos escritos a mano del conjunto de datos MNIST. El conjunto de datos MNIST es una gran base de datos de dígitos escritos a mano que se usa comúnmente para entrenar varios sistemas de procesamiento de imágenes. Nuestra red estará estructurada de la siguiente manera:

  • Una capa Flatten: Esta capa inicial cumple un propósito crucial. Transforma la entrada, que consiste en imágenes de 28x28 píxeles, en un vector unidimensional. Esta transformación es necesaria porque las capas densas subsiguientes esperan una entrada en forma de un arreglo 1D. Básicamente, "desenrolla" la imagen 2D en una sola línea de píxeles.
  • Dos capas Dense con activación ReLU: Estas son capas completamente conectadas, lo que significa que cada neurona en estas capas está conectada a todas las neuronas en las capas anterior y posterior. La función de activación Rectified Linear Unit (ReLU) se aplica para introducir no linealidad en el modelo, lo que le permite aprender patrones complejos. ReLU se elige por su eficiencia computacional y su capacidad para mitigar el problema del gradiente de desvanecimiento en redes profundas.
  • Una capa Dense final con activación softmax: Esta capa de salida está específicamente diseñada para la clasificación multiclase. Contiene 10 neuronas, una para cada dígito (0-9). La función de activación softmax asegura que la salida de estas neuronas sume 1, proporcionando efectivamente una distribución de probabilidad sobre las 10 posibles clases de dígitos.

Esta arquitectura, aunque simple, es lo suficientemente potente como para lograr una alta precisión en el conjunto de datos MNIST, demostrando la efectividad de incluso las estructuras básicas de redes neuronales cuando se aplican a problemas bien definidos.

Ejemplo: Construcción de un modelo secuencial

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping

# Load the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Normalize the input data
X_train, X_test = X_train / 255.0, X_test / 255.0

# Convert labels to one-hot encoding
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

# Define a Sequential model
model = Sequential([
    Flatten(input_shape=(28, 28)),  # Flatten the 28x28 input into a 1D vector
    Dense(256, activation='relu'),  # First hidden layer with 256 units and ReLU activation
    Dropout(0.3),                   # Dropout layer to prevent overfitting
    Dense(128, activation='relu'),  # Second hidden layer with 128 units and ReLU activation
    Dropout(0.2),                   # Another dropout layer
    Dense(64, activation='relu'),   # Third hidden layer with 64 units and ReLU activation
    Dense(10, activation='softmax') # Output layer for 10 classes (digits 0-9)
])

# Compile the model
model.compile(optimizer='adam', 
              loss='categorical_crossentropy', 
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=64, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Explicación del desglose del código:

  • Importaciones: Importamos las bibliotecas necesarias, incluyendo numpy para operaciones numéricas, matplotlib para gráficos y varios módulos de Keras para construir y entrenar la red neuronal.
  • Preparación de datos:
    • El conjunto de datos MNIST se carga utilizando mnist.load_data().
    • Los datos de entrada (X_train y X_test) se normalizan dividiendo por 255 para escalar los valores de los píxeles entre 0 y 1.
    • Las etiquetas (y_train y y_test) se convierten al formato codificado one-hot usando to_categorical().
  • Arquitectura del modelo:
    • Se crea un modelo Sequential con múltiples capas:
    • Capa Flatten para convertir la entrada 2D (28x28) en 1D.
    • Tres capas Dense con activación ReLU (256, 128 y 64 unidades).
    • Dos capas Dropout (tasas de abandono del 30% y 20%) para prevenir el sobreajuste.
    • Capa de salida Dense con 10 unidades y activación softmax para la clasificación multiclase.
  • Compilación del modelo:
    • Se utiliza el optimizador Adam.
    • Se elige crossentropy categórica como la función de pérdida para la clasificación multiclase.
    • La precisión se establece como la métrica a monitorear durante el entrenamiento.
  • Callbacks:
    • Se utiliza ModelCheckpoint para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  • Entrenamiento del modelo:
    • El modelo se entrena durante un máximo de 30 épocas con un tamaño de lote de 64.
    • El 20% de los datos de entrenamiento se utiliza para la validación.
    • Se aplican callbacks durante el entrenamiento.
  • Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  • Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) para los conjuntos de entrenamiento y validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este ejemplo proporciona un enfoque completo para construir, entrenar y evaluar una red neuronal para el conjunto de datos MNIST. Incluye características adicionales como dropout para la regularización, callbacks para optimizar el entrenamiento y visualizaciones para comprender mejor el rendimiento del modelo.

Entrenamiento y evaluación del modelo secuencial

Después de definir la arquitectura del modelo, pasamos a los pasos cruciales de entrenar y evaluar el modelo. Este proceso involucra dos funciones clave:

  1. La función fit(): Se utiliza para entrenar el modelo en nuestro conjunto de datos preparado. Durante el entrenamiento, el modelo aprende a mapear entradas a salidas ajustando sus parámetros internos (pesos y sesgos) en función de los datos de entrenamiento. La función fit() toma varios argumentos importantes:
    • X_train y y_train: Las características de entrada y las etiquetas correspondientes del conjunto de datos de entrenamiento
    • epochs: El número de veces que el modelo iterará sobre todo el conjunto de datos de entrenamiento
    • batch_size: El número de muestras procesadas antes de que se actualice el modelo
    • validation_data: Un conjunto de datos separado que se utiliza para evaluar el rendimiento del modelo durante el entrenamiento
  2. La función evaluate(): Después del entrenamiento, utilizamos esta función para evaluar el rendimiento del modelo en el conjunto de datos de prueba. Este paso es crucial ya que nos brinda una estimación imparcial de qué tan bien generaliza nuestro modelo a datos no vistos. La función evaluate() generalmente devuelve dos valores:
    • test_loss: Una medida del error del modelo en el conjunto de prueba
    • test_accuracy: La proporción de predicciones correctas realizadas por el modelo en el conjunto de prueba

Al utilizar estas funciones en conjunto, podemos entrenar nuestro modelo en los datos de entrenamiento y luego evaluar su efectividad en datos de prueba previamente no vistos, lo que nos brinda una comprensión completa del rendimiento y la capacidad de generalización de nuestro modelo.

Ejemplo: Entrenamiento y evaluación del modelo secuencial

import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train / 255.0, X_test / 255.0  # Normalize pixel values
y_train, y_test = to_categorical(y_train), to_categorical(y_test)  # One-hot encode labels

# Define the model
model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu'),
    Dense(64, activation='relu'),
    Dense(10, activation='softmax')
])

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Define callbacks
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True, monitor='val_accuracy', mode='max', verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1)

# Train the model
history = model.fit(X_train, y_train, 
                    epochs=30, 
                    batch_size=32, 
                    validation_split=0.2,
                    callbacks=[checkpoint, early_stopping])

# Evaluate the model on the test data
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
print(f"Test Accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
predictions = model.predict(X_test)
predicted_classes = np.argmax(predictions, axis=1)
true_classes = np.argmax(y_test, axis=1)

# Display some predictions
n_to_display = 10
indices = np.random.choice(len(X_test), n_to_display, replace=False)
fig, axes = plt.subplots(2, 5, figsize=(15, 6))
for i, idx in enumerate(indices):
    ax = axes[i//5, i%5]
    ax.imshow(X_test[idx].reshape(28, 28), cmap='gray')
    ax.set_title(f"True: {true_classes[idx]}, Pred: {predicted_classes[idx]}")
    ax.axis('off')
plt.tight_layout()
plt.show()

Este código demuestra el proceso de construir, entrenar y evaluar un modelo secuencial utilizando Keras para el conjunto de datos MNIST.

Desglose de los componentes principales:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluidos los componentes de TensorFlow/Keras.
    • El conjunto de datos MNIST se carga y preprocesa:
      • Las imágenes se normalizan dividiendo los valores de los píxeles por 255.
      • Las etiquetas se codifican en formato one-hot.
  2. Definición del modelo:
    • Se crea un modelo secuencial con las siguientes capas:
      • Capa Flatten para convertir la entrada 2D en 1D.
      • Dos capas Dense con activación ReLU (128 y 64 unidades).
      • Capa de salida Dense con activación softmax para 10 clases.
  3. Compilación del modelo:
    • El modelo se compila usando el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
  4. Callbacks:
    • ModelCheckpoint se utiliza para guardar el mejor modelo basado en la precisión de validación.
    • Se implementa EarlyStopping para detener el entrenamiento si la pérdida de validación no mejora en 5 épocas.
  5. Entrenamiento del modelo:
    • El modelo se entrena durante 30 épocas con un tamaño de lote de 32.
    • El 20% de los datos de entrenamiento se utiliza para validación.
  6. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  7. Visualización:
    • Se grafica el historial de entrenamiento (precisión y pérdida) tanto para el conjunto de entrenamiento como para el de validación.
    • Se muestran 10 imágenes de prueba aleatorias junto con sus etiquetas reales y las predicciones del modelo.

Este código proporciona un ejemplo completo del flujo de trabajo de machine learning para la clasificación de imágenes utilizando una arquitectura básica de red neuronal.

3.2.2 Construcción de modelos con la API funcional

La API funcional en Keras es una herramienta poderosa y flexible diseñada para construir arquitecturas de redes neuronales complejas. A diferencia de la API secuencial, que se limita a apilar capas de manera lineal, la API funcional permite la creación de estructuras de modelos más sofisticadas. A continuación, se explica más detalladamente sus capacidades:

  1. Conexiones no lineales entre capas: Con la API funcional, puedes definir modelos en los que las capas se conectan de manera no secuencial, lo que te permite crear bifurcaciones, conexiones de salto o incluso conexiones circulares entre capas, habilitando la construcción de topologías de red más complejas.
  2. Múltiples entradas y salidas: La API admite modelos con múltiples tensores de entrada y salida, lo cual es especialmente útil para tareas que requieren procesar diferentes tipos de datos simultáneamente o generar múltiples predicciones a partir de una sola entrada.
  3. Capas compartidas: Puedes reutilizar instancias de capas en diferentes partes de tu modelo. Esto es crucial para implementar arquitecturas como redes siamesas, donde se aplica un procesamiento idéntico a múltiples entradas.
  4. Conexiones residuales: La API funcional facilita la implementación de conexiones residuales, un componente clave de las redes profundas residuales (ResNets). Estas conexiones permiten que la información salte una o más capas, lo que ayuda a mitigar el problema del gradiente que desaparece en redes muy profundas.
  5. Composición de modelos: Puedes tratar modelos instanciados como capas y usarlos para construir modelos más grandes y complejos. Esta modularidad permite la creación de arquitecturas altamente sofisticadas combinando submodelos más simples.
  6. Capas personalizadas: La API funcional se integra perfectamente con capas definidas de forma personalizada, lo que te da la flexibilidad de incorporar operaciones especializadas en la arquitectura de tu modelo.
  7. Modelos tipo gráfico: Para tareas que requieren procesar datos estructurados en gráficos, como el análisis de redes sociales o la predicción de propiedades de moléculas, la API funcional te permite construir modelos que puedan manejar tales estructuras de datos complejas.

Estas características hacen que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Creación de un modelo con múltiples entradas y salidas

Exploremos una aplicación más avanzada de la API funcional creando un modelo con múltiples entradas y salidas. Este enfoque es particularmente útil para tareas complejas que requieren procesar diversos tipos de datos o generar múltiples predicciones de manera simultánea. Consideremos un escenario en el que estamos desarrollando una red de análisis de imágenes sofisticada. Esta red está diseñada para extraer dos piezas de información distintas de una sola imagen de entrada: la categoría del objeto representado y su color predominante.

Para lograr esto, diseñaremos un modelo con una base compartida que se ramifica en dos capas de salida separadas. La base compartida se encargará de extraer características generales de la imagen, mientras que las capas de salida especializadas se centrarán en predecir la categoría del objeto y su color, respectivamente. Esta arquitectura demuestra la flexibilidad de la API funcional, permitiéndonos crear modelos que puedan realizar múltiples tareas relacionadas de manera eficiente.

Por ejemplo, la predicción de categoría podría implicar clasificar el objeto en clases predefinidas (como coche, perro, silla), mientras que la predicción del color podría identificar el color primario (como rojo, azul, verde) del objeto. Al usar dos capas de salida separadas, podemos optimizar cada tarea de predicción de manera independiente, utilizando diferentes funciones de pérdida o métricas para cada salida.

Este enfoque multi-salida no solo muestra la versatilidad de la API funcional, sino que también ilustra cómo podemos diseñar modelos que imiten la percepción humana, donde múltiples atributos de un objeto se procesan e identifican simultáneamente. Tales modelos tienen aplicaciones prácticas en diversos campos, incluyendo la visión por computadora, la robótica y los sistemas de control de calidad automatizados en la manufactura.

Ejemplo: Construcción de un modelo con múltiples salidas utilizando la API funcional

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split

# Generate synthetic data
def generate_data(num_samples=1000):
    images = np.random.rand(num_samples, 64, 64, 3)
    categories = np.random.randint(0, 10, num_samples)
    colors = np.random.randint(0, 3, num_samples)
    return images, categories, colors

# Prepare data
X, y_category, y_color = generate_data(5000)
y_category = to_categorical(y_category, 10)
y_color = to_categorical(y_color, 3)

# Split data
X_train, X_test, y_category_train, y_category_test, y_color_train, y_color_test = train_test_split(
    X, y_category, y_color, test_size=0.2, random_state=42
)

# Define the input layer
input_layer = Input(shape=(64, 64, 3))  # Input shape is 64x64 RGB image

# Convolutional layers
x = Conv2D(32, (3, 3), activation='relu')(input_layer)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D((2, 2))(x)

# Flatten the output
x = Flatten()(x)

# Add shared dense layers
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)

# Define the first output for object category
category_output = Dense(10, activation='softmax', name='category_output')(x)

# Define the second output for object color
color_output = Dense(3, activation='softmax', name='color_output')(x)

# Create the model with multiple outputs
model = Model(inputs=input_layer, outputs=[category_output, color_output])

# Compile the model with different loss functions for each output
model.compile(optimizer='adam',
              loss={'category_output': 'categorical_crossentropy', 
                    'color_output': 'categorical_crossentropy'},
              loss_weights={'category_output': 1.0, 'color_output': 0.5},
              metrics=['accuracy'])

# Display the model summary
model.summary()

# Train the model
history = model.fit(
    X_train, 
    {'category_output': y_category_train, 'color_output': y_color_train},
    validation_data=(X_test, {'category_output': y_category_test, 'color_output': y_color_test}),
    epochs=10,
    batch_size=32
)

# Evaluate the model
test_loss, category_loss, color_loss, category_acc, color_acc = model.evaluate(
    X_test, 
    {'category_output': y_category_test, 'color_output': y_color_test}
)
print(f"Test category accuracy: {category_acc:.4f}")
print(f"Test color accuracy: {color_acc:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['category_output_accuracy'], label='Category Accuracy')
plt.plot(history.history['color_output_accuracy'], label='Color Accuracy')
plt.plot(history.history['val_category_output_accuracy'], label='Val Category Accuracy')
plt.plot(history.history['val_color_output_accuracy'], label='Val Color Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['category_output_loss'], label='Category Loss')
plt.plot(history.history['color_output_loss'], label='Color Loss')
plt.plot(history.history['val_category_output_loss'], label='Val Category Loss')
plt.plot(history.history['val_color_output_loss'], label='Val Color Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions
sample_image = X_test[0:1]
predictions = model.predict(sample_image)
predicted_category = np.argmax(predictions[0])
predicted_color = np.argmax(predictions[1])

print(f"Predicted category: {predicted_category}")
print(f"Predicted color: {predicted_color}")

# Display the sample image
plt.imshow(sample_image[0])
plt.title(f"Category: {predicted_category}, Color: {predicted_color}")
plt.axis('off')
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan las bibliotecas necesarias, incluyendo NumPy para operaciones numéricas, Matplotlib para visualización y varios módulos de Keras para construir y entrenar el modelo.
    • Se define una función generate_data() para crear datos sintéticos para nuestra tarea de clasificación con múltiples salidas.
    • Se generan 5000 muestras de imágenes RGB de 64x64 junto con etiquetas correspondientes de categoría (10 clases) y color (3 clases).
    • Las etiquetas se codifican en formato one-hot usando to_categorical().
    • Los datos se dividen en conjuntos de entrenamiento y prueba utilizando train_test_split().
  2. Arquitectura del modelo:
    • Se define una capa de entrada para imágenes RGB de 64x64.
    • Se añaden capas convolucionales (Conv2D) y capas de max pooling para extraer características de las imágenes.
    • La salida se aplana y pasa por dos capas densas (128 y 64 unidades) con activación ReLU.
    • Se definen dos capas de salida separadas:
      • Salida de categoría: 10 unidades con activación softmax para clasificar en 10 categorías.
      • Salida de color: 3 unidades con activación softmax para clasificar en 3 colores.
    • El modelo se crea usando la API funcional, especificando la entrada y múltiples salidas.
  3. Compilación del modelo:
    • El modelo se compila con el optimizador Adam.
    • Se usa la pérdida categorical crossentropy para ambas salidas.
    • Se especifican los pesos de la pérdida (1.0 para la categoría, 0.5 para el color) para equilibrar la importancia de cada tarea.
    • Se define la precisión como la métrica para ambas salidas.
  4. Entrenamiento del modelo:
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 32.
    • Se proporcionan los datos de entrenamiento y validación como diccionarios que asignan nombres de salida a sus datos respectivos.
  5. Evaluación del modelo:
    • El modelo se evalúa en el conjunto de prueba, imprimiendo la precisión para las predicciones de categoría y color.
  6. Visualización:
    • Se grafica el historial de entrenamiento, mostrando precisión y pérdida para ambas salidas a lo largo de las épocas.
    • Se utiliza una imagen de prueba del conjunto de prueba para hacer predicciones.
    • La imagen de prueba se muestra junto con su categoría y color predichos.

Este ejemplo demuestra un escenario realista de una tarea de clasificación con múltiples salidas, incluyendo la generación de datos, creación de modelo, entrenamiento, evaluación y visualización de los resultados. Muestra la flexibilidad de la API funcional de Keras para crear arquitecturas de modelos complejas con múltiples salidas y cómo manejar estos modelos a lo largo del flujo de trabajo de machine learning.

Capas compartidas y conexiones residuales

La API funcional en Keras ofrece la característica poderosa de capas compartidas, lo que permite reutilizar instancias de capas en diferentes partes de un modelo. Esta capacidad es particularmente valiosa para implementar arquitecturas avanzadas como las redes siamesas y las redes residuales. Las redes siamesas, comúnmente utilizadas en tareas de reconocimiento facial, usan procesamiento idéntico en múltiples entradas para comparar su similitud. Por otro lado, las redes residuales, ejemplificadas por arquitecturas como ResNet, utilizan conexiones de salto para permitir que la información evite una o más capas, lo que facilita el entrenamiento de redes muy profundas.

El concepto de capas compartidas se extiende más allá de estas arquitecturas específicas. Es una herramienta fundamental para crear modelos con compartición de pesos, lo que puede ser crucial en varios escenarios. Por ejemplo, en tareas de procesamiento de lenguaje natural como los sistemas de preguntas y respuestas, las capas compartidas pueden procesar tanto la pregunta como el contexto con el mismo conjunto de pesos, asegurando una extracción de características consistente. De manera similar, en el aprendizaje multimodal, donde se necesitan procesar entradas de diferentes fuentes (por ejemplo, imagen y texto), las capas compartidas pueden crear un espacio de representación común para estas entradas diversas.

Además, la flexibilidad de la API funcional permite la creación de topologías de modelos complejas que van más allá de las estructuras secuenciales simples. Esto incluye modelos con múltiples entradas o salidas, modelos con caminos ramificados e incluso modelos que incorporan bucles de retroalimentación. Tal versatilidad hace que la API funcional sea una herramienta indispensable para investigadores y profesionales que trabajan en proyectos avanzados de deep learning, permitiéndoles implementar arquitecturas de vanguardia y experimentar con nuevos diseños de modelos.

Ejemplo: Uso de capas compartidas en la API funcional

import numpy as np
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.utils import plot_model
from tensorflow.keras.datasets import mnist

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
y_train = np.eye(10)[y_train]
y_test = np.eye(10)[y_test]

# Define two inputs
input_a = Input(shape=(784,), name='input_a')
input_b = Input(shape=(784,), name='input_b')

# Define a shared dense layer
shared_dense = Dense(64, activation='relu', name='shared_dense')

# Apply the shared layer to both inputs
processed_a = shared_dense(input_a)
processed_b = shared_dense(input_b)

# Concatenate the processed inputs
concatenated = Concatenate(name='concatenate')([processed_a, processed_b])

# Add more layers
x = Dense(32, activation='relu', name='dense_1')(concatenated)
x = Dense(16, activation='relu', name='dense_2')(x)

# Add a final output layer
output = Dense(10, activation='softmax', name='output')(x)

# Create the model with shared layers
model = Model(inputs=[input_a, input_b], outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display the model summary
model.summary()

# Visualize the model architecture
plot_model(model, to_file='model_architecture.png', show_shapes=True, show_layer_names=True)

# Train the model
history = model.fit(
    [x_train, x_train],  # Use the same input twice for demonstration
    y_train,
    epochs=10,
    batch_size=128,
    validation_split=0.2,
    verbose=1
)

# Evaluate the model
test_loss, test_accuracy = model.evaluate([x_test, x_test], y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Make predictions
sample_input = x_test[:5]
predictions = model.predict([sample_input, sample_input])
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

Desglose completo del código:

  • Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se aplanan y normalizan, y las etiquetas se convierten a formato one-hot.
  • Arquitectura del modelo:
    • Se definen dos capas de entrada (input_a y input_b), ambas aceptan vectores de 784 dimensiones (imágenes aplanadas de 28x28).
    • Se crea una capa densa compartida con 64 unidades y activación ReLU.
    • La capa compartida se aplica a ambas entradas, demostrando la compartición de pesos.
    • Las entradas procesadas se concatenan usando la capa Concatenate.
    • Se añaden dos capas densas más (32 y 16 unidades) para un procesamiento adicional.
    • La capa de salida final tiene 10 unidades con activación softmax para la clasificación multiclase.
  • Creación y compilación del modelo:
    • El modelo se crea usando la API funcional, especificando múltiples entradas y una salida.
    • El modelo se compila con el optimizador Adam, la función de pérdida categorical crossentropy y la métrica de precisión.
  • Visualización del modelo:
    • Se llama a model.summary() para mostrar un resumen textual de la arquitectura del modelo.
    • Se usa plot_model() para generar una representación visual de la arquitectura del modelo.
  • Entrenamiento del modelo:
    • El modelo se entrena usando el método fit().
    • Para fines demostrativos, se usa la misma entrada (x_train) dos veces para simular dos entradas diferentes.
    • El entrenamiento se realiza durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  • Evaluación y predicción del modelo:
    • El modelo se evalúa en el conjunto de prueba para obtener la precisión.
    • Se hacen predicciones de muestra usando las primeras 5 imágenes de prueba.
    • Se imprimen las clases predichas para demostrar la salida del modelo.

Este ejemplo muestra un flujo de trabajo completo, incluyendo la preparación de datos, la creación de un modelo con capas compartidas, el entrenamiento, la evaluación y la realización de predicciones. Ilustra la flexibilidad de la API funcional para crear arquitecturas de modelos complejas con componentes compartidos y múltiples entradas.

Combinando las APIs Sequential y Funcional

La flexibilidad de Keras permite la integración sin problemas de las APIs Sequential y Funcional, lo que permite la creación de arquitecturas de modelos altamente personalizables y complejas. Esta poderosa combinación ofrece a los desarrolladores la capacidad de aprovechar la simplicidad de la API Sequential para pilas de capas sencillas mientras se utiliza la versatilidad de la API Funcional para diseños de modelos más intrincados.

Al combinar estas APIs, puedes crear modelos híbridos que se beneficien de ambos enfoques. Por ejemplo, podrías usar la API Sequential para definir rápidamente una serie de capas para la extracción de características, y luego emplear la API Funcional para introducir caminos ramificados, múltiples entradas o salidas, o capas compartidas. Este enfoque es especialmente útil cuando se trabaja con aprendizaje transferido, donde los modelos Sequential preentrenados pueden incorporarse en arquitecturas más complejas.

Además, esta combinación permite la integración fácil de capas personalizadas, conexiones de salto, e incluso la implementación de arquitecturas avanzadas como redes residuales o mecanismos de atención. La capacidad de mezclar y combinar estas APIs proporciona un alto grado de flexibilidad, lo que facilita experimentar con nuevos diseños de modelos y adaptarse a requisitos específicos del problema sin sacrificar la naturaleza intuitiva de la construcción de modelos en Keras.

Ejemplo: Combinando modelos Sequential y Funcional

import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, Dense, Flatten
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt

# Load and preprocess the MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)

# Build a Sequential model
sequential_model = Sequential([
    Flatten(input_shape=(28, 28)),
    Dense(128, activation='relu', name='sequential_dense')
])

# Define an input using the Functional API
input_layer = Input(shape=(28, 28))

# Pass the input through the Sequential model
x = sequential_model(input_layer)

# Add more layers using the Functional API
x = Dense(64, activation='relu', name='functional_dense_1')(x)
output = Dense(10, activation='softmax', name='output')(x)

# Create the final model
model = Model(inputs=input_layer, outputs=output)

# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# Display model summary
model.summary()

# Train the model
history = model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2, verbose=1)

# Evaluate the model
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print(f"Test accuracy: {test_accuracy:.4f}")

# Plot training history
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Model Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()

plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Model Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()

plt.tight_layout()
plt.show()

# Make predictions on a sample
sample = x_test[:5]
predictions = model.predict(sample)
predicted_classes = np.argmax(predictions, axis=1)
print("Predicted classes:", predicted_classes)

# Visualize sample predictions
plt.figure(figsize=(15, 3))
for i in range(5):
    plt.subplot(1, 5, i+1)
    plt.imshow(sample[i].reshape(28, 28), cmap='gray')
    plt.title(f"Predicted: {predicted_classes[i]}")
    plt.axis('off')
plt.tight_layout()
plt.show()

Desglose completo del código:

  1. Importaciones y preparación de datos:
    • Se importan los módulos necesarios de TensorFlow y Keras, así como NumPy y Matplotlib para la manipulación y visualización de datos.
    • Se carga y preprocesa el conjunto de datos MNIST. Las imágenes se normalizan y las etiquetas se convierten a formato one-hot.
  2. Arquitectura del modelo:
    • Se crea un modelo Sequential con una capa Flatten y una capa Dense.
    • Se define una capa de entrada usando la API funcional.
    • El modelo Sequential se aplica a la capa de entrada.
    • Se añaden capas Dense adicionales utilizando la API funcional.
    • El modelo final se crea especificando las capas de entrada y salida.
  3. Compilación y entrenamiento del modelo:
    • El modelo se compila con el optimizador Adam, la pérdida categorical crossentropy y la métrica de precisión.
    • Se muestra el resumen del modelo para ver la arquitectura.
    • El modelo se entrena durante 10 épocas con un tamaño de lote de 128 y una división de validación del 20%.
  4. Evaluación del modelo:
    • El modelo entrenado se evalúa en el conjunto de prueba para obtener la precisión final.
  5. Visualización del historial de entrenamiento:
    • Se trazan las curvas de precisión de entrenamiento y validación a lo largo de las épocas.
    • Se trazan las curvas de pérdida de entrenamiento y validación a lo largo de las épocas.
  6. Predicciones:
    • Se hacen predicciones sobre una muestra de 5 imágenes de prueba.
    • Se imprimen las clases predichas.
  7. Visualización de predicciones de muestra:
    • Se muestran las 5 imágenes de muestra junto con sus clases predichas.

Este ejemplo demuestra un flujo de trabajo completo que combina las APIs Sequential y Funcional en Keras. Incluye la preparación de datos, la creación del modelo, el entrenamiento, la evaluación y la visualización de resultados. El código muestra cómo aprovechar ambas APIs para crear una arquitectura de modelo flexible, entrenarla con datos reales y analizar su rendimiento.