递归网络(RNN)不会学习非常简单的功能(问题中显示的图表)

时间:2018-10-17 14:23:05

标签: python pytorch recurrent-neural-network

因此,我正在尝试训练一个简单的递归网络以检测输入信号中的“突发”。下图显示了RNN的输入信号(蓝色)和所需的(分类)输出,以红色显示。

Then end of the sine-shaped input signal burst should be detected.

因此,每当检测到突发时,网络的输出应从1切换为0,并保持与该输出相同的状态。在用于训练RNN的输入序列之间唯一改变的是突发发生的时间步长。

遵循https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/403_RNN_regressor.py上的教程之后,我无法学习RNN。学习的RNN始终以“无内存”方式运行,即不使用内存进行预测,如以下示例行为所示:

The same plot as before, but this time with the output behavior of the network.

绿线显示网络的预测输出。 在此示例中我怎么做才能使网络无法正确学习?网络任务不是很简单吗?

我正在使用:

  1. torch.nn.CrossEntropyLoss作为损失函数
  2. 用于学习的Adam优化器
  3. 具有16个内部/隐藏节点和2个输出节点的RNN。他们使用了torch.RNN类的默认激活功能。

已使用不同的随机种子重复了几次实验,但结果几乎没有差异。我使用了以下代码:

import torch
import numpy, math
import matplotlib.pyplot as plt

nofSequences = 5
maxLength = 130

# Generate training data
x_np = numpy.zeros((nofSequences,maxLength,1))
y_np = numpy.zeros((nofSequences,maxLength))
numpy.random.seed(1)
for i in range(0,nofSequences):
    startPos = numpy.random.random()*50
    for j in range(0,maxLength):
        if j>=startPos and j<startPos+10:
            x_np[i,j,0] = math.sin((j-startPos)*math.pi/10)
        else:
            x_np[i,j,0] = 0.0
        if j<startPos+10:
            y_np[i,j] = 1
        else:
            y_np[i,j] = 0


# Define the neural network
INPUT_SIZE = 1
class RNN(torch.nn.Module):
    def __init__(self):
        super(RNN, self).__init__()

        self.rnn = torch.nn.RNN(
            input_size=INPUT_SIZE,
            hidden_size=16,     # rnn hidden unit
            num_layers=1,       # number of rnn layer
            batch_first=True,
        )
        self.out = torch.nn.Linear(16, 2)

    def forward(self, x, h_state):
        r_out, h_state = self.rnn(x, h_state)

        outs = []    # save all predictions
        for time_step in range(r_out.size(1)):    # calculate output for each time step
            outs.append(self.out(r_out[:, time_step, :]))
        return torch.stack(outs, dim=1), h_state

# Learn the network
rnn = RNN()
optimizer = torch.optim.Adam(rnn.parameters(), lr=0.01)
h_state = None      # for initial hidden state

x = torch.Tensor(x_np)    # shape (batch, time_step, input_size)
y = torch.Tensor(y_np).long()

torch.manual_seed(2)
numpy.random.seed(2)

for step in range(100):

    prediction, h_state = rnn(x, h_state)   # rnn output

    # !! next step is important !!
    h_state = h_state.data        # repack the hidden state, break the connection from last iteration

    loss = torch.nn.CrossEntropyLoss()(prediction.reshape((-1,2)),torch.autograd.Variable(y.reshape((-1,))))         # calculate loss
    optimizer.zero_grad()                   # clear gradients for this training step
    loss.backward()                         # backpropagation, compute gradients
    optimizer.step()                        # apply gradients

    errTrain = (prediction.max(2)[1].data != y).float().mean()
    print("Error Training:",errTrain.item())

对于那些想重现实验的人,使用以下代码(使用Jupyter Notebook)绘制绘图:

steps = range(0,maxLength)
plotChoice = 3

plt.figure(1, figsize=(12, 5))
plt.ion()           # continuously plot

plt.plot(steps, y_np[plotChoice,:].flatten(), 'r-')
plt.plot(steps, numpy.argmax(prediction.detach().numpy()[plotChoice,:,:],axis=1), 'g-')
plt.plot(steps, x_np[plotChoice,:,0].flatten(), 'b-')

plt.ioff()
plt.show()

1 个答案:

答案 0 :(得分:9)

根据tourch.nn.RNN的文档,RNN实际上是一个Elman网络,并且具有here所示的以下属性。 Elman网络的输出仅取决于隐藏状态,而隐藏状态则取决于最后一个输入和上一个隐藏状态。

由于我们设置了“ h_state = h_state.data”,因此实际上我们使用了最后一个序列的隐藏状态来预测新序列的第一个状态,这将导致输出严重依赖于前一个序列的最后一个输出顺序(为0)。如果我们在序列的开头或结尾处,Elman网络无法分开,它只能“看到”状态和最后输入。

要解决此问题,我们可以设置“ h_state = None”。 现在,每个新序列都从一个空状态开始。此结果将导致以下预测(其中绿线再次显示该预测)。enter image description here 现在我们从1开始,但是在脉冲再次将其推回之前迅速下降到0。 Elman网络可以解决一些时间依赖性,但是它不善于记住长期依赖性,并且会收敛到该输入的“最常见输出”。

因此,为了解决此问题,我建议使用一个众所周知的能够很好地处理长期依赖关系的网络,即长期短期记忆(LSTM)rnn,有关更多信息,请参见torch.nn.LSTM。保持“ h_state = None”,然后将torch.nn.RNN更改为torch.nn.LSTM。

有关完整的代码和图解,请参见下文

input.keyBy(0, 1).timeWindow(Time.days(1))

enter image description here