我正在尝试将注意力机制添加到堆栈LSTM实现https://github.com/salesforce/awd-lstm-lm中
所有在线示例都使用编码器-解码器架构,我不想使用它(注意力机制必须使用吗?)。
def __init__(self, rnn_type, ntoken, ninp, nhid, nlayers, dropout=0.5, dropouth=0.5, dropouti=0.5, dropoute=0.1, wdrop=0, tie_weights=False):
super(RNNModel, self).__init__()
self.encoder = nn.Embedding(ntoken, ninp)
self.rnns = [torch.nn.LSTM(ninp if l == 0 else nhid, nhid if l != nlayers - 1 else (ninp if tie_weights else nhid), 1, dropout=0) for l in range(nlayers)]
for rnn in self.rnns:
rnn.linear = WeightDrop(rnn.linear, ['weight'], dropout=wdrop)
self.rnns = torch.nn.ModuleList(self.rnns)
self.attn_fc = torch.nn.Linear(ninp, 1)
self.decoder = nn.Linear(nhid, ntoken)
self.init_weights()
def attention(self, rnn_out, state):
state = torch.transpose(state, 1,2)
weights = torch.bmm(rnn_out, state)# torch.bmm(rnn_out, state)
weights = torch.nn.functional.softmax(weights)#.squeeze(2)).unsqueeze(2)
rnn_out_t = torch.transpose(rnn_out, 1, 2)
bmmed = torch.bmm(rnn_out_t, weights)
bmmed = bmmed.squeeze(2)
return bmmed
def forward(self, input, hidden, return_h=False, decoder=False, encoder_outputs=None):
emb = embedded_dropout(self.encoder, input, dropout=self.dropoute if self.training else 0)
emb = self.lockdrop(emb, self.dropouti)
new_hidden = []
raw_outputs = []
outputs = []
for l, rnn in enumerate(self.rnns):
temp = []
for item in emb:
item = item.unsqueeze(0)
raw_output, new_h = rnn(item, hidden[l])
raw_output = self.attention(raw_output, new_h[0])
temp.append(raw_output)
raw_output = torch.stack(temp)
raw_output = raw_output.squeeze(1)
new_hidden.append(new_h)
raw_outputs.append(raw_output)
if l != self.nlayers - 1:
raw_output = self.lockdrop(raw_output, self.dropouth)
outputs.append(raw_output)
hidden = new_hidden
output = self.lockdrop(raw_output, self.dropout)
outputs.append(output)
outputs = torch.stack(outputs).squeeze(0)
outputs = torch.transpose(outputs, 2,1)
output = output.transpose(2,1)
output = output.contiguous()
decoded = self.decoder(output.view(output.size(0)*output.size(1), output.size(2)))
result = decoded.view(output.size(0), output.size(1), decoded.size(1))
if return_h:
return result, hidden, raw_outputs, outputs
return result, hidden

这个模型正在训练,但与没有注意力模型的模型相比,我的损失相当高。
发布于 2018-03-12 16:48:52
我理解你的问题,但要按照你的代码找出损失没有减少的原因是有点困难的。此外,还不清楚为什么要将RNN的最后一个隐藏状态与每个时间步的所有隐藏状态进行比较。
请注意,如果你以正确的方式使用一个特殊的技巧/机制是很有用的。你试图使用注意力机制的方式,我不确定这是不是正确的方式。所以,不要期望因为你在模型中使用了注意力技巧,你就会得到好的结果!你应该想想,为什么注意力机制会给你想要的任务带来好处?
你没有清楚地提到你的目标是什么?由于您已经指出了一个包含语言建模代码的repo,我猜任务是:给定一系列令牌,预测下一个令牌。
我在您的代码中看到的一个可能的问题是:在for item in emb:循环中,您将始终使用嵌入作为每个LSTM层的输入,因此堆叠的LSTM对我来说没有意义。
现在,让我先回答你的问题,然后一步一步地展示如何构建你想要的NN架构。
我需要使用编码器-解码器架构才能使用注意力机制吗?
编码器-解码器架构更为人所知的是序列到序列的学习,它被广泛地应用于许多生成任务,例如机器翻译。你的问题的答案是no,你不需要使用任何特定的神经网络架构来使用注意力机制。
您在图中展示的结构有点模棱两可,但应该很容易实现。由于我不清楚您的实现方式,我正在尝试引导您使用更好的方式来实现它。对于下面的讨论,我假设我们正在处理文本输入。
假设我们有一个形状为16 x 10的输入,其中16是batch_size,10是seq_len。我们可以假设我们在一个小批量中有16个句子,每个句子的长度是10。
batch_size, vocab_size = 16, 100
mat = np.random.randint(vocab_size, size=(batch_size, 10))
input_var = Variable(torch.from_numpy(mat))这里,100可以被认为是词汇表的大小。重要的是要注意,在我提供的整个例子中,我假设batch_size是所有相应张量/变量的第一维。
现在,让我们嵌入输入变量。
embedding = nn.Embedding(100, 50)
embed = embedding(input_var)嵌入之后,我们得到了一个形状变量16 x 10 x 50,其中50是嵌入大小。
现在,让我们定义一个两层的单向LSTM,每层有100个隐藏单元。
rnns = nn.ModuleList()
nlayers, input_size, hidden_size = 2, 50, 100
for i in range(nlayers):
input_size = input_size if i == 0 else hidden_size
rnns.append(nn.LSTM(input_size, hidden_size, 1, batch_first=True))然后,我们可以将输入提供给这个2层LSTM以获得输出。
sent_variable = embed
outputs, hid = [], []
for i in range(nlayers):
if i != 0:
sent_variable = F.dropout(sent_variable, p=0.3, training=True)
output, hidden = rnns[i](sent_variable)
outputs.append(output)
hid.append(hidden[0].squeeze(0))
sent_variable = output
rnn_out = torch.cat(outputs, 2)
hid = torch.cat(hid, 1)现在,您可以简单地使用hid来预测下一个单词。我建议你这样做。这里,hid的形状是batch_size x (num_layers*hidden_size)。
但是,由于您希望使用注意力来计算最后一个隐藏状态与LSTM层产生的每个隐藏状态之间的软对齐分数,因此让我们这样做。
sent_variable = embed
hid, con = [], []
for i in range(nlayers):
if i != 0:
sent_variable = F.dropout(sent_variable, p=0.3, training=True)
output, hidden = rnns[i](sent_variable)
sent_variable = output
hidden = hidden[0].squeeze(0) # batch_size x hidden_size
hid.append(hidden)
weights = torch.bmm(output[:, 0:-1, :], hidden.unsqueeze(2)).squeeze(2)
soft_weights = F.softmax(weights, 1) # batch_size x seq_len
context = torch.bmm(output[:, 0:-1, :].transpose(1, 2), soft_weights.unsqueeze(2)).squeeze(2)
con.append(context)
hid, con = torch.cat(hid, 1), torch.cat(con, 1)
combined = torch.cat((hid, con), 1)在这里,我们计算最后一个状态与每个时间步的所有状态之间的软对齐分数。然后我们计算一个上下文向量,它只是所有隐藏状态的线性组合。我们将它们组合在一起形成一个单一的表示。
请注意,我已经从output:output[:, 0:-1, :]中删除了最后一个隐藏状态,因为您正在与最后一个隐藏状态本身进行比较。
最终的combined表示存储在每一层产生的最后隐藏状态和上下文向量。您可以直接使用此表示法来预测下一个单词。
预测下一个单词是直截了当的,因为你正在使用一个简单的线性层。
编辑:我们可以做以下操作来预测下一个单词。
decoder = nn.Linear(nlayers * hidden_size * 2, vocab_size)
dec_out = decoder(combined)这里,dec_out的形状是batch_size x vocab_size。现在,我们可以计算负对数似然损失,稍后将用于反向传播。
在计算负对数似然损失之前,我们需要对解码器的输出应用log_softmax。
dec_out = F.log_softmax(dec_out, 1)
target = np.random.randint(vocab_size, size=(batch_size))
target = Variable(torch.from_numpy(target))并且,我们还定义了计算损失所需的目标。详情请参见NLLLoss。因此,现在我们可以按如下方式计算损失。
criterion = nn.NLLLoss()
loss = criterion(dec_out, target)
print(loss)打印损失值为:
Variable containing:
4.6278
[torch.FloatTensor of size 1]希望整个解释能对你有所帮助!
发布于 2018-03-06 17:22:27
要注意的是,不同语言中的词序是不同的,因此在解码目标语言中的第5个单词时,您可能需要注意源语言中的第3个单词(或第3个单词的编码),因为这些单词彼此对应。这就是为什么你最常看到的注意力与编码器和解码器结构一起使用。
如果我没记错的话,你在做下一个单词预测?在这种情况下,使用注意力可能仍然有意义,因为下一个单词可能在很大程度上依赖于过去的单词4步骤。
所以基本上你需要的是:
rnn:接受shape MBxninp的input和shape MBxnhid的hidden,输出shape MBxnhid的h。
h, next_hidden = rnn(input, hidden)注意:h的顺序是什么,最后一个h_last通过给每个w一个权重来决定它们的重要性。
w = attention(hs, h_last)其中w的形状为seq_len x MB x 1,hs的形状为seq_len x MB x nhid,h_last的形状为MB x nhid。
现在根据w对hs进行加权:
h_att = torch.sum(w*hs, dim=0) #shape MB x n_hid现在的重点是,你需要为每一个时间步做这件事:
h_att_list = []
h_list = []
hidden = hidden_init
for word in embedded_words:
h, hidden = rnn(word, hidden)
h_list.append(h)
h_att = attention(torch.stack(h_list), h)
h_att_list.append(h_att)然后,您可以在h_att_list上应用解码器(可能需要使用MLP,而不仅仅是线性变换)。
https://stackoverflow.com/questions/49086221
复制相似问题