Lo que sí voy a hacer en esta entrada es mostrar como se comporta una LSTM al cambiar algunos parámetros, creando un código usando Keras muy fácil de entender. La idea es ver que tal predice el comportamiento de una sinusoide, lo cual no es una tarea trivial para una red neuronal multicapa sin recursión. He aquí la bestia con la que lidiar (modo irónico off)
El código para crear la sinusoide es bastante simple:
# Creo el eje de timpo y la sinusoide
x = np.linspace(-4*np.pi, 4*np.pi, 100)
features = np.sin(x)
features = np.reshape(features, (features.shape[0],1))
# Dibujo features y su eje
plt.style.use('fivethirtyeight')
font1 = {'family':'serif', 'color':'blue', 'size':20}
font2 = {'family':'serif', 'color':'darkred', 'size':15}
plt.plot(x,features)
plt.xlabel('Tiempo', fontdict=font2)
plt.ylabel('Amplitud', fontdict=font2)
plt.title('Sinusoide pura', fontdict=font1)
El siguiente paso es preparar los datos de entrenamiento y los datos de prueba (training and test). Para eso basta con tomar un trozo de la sinusoide como entrenamiento y dejar el resto para prueba. Como puede verse la sinusoide tiene cuatro ciclos completos. Se van a utilizar tres de ellos (el 75%) para entrenamiento y el restante para prueba. Los datos de entrenamiento se van a dividir en trozos de 10 muestras, utizandose la muestra 11 como la que debe ser predicha por la red LSTM:
y así sucesivamente hasta haber usado el 75% de los datos. El código para hacer eso es el siguiente:
# Tomando el 75% de los datos para entrenamiento
training_dataset_length = math.ceil(len(features) * .75)
train_data = scaled_data[0:training_dataset_length, :]
#Dividiendo los datos y creando x_train e y_train
x_train = []
y_train = []
for i in range(10, len(train_data)):
plt.show()
x_train.append(train_data[i-10:i, 0])
y_train.append(train_data[i, 0])
A la implementación de Keras le gusta usar arrays de numpy y además no permite arrays que sólo tengan una dimensión. Esto es simple de arreglar:
#Convirtiendo en arrays de numpy y reformateando los datos en arrays 3-D aceptados
#por el modelo LSTM de Keras [muestras, tiempo, caracteristicas]
x_train, y_train = np.array(x_train), np.array(y_train)y_train = []
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))
El siguiente paso es crear la red y es aquí donde se puede empezar a jugar con distintas configuraciones. Para empezar, una capa LSTM de 10 unidades y una de salida. Para entrenar se utiliza un configuración bastante estándar:
# Se pone una capa de LSTM de 10 unidades
model = Sequential()
model.add(LSTM(units=10))
# Y una capa de una unidad para dar la salida, ya que buscamos una salida
# unidimensional (vector) para que sea totalmente conectada, se usa dense
model.add(Dense(units=1))
#compilar el modelo y entrenar en batches de 50
# (tenemos 65 muestras en el training)
# y entrenar durante 50 epochs
model.compile(optimizer = 'adam', loss='mean_squared_error')
model.fit(x_train, y_train, epochs=50, batch_size=50)
El resultado que se obtiene es:
El resultado es mejorable, la red esta subestimando algunas salidas, es asimétrica (el valle llega a -0,50 mientras que el pico solo a 0.40), el primer pico está desplazado, el segundo pico parece que está más deformado...
¿Qué tal quintuplicando las unidades de la capa LSTM (unit=50)?
Era de esperar que al aumentar el número de unidades la predicción mejorase, pero el resultado es casi perfecto (no me lo esperaba).
Tiremos la casa por la ventana, ¡2500 unidades!
Problema 1: mientras más unidades más memoria se necesita, con lo que se puede llegar a bloquear el equipo... mala idea.
Epoch 1/50
2021-06-27 18:43:51.494772:Allocation of 100000000 exceeds 10% of free system memory.
2021-06-27 18:43:51.552636:Allocation of 100000000 exceeds 10% of free system memory.
Problema 2: el tiempo de entrenamiento muchísimo mayor... muy mala idea.
Problema 3: overfitting que tiene que ver con que la red deja de generalizar y se aprende los patrones. En este caso seguro, porque los patrones de entrenamiento y de test son iguales... para este ejemplo no es un problema, para sistemas que están trabajando con datos reales es ... muy muy mala idea.
¡Creatividad al poder, múltples capas de LSTM!
Es posible poner varias capas de LSTMs, lo que añade que la red además de buscar patrones dentro de la dimensión temporal, los busque dentro de la dimensión de muestras o carácteristicas (recordando que la LSTM trabaja con [muestras, tiempo, carácteristicas], ¿y sí la muestra n-1 contuviese información útil para procesar la muestra n?
Hay un buen artículo que hablan sobre el tema How to construct Deep Recurrent Neural Networks. Para hacer esto con el modelo LSTM las salidas de una capa de la LSTM tienen que alimentar la siguiente capa. En la práctica en Keras, es necesario que la capa n-1 genere una salida 3-D (que son los que acepta como entrada el modelo LSTM). Para hacer esto, basta con poner el parámetro return_sequences=True, en todas las capas salvo en la última. Se pueden poner todas las capas LSTM que se deseen y además cada capa puede tener las unidades que se deseen (¡fiesta total!). He aquí el código:
# Se pone una capa de LSTM de 10 unidades
model = Sequential()
model.add(LSTM(units=10, return_sequences=True))
# Añadiendo una segunda capa esta vez 50 unidades
model.add(LSTM(units=50, return_sequences=True))
# Adding una tercera capa, 15 unidades
model.add(LSTM(units=15, return_sequences=True))
# Adding una cuarta capa, 25 unidades
model.add(LSTM(units=25, return_sequences=True))
# añadiendo una quita capa, el return_sequences=True no es necesario
model.add(LSTM(units=50))
# Y una capa de una unidad para dar la salida, ya que buscamos una salida
# unidimensional (vector) para que sea totalmente conectada, se usa dense
model.add(Dense(units=1))
Más creativo imposible... ¿el resultado? igual de creativo:
La red profunda LSTM empieza a buscar patrones dónde no los hay y acaba muy confundida. Añadir capas de LSTM no siempre lleva al éxito, aunque eso dependerá de la complejidad de los datos, de hecho en este ejemplo particular con una sinusoide, por mucho que se juegue con el número de capas y de unidades, el resultado nunca supera al de una sola capa LSTM. Esto no quiere decir que no sea buena idea en algún caso, como por ejemplo para reconocimiento de voz (Speech recognition with deep recurrent neural networks), así que ¡ojito!





No hay comentarios:
Publicar un comentario