Menu iconMenu icon
Superhéroe de Aprendizaje Profundo e IA

Capítulo 9: Proyectos Prácticos

9.4 Proyecto 4: Predicción de series temporales con LSTMs (Mejorado)

La predicción de series temporales juega un papel crucial en numerosos dominios, que incluyen pero no se limitan a análisis financieros, predicciones meteorológicas y estimaciones de demanda en la gestión de la cadena de suministro. Este proyecto profundiza en la aplicación de redes Long Short-Term Memory (LSTM), un tipo sofisticado de red neuronal recurrente, con el fin de predecir valores futuros dentro de una serie temporal. Nuestro enfoque específico se centra en el ámbito de la predicción de precios de acciones, una aplicación desafiante y de gran importancia económica en la predicción de series temporales.

Basándonos en nuestro proyecto original, buscamos implementar una serie de mejoras diseñadas para aumentar significativamente tanto el rendimiento como la robustez de nuestro modelo. Estas mejoras abarcan varios aspectos de la tubería de machine learning, desde la preprocesamiento de datos y la ingeniería de características hasta la arquitectura del modelo y las metodologías de entrenamiento. Al incorporar estos avances, buscamos crear un sistema de predicción más preciso, confiable e interpretable que pueda captar eficazmente los patrones y dependencias complejas inherentes a los movimientos de precios de acciones.

Mediante este enfoque mejorado, no solo pretendemos mejorar la precisión predictiva, sino también obtener una comprensión más profunda de los factores subyacentes que impulsan las fluctuaciones en los precios de las acciones. Este proyecto sirve como una exploración integral de técnicas de vanguardia en la predicción de series temporales, demostrando el potencial de los métodos avanzados de machine learning para abordar desafíos reales en la predicción financiera.

9.4.1 Recolección y preprocesamiento de datos

Para mejorar la robustez de nuestro conjunto de datos, implementaremos pasos exhaustivos de recolección y preprocesamiento de datos. Esta expansión implica recopilar una gama más amplia de datos históricos, incorporar características adicionales relevantes y aplicar técnicas avanzadas de preprocesamiento.

Al hacerlo, buscamos crear un conjunto de datos más completo e informativo que capture los patrones y relaciones matizados dentro de los movimientos de precios de las acciones. Este conjunto de datos mejorado servirá como una base sólida para nuestro modelo LSTM, lo que podría llevar a predicciones más precisas y confiables.

import pandas as pd
import numpy as np
import yfinance as yf
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Fetch more historical data and additional features
stock_data = yf.download('GOOGL', start='2000-01-01', end='2023-12-31')
stock_data['Returns'] = stock_data['Close'].pct_change()
stock_data['MA50'] = stock_data['Close'].rolling(window=50).mean()
stock_data['MA200'] = stock_data['Close'].rolling(window=200).mean()
stock_data['Volume_MA'] = stock_data['Volume'].rolling(window=20).mean()
stock_data.dropna(inplace=True)

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(stock_data[['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']])

# Create sequences
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:(i + seq_length), :])
        y.append(data[i + seq_length, 0])
    return np.array(X), np.array(y)

sequence_length = 60
X, y = create_sequences(scaled_data, sequence_length)

# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Desglose:

  • Recolección de datos: El código utiliza la librería yfinance para descargar datos históricos de las acciones de Google (GOOGL) desde el 1 de enero de 2000 hasta el 31 de diciembre de 2023.
  • Ingeniería de características: Se crean varias características nuevas:
    • Retornos: Cambio porcentual en el precio de cierre.
    • MA50: Media móvil de 50 días del precio de cierre.
    • MA200: Media móvil de 200 días del precio de cierre.
    • Volume_MA: Media móvil de 20 días del volumen de negociación.
  • Normalización de datos: Se utiliza el MinMaxScaler para escalar todas las características a un rango entre 0 y 1, lo cual es importante para el entrenamiento de redes neuronales.
  • Creación de secuencias: Se define una función create_sequences() para generar secuencias de entrada y los valores objetivo correspondientes. Utiliza un enfoque de ventana deslizante con una longitud de secuencia de 60 días.
  • División de datos: El conjunto de datos se divide en conjuntos de entrenamiento y prueba, reservando el 20% de los datos para pruebas.

Este pipeline de preprocesamiento crea un conjunto de datos robusto que captura varios aspectos de los movimientos de precios de acciones, proporcionando una base sólida para que el modelo LSTM aprenda.

9.4.2 Arquitectura LSTM mejorada

En este paso, diseñaremos una arquitectura LSTM avanzada y robusta, incorporando múltiples capas e implementando técnicas de dropout para una regularización efectiva. Este diseño mejorado tiene como objetivo captar las complejas dependencias temporales en los datos de series temporales, mientras mitiga los problemas de sobreajuste.

Al agregar estratégicamente profundidad a nuestra red e introducir capas de dropout, buscamos mejorar la capacidad del modelo para generalizar a partir de los datos de entrenamiento y hacer predicciones más precisas en patrones de precios de acciones no vistos. La arquitectura sofisticada que construiremos equilibrará la complejidad del modelo con la capacidad de generalización, lo que potencialmente llevará a un mejor rendimiento en la predicción de precios de acciones.

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam

def build_improved_lstm_model(input_shape):
    model = Sequential([
        LSTM(100, return_sequences=True, input_shape=input_shape),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100, return_sequences=True),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100),
        BatchNormalization(),
        Dropout(0.2),
        Dense(50, activation='relu'),
        Dense(1)
    ])
    model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error')
    return model

model = build_improved_lstm_model((X_train.shape[1], X_train.shape[2]))
model.summary()

Desglose:

  • Importaciones: Se importan los módulos necesarios de Keras para construir el modelo.
  • Arquitectura del modelo: La función build_improved_lstm_model crea un modelo Secuencial con las siguientes capas:
    • Tres capas LSTM con 100 unidades cada una, siendo las dos primeras capas de retorno de secuencias.
    • Capas de BatchNormalization después de cada capa LSTM para normalizar las activaciones.
    • Capas de Dropout (tasa del 20%) para regularización y prevenir el sobreajuste.
    • Una capa Dense con 50 unidades y activación ReLU.
    • Una capa Dense final con 1 unidad para la predicción de salida.
  • Compilación del modelo: El modelo se compila utilizando el optimizador Adam con una tasa de aprendizaje de 0.001 y el error cuadrático medio como función de pérdida.
  • Creación del modelo: Se crea una instancia del modelo utilizando la forma de entrada de los datos de entrenamiento.
  • Resumen del modelo: La llamada a model.summary() imprime la estructura del modelo, mostrando las capas y el número de parámetros.

Esta arquitectura tiene como objetivo captar dependencias temporales complejas en los datos de precios de acciones, utilizando técnicas como dropout y batch normalization para mejorar la generalización y la estabilidad durante el entrenamiento.

9.4.3 Entrenamiento con Early Stopping y programación de tasa de aprendizaje

Para mejorar el proceso de entrenamiento y optimizar el rendimiento del modelo, implementaremos dos técnicas clave: early stopping y la programación de la tasa de aprendizaje. El early stopping ayuda a prevenir el sobreajuste al detener el proceso de entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Esto asegura que capturemos el modelo en su mejor capacidad de generalización.

Por otro lado, la programación de la tasa de aprendizaje ajusta dinámicamente la tasa de aprendizaje durante el entrenamiento. Este enfoque adaptativo permite que el modelo haga actualizaciones más grandes en las primeras etapas del entrenamiento y ajustes más finos a medida que converge, lo que puede llevar a una convergencia más rápida y un mejor rendimiento general.

Al incorporar estas estrategias avanzadas de entrenamiento, buscamos lograr un proceso de entrenamiento más eficiente y un modelo que generalice bien en datos no vistos, mejorando así nuestras predicciones de precios de acciones.

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau

early_stopping = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10, min_lr=0.00001)

history = model.fit(
    X_train, y_train,
    epochs=200,
    batch_size=32,
    validation_split=0.2,
    callbacks=[early_stopping, lr_scheduler],
    verbose=1
)

Desglose del código:

  • Importación de callbacks: El código importa EarlyStopping y ReduceLROnPlateau de los callbacks de Keras.
  • Early Stopping: Esta técnica detiene el entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • patience=20: Esperará 20 épocas antes de detenerse si no se observa mejora.
    • restore_best_weights=True: Restaurará los pesos del modelo de la época con el mejor valor de la cantidad monitoreada.
  • Programador de la tasa de aprendizaje: Ajusta la tasa de aprendizaje durante el entrenamiento. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • factor=0.5: Reducirá la tasa de aprendizaje a la mitad cuando se active.
    • patience=10: Esperará 10 épocas antes de reducir la tasa de aprendizaje.
    • min_lr=0.00001: La tasa de aprendizaje mínima.
  • Entrenamiento del modelo: La función model.fit() entrena el modelo con estos parámetros:
    • epochs=200: Número máximo de épocas de entrenamiento.
    • batch_size=32: Número de muestras por actualización de gradiente.
    • validation_split=0.2: El 20% de los datos de entrenamiento se utilizarán para la validación.
    • callbacks=[early_stopping, lr_scheduler]: Se aplican el early stopping y el programador de tasa de aprendizaje durante el entrenamiento.
    • verbose=1: Esto mostrará barras de progreso durante el entrenamiento.

Estas técnicas tienen como objetivo mejorar el proceso de entrenamiento, prevenir el sobreajuste y, potencialmente, mejorar el rendimiento del modelo.

9.4.4 Evaluación y visualización del modelo

Para evaluar exhaustivamente el rendimiento del modelo y obtener una visión más profunda de sus predicciones, implementaremos una estrategia de evaluación integral. Este enfoque incluirá diversas métricas cuantitativas para medir precisión y error, así como representaciones visuales de las predicciones del modelo en comparación con los valores reales. Al combinar estos métodos, podremos comprender mejor las fortalezas y limitaciones de nuestro modelo LSTM en la predicción de precios de acciones.

Nuestra evaluación incluirá los siguientes componentes clave:

  • Cálculo de métricas estándar de regresión, como el error cuadrático medio (MSE), el error absoluto medio (MAE) y el coeficiente de determinación (R2).
  • Gráficos de series temporales que comparan los valores predichos con los precios reales de las acciones.
  • Análisis de residuos para identificar cualquier patrón en los errores de predicción.
  • Evaluación de ventana deslizante para analizar el rendimiento del modelo en diferentes periodos de tiempo.

Este enfoque de evaluación multifacético proporcionará una comprensión matizada de las capacidades predictivas de nuestro modelo y ayudará a identificar áreas de mejora en futuras iteraciones.

import matplotlib.pyplot as plt

# Make predictions
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# Inverse transform predictions
train_predictions = scaler.inverse_transform(np.concatenate((train_predictions, np.zeros((len(train_predictions), 5))), axis=1))[:, 0]
test_predictions = scaler.inverse_transform(np.concatenate((test_predictions, np.zeros((len(test_predictions), 5))), axis=1))[:, 0]
y_train_actual = scaler.inverse_transform(np.concatenate((y_train.reshape(-1, 1), np.zeros((len(y_train), 5))), axis=1))[:, 0]
y_test_actual = scaler.inverse_transform(np.concatenate((y_test.reshape(-1, 1), np.zeros((len(y_test), 5))), axis=1))[:, 0]

# Visualize predictions
plt.figure(figsize=(15, 6))
plt.plot(y_test_actual, label='Actual')
plt.plot(test_predictions, label='Predicted')
plt.title('LSTM Model: Actual vs Predicted Stock Prices')
plt.xlabel('Time')
plt.ylabel('Stock Price')
plt.legend()
plt.show()

# Evaluate model performance
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

mse = mean_squared_error(y_test_actual, test_predictions)
mae = mean_absolute_error(y_test_actual, test_predictions)
r2 = r2_score(y_test_actual, test_predictions)

print(f'Mean Squared Error: {mse}')
print(f'Mean Absolute Error: {mae}')
print(f'R-squared Score: {r2}')

Desglose de lo que hace el código:

  • Predicciones: El modelo realiza predicciones tanto en los conjuntos de datos de entrenamiento como de prueba.
  • Transformación inversa: Las predicciones y los valores reales se transforman inversamente para devolverlos a su escala original. Esto es necesario porque los datos fueron escalados durante el preprocesamiento.
  • Visualización: Se crea un gráfico para comparar los precios reales de las acciones con los predichos para el conjunto de prueba. Esta representación visual ayuda a entender qué tan bien se alinean las predicciones del modelo con los datos reales.
  • Métricas de rendimiento: El código calcula tres métricas clave de rendimiento:
    • Error cuadrático medio (MSE): Mide la diferencia cuadrática media entre los valores predichos y los reales.
    • Error absoluto medio (MAE): Mide la diferencia absoluta media entre los valores predichos y los reales.
    • Coeficiente de determinación (R2): Indica la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s).

Estas métricas proporcionan una evaluación cuantitativa del rendimiento del modelo, ayudando a evaluar su precisión y capacidad predictiva en la previsión de precios de acciones.

9.4.5 Análisis de importancia de características

Para obtener una visión más profunda del proceso de toma de decisiones de nuestro modelo, implementaremos un análisis exhaustivo de la importancia de las características. Este paso crucial nos ayudará a entender qué características contribuyen más significativamente a las predicciones, lo que nos permitirá:

  1. Identificar los factores más influyentes en los movimientos de precios de acciones.
  2. Posiblemente refinar nuestra selección de características para futuras iteraciones.
  3. Proporcionar información valiosa a las partes interesadas sobre los principales impulsores de los cambios en los precios de las acciones.

Utilizaremos la importancia por permutación, un método independiente del modelo que mide el aumento del error de predicción después de permutar cada característica. Este enfoque nos dará una visión clara del impacto de cada característica en el rendimiento de nuestro modelo LSTM.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.inspection import permutation_importance

def reshape_features(X):
    """Reshape 3D sequence data (samples, timesteps, features) into 2D for feature importance analysis."""
    return X.reshape((X.shape[0], -1))

# Reshape X_test for permutation importance analysis
X_test_reshaped = reshape_features(X_test)

# Define a wrapper function for Keras model predictions
def model_predict(X):
    X = X.reshape((-1, sequence_length, X.shape[1] // sequence_length))  # Reshape back to 3D
    return model.predict(X, verbose=0).flatten()

# Compute permutation importance
r = permutation_importance(model_predict, X_test_reshaped, y_test, n_repeats=10, random_state=42, scoring='neg_mean_squared_error')

# Adjust feature names for the reshaped input
feature_names_expanded = [f"{feature}_t{t}" for t in range(sequence_length) for feature in ['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']]
feature_importance = pd.DataFrame({'feature': feature_names_expanded, 'importance': r.importances_mean})

# Aggregate importance scores for each original feature
feature_importance = feature_importance.groupby(lambda x: feature_importance['feature'][x].split('_')[0]).mean()
feature_importance = feature_importance.sort_values('importance', ascending=False)

# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar(feature_importance.index, feature_importance['importance'])
plt.title('Feature Importance (Permutation Importance)')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Aquí está el desglose de lo que hace el código:

  1. Importa permutation_importance de scikit-learn.
    • Esta función ayuda a evaluar cuánto contribuye cada característica a las predicciones del modelo mediante la aleatorización de los valores de las características y la medición del impacto en la precisión.
  2. Define reshape_features() para aplanar la entrada secuencial 3D (muestras, pasos de tiempo, características) en un formato 2D (muestras, características × pasos de tiempo).
    • Esto es necesario porque permutation_importance espera un array 2D como entrada.
  3. Remodela X_test usando reshape_features(X_test).
    • Este paso asegura que los datos de prueba tengan el formato correcto para el análisis de importancia por permutación.
  4. Define model_predict() para adaptar el método predict() del modelo LSTM para que funcione con permutation_importance.
    • Como los LSTM esperan una entrada 3D (muestras, pasos de tiempo, características), esta función remodela los datos de vuelta a 3D antes de hacer predicciones.
  5. Calcula la importancia por permutación usando:
    • El modelo LSTM entrenado
    • Los datos de prueba remodelados
    • Las etiquetas de prueba (y_test)
    • n_repeats=10 para estabilidad, lo que significa que el cálculo de importancia se repite 10 veces.
  6. Genera nombres de características expandidos para reflejar múltiples pasos de tiempo en la entrada secuencial.
    • A cada nombre de característica se le añade su índice de paso de tiempo (por ejemplo, Close_t0Close_t1, ...).
    • Esto asegura que las características de diferentes pasos de tiempo estén diferenciadas en el análisis de importancia.
  7. Crea un DataFrame que:
    • Mapea los nombres de las características con sus puntuaciones de importancia.
    • Agrupa por nombres de características originales (por ejemplo, agregando Close_t0 a Close_t59 en Close).
    • Promedia las puntuaciones de importancia por característica y las ordena en orden descendente.
  8. Crea un gráfico de barras para visualizar las puntuaciones de importancia de las características.
    • Las características más importantes aparecen en la parte superior, ayudando a identificar qué factores tienen el mayor impacto en las predicciones de precios de acciones.

9.4.6 Método de conjunto (Ensemble)

Para mejorar la robustez y precisión de nuestras predicciones, implementaremos un conjunto de modelos LSTM. Este enfoque implica entrenar varios modelos LSTM de manera independiente y luego combinar sus predicciones. Al aprovechar la sabiduría colectiva de múltiples modelos, es posible lograr predicciones más estables y precisas.

El método de conjunto puede ayudar a mitigar los sesgos de los modelos individuales y reducir el impacto del sobreajuste, lo que conduce a un mejor rendimiento general en la predicción de precios de acciones. Esta técnica es particularmente valiosa en el contexto de las predicciones financieras, donde pequeñas mejoras en la precisión pueden tener importantes implicaciones en el mundo real.

def create_ensemble(n_models, input_shape):
    models = []
    for _ in range(n_models):
        model = build_improved_lstm_model(input_shape)
        models.append(model)
    return models

n_models = 3
ensemble = create_ensemble(n_models, (X_train.shape[1], X_train.shape[2]))

# Train each model in the ensemble
for i, model in enumerate(ensemble):
    print(f"Training model {i+1}/{n_models}")
    model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2, 
              callbacks=[early_stopping, lr_scheduler], verbose=0)

# Make ensemble predictions
ensemble_predictions = np.mean([model.predict(X_test) for model in ensemble], axis=0)

# Inverse transform ensemble predictions
ensemble_predictions = scaler.inverse_transform(np.concatenate((ensemble_predictions, np.zeros((len(ensemble_predictions), 5))), axis=1))[:, 0]

# Evaluate ensemble performance
ensemble_mse = mean_squared_error(y_test_actual, ensemble_predictions)
ensemble_mae = mean_absolute_error(y_test_actual, ensemble_predictions)
ensemble_r2 = r2_score(y_test_actual, ensemble_predictions)

print(f'Ensemble Mean Squared Error: {ensemble_mse}')
print(f'Ensemble Mean Absolute Error: {ensemble_mae}')
print(f'Ensemble R-squared Score: {ensemble_r2}')

Desglose del código:

  • Función para crear un conjunto (ensemble): La función create_ensemble() crea múltiples modelos LSTM, cada uno con la misma arquitectura pero con inicializaciones potencialmente diferentes.
  • Creación del conjunto: Se crea un conjunto de 3 modelos utilizando la forma de entrada de los datos de entrenamiento.
  • Entrenamiento del modelo: Cada modelo en el conjunto se entrena de forma independiente con los mismos datos de entrenamiento, utilizando early stopping y la programación de la tasa de aprendizaje para optimización.
  • Predicciones del conjunto: Las predicciones se realizan promediando las salidas de todos los modelos en el conjunto.
  • Transformación inversa: Las predicciones del conjunto se transforman inversamente para devolverlas a su escala original.
  • Evaluación del rendimiento: El rendimiento del conjunto se evalúa utilizando el Error Cuadrático Medio (MSE), el Error Absoluto Medio (MAE) y el coeficiente de determinación (R2).

Este enfoque de conjunto tiene como objetivo mejorar la precisión y robustez de las predicciones al aprovechar múltiples modelos, mitigando potencialmente los sesgos individuales de los modelos y reduciendo el sobreajuste.

9.4.7 Conclusión

Este proyecto mejorado demuestra varias mejoras respecto a la tarea original de predicción de series temporales basada en LSTM. Hemos implementado un pipeline de preprocesamiento de datos más sofisticado, que incluye características adicionales y un escalado adecuado. La arquitectura LSTM ha sido mejorada con múltiples capas, normalización por lotes y dropout para una mejor regularización.

También hemos incorporado técnicas avanzadas de entrenamiento, como el early stopping y la programación de la tasa de aprendizaje. El proceso de evaluación ahora incluye métricas y visualizaciones más completas, proporcionando una visión más profunda del rendimiento del modelo. Además, hemos introducido un análisis de la importancia de las características para comprender el impacto de diferentes entradas en las predicciones.

Finalmente, se ha implementado un método de conjunto (ensemble) para mejorar potencialmente la precisión y la robustez de las predicciones. Estas mejoras proporcionan un enfoque más robusto y perspicaz para la predicción de series temporales, especialmente en el contexto de la predicción de precios de acciones.

9.4 Proyecto 4: Predicción de series temporales con LSTMs (Mejorado)

La predicción de series temporales juega un papel crucial en numerosos dominios, que incluyen pero no se limitan a análisis financieros, predicciones meteorológicas y estimaciones de demanda en la gestión de la cadena de suministro. Este proyecto profundiza en la aplicación de redes Long Short-Term Memory (LSTM), un tipo sofisticado de red neuronal recurrente, con el fin de predecir valores futuros dentro de una serie temporal. Nuestro enfoque específico se centra en el ámbito de la predicción de precios de acciones, una aplicación desafiante y de gran importancia económica en la predicción de series temporales.

Basándonos en nuestro proyecto original, buscamos implementar una serie de mejoras diseñadas para aumentar significativamente tanto el rendimiento como la robustez de nuestro modelo. Estas mejoras abarcan varios aspectos de la tubería de machine learning, desde la preprocesamiento de datos y la ingeniería de características hasta la arquitectura del modelo y las metodologías de entrenamiento. Al incorporar estos avances, buscamos crear un sistema de predicción más preciso, confiable e interpretable que pueda captar eficazmente los patrones y dependencias complejas inherentes a los movimientos de precios de acciones.

Mediante este enfoque mejorado, no solo pretendemos mejorar la precisión predictiva, sino también obtener una comprensión más profunda de los factores subyacentes que impulsan las fluctuaciones en los precios de las acciones. Este proyecto sirve como una exploración integral de técnicas de vanguardia en la predicción de series temporales, demostrando el potencial de los métodos avanzados de machine learning para abordar desafíos reales en la predicción financiera.

9.4.1 Recolección y preprocesamiento de datos

Para mejorar la robustez de nuestro conjunto de datos, implementaremos pasos exhaustivos de recolección y preprocesamiento de datos. Esta expansión implica recopilar una gama más amplia de datos históricos, incorporar características adicionales relevantes y aplicar técnicas avanzadas de preprocesamiento.

Al hacerlo, buscamos crear un conjunto de datos más completo e informativo que capture los patrones y relaciones matizados dentro de los movimientos de precios de las acciones. Este conjunto de datos mejorado servirá como una base sólida para nuestro modelo LSTM, lo que podría llevar a predicciones más precisas y confiables.

import pandas as pd
import numpy as np
import yfinance as yf
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Fetch more historical data and additional features
stock_data = yf.download('GOOGL', start='2000-01-01', end='2023-12-31')
stock_data['Returns'] = stock_data['Close'].pct_change()
stock_data['MA50'] = stock_data['Close'].rolling(window=50).mean()
stock_data['MA200'] = stock_data['Close'].rolling(window=200).mean()
stock_data['Volume_MA'] = stock_data['Volume'].rolling(window=20).mean()
stock_data.dropna(inplace=True)

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(stock_data[['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']])

# Create sequences
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:(i + seq_length), :])
        y.append(data[i + seq_length, 0])
    return np.array(X), np.array(y)

sequence_length = 60
X, y = create_sequences(scaled_data, sequence_length)

# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Desglose:

  • Recolección de datos: El código utiliza la librería yfinance para descargar datos históricos de las acciones de Google (GOOGL) desde el 1 de enero de 2000 hasta el 31 de diciembre de 2023.
  • Ingeniería de características: Se crean varias características nuevas:
    • Retornos: Cambio porcentual en el precio de cierre.
    • MA50: Media móvil de 50 días del precio de cierre.
    • MA200: Media móvil de 200 días del precio de cierre.
    • Volume_MA: Media móvil de 20 días del volumen de negociación.
  • Normalización de datos: Se utiliza el MinMaxScaler para escalar todas las características a un rango entre 0 y 1, lo cual es importante para el entrenamiento de redes neuronales.
  • Creación de secuencias: Se define una función create_sequences() para generar secuencias de entrada y los valores objetivo correspondientes. Utiliza un enfoque de ventana deslizante con una longitud de secuencia de 60 días.
  • División de datos: El conjunto de datos se divide en conjuntos de entrenamiento y prueba, reservando el 20% de los datos para pruebas.

Este pipeline de preprocesamiento crea un conjunto de datos robusto que captura varios aspectos de los movimientos de precios de acciones, proporcionando una base sólida para que el modelo LSTM aprenda.

9.4.2 Arquitectura LSTM mejorada

En este paso, diseñaremos una arquitectura LSTM avanzada y robusta, incorporando múltiples capas e implementando técnicas de dropout para una regularización efectiva. Este diseño mejorado tiene como objetivo captar las complejas dependencias temporales en los datos de series temporales, mientras mitiga los problemas de sobreajuste.

Al agregar estratégicamente profundidad a nuestra red e introducir capas de dropout, buscamos mejorar la capacidad del modelo para generalizar a partir de los datos de entrenamiento y hacer predicciones más precisas en patrones de precios de acciones no vistos. La arquitectura sofisticada que construiremos equilibrará la complejidad del modelo con la capacidad de generalización, lo que potencialmente llevará a un mejor rendimiento en la predicción de precios de acciones.

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam

def build_improved_lstm_model(input_shape):
    model = Sequential([
        LSTM(100, return_sequences=True, input_shape=input_shape),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100, return_sequences=True),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100),
        BatchNormalization(),
        Dropout(0.2),
        Dense(50, activation='relu'),
        Dense(1)
    ])
    model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error')
    return model

model = build_improved_lstm_model((X_train.shape[1], X_train.shape[2]))
model.summary()

Desglose:

  • Importaciones: Se importan los módulos necesarios de Keras para construir el modelo.
  • Arquitectura del modelo: La función build_improved_lstm_model crea un modelo Secuencial con las siguientes capas:
    • Tres capas LSTM con 100 unidades cada una, siendo las dos primeras capas de retorno de secuencias.
    • Capas de BatchNormalization después de cada capa LSTM para normalizar las activaciones.
    • Capas de Dropout (tasa del 20%) para regularización y prevenir el sobreajuste.
    • Una capa Dense con 50 unidades y activación ReLU.
    • Una capa Dense final con 1 unidad para la predicción de salida.
  • Compilación del modelo: El modelo se compila utilizando el optimizador Adam con una tasa de aprendizaje de 0.001 y el error cuadrático medio como función de pérdida.
  • Creación del modelo: Se crea una instancia del modelo utilizando la forma de entrada de los datos de entrenamiento.
  • Resumen del modelo: La llamada a model.summary() imprime la estructura del modelo, mostrando las capas y el número de parámetros.

Esta arquitectura tiene como objetivo captar dependencias temporales complejas en los datos de precios de acciones, utilizando técnicas como dropout y batch normalization para mejorar la generalización y la estabilidad durante el entrenamiento.

9.4.3 Entrenamiento con Early Stopping y programación de tasa de aprendizaje

Para mejorar el proceso de entrenamiento y optimizar el rendimiento del modelo, implementaremos dos técnicas clave: early stopping y la programación de la tasa de aprendizaje. El early stopping ayuda a prevenir el sobreajuste al detener el proceso de entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Esto asegura que capturemos el modelo en su mejor capacidad de generalización.

Por otro lado, la programación de la tasa de aprendizaje ajusta dinámicamente la tasa de aprendizaje durante el entrenamiento. Este enfoque adaptativo permite que el modelo haga actualizaciones más grandes en las primeras etapas del entrenamiento y ajustes más finos a medida que converge, lo que puede llevar a una convergencia más rápida y un mejor rendimiento general.

Al incorporar estas estrategias avanzadas de entrenamiento, buscamos lograr un proceso de entrenamiento más eficiente y un modelo que generalice bien en datos no vistos, mejorando así nuestras predicciones de precios de acciones.

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau

early_stopping = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10, min_lr=0.00001)

history = model.fit(
    X_train, y_train,
    epochs=200,
    batch_size=32,
    validation_split=0.2,
    callbacks=[early_stopping, lr_scheduler],
    verbose=1
)

Desglose del código:

  • Importación de callbacks: El código importa EarlyStopping y ReduceLROnPlateau de los callbacks de Keras.
  • Early Stopping: Esta técnica detiene el entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • patience=20: Esperará 20 épocas antes de detenerse si no se observa mejora.
    • restore_best_weights=True: Restaurará los pesos del modelo de la época con el mejor valor de la cantidad monitoreada.
  • Programador de la tasa de aprendizaje: Ajusta la tasa de aprendizaje durante el entrenamiento. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • factor=0.5: Reducirá la tasa de aprendizaje a la mitad cuando se active.
    • patience=10: Esperará 10 épocas antes de reducir la tasa de aprendizaje.
    • min_lr=0.00001: La tasa de aprendizaje mínima.
  • Entrenamiento del modelo: La función model.fit() entrena el modelo con estos parámetros:
    • epochs=200: Número máximo de épocas de entrenamiento.
    • batch_size=32: Número de muestras por actualización de gradiente.
    • validation_split=0.2: El 20% de los datos de entrenamiento se utilizarán para la validación.
    • callbacks=[early_stopping, lr_scheduler]: Se aplican el early stopping y el programador de tasa de aprendizaje durante el entrenamiento.
    • verbose=1: Esto mostrará barras de progreso durante el entrenamiento.

Estas técnicas tienen como objetivo mejorar el proceso de entrenamiento, prevenir el sobreajuste y, potencialmente, mejorar el rendimiento del modelo.

9.4.4 Evaluación y visualización del modelo

Para evaluar exhaustivamente el rendimiento del modelo y obtener una visión más profunda de sus predicciones, implementaremos una estrategia de evaluación integral. Este enfoque incluirá diversas métricas cuantitativas para medir precisión y error, así como representaciones visuales de las predicciones del modelo en comparación con los valores reales. Al combinar estos métodos, podremos comprender mejor las fortalezas y limitaciones de nuestro modelo LSTM en la predicción de precios de acciones.

Nuestra evaluación incluirá los siguientes componentes clave:

  • Cálculo de métricas estándar de regresión, como el error cuadrático medio (MSE), el error absoluto medio (MAE) y el coeficiente de determinación (R2).
  • Gráficos de series temporales que comparan los valores predichos con los precios reales de las acciones.
  • Análisis de residuos para identificar cualquier patrón en los errores de predicción.
  • Evaluación de ventana deslizante para analizar el rendimiento del modelo en diferentes periodos de tiempo.

Este enfoque de evaluación multifacético proporcionará una comprensión matizada de las capacidades predictivas de nuestro modelo y ayudará a identificar áreas de mejora en futuras iteraciones.

import matplotlib.pyplot as plt

# Make predictions
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# Inverse transform predictions
train_predictions = scaler.inverse_transform(np.concatenate((train_predictions, np.zeros((len(train_predictions), 5))), axis=1))[:, 0]
test_predictions = scaler.inverse_transform(np.concatenate((test_predictions, np.zeros((len(test_predictions), 5))), axis=1))[:, 0]
y_train_actual = scaler.inverse_transform(np.concatenate((y_train.reshape(-1, 1), np.zeros((len(y_train), 5))), axis=1))[:, 0]
y_test_actual = scaler.inverse_transform(np.concatenate((y_test.reshape(-1, 1), np.zeros((len(y_test), 5))), axis=1))[:, 0]

# Visualize predictions
plt.figure(figsize=(15, 6))
plt.plot(y_test_actual, label='Actual')
plt.plot(test_predictions, label='Predicted')
plt.title('LSTM Model: Actual vs Predicted Stock Prices')
plt.xlabel('Time')
plt.ylabel('Stock Price')
plt.legend()
plt.show()

# Evaluate model performance
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

mse = mean_squared_error(y_test_actual, test_predictions)
mae = mean_absolute_error(y_test_actual, test_predictions)
r2 = r2_score(y_test_actual, test_predictions)

print(f'Mean Squared Error: {mse}')
print(f'Mean Absolute Error: {mae}')
print(f'R-squared Score: {r2}')

Desglose de lo que hace el código:

  • Predicciones: El modelo realiza predicciones tanto en los conjuntos de datos de entrenamiento como de prueba.
  • Transformación inversa: Las predicciones y los valores reales se transforman inversamente para devolverlos a su escala original. Esto es necesario porque los datos fueron escalados durante el preprocesamiento.
  • Visualización: Se crea un gráfico para comparar los precios reales de las acciones con los predichos para el conjunto de prueba. Esta representación visual ayuda a entender qué tan bien se alinean las predicciones del modelo con los datos reales.
  • Métricas de rendimiento: El código calcula tres métricas clave de rendimiento:
    • Error cuadrático medio (MSE): Mide la diferencia cuadrática media entre los valores predichos y los reales.
    • Error absoluto medio (MAE): Mide la diferencia absoluta media entre los valores predichos y los reales.
    • Coeficiente de determinación (R2): Indica la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s).

Estas métricas proporcionan una evaluación cuantitativa del rendimiento del modelo, ayudando a evaluar su precisión y capacidad predictiva en la previsión de precios de acciones.

9.4.5 Análisis de importancia de características

Para obtener una visión más profunda del proceso de toma de decisiones de nuestro modelo, implementaremos un análisis exhaustivo de la importancia de las características. Este paso crucial nos ayudará a entender qué características contribuyen más significativamente a las predicciones, lo que nos permitirá:

  1. Identificar los factores más influyentes en los movimientos de precios de acciones.
  2. Posiblemente refinar nuestra selección de características para futuras iteraciones.
  3. Proporcionar información valiosa a las partes interesadas sobre los principales impulsores de los cambios en los precios de las acciones.

Utilizaremos la importancia por permutación, un método independiente del modelo que mide el aumento del error de predicción después de permutar cada característica. Este enfoque nos dará una visión clara del impacto de cada característica en el rendimiento de nuestro modelo LSTM.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.inspection import permutation_importance

def reshape_features(X):
    """Reshape 3D sequence data (samples, timesteps, features) into 2D for feature importance analysis."""
    return X.reshape((X.shape[0], -1))

# Reshape X_test for permutation importance analysis
X_test_reshaped = reshape_features(X_test)

# Define a wrapper function for Keras model predictions
def model_predict(X):
    X = X.reshape((-1, sequence_length, X.shape[1] // sequence_length))  # Reshape back to 3D
    return model.predict(X, verbose=0).flatten()

# Compute permutation importance
r = permutation_importance(model_predict, X_test_reshaped, y_test, n_repeats=10, random_state=42, scoring='neg_mean_squared_error')

# Adjust feature names for the reshaped input
feature_names_expanded = [f"{feature}_t{t}" for t in range(sequence_length) for feature in ['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']]
feature_importance = pd.DataFrame({'feature': feature_names_expanded, 'importance': r.importances_mean})

# Aggregate importance scores for each original feature
feature_importance = feature_importance.groupby(lambda x: feature_importance['feature'][x].split('_')[0]).mean()
feature_importance = feature_importance.sort_values('importance', ascending=False)

# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar(feature_importance.index, feature_importance['importance'])
plt.title('Feature Importance (Permutation Importance)')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Aquí está el desglose de lo que hace el código:

  1. Importa permutation_importance de scikit-learn.
    • Esta función ayuda a evaluar cuánto contribuye cada característica a las predicciones del modelo mediante la aleatorización de los valores de las características y la medición del impacto en la precisión.
  2. Define reshape_features() para aplanar la entrada secuencial 3D (muestras, pasos de tiempo, características) en un formato 2D (muestras, características × pasos de tiempo).
    • Esto es necesario porque permutation_importance espera un array 2D como entrada.
  3. Remodela X_test usando reshape_features(X_test).
    • Este paso asegura que los datos de prueba tengan el formato correcto para el análisis de importancia por permutación.
  4. Define model_predict() para adaptar el método predict() del modelo LSTM para que funcione con permutation_importance.
    • Como los LSTM esperan una entrada 3D (muestras, pasos de tiempo, características), esta función remodela los datos de vuelta a 3D antes de hacer predicciones.
  5. Calcula la importancia por permutación usando:
    • El modelo LSTM entrenado
    • Los datos de prueba remodelados
    • Las etiquetas de prueba (y_test)
    • n_repeats=10 para estabilidad, lo que significa que el cálculo de importancia se repite 10 veces.
  6. Genera nombres de características expandidos para reflejar múltiples pasos de tiempo en la entrada secuencial.
    • A cada nombre de característica se le añade su índice de paso de tiempo (por ejemplo, Close_t0Close_t1, ...).
    • Esto asegura que las características de diferentes pasos de tiempo estén diferenciadas en el análisis de importancia.
  7. Crea un DataFrame que:
    • Mapea los nombres de las características con sus puntuaciones de importancia.
    • Agrupa por nombres de características originales (por ejemplo, agregando Close_t0 a Close_t59 en Close).
    • Promedia las puntuaciones de importancia por característica y las ordena en orden descendente.
  8. Crea un gráfico de barras para visualizar las puntuaciones de importancia de las características.
    • Las características más importantes aparecen en la parte superior, ayudando a identificar qué factores tienen el mayor impacto en las predicciones de precios de acciones.

9.4.6 Método de conjunto (Ensemble)

Para mejorar la robustez y precisión de nuestras predicciones, implementaremos un conjunto de modelos LSTM. Este enfoque implica entrenar varios modelos LSTM de manera independiente y luego combinar sus predicciones. Al aprovechar la sabiduría colectiva de múltiples modelos, es posible lograr predicciones más estables y precisas.

El método de conjunto puede ayudar a mitigar los sesgos de los modelos individuales y reducir el impacto del sobreajuste, lo que conduce a un mejor rendimiento general en la predicción de precios de acciones. Esta técnica es particularmente valiosa en el contexto de las predicciones financieras, donde pequeñas mejoras en la precisión pueden tener importantes implicaciones en el mundo real.

def create_ensemble(n_models, input_shape):
    models = []
    for _ in range(n_models):
        model = build_improved_lstm_model(input_shape)
        models.append(model)
    return models

n_models = 3
ensemble = create_ensemble(n_models, (X_train.shape[1], X_train.shape[2]))

# Train each model in the ensemble
for i, model in enumerate(ensemble):
    print(f"Training model {i+1}/{n_models}")
    model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2, 
              callbacks=[early_stopping, lr_scheduler], verbose=0)

# Make ensemble predictions
ensemble_predictions = np.mean([model.predict(X_test) for model in ensemble], axis=0)

# Inverse transform ensemble predictions
ensemble_predictions = scaler.inverse_transform(np.concatenate((ensemble_predictions, np.zeros((len(ensemble_predictions), 5))), axis=1))[:, 0]

# Evaluate ensemble performance
ensemble_mse = mean_squared_error(y_test_actual, ensemble_predictions)
ensemble_mae = mean_absolute_error(y_test_actual, ensemble_predictions)
ensemble_r2 = r2_score(y_test_actual, ensemble_predictions)

print(f'Ensemble Mean Squared Error: {ensemble_mse}')
print(f'Ensemble Mean Absolute Error: {ensemble_mae}')
print(f'Ensemble R-squared Score: {ensemble_r2}')

Desglose del código:

  • Función para crear un conjunto (ensemble): La función create_ensemble() crea múltiples modelos LSTM, cada uno con la misma arquitectura pero con inicializaciones potencialmente diferentes.
  • Creación del conjunto: Se crea un conjunto de 3 modelos utilizando la forma de entrada de los datos de entrenamiento.
  • Entrenamiento del modelo: Cada modelo en el conjunto se entrena de forma independiente con los mismos datos de entrenamiento, utilizando early stopping y la programación de la tasa de aprendizaje para optimización.
  • Predicciones del conjunto: Las predicciones se realizan promediando las salidas de todos los modelos en el conjunto.
  • Transformación inversa: Las predicciones del conjunto se transforman inversamente para devolverlas a su escala original.
  • Evaluación del rendimiento: El rendimiento del conjunto se evalúa utilizando el Error Cuadrático Medio (MSE), el Error Absoluto Medio (MAE) y el coeficiente de determinación (R2).

Este enfoque de conjunto tiene como objetivo mejorar la precisión y robustez de las predicciones al aprovechar múltiples modelos, mitigando potencialmente los sesgos individuales de los modelos y reduciendo el sobreajuste.

9.4.7 Conclusión

Este proyecto mejorado demuestra varias mejoras respecto a la tarea original de predicción de series temporales basada en LSTM. Hemos implementado un pipeline de preprocesamiento de datos más sofisticado, que incluye características adicionales y un escalado adecuado. La arquitectura LSTM ha sido mejorada con múltiples capas, normalización por lotes y dropout para una mejor regularización.

También hemos incorporado técnicas avanzadas de entrenamiento, como el early stopping y la programación de la tasa de aprendizaje. El proceso de evaluación ahora incluye métricas y visualizaciones más completas, proporcionando una visión más profunda del rendimiento del modelo. Además, hemos introducido un análisis de la importancia de las características para comprender el impacto de diferentes entradas en las predicciones.

Finalmente, se ha implementado un método de conjunto (ensemble) para mejorar potencialmente la precisión y la robustez de las predicciones. Estas mejoras proporcionan un enfoque más robusto y perspicaz para la predicción de series temporales, especialmente en el contexto de la predicción de precios de acciones.

9.4 Proyecto 4: Predicción de series temporales con LSTMs (Mejorado)

La predicción de series temporales juega un papel crucial en numerosos dominios, que incluyen pero no se limitan a análisis financieros, predicciones meteorológicas y estimaciones de demanda en la gestión de la cadena de suministro. Este proyecto profundiza en la aplicación de redes Long Short-Term Memory (LSTM), un tipo sofisticado de red neuronal recurrente, con el fin de predecir valores futuros dentro de una serie temporal. Nuestro enfoque específico se centra en el ámbito de la predicción de precios de acciones, una aplicación desafiante y de gran importancia económica en la predicción de series temporales.

Basándonos en nuestro proyecto original, buscamos implementar una serie de mejoras diseñadas para aumentar significativamente tanto el rendimiento como la robustez de nuestro modelo. Estas mejoras abarcan varios aspectos de la tubería de machine learning, desde la preprocesamiento de datos y la ingeniería de características hasta la arquitectura del modelo y las metodologías de entrenamiento. Al incorporar estos avances, buscamos crear un sistema de predicción más preciso, confiable e interpretable que pueda captar eficazmente los patrones y dependencias complejas inherentes a los movimientos de precios de acciones.

Mediante este enfoque mejorado, no solo pretendemos mejorar la precisión predictiva, sino también obtener una comprensión más profunda de los factores subyacentes que impulsan las fluctuaciones en los precios de las acciones. Este proyecto sirve como una exploración integral de técnicas de vanguardia en la predicción de series temporales, demostrando el potencial de los métodos avanzados de machine learning para abordar desafíos reales en la predicción financiera.

9.4.1 Recolección y preprocesamiento de datos

Para mejorar la robustez de nuestro conjunto de datos, implementaremos pasos exhaustivos de recolección y preprocesamiento de datos. Esta expansión implica recopilar una gama más amplia de datos históricos, incorporar características adicionales relevantes y aplicar técnicas avanzadas de preprocesamiento.

Al hacerlo, buscamos crear un conjunto de datos más completo e informativo que capture los patrones y relaciones matizados dentro de los movimientos de precios de las acciones. Este conjunto de datos mejorado servirá como una base sólida para nuestro modelo LSTM, lo que podría llevar a predicciones más precisas y confiables.

import pandas as pd
import numpy as np
import yfinance as yf
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Fetch more historical data and additional features
stock_data = yf.download('GOOGL', start='2000-01-01', end='2023-12-31')
stock_data['Returns'] = stock_data['Close'].pct_change()
stock_data['MA50'] = stock_data['Close'].rolling(window=50).mean()
stock_data['MA200'] = stock_data['Close'].rolling(window=200).mean()
stock_data['Volume_MA'] = stock_data['Volume'].rolling(window=20).mean()
stock_data.dropna(inplace=True)

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(stock_data[['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']])

# Create sequences
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:(i + seq_length), :])
        y.append(data[i + seq_length, 0])
    return np.array(X), np.array(y)

sequence_length = 60
X, y = create_sequences(scaled_data, sequence_length)

# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Desglose:

  • Recolección de datos: El código utiliza la librería yfinance para descargar datos históricos de las acciones de Google (GOOGL) desde el 1 de enero de 2000 hasta el 31 de diciembre de 2023.
  • Ingeniería de características: Se crean varias características nuevas:
    • Retornos: Cambio porcentual en el precio de cierre.
    • MA50: Media móvil de 50 días del precio de cierre.
    • MA200: Media móvil de 200 días del precio de cierre.
    • Volume_MA: Media móvil de 20 días del volumen de negociación.
  • Normalización de datos: Se utiliza el MinMaxScaler para escalar todas las características a un rango entre 0 y 1, lo cual es importante para el entrenamiento de redes neuronales.
  • Creación de secuencias: Se define una función create_sequences() para generar secuencias de entrada y los valores objetivo correspondientes. Utiliza un enfoque de ventana deslizante con una longitud de secuencia de 60 días.
  • División de datos: El conjunto de datos se divide en conjuntos de entrenamiento y prueba, reservando el 20% de los datos para pruebas.

Este pipeline de preprocesamiento crea un conjunto de datos robusto que captura varios aspectos de los movimientos de precios de acciones, proporcionando una base sólida para que el modelo LSTM aprenda.

9.4.2 Arquitectura LSTM mejorada

En este paso, diseñaremos una arquitectura LSTM avanzada y robusta, incorporando múltiples capas e implementando técnicas de dropout para una regularización efectiva. Este diseño mejorado tiene como objetivo captar las complejas dependencias temporales en los datos de series temporales, mientras mitiga los problemas de sobreajuste.

Al agregar estratégicamente profundidad a nuestra red e introducir capas de dropout, buscamos mejorar la capacidad del modelo para generalizar a partir de los datos de entrenamiento y hacer predicciones más precisas en patrones de precios de acciones no vistos. La arquitectura sofisticada que construiremos equilibrará la complejidad del modelo con la capacidad de generalización, lo que potencialmente llevará a un mejor rendimiento en la predicción de precios de acciones.

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam

def build_improved_lstm_model(input_shape):
    model = Sequential([
        LSTM(100, return_sequences=True, input_shape=input_shape),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100, return_sequences=True),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100),
        BatchNormalization(),
        Dropout(0.2),
        Dense(50, activation='relu'),
        Dense(1)
    ])
    model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error')
    return model

model = build_improved_lstm_model((X_train.shape[1], X_train.shape[2]))
model.summary()

Desglose:

  • Importaciones: Se importan los módulos necesarios de Keras para construir el modelo.
  • Arquitectura del modelo: La función build_improved_lstm_model crea un modelo Secuencial con las siguientes capas:
    • Tres capas LSTM con 100 unidades cada una, siendo las dos primeras capas de retorno de secuencias.
    • Capas de BatchNormalization después de cada capa LSTM para normalizar las activaciones.
    • Capas de Dropout (tasa del 20%) para regularización y prevenir el sobreajuste.
    • Una capa Dense con 50 unidades y activación ReLU.
    • Una capa Dense final con 1 unidad para la predicción de salida.
  • Compilación del modelo: El modelo se compila utilizando el optimizador Adam con una tasa de aprendizaje de 0.001 y el error cuadrático medio como función de pérdida.
  • Creación del modelo: Se crea una instancia del modelo utilizando la forma de entrada de los datos de entrenamiento.
  • Resumen del modelo: La llamada a model.summary() imprime la estructura del modelo, mostrando las capas y el número de parámetros.

Esta arquitectura tiene como objetivo captar dependencias temporales complejas en los datos de precios de acciones, utilizando técnicas como dropout y batch normalization para mejorar la generalización y la estabilidad durante el entrenamiento.

9.4.3 Entrenamiento con Early Stopping y programación de tasa de aprendizaje

Para mejorar el proceso de entrenamiento y optimizar el rendimiento del modelo, implementaremos dos técnicas clave: early stopping y la programación de la tasa de aprendizaje. El early stopping ayuda a prevenir el sobreajuste al detener el proceso de entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Esto asegura que capturemos el modelo en su mejor capacidad de generalización.

Por otro lado, la programación de la tasa de aprendizaje ajusta dinámicamente la tasa de aprendizaje durante el entrenamiento. Este enfoque adaptativo permite que el modelo haga actualizaciones más grandes en las primeras etapas del entrenamiento y ajustes más finos a medida que converge, lo que puede llevar a una convergencia más rápida y un mejor rendimiento general.

Al incorporar estas estrategias avanzadas de entrenamiento, buscamos lograr un proceso de entrenamiento más eficiente y un modelo que generalice bien en datos no vistos, mejorando así nuestras predicciones de precios de acciones.

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau

early_stopping = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10, min_lr=0.00001)

history = model.fit(
    X_train, y_train,
    epochs=200,
    batch_size=32,
    validation_split=0.2,
    callbacks=[early_stopping, lr_scheduler],
    verbose=1
)

Desglose del código:

  • Importación de callbacks: El código importa EarlyStopping y ReduceLROnPlateau de los callbacks de Keras.
  • Early Stopping: Esta técnica detiene el entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • patience=20: Esperará 20 épocas antes de detenerse si no se observa mejora.
    • restore_best_weights=True: Restaurará los pesos del modelo de la época con el mejor valor de la cantidad monitoreada.
  • Programador de la tasa de aprendizaje: Ajusta la tasa de aprendizaje durante el entrenamiento. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • factor=0.5: Reducirá la tasa de aprendizaje a la mitad cuando se active.
    • patience=10: Esperará 10 épocas antes de reducir la tasa de aprendizaje.
    • min_lr=0.00001: La tasa de aprendizaje mínima.
  • Entrenamiento del modelo: La función model.fit() entrena el modelo con estos parámetros:
    • epochs=200: Número máximo de épocas de entrenamiento.
    • batch_size=32: Número de muestras por actualización de gradiente.
    • validation_split=0.2: El 20% de los datos de entrenamiento se utilizarán para la validación.
    • callbacks=[early_stopping, lr_scheduler]: Se aplican el early stopping y el programador de tasa de aprendizaje durante el entrenamiento.
    • verbose=1: Esto mostrará barras de progreso durante el entrenamiento.

Estas técnicas tienen como objetivo mejorar el proceso de entrenamiento, prevenir el sobreajuste y, potencialmente, mejorar el rendimiento del modelo.

9.4.4 Evaluación y visualización del modelo

Para evaluar exhaustivamente el rendimiento del modelo y obtener una visión más profunda de sus predicciones, implementaremos una estrategia de evaluación integral. Este enfoque incluirá diversas métricas cuantitativas para medir precisión y error, así como representaciones visuales de las predicciones del modelo en comparación con los valores reales. Al combinar estos métodos, podremos comprender mejor las fortalezas y limitaciones de nuestro modelo LSTM en la predicción de precios de acciones.

Nuestra evaluación incluirá los siguientes componentes clave:

  • Cálculo de métricas estándar de regresión, como el error cuadrático medio (MSE), el error absoluto medio (MAE) y el coeficiente de determinación (R2).
  • Gráficos de series temporales que comparan los valores predichos con los precios reales de las acciones.
  • Análisis de residuos para identificar cualquier patrón en los errores de predicción.
  • Evaluación de ventana deslizante para analizar el rendimiento del modelo en diferentes periodos de tiempo.

Este enfoque de evaluación multifacético proporcionará una comprensión matizada de las capacidades predictivas de nuestro modelo y ayudará a identificar áreas de mejora en futuras iteraciones.

import matplotlib.pyplot as plt

# Make predictions
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# Inverse transform predictions
train_predictions = scaler.inverse_transform(np.concatenate((train_predictions, np.zeros((len(train_predictions), 5))), axis=1))[:, 0]
test_predictions = scaler.inverse_transform(np.concatenate((test_predictions, np.zeros((len(test_predictions), 5))), axis=1))[:, 0]
y_train_actual = scaler.inverse_transform(np.concatenate((y_train.reshape(-1, 1), np.zeros((len(y_train), 5))), axis=1))[:, 0]
y_test_actual = scaler.inverse_transform(np.concatenate((y_test.reshape(-1, 1), np.zeros((len(y_test), 5))), axis=1))[:, 0]

# Visualize predictions
plt.figure(figsize=(15, 6))
plt.plot(y_test_actual, label='Actual')
plt.plot(test_predictions, label='Predicted')
plt.title('LSTM Model: Actual vs Predicted Stock Prices')
plt.xlabel('Time')
plt.ylabel('Stock Price')
plt.legend()
plt.show()

# Evaluate model performance
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

mse = mean_squared_error(y_test_actual, test_predictions)
mae = mean_absolute_error(y_test_actual, test_predictions)
r2 = r2_score(y_test_actual, test_predictions)

print(f'Mean Squared Error: {mse}')
print(f'Mean Absolute Error: {mae}')
print(f'R-squared Score: {r2}')

Desglose de lo que hace el código:

  • Predicciones: El modelo realiza predicciones tanto en los conjuntos de datos de entrenamiento como de prueba.
  • Transformación inversa: Las predicciones y los valores reales se transforman inversamente para devolverlos a su escala original. Esto es necesario porque los datos fueron escalados durante el preprocesamiento.
  • Visualización: Se crea un gráfico para comparar los precios reales de las acciones con los predichos para el conjunto de prueba. Esta representación visual ayuda a entender qué tan bien se alinean las predicciones del modelo con los datos reales.
  • Métricas de rendimiento: El código calcula tres métricas clave de rendimiento:
    • Error cuadrático medio (MSE): Mide la diferencia cuadrática media entre los valores predichos y los reales.
    • Error absoluto medio (MAE): Mide la diferencia absoluta media entre los valores predichos y los reales.
    • Coeficiente de determinación (R2): Indica la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s).

Estas métricas proporcionan una evaluación cuantitativa del rendimiento del modelo, ayudando a evaluar su precisión y capacidad predictiva en la previsión de precios de acciones.

9.4.5 Análisis de importancia de características

Para obtener una visión más profunda del proceso de toma de decisiones de nuestro modelo, implementaremos un análisis exhaustivo de la importancia de las características. Este paso crucial nos ayudará a entender qué características contribuyen más significativamente a las predicciones, lo que nos permitirá:

  1. Identificar los factores más influyentes en los movimientos de precios de acciones.
  2. Posiblemente refinar nuestra selección de características para futuras iteraciones.
  3. Proporcionar información valiosa a las partes interesadas sobre los principales impulsores de los cambios en los precios de las acciones.

Utilizaremos la importancia por permutación, un método independiente del modelo que mide el aumento del error de predicción después de permutar cada característica. Este enfoque nos dará una visión clara del impacto de cada característica en el rendimiento de nuestro modelo LSTM.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.inspection import permutation_importance

def reshape_features(X):
    """Reshape 3D sequence data (samples, timesteps, features) into 2D for feature importance analysis."""
    return X.reshape((X.shape[0], -1))

# Reshape X_test for permutation importance analysis
X_test_reshaped = reshape_features(X_test)

# Define a wrapper function for Keras model predictions
def model_predict(X):
    X = X.reshape((-1, sequence_length, X.shape[1] // sequence_length))  # Reshape back to 3D
    return model.predict(X, verbose=0).flatten()

# Compute permutation importance
r = permutation_importance(model_predict, X_test_reshaped, y_test, n_repeats=10, random_state=42, scoring='neg_mean_squared_error')

# Adjust feature names for the reshaped input
feature_names_expanded = [f"{feature}_t{t}" for t in range(sequence_length) for feature in ['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']]
feature_importance = pd.DataFrame({'feature': feature_names_expanded, 'importance': r.importances_mean})

# Aggregate importance scores for each original feature
feature_importance = feature_importance.groupby(lambda x: feature_importance['feature'][x].split('_')[0]).mean()
feature_importance = feature_importance.sort_values('importance', ascending=False)

# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar(feature_importance.index, feature_importance['importance'])
plt.title('Feature Importance (Permutation Importance)')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Aquí está el desglose de lo que hace el código:

  1. Importa permutation_importance de scikit-learn.
    • Esta función ayuda a evaluar cuánto contribuye cada característica a las predicciones del modelo mediante la aleatorización de los valores de las características y la medición del impacto en la precisión.
  2. Define reshape_features() para aplanar la entrada secuencial 3D (muestras, pasos de tiempo, características) en un formato 2D (muestras, características × pasos de tiempo).
    • Esto es necesario porque permutation_importance espera un array 2D como entrada.
  3. Remodela X_test usando reshape_features(X_test).
    • Este paso asegura que los datos de prueba tengan el formato correcto para el análisis de importancia por permutación.
  4. Define model_predict() para adaptar el método predict() del modelo LSTM para que funcione con permutation_importance.
    • Como los LSTM esperan una entrada 3D (muestras, pasos de tiempo, características), esta función remodela los datos de vuelta a 3D antes de hacer predicciones.
  5. Calcula la importancia por permutación usando:
    • El modelo LSTM entrenado
    • Los datos de prueba remodelados
    • Las etiquetas de prueba (y_test)
    • n_repeats=10 para estabilidad, lo que significa que el cálculo de importancia se repite 10 veces.
  6. Genera nombres de características expandidos para reflejar múltiples pasos de tiempo en la entrada secuencial.
    • A cada nombre de característica se le añade su índice de paso de tiempo (por ejemplo, Close_t0Close_t1, ...).
    • Esto asegura que las características de diferentes pasos de tiempo estén diferenciadas en el análisis de importancia.
  7. Crea un DataFrame que:
    • Mapea los nombres de las características con sus puntuaciones de importancia.
    • Agrupa por nombres de características originales (por ejemplo, agregando Close_t0 a Close_t59 en Close).
    • Promedia las puntuaciones de importancia por característica y las ordena en orden descendente.
  8. Crea un gráfico de barras para visualizar las puntuaciones de importancia de las características.
    • Las características más importantes aparecen en la parte superior, ayudando a identificar qué factores tienen el mayor impacto en las predicciones de precios de acciones.

9.4.6 Método de conjunto (Ensemble)

Para mejorar la robustez y precisión de nuestras predicciones, implementaremos un conjunto de modelos LSTM. Este enfoque implica entrenar varios modelos LSTM de manera independiente y luego combinar sus predicciones. Al aprovechar la sabiduría colectiva de múltiples modelos, es posible lograr predicciones más estables y precisas.

El método de conjunto puede ayudar a mitigar los sesgos de los modelos individuales y reducir el impacto del sobreajuste, lo que conduce a un mejor rendimiento general en la predicción de precios de acciones. Esta técnica es particularmente valiosa en el contexto de las predicciones financieras, donde pequeñas mejoras en la precisión pueden tener importantes implicaciones en el mundo real.

def create_ensemble(n_models, input_shape):
    models = []
    for _ in range(n_models):
        model = build_improved_lstm_model(input_shape)
        models.append(model)
    return models

n_models = 3
ensemble = create_ensemble(n_models, (X_train.shape[1], X_train.shape[2]))

# Train each model in the ensemble
for i, model in enumerate(ensemble):
    print(f"Training model {i+1}/{n_models}")
    model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2, 
              callbacks=[early_stopping, lr_scheduler], verbose=0)

# Make ensemble predictions
ensemble_predictions = np.mean([model.predict(X_test) for model in ensemble], axis=0)

# Inverse transform ensemble predictions
ensemble_predictions = scaler.inverse_transform(np.concatenate((ensemble_predictions, np.zeros((len(ensemble_predictions), 5))), axis=1))[:, 0]

# Evaluate ensemble performance
ensemble_mse = mean_squared_error(y_test_actual, ensemble_predictions)
ensemble_mae = mean_absolute_error(y_test_actual, ensemble_predictions)
ensemble_r2 = r2_score(y_test_actual, ensemble_predictions)

print(f'Ensemble Mean Squared Error: {ensemble_mse}')
print(f'Ensemble Mean Absolute Error: {ensemble_mae}')
print(f'Ensemble R-squared Score: {ensemble_r2}')

Desglose del código:

  • Función para crear un conjunto (ensemble): La función create_ensemble() crea múltiples modelos LSTM, cada uno con la misma arquitectura pero con inicializaciones potencialmente diferentes.
  • Creación del conjunto: Se crea un conjunto de 3 modelos utilizando la forma de entrada de los datos de entrenamiento.
  • Entrenamiento del modelo: Cada modelo en el conjunto se entrena de forma independiente con los mismos datos de entrenamiento, utilizando early stopping y la programación de la tasa de aprendizaje para optimización.
  • Predicciones del conjunto: Las predicciones se realizan promediando las salidas de todos los modelos en el conjunto.
  • Transformación inversa: Las predicciones del conjunto se transforman inversamente para devolverlas a su escala original.
  • Evaluación del rendimiento: El rendimiento del conjunto se evalúa utilizando el Error Cuadrático Medio (MSE), el Error Absoluto Medio (MAE) y el coeficiente de determinación (R2).

Este enfoque de conjunto tiene como objetivo mejorar la precisión y robustez de las predicciones al aprovechar múltiples modelos, mitigando potencialmente los sesgos individuales de los modelos y reduciendo el sobreajuste.

9.4.7 Conclusión

Este proyecto mejorado demuestra varias mejoras respecto a la tarea original de predicción de series temporales basada en LSTM. Hemos implementado un pipeline de preprocesamiento de datos más sofisticado, que incluye características adicionales y un escalado adecuado. La arquitectura LSTM ha sido mejorada con múltiples capas, normalización por lotes y dropout para una mejor regularización.

También hemos incorporado técnicas avanzadas de entrenamiento, como el early stopping y la programación de la tasa de aprendizaje. El proceso de evaluación ahora incluye métricas y visualizaciones más completas, proporcionando una visión más profunda del rendimiento del modelo. Además, hemos introducido un análisis de la importancia de las características para comprender el impacto de diferentes entradas en las predicciones.

Finalmente, se ha implementado un método de conjunto (ensemble) para mejorar potencialmente la precisión y la robustez de las predicciones. Estas mejoras proporcionan un enfoque más robusto y perspicaz para la predicción de series temporales, especialmente en el contexto de la predicción de precios de acciones.

9.4 Proyecto 4: Predicción de series temporales con LSTMs (Mejorado)

La predicción de series temporales juega un papel crucial en numerosos dominios, que incluyen pero no se limitan a análisis financieros, predicciones meteorológicas y estimaciones de demanda en la gestión de la cadena de suministro. Este proyecto profundiza en la aplicación de redes Long Short-Term Memory (LSTM), un tipo sofisticado de red neuronal recurrente, con el fin de predecir valores futuros dentro de una serie temporal. Nuestro enfoque específico se centra en el ámbito de la predicción de precios de acciones, una aplicación desafiante y de gran importancia económica en la predicción de series temporales.

Basándonos en nuestro proyecto original, buscamos implementar una serie de mejoras diseñadas para aumentar significativamente tanto el rendimiento como la robustez de nuestro modelo. Estas mejoras abarcan varios aspectos de la tubería de machine learning, desde la preprocesamiento de datos y la ingeniería de características hasta la arquitectura del modelo y las metodologías de entrenamiento. Al incorporar estos avances, buscamos crear un sistema de predicción más preciso, confiable e interpretable que pueda captar eficazmente los patrones y dependencias complejas inherentes a los movimientos de precios de acciones.

Mediante este enfoque mejorado, no solo pretendemos mejorar la precisión predictiva, sino también obtener una comprensión más profunda de los factores subyacentes que impulsan las fluctuaciones en los precios de las acciones. Este proyecto sirve como una exploración integral de técnicas de vanguardia en la predicción de series temporales, demostrando el potencial de los métodos avanzados de machine learning para abordar desafíos reales en la predicción financiera.

9.4.1 Recolección y preprocesamiento de datos

Para mejorar la robustez de nuestro conjunto de datos, implementaremos pasos exhaustivos de recolección y preprocesamiento de datos. Esta expansión implica recopilar una gama más amplia de datos históricos, incorporar características adicionales relevantes y aplicar técnicas avanzadas de preprocesamiento.

Al hacerlo, buscamos crear un conjunto de datos más completo e informativo que capture los patrones y relaciones matizados dentro de los movimientos de precios de las acciones. Este conjunto de datos mejorado servirá como una base sólida para nuestro modelo LSTM, lo que podría llevar a predicciones más precisas y confiables.

import pandas as pd
import numpy as np
import yfinance as yf
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# Fetch more historical data and additional features
stock_data = yf.download('GOOGL', start='2000-01-01', end='2023-12-31')
stock_data['Returns'] = stock_data['Close'].pct_change()
stock_data['MA50'] = stock_data['Close'].rolling(window=50).mean()
stock_data['MA200'] = stock_data['Close'].rolling(window=200).mean()
stock_data['Volume_MA'] = stock_data['Volume'].rolling(window=20).mean()
stock_data.dropna(inplace=True)

# Normalize the data
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(stock_data[['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']])

# Create sequences
def create_sequences(data, seq_length):
    X, y = [], []
    for i in range(len(data) - seq_length):
        X.append(data[i:(i + seq_length), :])
        y.append(data[i + seq_length, 0])
    return np.array(X), np.array(y)

sequence_length = 60
X, y = create_sequences(scaled_data, sequence_length)

# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Desglose:

  • Recolección de datos: El código utiliza la librería yfinance para descargar datos históricos de las acciones de Google (GOOGL) desde el 1 de enero de 2000 hasta el 31 de diciembre de 2023.
  • Ingeniería de características: Se crean varias características nuevas:
    • Retornos: Cambio porcentual en el precio de cierre.
    • MA50: Media móvil de 50 días del precio de cierre.
    • MA200: Media móvil de 200 días del precio de cierre.
    • Volume_MA: Media móvil de 20 días del volumen de negociación.
  • Normalización de datos: Se utiliza el MinMaxScaler para escalar todas las características a un rango entre 0 y 1, lo cual es importante para el entrenamiento de redes neuronales.
  • Creación de secuencias: Se define una función create_sequences() para generar secuencias de entrada y los valores objetivo correspondientes. Utiliza un enfoque de ventana deslizante con una longitud de secuencia de 60 días.
  • División de datos: El conjunto de datos se divide en conjuntos de entrenamiento y prueba, reservando el 20% de los datos para pruebas.

Este pipeline de preprocesamiento crea un conjunto de datos robusto que captura varios aspectos de los movimientos de precios de acciones, proporcionando una base sólida para que el modelo LSTM aprenda.

9.4.2 Arquitectura LSTM mejorada

En este paso, diseñaremos una arquitectura LSTM avanzada y robusta, incorporando múltiples capas e implementando técnicas de dropout para una regularización efectiva. Este diseño mejorado tiene como objetivo captar las complejas dependencias temporales en los datos de series temporales, mientras mitiga los problemas de sobreajuste.

Al agregar estratégicamente profundidad a nuestra red e introducir capas de dropout, buscamos mejorar la capacidad del modelo para generalizar a partir de los datos de entrenamiento y hacer predicciones más precisas en patrones de precios de acciones no vistos. La arquitectura sofisticada que construiremos equilibrará la complejidad del modelo con la capacidad de generalización, lo que potencialmente llevará a un mejor rendimiento en la predicción de precios de acciones.

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout, BatchNormalization
from tensorflow.keras.optimizers import Adam

def build_improved_lstm_model(input_shape):
    model = Sequential([
        LSTM(100, return_sequences=True, input_shape=input_shape),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100, return_sequences=True),
        BatchNormalization(),
        Dropout(0.2),
        LSTM(100),
        BatchNormalization(),
        Dropout(0.2),
        Dense(50, activation='relu'),
        Dense(1)
    ])
    model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error')
    return model

model = build_improved_lstm_model((X_train.shape[1], X_train.shape[2]))
model.summary()

Desglose:

  • Importaciones: Se importan los módulos necesarios de Keras para construir el modelo.
  • Arquitectura del modelo: La función build_improved_lstm_model crea un modelo Secuencial con las siguientes capas:
    • Tres capas LSTM con 100 unidades cada una, siendo las dos primeras capas de retorno de secuencias.
    • Capas de BatchNormalization después de cada capa LSTM para normalizar las activaciones.
    • Capas de Dropout (tasa del 20%) para regularización y prevenir el sobreajuste.
    • Una capa Dense con 50 unidades y activación ReLU.
    • Una capa Dense final con 1 unidad para la predicción de salida.
  • Compilación del modelo: El modelo se compila utilizando el optimizador Adam con una tasa de aprendizaje de 0.001 y el error cuadrático medio como función de pérdida.
  • Creación del modelo: Se crea una instancia del modelo utilizando la forma de entrada de los datos de entrenamiento.
  • Resumen del modelo: La llamada a model.summary() imprime la estructura del modelo, mostrando las capas y el número de parámetros.

Esta arquitectura tiene como objetivo captar dependencias temporales complejas en los datos de precios de acciones, utilizando técnicas como dropout y batch normalization para mejorar la generalización y la estabilidad durante el entrenamiento.

9.4.3 Entrenamiento con Early Stopping y programación de tasa de aprendizaje

Para mejorar el proceso de entrenamiento y optimizar el rendimiento del modelo, implementaremos dos técnicas clave: early stopping y la programación de la tasa de aprendizaje. El early stopping ayuda a prevenir el sobreajuste al detener el proceso de entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Esto asegura que capturemos el modelo en su mejor capacidad de generalización.

Por otro lado, la programación de la tasa de aprendizaje ajusta dinámicamente la tasa de aprendizaje durante el entrenamiento. Este enfoque adaptativo permite que el modelo haga actualizaciones más grandes en las primeras etapas del entrenamiento y ajustes más finos a medida que converge, lo que puede llevar a una convergencia más rápida y un mejor rendimiento general.

Al incorporar estas estrategias avanzadas de entrenamiento, buscamos lograr un proceso de entrenamiento más eficiente y un modelo que generalice bien en datos no vistos, mejorando así nuestras predicciones de precios de acciones.

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau

early_stopping = EarlyStopping(monitor='val_loss', patience=20, restore_best_weights=True)
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10, min_lr=0.00001)

history = model.fit(
    X_train, y_train,
    epochs=200,
    batch_size=32,
    validation_split=0.2,
    callbacks=[early_stopping, lr_scheduler],
    verbose=1
)

Desglose del código:

  • Importación de callbacks: El código importa EarlyStopping y ReduceLROnPlateau de los callbacks de Keras.
  • Early Stopping: Esta técnica detiene el entrenamiento cuando el rendimiento del modelo en el conjunto de validación deja de mejorar. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • patience=20: Esperará 20 épocas antes de detenerse si no se observa mejora.
    • restore_best_weights=True: Restaurará los pesos del modelo de la época con el mejor valor de la cantidad monitoreada.
  • Programador de la tasa de aprendizaje: Ajusta la tasa de aprendizaje durante el entrenamiento. Los parámetros son:
    • monitor='val_loss': Observa la pérdida de validación.
    • factor=0.5: Reducirá la tasa de aprendizaje a la mitad cuando se active.
    • patience=10: Esperará 10 épocas antes de reducir la tasa de aprendizaje.
    • min_lr=0.00001: La tasa de aprendizaje mínima.
  • Entrenamiento del modelo: La función model.fit() entrena el modelo con estos parámetros:
    • epochs=200: Número máximo de épocas de entrenamiento.
    • batch_size=32: Número de muestras por actualización de gradiente.
    • validation_split=0.2: El 20% de los datos de entrenamiento se utilizarán para la validación.
    • callbacks=[early_stopping, lr_scheduler]: Se aplican el early stopping y el programador de tasa de aprendizaje durante el entrenamiento.
    • verbose=1: Esto mostrará barras de progreso durante el entrenamiento.

Estas técnicas tienen como objetivo mejorar el proceso de entrenamiento, prevenir el sobreajuste y, potencialmente, mejorar el rendimiento del modelo.

9.4.4 Evaluación y visualización del modelo

Para evaluar exhaustivamente el rendimiento del modelo y obtener una visión más profunda de sus predicciones, implementaremos una estrategia de evaluación integral. Este enfoque incluirá diversas métricas cuantitativas para medir precisión y error, así como representaciones visuales de las predicciones del modelo en comparación con los valores reales. Al combinar estos métodos, podremos comprender mejor las fortalezas y limitaciones de nuestro modelo LSTM en la predicción de precios de acciones.

Nuestra evaluación incluirá los siguientes componentes clave:

  • Cálculo de métricas estándar de regresión, como el error cuadrático medio (MSE), el error absoluto medio (MAE) y el coeficiente de determinación (R2).
  • Gráficos de series temporales que comparan los valores predichos con los precios reales de las acciones.
  • Análisis de residuos para identificar cualquier patrón en los errores de predicción.
  • Evaluación de ventana deslizante para analizar el rendimiento del modelo en diferentes periodos de tiempo.

Este enfoque de evaluación multifacético proporcionará una comprensión matizada de las capacidades predictivas de nuestro modelo y ayudará a identificar áreas de mejora en futuras iteraciones.

import matplotlib.pyplot as plt

# Make predictions
train_predictions = model.predict(X_train)
test_predictions = model.predict(X_test)

# Inverse transform predictions
train_predictions = scaler.inverse_transform(np.concatenate((train_predictions, np.zeros((len(train_predictions), 5))), axis=1))[:, 0]
test_predictions = scaler.inverse_transform(np.concatenate((test_predictions, np.zeros((len(test_predictions), 5))), axis=1))[:, 0]
y_train_actual = scaler.inverse_transform(np.concatenate((y_train.reshape(-1, 1), np.zeros((len(y_train), 5))), axis=1))[:, 0]
y_test_actual = scaler.inverse_transform(np.concatenate((y_test.reshape(-1, 1), np.zeros((len(y_test), 5))), axis=1))[:, 0]

# Visualize predictions
plt.figure(figsize=(15, 6))
plt.plot(y_test_actual, label='Actual')
plt.plot(test_predictions, label='Predicted')
plt.title('LSTM Model: Actual vs Predicted Stock Prices')
plt.xlabel('Time')
plt.ylabel('Stock Price')
plt.legend()
plt.show()

# Evaluate model performance
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

mse = mean_squared_error(y_test_actual, test_predictions)
mae = mean_absolute_error(y_test_actual, test_predictions)
r2 = r2_score(y_test_actual, test_predictions)

print(f'Mean Squared Error: {mse}')
print(f'Mean Absolute Error: {mae}')
print(f'R-squared Score: {r2}')

Desglose de lo que hace el código:

  • Predicciones: El modelo realiza predicciones tanto en los conjuntos de datos de entrenamiento como de prueba.
  • Transformación inversa: Las predicciones y los valores reales se transforman inversamente para devolverlos a su escala original. Esto es necesario porque los datos fueron escalados durante el preprocesamiento.
  • Visualización: Se crea un gráfico para comparar los precios reales de las acciones con los predichos para el conjunto de prueba. Esta representación visual ayuda a entender qué tan bien se alinean las predicciones del modelo con los datos reales.
  • Métricas de rendimiento: El código calcula tres métricas clave de rendimiento:
    • Error cuadrático medio (MSE): Mide la diferencia cuadrática media entre los valores predichos y los reales.
    • Error absoluto medio (MAE): Mide la diferencia absoluta media entre los valores predichos y los reales.
    • Coeficiente de determinación (R2): Indica la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s).

Estas métricas proporcionan una evaluación cuantitativa del rendimiento del modelo, ayudando a evaluar su precisión y capacidad predictiva en la previsión de precios de acciones.

9.4.5 Análisis de importancia de características

Para obtener una visión más profunda del proceso de toma de decisiones de nuestro modelo, implementaremos un análisis exhaustivo de la importancia de las características. Este paso crucial nos ayudará a entender qué características contribuyen más significativamente a las predicciones, lo que nos permitirá:

  1. Identificar los factores más influyentes en los movimientos de precios de acciones.
  2. Posiblemente refinar nuestra selección de características para futuras iteraciones.
  3. Proporcionar información valiosa a las partes interesadas sobre los principales impulsores de los cambios en los precios de las acciones.

Utilizaremos la importancia por permutación, un método independiente del modelo que mide el aumento del error de predicción después de permutar cada característica. Este enfoque nos dará una visión clara del impacto de cada característica en el rendimiento de nuestro modelo LSTM.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.inspection import permutation_importance

def reshape_features(X):
    """Reshape 3D sequence data (samples, timesteps, features) into 2D for feature importance analysis."""
    return X.reshape((X.shape[0], -1))

# Reshape X_test for permutation importance analysis
X_test_reshaped = reshape_features(X_test)

# Define a wrapper function for Keras model predictions
def model_predict(X):
    X = X.reshape((-1, sequence_length, X.shape[1] // sequence_length))  # Reshape back to 3D
    return model.predict(X, verbose=0).flatten()

# Compute permutation importance
r = permutation_importance(model_predict, X_test_reshaped, y_test, n_repeats=10, random_state=42, scoring='neg_mean_squared_error')

# Adjust feature names for the reshaped input
feature_names_expanded = [f"{feature}_t{t}" for t in range(sequence_length) for feature in ['Close', 'Volume', 'Returns', 'MA50', 'MA200', 'Volume_MA']]
feature_importance = pd.DataFrame({'feature': feature_names_expanded, 'importance': r.importances_mean})

# Aggregate importance scores for each original feature
feature_importance = feature_importance.groupby(lambda x: feature_importance['feature'][x].split('_')[0]).mean()
feature_importance = feature_importance.sort_values('importance', ascending=False)

# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar(feature_importance.index, feature_importance['importance'])
plt.title('Feature Importance (Permutation Importance)')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

Aquí está el desglose de lo que hace el código:

  1. Importa permutation_importance de scikit-learn.
    • Esta función ayuda a evaluar cuánto contribuye cada característica a las predicciones del modelo mediante la aleatorización de los valores de las características y la medición del impacto en la precisión.
  2. Define reshape_features() para aplanar la entrada secuencial 3D (muestras, pasos de tiempo, características) en un formato 2D (muestras, características × pasos de tiempo).
    • Esto es necesario porque permutation_importance espera un array 2D como entrada.
  3. Remodela X_test usando reshape_features(X_test).
    • Este paso asegura que los datos de prueba tengan el formato correcto para el análisis de importancia por permutación.
  4. Define model_predict() para adaptar el método predict() del modelo LSTM para que funcione con permutation_importance.
    • Como los LSTM esperan una entrada 3D (muestras, pasos de tiempo, características), esta función remodela los datos de vuelta a 3D antes de hacer predicciones.
  5. Calcula la importancia por permutación usando:
    • El modelo LSTM entrenado
    • Los datos de prueba remodelados
    • Las etiquetas de prueba (y_test)
    • n_repeats=10 para estabilidad, lo que significa que el cálculo de importancia se repite 10 veces.
  6. Genera nombres de características expandidos para reflejar múltiples pasos de tiempo en la entrada secuencial.
    • A cada nombre de característica se le añade su índice de paso de tiempo (por ejemplo, Close_t0Close_t1, ...).
    • Esto asegura que las características de diferentes pasos de tiempo estén diferenciadas en el análisis de importancia.
  7. Crea un DataFrame que:
    • Mapea los nombres de las características con sus puntuaciones de importancia.
    • Agrupa por nombres de características originales (por ejemplo, agregando Close_t0 a Close_t59 en Close).
    • Promedia las puntuaciones de importancia por característica y las ordena en orden descendente.
  8. Crea un gráfico de barras para visualizar las puntuaciones de importancia de las características.
    • Las características más importantes aparecen en la parte superior, ayudando a identificar qué factores tienen el mayor impacto en las predicciones de precios de acciones.

9.4.6 Método de conjunto (Ensemble)

Para mejorar la robustez y precisión de nuestras predicciones, implementaremos un conjunto de modelos LSTM. Este enfoque implica entrenar varios modelos LSTM de manera independiente y luego combinar sus predicciones. Al aprovechar la sabiduría colectiva de múltiples modelos, es posible lograr predicciones más estables y precisas.

El método de conjunto puede ayudar a mitigar los sesgos de los modelos individuales y reducir el impacto del sobreajuste, lo que conduce a un mejor rendimiento general en la predicción de precios de acciones. Esta técnica es particularmente valiosa en el contexto de las predicciones financieras, donde pequeñas mejoras en la precisión pueden tener importantes implicaciones en el mundo real.

def create_ensemble(n_models, input_shape):
    models = []
    for _ in range(n_models):
        model = build_improved_lstm_model(input_shape)
        models.append(model)
    return models

n_models = 3
ensemble = create_ensemble(n_models, (X_train.shape[1], X_train.shape[2]))

# Train each model in the ensemble
for i, model in enumerate(ensemble):
    print(f"Training model {i+1}/{n_models}")
    model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2, 
              callbacks=[early_stopping, lr_scheduler], verbose=0)

# Make ensemble predictions
ensemble_predictions = np.mean([model.predict(X_test) for model in ensemble], axis=0)

# Inverse transform ensemble predictions
ensemble_predictions = scaler.inverse_transform(np.concatenate((ensemble_predictions, np.zeros((len(ensemble_predictions), 5))), axis=1))[:, 0]

# Evaluate ensemble performance
ensemble_mse = mean_squared_error(y_test_actual, ensemble_predictions)
ensemble_mae = mean_absolute_error(y_test_actual, ensemble_predictions)
ensemble_r2 = r2_score(y_test_actual, ensemble_predictions)

print(f'Ensemble Mean Squared Error: {ensemble_mse}')
print(f'Ensemble Mean Absolute Error: {ensemble_mae}')
print(f'Ensemble R-squared Score: {ensemble_r2}')

Desglose del código:

  • Función para crear un conjunto (ensemble): La función create_ensemble() crea múltiples modelos LSTM, cada uno con la misma arquitectura pero con inicializaciones potencialmente diferentes.
  • Creación del conjunto: Se crea un conjunto de 3 modelos utilizando la forma de entrada de los datos de entrenamiento.
  • Entrenamiento del modelo: Cada modelo en el conjunto se entrena de forma independiente con los mismos datos de entrenamiento, utilizando early stopping y la programación de la tasa de aprendizaje para optimización.
  • Predicciones del conjunto: Las predicciones se realizan promediando las salidas de todos los modelos en el conjunto.
  • Transformación inversa: Las predicciones del conjunto se transforman inversamente para devolverlas a su escala original.
  • Evaluación del rendimiento: El rendimiento del conjunto se evalúa utilizando el Error Cuadrático Medio (MSE), el Error Absoluto Medio (MAE) y el coeficiente de determinación (R2).

Este enfoque de conjunto tiene como objetivo mejorar la precisión y robustez de las predicciones al aprovechar múltiples modelos, mitigando potencialmente los sesgos individuales de los modelos y reduciendo el sobreajuste.

9.4.7 Conclusión

Este proyecto mejorado demuestra varias mejoras respecto a la tarea original de predicción de series temporales basada en LSTM. Hemos implementado un pipeline de preprocesamiento de datos más sofisticado, que incluye características adicionales y un escalado adecuado. La arquitectura LSTM ha sido mejorada con múltiples capas, normalización por lotes y dropout para una mejor regularización.

También hemos incorporado técnicas avanzadas de entrenamiento, como el early stopping y la programación de la tasa de aprendizaje. El proceso de evaluación ahora incluye métricas y visualizaciones más completas, proporcionando una visión más profunda del rendimiento del modelo. Además, hemos introducido un análisis de la importancia de las características para comprender el impacto de diferentes entradas en las predicciones.

Finalmente, se ha implementado un método de conjunto (ensemble) para mejorar potencialmente la precisión y la robustez de las predicciones. Estas mejoras proporcionan un enfoque más robusto y perspicaz para la predicción de series temporales, especialmente en el contexto de la predicción de precios de acciones.