TensorFlow: Redes neuronales
Domina la creación de redes neuronales con la API Keras de TensorFlow. Descubre cómo diseñar, implementar y entrenar modelos neuronales desde cero, entendiendo cada componente esencial y aplicando buenas prácticas para problemas reales de machine learning
Aprende TensorFlow GRATIS y certifícateTensorFlow ofrece varias formas de construir redes neuronales, pero Keras se ha convertido en la API estándar y recomendada para el desarrollo de estos modelos debido a su simplicidad y flexibilidad. Keras está integrado directamente en TensorFlow desde la versión 2.0, proporcionando una interfaz de alto nivel que facilita la creación, entrenamiento y evaluación de modelos de aprendizaje profundo.
Keras sigue una filosofía de diseño centrada en la experiencia del desarrollador, siendo intuitivo, modular y extensible. Esto permite construir redes neuronales complejas con pocas líneas de código, manteniendo al mismo tiempo la potencia y flexibilidad de TensorFlow como motor de cómputo subyacente.
La estructura fundamental de cualquier red neuronal en Keras se compone de capas que procesan tensores de entrada y producen tensores de salida. Estas capas encapsulan tanto los parámetros entrenables (pesos y sesgos) como las operaciones que transforman los datos.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# Verificamos la versión de Keras que estamos utilizando
print(f"Versión de TensorFlow: {tf.__version__}")
print(f"Versión de Keras: {keras.__version__}")
En Keras existen dos formas principales de construir modelos: la API Secuencial y la API Funcional. La primera es más sencilla y adecuada para redes con una sola entrada y una sola salida, donde las capas se conectan secuencialmente. La segunda ofrece mayor flexibilidad para crear arquitecturas complejas con múltiples entradas, salidas o conexiones no lineales.
Veamos primero cómo construir un modelo secuencial básico:
# Modelo secuencial para clasificación de imágenes
modelo = keras.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
])
Este ejemplo ilustra una arquitectura convolucional típica para clasificación de imágenes, donde las capas convolutivas extraen características, las capas de pooling reducen la dimensionalidad, y las capas densas realizan la clasificación final.
Para modelos con estructuras más complejas, la API Funcional proporciona mayor flexibilidad:
# Modelo funcional con múltiples entradas
entrada_texto = keras.Input(shape=(100,))
entrada_imagen = keras.Input(shape=(28, 28, 3))
# Procesar texto
x1 = layers.Embedding(20000, 128)(entrada_texto)
x1 = layers.LSTM(32)(x1)
# Procesar imagen
x2 = layers.Conv2D(32, (3, 3))(entrada_imagen)
x2 = layers.MaxPooling2D((2, 2))(x2)
x2 = layers.Flatten()(x2)
# Combinar ambas entradas
combinado = layers.concatenate([x1, x2])
salida = layers.Dense(1, activation='sigmoid')(combinado)
# Crear el modelo especificando entradas y salidas
modelo = keras.Model(inputs=[entrada_texto, entrada_imagen], outputs=salida)
Las capas son los bloques fundamentales de cualquier red neuronal en Keras. TensorFlow ofrece una amplia variedad de capas predefinidas:
- Capas básicas: Dense (completamente conectada), Dropout, Activation
- Capas convolucionales: Conv1D, Conv2D, Conv3D, SeparableConv2D
- Capas de pooling: MaxPooling2D, AveragePooling2D, GlobalMaxPooling2D
- Capas recurrentes: SimpleRNN, LSTM, GRU, Bidirectional
- Capas de normalización: BatchNormalization, LayerNormalization
- Capas de regularización: Dropout, ActivityRegularization, SpatialDropout
- Capas de atención: Attention, MultiHeadAttention
- Capas para procesamiento de secuencias: Embedding, TimeDistributed
- Capas para operaciones de fusión: Add, Concatenate, Multiply, Average
La compilación del modelo es un paso crucial antes de iniciar el entrenamiento. Aquí definimos el optimizador, la función de pérdida y las métricas que evaluarán el rendimiento:
modelo.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss=keras.losses.SparseCategoricalCrossentropy(),
metrics=['accuracy']
)
TensorFlow ofrece varios optimizadores como SGD, Adam, RMSprop o Adagrad, cada uno con sus propias características y casos de uso recomendados. Igualmente, podemos elegir entre diversas funciones de pérdida según el tipo de problema, como BinaryCrossentropy para clasificación binaria, MeanSquaredError para regresión o KLDivergence para distribuciones probabilísticas.
Una vez compilado el modelo, podemos entrenarlo con el método fit()
:
# Supongamos que x_train e y_train son nuestros datos de entrenamiento
history = modelo.fit(
x_train, y_train,
epochs=10,
batch_size=64,
validation_split=0.2,
callbacks=[
keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True),
keras.callbacks.ModelCheckpoint('mejor_modelo.keras', save_best_only=True),
keras.callbacks.TensorBoard(log_dir='./logs')
]
)
Los callbacks son funciones que se ejecutan en determinados momentos durante el entrenamiento y permiten automatizar tareas como detener el entrenamiento cuando no hay mejora (EarlyStopping), guardar el mejor modelo (ModelCheckpoint) o registrar métricas para su visualización posterior (TensorBoard).
El objeto history
devuelto por fit()
contiene información valiosa sobre el proceso de entrenamiento:
import matplotlib.pyplot as plt
# Visualizar la evolución del entrenamiento
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='Pérdida entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida validación')
plt.title('Evolución de la pérdida')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(history.history['accuracy'], label='Precisión entrenamiento')
plt.plot(history.history['val_accuracy'], label='Precisión validación')
plt.title('Evolución de la precisión')
plt.legend()
plt.tight_layout()
plt.show()
Una vez entrenado, podemos evaluar el rendimiento del modelo en un conjunto de datos de prueba:
resultados = modelo.evaluate(x_test, y_test, verbose=1)
print(f"Pérdida en test: {resultados[0]:.4f}")
print(f"Precisión en test: {resultados[1]:.4f}")
Y realizar predicciones con nuevos datos:
predicciones = modelo.predict(nuevos_datos)
Las arquitecturas de redes neuronales son muy diversas y se adaptan a diferentes tipos de problemas. Algunas arquitecturas fundamentales incluyen:
- Redes fully-connected (MLP): Para datos tabulares y problemas sencillos
- Redes convolucionales (CNN): Para procesar datos con estructura de rejilla como imágenes
- Redes recurrentes (RNN, LSTM, GRU): Para secuencias y datos temporales
- Redes con atención: Para capturar dependencias a larga distancia en secuencias
- Autoencoders: Para reducción de dimensionalidad y aprendizaje no supervisado
- Redes generativas adversarias (GAN): Para generar nuevos datos similares a los de entrenamiento
Keras facilita la implementación de estas arquitecturas mediante sus capas predefinidas. Por ejemplo, una red neuronal recurrente para procesamiento de texto:
modelo = keras.Sequential([
layers.Embedding(input_dim=10000, output_dim=128, input_length=100),
layers.Bidirectional(layers.LSTM(64, return_sequences=True)),
layers.Bidirectional(layers.LSTM(32)),
layers.Dense(64, activation='relu'),
layers.Dropout(0.5),
layers.Dense(1, activation='sigmoid')
])
Una característica poderosa de Keras es la capacidad de crear capas personalizadas para implementar funcionalidades específicas:
class CapaPersonalizada(layers.Layer):
def __init__(self, unidades=32, **kwargs):
super(CapaPersonalizada, self).__init__(**kwargs)
self.unidades = unidades
def build(self, input_shape):
self.w = self.add_weight(
shape=(input_shape[-1], self.unidades),
initializer='random_normal',
trainable=True,
name='pesos'
)
self.b = self.add_weight(
shape=(self.unidades,),
initializer='zeros',
trainable=True,
name='sesgo'
)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
def get_config(self):
config = super(CapaPersonalizada, self).get_config()
config.update({'unidades': self.unidades})
return config
Además de las capas personalizadas, podemos crear funciones de pérdida y métricas personalizadas para problemas específicos:
# Función de pérdida personalizada
def focal_loss(gamma=2.0, alpha=0.25):
def focal_loss_fn(y_true, y_pred):
pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
pt_1 = tf.clip_by_value(pt_1, 1e-9, 1.0)
pt_0 = tf.clip_by_value(pt_0, 1e-9, 1.0)
return -tf.reduce_sum(alpha * tf.pow(1. - pt_1, gamma) * tf.math.log(pt_1)) - \
tf.reduce_sum((1-alpha) * tf.pow(pt_0, gamma) * tf.math.log(1. - pt_0))
return focal_loss_fn
# Métrica personalizada
class F1Score(keras.metrics.Metric):
def __init__(self, name='f1_score', **kwargs):
super(F1Score, self).__init__(name=name, **kwargs)
self.precision = keras.metrics.Precision()
self.recall = keras.metrics.Recall()
def update_state(self, y_true, y_pred, sample_weight=None):
self.precision.update_state(y_true, y_pred, sample_weight)
self.recall.update_state(y_true, y_pred, sample_weight)
def result(self):
p = self.precision.result()
r = self.recall.result()
return (2 * p * r) / (p + r + keras.backend.epsilon())
def reset_state(self):
self.precision.reset_st
Lecciones de este módulo de TensorFlow
Lecciones de programación del módulo Redes neuronales del curso de TensorFlow.