PyTorch 0.4 LSTM:为什么每个时代变慢?

时间:2018-06-17 18:51:48

标签: python-3.x pytorch

我在GPU上有一个PyTorch 0.4 LSTM的玩具模型。玩具问题的总体思路是我将单个3向量定义为输入,并定义旋转矩阵R.然后,地面实况目标是向量序列:在T0,输入向量;在T1处,输入矢量由R旋转;在T2,输入由R旋转两次,等等(输入填充输出长度,T1后为零输入)

损失是地面实况与产出之间的平均L2差异。旋转矩阵,输入/输出数据的构造和损失函数可能不感兴趣,这里没有显示。

没关系,结果非常糟糕:为什么每过一个时代这会变得越来越慢?!

我在下面显示了GPU上的信息,但这也发生在CPU上(只有更长的时间。)执行这个愚蠢小事的十个时代的时间迅速增长。只是看着数字滚动,这是非常明显的。

epoch:   0,     loss: 0.1753,   time previous: 33:28.616360 time now: 33:28.622033  time delta: 0:00:00.005673
epoch:  10,     loss: 0.2568,   time previous: 33:28.622033 time now: 33:28.830665  time delta: 0:00:00.208632
epoch:  20,     loss: 0.2092,   time previous: 33:28.830665 time now: 33:29.324966  time delta: 0:00:00.494301
epoch:  30,     loss: 0.2663,   time previous: 33:29.324966 time now: 33:30.109241  time delta: 0:00:00.784275
epoch:  40,     loss: 0.1965,   time previous: 33:30.109241 time now: 33:31.184024  time delta: 0:00:01.074783
epoch:  50,     loss: 0.2232,   time previous: 33:31.184024 time now: 33:32.556106  time delta: 0:00:01.372082
epoch:  60,     loss: 0.1258,   time previous: 33:32.556106 time now: 33:34.215477  time delta: 0:00:01.659371
epoch:  70,     loss: 0.2237,   time previous: 33:34.215477 time now: 33:36.173928  time delta: 0:00:01.958451
epoch:  80,     loss: 0.1076,   time previous: 33:36.173928 time now: 33:38.436041  time delta: 0:00:02.262113
epoch:  90,     loss: 0.1194,   time previous: 33:38.436041 time now: 33:40.978748  time delta: 0:00:02.542707
epoch: 100,     loss: 0.2099,   time previous: 33:40.978748 time now: 33:43.844310  time delta: 0:00:02.865562

模特:

class Sequence(torch.nn.Module):
def __init__ (self):
    super(Sequence, self).__init__()

    self.lstm1 = nn.LSTM(3,30)
    self.lstm2 = nn.LSTM(30,300)
    self.lstm3 = nn.LSTM(300,30)
    self.lstm4 = nn.LSTM(30,3)

    self.hidden1 = self.init_hidden(dim=30)
    self.hidden2 = self.init_hidden(dim=300)
    self.hidden3 = self.init_hidden(dim=30)
    self.hidden4 = self.init_hidden(dim=3)

    self.dense   = torch.nn.Linear(30, 3)   
    self.relu    = nn.LeakyReLU()

def init_hidden(self, dim):
    return (torch.zeros(1, 1, dim).to(device)  ,torch.zeros(1, 1, dim).to(device)  )      

def forward(self, inputs):
    out1, self.hidden1 = self.lstm1(inputs, self.hidden1)
    out2, self.hidden2 = self.lstm2(out1,   self.hidden2)
    out3, self.hidden3 = self.lstm3(out2,   self.hidden3)
    #out4, self.hidden4 = self.lstm4(out3,   self.hidden4)   

    # This is intended to act as a dense layer on the output of the LSTM
    out4               = self.relu(self.dense(out3))        

    return out4

训练循环:

sequence = Sequence().to(device)

criterion = L2_Loss()
optimizer = torch.optim.Adam(sequence.parameters())
_, _, _, R = getRotation(np.pi/27, np.pi/26, np.pi/25)

losses = []
date1 = datetime.datetime.now()
for epoch in range(1001):
    # Define input as a Variable-- each row of 3 is a vector, a distinct input
    # Define target directly from input by applicatin of rotation vector
    # Define predictions by running input through model 

    inputs       = getInput(25)
    targets      = getOutput(inputs, R)

    inputs       = torch.cat(inputs).view(len(inputs), 1, -1).to(device)
    targets      = torch.cat(targets).view(len(targets), 1, -1).to(device)

    target_preds = sequence(inputs)
    target_preds = target_preds.view(len(target_preds), 1, -1)
    loss = criterion(targets, target_preds).to(device)

    losses.append(loss.data[0])
    if (epoch % 10 == 0):
        date2 = datetime.datetime.now()
        print("epoch: %3d, \tloss: %6.4f, \ttime previous: %s\ttime now: %s\ttime delta: %s" % (epoch, loss.data[0], date1.strftime("%M:%S.%f"), date2.strftime("%M:%S.%f"), date2 - date1))
        date1 = date2
    # Zero out the grads, run the loss backward, and optimize on the grads
    optimizer.zero_grad()
    loss.backward(retain_graph=True)
    optimizer.step() 

1 个答案:

答案 0 :(得分:0)

简短的回答:由于我们没有分离隐藏层,因此系统随着时间的推移不断向后传播,从而占用更多内存并需要更多时间。

长答案:此答案旨在在没有老师强迫的情况下工作。 “教师强制”是指所有时间步长的所有输入均为“基本事实”输入值。相反,在没有老师强迫的情况下,每个时间步长的输入都是前一个时间步长的输出,而不管该数据在训练方案中处于多早(因此有多疯狂)。

这是PyTorch中的手动操作,它要求我们不仅跟踪输出,而且跟踪每一步网络的隐藏状态,因此我们可以将其提供给下一个。分离必须发生,而不是在每个时间步,而是在每个序列的开始。似乎可行的方法是在“序列”模型中定义“分离”方法(它将手动分离所有隐藏层),并显式调用它 在optimizer.step()之后。

这可以防止隐藏状态的逐渐积累,防止逐渐变慢,并且仍然可以训练网络。

我不能真正为其提供担保,因为我只是将其用于玩具模型,而不是真正的问题。

注1:可能还有更好的方法来分解网络的初始化并使用它代替手动分离。

注2:loss.backward(retain_graph=True)语句保留图形,因为错误消息提示它。一旦执行了分离,该警告就会消失。

我不接受这个答案,希望有知识的人会增加他们的专业知识。