我在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()
答案 0 :(得分:0)
简短的回答:由于我们没有分离隐藏层,因此系统随着时间的推移不断向后传播,从而占用更多内存并需要更多时间。
长答案:此答案旨在在没有老师强迫的情况下工作。 “教师强制”是指所有时间步长的所有输入均为“基本事实”输入值。相反,在没有老师强迫的情况下,每个时间步长的输入都是前一个时间步长的输出,而不管该数据在训练方案中处于多早(因此有多疯狂)。
这是PyTorch中的手动操作,它要求我们不仅跟踪输出,而且跟踪每一步网络的隐藏状态,因此我们可以将其提供给下一个。分离必须发生,而不是在每个时间步,而是在每个序列的开始。似乎可行的方法是在“序列”模型中定义“分离”方法(它将手动分离所有隐藏层),并显式调用它 在optimizer.step()之后。
这可以防止隐藏状态的逐渐积累,防止逐渐变慢,并且仍然可以训练网络。
我不能真正为其提供担保,因为我只是将其用于玩具模型,而不是真正的问题。
注1:可能还有更好的方法来分解网络的初始化并使用它代替手动分离。
注2:loss.backward(retain_graph=True)
语句保留图形,因为错误消息提示它。一旦执行了分离,该警告就会消失。
我不接受这个答案,希望有知识的人会增加他们的专业知识。