Este documento presenta un tutorial práctico sobre los componentes fundamentales de Keras, mostrando dos ejemplos completos de aprendizaje automático: regresión lineal y clasificación binaria.
- Construcción de Modelos
Keras ofrece múltiples formas de crear redes neuronales. El método más directo es utilizar la clase keras.Sequential(), que representa un modelo donde las capas se apilan linealmente sin conexiones avanzadas entre capas no adyacentes.
Para añadir capas a un modelo secuencial, se emplea el método add(). Cada capa contiene:
- Una función de cálculo (método
call) que procesa tensores de entrada - Variables de estado (pesos) almacenadas en tensores de TensorFlow
Para interactuar con los pesos de una capa:
capa.get_weights(): Devuelve los pesos como una lista de matrices NumPycapa.set_weights(pesos): Establece los pesos desde una lista de matrcies NumPy
La primera capa de un modelo requiere especificar las dimensiones de entrada mediante input_shape (tupla de enteros sin incluir el tamaño del batch):
from tensorflow import keras
from tensorflow.keras.layers import Dense
# Modelo de regresión lineal simple
num_caracteristicas = 1
modelo = keras.Sequential([
Dense(units=1, input_shape=(num_caracteristicas,))
])
Alternativamente, se puede crear el modelo vacío y añadir capas dinámicamente:
modelo = keras.Sequential()
modelo.add(Dense(units=1, input_shape=(num_caracteristicas,)))
La capa Dense implementa la operación: output = activation(dot(input, kernel) + bias). Sus parámetros principales incluyen:
- units: Entero positivo, dimensión del espacio de salida
- activation: Función de activación a utilizar. Si es
None, se aplica una activación lineal identidad - use_bias: Booleano, si incluir el término de sesgo
- kernel_initializer: Inicializador para la matriz de pesos
- bias_initializer: Inicializador para el vector de sesgo
Para la compilación del modelo, se utiliza el método compile() que configura el proceso de entrenamiento:
modelo.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss='mean_squared_error',
metrics=[keras.metrics.RootMeanSquaredError()]
)
Los componentes esenciales son:
Optimizador: Transforma los gradientes en actualizaciones de parámetros. Opciones comunes incluyen:
Adam: Adam optimization algorithmRMSprop: Root Mean Square PropagationSGD: Stochastic Gradient Descent
Los optimizadores aceptan parámetros globales como clipnorm y clipvalue para limitar la magnitud de los gradientes y evitar problemas de explosión de gradientes.
Función de pérdida: Objetivo que el modelo intenta minimizar durante el entrenamiento. Para regresión se utiliza típicamente 'mean_squared_error' o 'mae'; para clasificación binaria 'binary_crossentropy' y para clasificación multiclase 'categorical_crossentropy'.
Métricas: Lista de funciones para evaluar el rendimiento del modelo. Ejemplos incluyen accuracy, precision, recall, AUC, etc.
Función completa para crear un modelo de regresión lineal:
def construir_modelo(tasa_aprendizaje, num_caracteristicas):
"""Crea y compila un modelo de regresión lineal simple."""
modelo = keras.Sequential()
# Una única neurona en una capa densa para regresión lineal
modelo.add(Dense(units=1, input_shape=(num_caracteristicas,)))
# Compilación del modelo para minimización del error cuadrático medio
modelo.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=tasa_aprendizaje),
loss='mean_squared_error',
metrics=[keras.metrics.RootMeanSquaredError()]
)
return modelo
- Entrenamiento del Modelo
El entrenamiento se realiza mediante el método fit(), que recibe los datos de entrada y las etiquetas:
history = modelo.fit(
x=caracteristicas_entrenamiento,
y=etiquetas_entrenamiento,
batch_size=32,
epochs=100,
validation_split=0.2,
verbose=1
)
Parámetros fundamentales:
- x: Datos de entrada como array NumPy, diccionario de tensores, o generador
- y: Etiquetas correspondientes a los datos de entrada
- batch_size: Número de muestras por actualización de gradiente
- epochs: Número de iteraciones completas sobre todo el conjunto de datos
- validation_split: Fracción de datos para validación (entre 0 y 1)
- verbose: Modo de visualización (0=silencioso, 1=barra de progreso, 2=una línea)
El objeto History retornado contiene:
history.history: Diccionario con la evolución de métricas durante el entrenamientohistory.epoch: Lista con los índices de cada época
Para obtener los parámetros aprendidos (pesos y sesgos):
pesos = modelo.get_weights()
# pesos[0]: matriz de pesos de la primera capa
# pesos[1]: vector de sesgos de la primera capa
- Predicción y Evaluación
Predicción sobre nuevos datos:
predicciones = modelo.predict(datos_nuevos)
# Retorna un array NumPy con las predicciones
Evaluación del modelo:
resultados = modelo.evaluate(
x=test_features,
y=test_labels,
batch_size=32,
verbose=1,
return_dict=True
)
Retorna la pérdida y las métricas calculadas sobre el conjunto de prueba.
- Ejemplo Completo: Clasificación Binaria
Configuración de reproducibilidad:
keras.utils.set_random_seed(42)
import numpy as np
import random
np.random.seed(42)
random.seed(42)
Preparación de datos:
# Conversión de etiquetas categóricas a valores numéricos
datos['etiqueta_binaria'] = (datos['categoria'] == 'positiva').astype(int)
# División del conjunto de datos
n_muestras = len(datos)
indice_80 = int(n_muestras * 0.8)
indice_90 = int(n_muestras * 0.9)
datos_mezclados = datos.sample(frac=1, random_state=42)
entrenamiento = datos_mezclados.iloc[:indice_80]
validacion = datos_mezclados.iloc[indice_80:indice_90]
prueba = datos_mezclados.iloc[indice_90:]
# Separación de características y etiquetas
caracteristicas_ent = entrenamiento.drop(columns=['categoria', 'etiqueta_binaria'])
etiquetas_ent = entrenamiento['etiqueta_binaria'].to_numpy()
caracteristicas_val = validacion.drop(columns=['categoria', 'etiqueta_binaria'])
etiquetas_val = validacion['etiqueta_binaria'].to_numpy()
caracteristicas_prueba = prueba.drop(columns=['categoria', 'etiqueta_binaria'])
etiquetas_prueba = prueba['etiqueta_binaria'].to_numpy()
Definición de configuración experimental:
from dataclasses import dataclass
import pandas as pd
@dataclass
class ConfiguracionExperimento:
tasa_aprendizaje: float
epocas: int
tamano_lote: int
umbral_clasificacion: float
caracteristicas: list[str]
@dataclass
class Experimento:
nombre: str
configuracion: ConfiguracionExperimento
modelo: keras.Model
epocas: list[int]
historial_metricas: pd.DataFrame
def obtener_metrica_final(self, nombre_metrica: str) -> float:
if nombre_metrica not in self.historial_metricas.columns:
raise ValueError(f'Métrica desconocida: {nombre_metrica}')
return self.historial_metricas[nombre_metrica].iloc[-1]
Construcción del modelo con entrada explícita:
def crear_modelo_clasificacion(config: ConfiguracionExperimento, metricas) -> keras.Model:
# Definir entradas explícitas para cada característica
entradas = [
keras.Input(name=feat, shape=(1,))
for feat in config.caracteristicas
]
# Concatenar las entradas en un único tensor
entrada_concatenada = keras.layers.Concatenate()(entradas)
# Capa densa con activación sigmoide para clasificación binaria
capa_densa = Dense(
units=1,
activation=keras.activations.sigmoid,
name='capa_salida'
)
salida = capa_densa(entrada_concatenada)
modelo = keras.Model(inputs=entradas, outputs=salida)
modelo.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=config.tasa_aprendizaje),
loss=keras.losses.BinaryCrossentropy(),
metrics=metricas
)
return modelo
Función de entrenamiento:
def entrenar_modelo(nombre: str, modelo: keras.Model, datos: pd.DataFrame,
etiquetas: np.ndarray, config: ConfiguracionExperimento) -> Experimento:
caracteristicas = {
nombre_feat: np.array(datos[nombre_feat])
for nombre_feat in config.caracteristicas
}
historial = modelo.fit(
x=caracteristicas,
y=etiquetas,
batch_size=config.tamano_lote,
epochs=config.epocas,
validation_data=(caracteristicas_val, etiquetas_val)
)
return Experimento(
nombre=nombre,
configuracion=config,
modelo=modelo,
epocas=historial.epoch,
historial_metricas=pd.DataFrame(historial.history)
)
Definición de métricas:
umbral = 0.5
metricas = [
keras.metrics.BinaryAccuracy(name='exactitud', threshold=umbral),
keras.metrics.Precision(name='precision', thresholds=umbral),
keras.metrics.Recall(name='recall', thresholds=umbral),
keras.metrics.AUC(name='auc', num_thresholds=100)
]
Ejecución del experimento:
caracteristicas_seleccionadas = ['caracteristica_a', 'caracteristica_b', 'caracteristica_c']
configuracion = ConfiguracionExperimento(
tasa_aprendizaje=0.001,
epocas=50,
tamano_lote=32,
umbral_clasificacion=0.5,
caracteristicas=caracteristicas_seleccionadas
)
modelo = crear_modelo_clasificacion(configuracion, metricas)
experimento = entrenar_modelo('experimento_base', modelo,
caracteristicas_ent, etiquetas_ent, configuracion)
Visualización de resultados:
import matplotlib.pyplot as plt
def graficar_metricas(experimento: Experimento, lista_metricas: list[str]):
plt.figure(figsize=(10, 6))
for metrica in lista_metricas:
plt.plot(experimento.epocas, experimento.historial_metricas[metrica],
label=metrica)
plt.xlabel('Época')
plt.ylabel('Valor de Métrica')
plt.grid(True)
plt.legend()
plt.show()
graficar_metricas(experimento, ['exactitud', 'precision', 'recall'])
graficar_metricas(experimento, ['auc'])
Evaluación en conjunto de prueba:
def evaluar_experimento(exp: Experimento, datos_prueba: pd.DataFrame,
etiquetas_prueba: np.ndarray) -> dict:
caracteristicas_prueba = {
nombre_feat: np.array(datos_prueba[nombre_feat])
for nombre_feat in exp.configuracion.caracteristicas
}
return exp.modelo.evaluate(
x=caracteristicas_prueba,
y=etiquetas_prueba,
batch_size=exp.configuracion.tamano_lote,
verbose=0,
return_dict=True
)
metricas_prueba = evaluar_experimento(experimento, caracteristicas_prueba, etiquetas_prueba)
print('Comparación entre entrenamiento y prueba:')
for nombre, valor in metricas_prueba.items():
print(f'{nombre} - Entrenamiento: {experimento.obtener_metrica_final(nombre):.4f}, Prueba: {valor:.4f}')
- Consideraciones Adicionales
- La clase base
keras.layers.Layeracepta el parámetronamepara identificar capas de forma única dentro del modelo - Para modelos con múltiples entradas, se puede utilizar
keras.layers.Concatenate()para fusionar tensores - Los modelos también pueden construirse mediante la API funcional:
modelo = keras.Model(inputs=entradas, outputs=salidas) - Es importante verificar que las formas (shapes) de las capas sean compatibles entre sí para evitar errores en tiempo de ejecución