PyTorch - Red neuronal recurrente

Las redes neuronales recurrentes son un tipo de algoritmo orientado al aprendizaje profundo que sigue un enfoque secuencial. En las redes neuronales, siempre asumimos que cada entrada y salida es independiente de todas las demás capas. Este tipo de redes neuronales se denominan recurrentes porque realizan cálculos matemáticos de manera secuencial completando una tarea tras otra.

El siguiente diagrama especifica el enfoque completo y el funcionamiento de las redes neuronales recurrentes:

En la figura anterior, c1, c2, c3 y x1 se consideran entradas que incluyen algunos valores de entrada ocultos, a saber, h1, h2 y h3 entregando la salida respectiva de o1. Ahora nos centraremos en implementar PyTorch para crear una onda sinusoidal con la ayuda de redes neuronales recurrentes.

Durante el entrenamiento, seguiremos un enfoque de entrenamiento para nuestro modelo con un punto de datos a la vez. La secuencia de entrada x consta de 20 puntos de datos y se considera que la secuencia objetivo es la misma que la secuencia de entrada.

Paso 1

Importe los paquetes necesarios para implementar redes neuronales recurrentes utilizando el siguiente código:

import torch
from torch.autograd import Variable
import numpy as np
import pylab as pl
import torch.nn.init as init

Paso 2

Estableceremos los hiperparámetros del modelo con el tamaño de la capa de entrada establecido en 7. Habrá 6 neuronas de contexto y 1 neurona de entrada para crear la secuencia objetivo.

dtype = torch.FloatTensor
input_size, hidden_size, output_size = 7, 6, 1
epochs = 300
seq_length = 20
lr = 0.1
data_time_steps = np.linspace(2, 10, seq_length + 1)
data = np.sin(data_time_steps)
data.resize((seq_length + 1, 1))

x = Variable(torch.Tensor(data[:-1]).type(dtype), requires_grad=False)
y = Variable(torch.Tensor(data[1:]).type(dtype), requires_grad=False)

Generaremos datos de entrenamiento, donde x es la secuencia de datos de entrada e y es la secuencia objetivo requerida.

Paso 3

Los pesos se inicializan en la red neuronal recurrente utilizando una distribución normal con media cero. W1 representará la aceptación de las variables de entrada y w2 representará la salida que se genera como se muestra a continuación:

w1 = torch.FloatTensor(input_size, 
hidden_size).type(dtype)
init.normal(w1, 0.0, 0.4)
w1 = Variable(w1, requires_grad = True)
w2 = torch.FloatTensor(hidden_size, output_size).type(dtype)
init.normal(w2, 0.0, 0.3)
w2 = Variable(w2, requires_grad = True)

Etapa 4

Ahora, es importante crear una función para la retroalimentación que defina de manera única la red neuronal.

def forward(input, context_state, w1, w2):
   xh = torch.cat((input, context_state), 1)
   context_state = torch.tanh(xh.mm(w1))
   out = context_state.mm(w2)
   return (out, context_state)

Paso 5

El siguiente paso es iniciar el procedimiento de entrenamiento de la implementación de la onda sinusoidal de la red neuronal recurrente. El ciclo externo itera sobre cada ciclo y el ciclo interno itera a través del elemento de secuencia. Aquí, también calcularemos el error cuadrático medio (MSE) que ayuda en la predicción de variables continuas.

for i in range(epochs):
   total_loss = 0
   context_state = Variable(torch.zeros((1, hidden_size)).type(dtype), requires_grad = True)
   for j in range(x.size(0)):
      input = x[j:(j+1)]
      target = y[j:(j+1)]
      (pred, context_state) = forward(input, context_state, w1, w2)
      loss = (pred - target).pow(2).sum()/2
      total_loss += loss
      loss.backward()
      w1.data -= lr * w1.grad.data
      w2.data -= lr * w2.grad.data
      w1.grad.data.zero_()
      w2.grad.data.zero_()
      context_state = Variable(context_state.data)
   if i % 10 == 0:
      print("Epoch: {} loss {}".format(i, total_loss.data[0]))

context_state = Variable(torch.zeros((1, hidden_size)).type(dtype), requires_grad = False)
predictions = []

for i in range(x.size(0)):
   input = x[i:i+1]
   (pred, context_state) = forward(input, context_state, w1, w2)
   context_state = context_state
   predictions.append(pred.data.numpy().ravel()[0])

Paso 6

Ahora es el momento de graficar la onda sinusoidal como se necesita.

pl.scatter(data_time_steps[:-1], x.data.numpy(), s = 90, label = "Actual")
pl.scatter(data_time_steps[1:], predictions, label = "Predicted")
pl.legend()
pl.show()

Salida

El resultado del proceso anterior es el siguiente: