Tabla de Contenidos

10. Redes Neuronales Recurrentes

Las redes neuronales recurrentes son redes neuronales que dependen de los datos anteriores, es decir que tienen "memoria".

Por ejemplo, al predecir si una foto es un tipo de flor, no depende de lo que se haya predecido antes pero en una red neuronal si. Son redes que predicen secuencias de datos como por ejemplo series de temperatura, o cotizaciones de bolsa, etc:

Para hacer este tipo de redes se usan nuevas capas que son:

En Keras las 3 capas se definen así:

from tensorflow.keras.layers import RNN
from tensorflow.keras.layers import LSTM
from tensorflow.keras.layers import GRU

Por ahora no vamos a contar como son estas neuronas pero su esquema es este:

Las fórmulas matemáticas de cada tipo de neurona se puede ver en RNN, LSTM & GRU

Más información:

Ejemplo

Vamos a hacer una red neuronal que dado 24 temperatura (una por cada hora) obtener la temperatura que hará 12 horas despues.

def get_serie_tiempo(datetime_start,num_dias=30,pendiente=0.005,amplitud=5,ruido_amplitud=1):
    np.random.seed(7)

    num_datos = num_dias*24
    


    x = np.linspace(0, num_dias * 2 * np.pi, num_datos)  # Eje x (progresión del tiempo)
    fechas = pd.date_range(start=datetime_start, periods=num_datos, freq="h")
    tendencia = np.linspace(0, pendiente * num_datos, num_datos)  # Tendencia ascendente
    onda = amplitud * np.sin(x)  # Onda sinusoidal
    ruido = np.random.normal(0, ruido_amplitud, num_datos)  # Ruido aleatorio

    datos = tendencia + onda + ruido

    return fechas,datos

fechas,temperaturas=get_serie_tiempo(datetime(2025, 1, 1, 10, 0, 0),num_dias=200,ruido_amplitud=0.5)

figure=plt.figure(figsize=(20,10))

axes = figure.add_subplot(2,1,1)
axes.plot(fechas,temperaturas)
axes_configure_labels(axes,"Datos originales","Tiempo","Temperatura")



def get_datos(serie_datos,tamanyo_ventana=24,adelanto_prediccion=12):
    size=len(serie_datos)



    x=[]
    y=[]
    for i in range(size-tamanyo_ventana-adelanto_prediccion):
        x.append(serie_datos[i:i+tamanyo_ventana])
        y.append(serie_datos[i+tamanyo_ventana+adelanto_prediccion])

    

    x=np.array(x)
    y=np.array(y)



    size_entrenamiento=math.floor(x.shape[0]*0.8)

    x_entrenamiento=x[0:size_entrenamiento]
    x_validacion=x[size_entrenamiento:]
    y_entrenamiento=y[0:size_entrenamiento]
    y_validacion=y[size_entrenamiento:]


    return x_entrenamiento.reshape(-1,tamanyo_ventana,1), y_entrenamiento.reshape(-1,1), x_validacion.reshape(-1,tamanyo_ventana,1), y_validacion.reshape(-1,1)


scaler = MinMaxScaler()
temperaturas_scaled = scaler.fit_transform(temperaturas.reshape(-1,1)).reshape(temperaturas.shape)
x_entrenamiento, y_entrenamiento, x_validacion, y_validacion=get_datos(temperaturas_scaled,tamanyo_ventana=24,adelanto_prediccion=12)

En nuestro ejemplo lo que hemos hecho es que para cada 24 temperaturas (tamanyo_ventana=24) , obtener la temperatura que habrá 12 horas después (adelanto_prediccion=12). Tambien hemos escalado los datos, lo cual es importante para que la red funcione mejor. Por último fíjate que tanto la x como la y son matrices x_entrenamiento.reshape(-1,tamanyo_ventana,1) y y_entrenamiento.reshape(-1,1)

def get_model(shape):
    model=Sequential()
    model.add(Input(shape=shape))
    model.add(GRU(24, activation='swish', return_sequences=True))
    model.add(Dropout(0.1))       
    model.add(GRU(50, activation='swish', return_sequences=True ))
    model.add(Dropout(0.1))  
    model.add(GRU(24, activation='swish', return_sequences=False))
    model.add(Dropout(0.1))    
    model.add(Dense(1, activation='linear'))
    model.compile(loss='mean_squared_error',optimizer="adam")

    return model

def fit(model,x_entrenamiento, y_entrenamiento, x_validacion, y_validacion,epochs):
    history=model.fit(x_entrenamiento,y_entrenamiento,validation_data=(x_validacion,y_validacion),epochs=epochs,verbose=False)

    return history  

model=get_model(shape=x_entrenamiento.shape[1:])
history=fit(model,x_entrenamiento, y_entrenamiento, x_validacion, y_validacion,30)
Fijarse que en las capas GRU hay que indicar return_sequences=True excepto en la última que debe ser return_sequences=False

figure=plt.figure(figsize=(6,4))
axes = figure.add_subplot(1,1,1)
axes.plot(history.history['loss'],label=f"Loss:{history.history['loss'][-1]:.3f}")
axes.plot(history.history['val_loss'],label=f"Val Loss:{history.history['val_loss'][-1]:.3f}")
axes_configure_labels(axes,"Loss por época","Épocas","MSE")



y_pred_entrenamiento=model.predict(x_entrenamiento)
y_entrenamiento_inverse_transform=scaler.inverse_transform(y_entrenamiento)
y_pred_entrenamiento_inverse_transform=scaler.inverse_transform(y_pred_entrenamiento)



y_pred_validacion=model.predict(x_validacion)
y_validacion_inverse_transform=scaler.inverse_transform(y_validacion)
y_pred_validacion_inverse_transform=scaler.inverse_transform(y_pred_validacion)


figure=plt.figure(figsize=(20,25))


axes = figure.add_subplot(4,1,1)
axes.plot(y_entrenamiento_inverse_transform,label="Real")
axes.plot(y_pred_entrenamiento_inverse_transform,label="Predicha")
axes_configure_labels(axes,"Todos Entrenamiento","Tiempo","Temperatura")
axes.set_xlim(xmin=0,xmax=y_entrenamiento_inverse_transform.shape[0])
axes.set_ylim(ymin=-6,ymax=16)

axes = figure.add_subplot(4,1,2)
axes.plot(y_validacion_inverse_transform,label="Real")
axes.plot(y_pred_validacion_inverse_transform,label="Predicha")
axes_configure_labels(axes,"Todos Validacion","Tiempo","Temperatura")
axes.set_xlim(xmin=0,xmax=y_entrenamiento_inverse_transform.shape[0])
axes.set_ylim(ymin=-6,ymax=16)


axes = figure.add_subplot(4,1,3)
axes.plot(y_entrenamiento_inverse_transform,label="Real")
axes.plot(y_pred_entrenamiento_inverse_transform,label="Predicha")
axes_configure_labels(axes,"Últimos Entrenamiento","Tiempo","Temperatura")
axes.set_xlim(xmin=y_entrenamiento_inverse_transform.shape[0]-500,xmax=y_entrenamiento_inverse_transform.shape[0])
axes.set_ylim(ymin=-6,ymax=16)

axes = figure.add_subplot(4,1,4)
axes.plot(y_validacion_inverse_transform,label="Real")
axes.plot(y_pred_validacion_inverse_transform,label="Predicha")
axes_configure_labels(axes,"Últimos Validacion","Tiempo","Temperatura")
axes.set_xlim(xmin=y_validacion_inverse_transform.shape[0]-500,xmax=y_validacion_inverse_transform.shape[0])
axes.set_ylim(ymin=-6,ymax=16)