Beim Deklarieren eines LSTM beim Umgang mit bidirektionalem LSTM in PyTorch, wie in der LSTM-Referenz (https://pytorch.org/docs/stable/nn.html?highlight=lstm#torch.nn.LSTM) Es ist in Ordnung, nur "bidirektional = wahr" für anzugeben, und es ist sehr einfach zu handhaben (Keras ist in Ordnung, nur um das LSTM mit Bidrectional zu umgeben). Wenn ich mir die Referenz anschaue, denke ich nicht, dass die Ausgabe von LSTM Bidirectional viel erwähnt wird. Selbst wenn ich es gegoogelt habe, konnte ich die Ausgabespezifikationen von Bidirectional LSTM in PyTorch nicht verstehen, daher werde ich es hier zusammenfassen.
Wie Sie aus den Referenzen 1 und 2 sehen können, können Sie sehen, dass die bidirektionalen RNN und LSTM sehr einfach sind, da sich die RNN und LSTM in der vorderen und hinteren Richtung überlappen.
Ich werde es vorerst tatsächlich benutzen.
import torch
import torch.nn as nn
#5 eingebettete Dimensionen für jede Serie
#Die Größe der verborgenen Schicht der LSTM-Schicht beträgt 6
# batch_first=True für das Eingabeformat(batch_size, vocab_size, embedding_dim)Ich mache
# bidrectional=Deklarieren Sie bidirektionales LSTM mit True
bilstm = nn.LSTM(5, 6, batch_first=True, bidirectional=True)
#Chargengröße 1
#Die Länge der Serie beträgt 4
#Die Anzahl der eingebetteten Dimensionen jeder Serie beträgt 5
#Erzeugen Sie einen Tensor wie
a = torch.rand(1, 4, 5)
print(a)
#tensor([[[0.1360, 0.4574, 0.4842, 0.6409, 0.1980],
# [0.0364, 0.4133, 0.0836, 0.2871, 0.3542],
# [0.7796, 0.7209, 0.1754, 0.0147, 0.6572],
# [0.1504, 0.1003, 0.6787, 0.1602, 0.6571]]])
#Wie ein normales LSTM verfügt es über zwei Ausgänge, sodass beide empfangen werden.
out, hc = bilstm(a)
print(out)
#tensor([[[-0.0611, 0.0054, -0.0828, 0.0416, -0.0570, -0.1117, 0.0902, -0.0747, -0.0215, -0.1434, -0.2318, 0.0783],
# [-0.1194, -0.0127, -0.2058, 0.1152, -0.1627, -0.2206, 0.0747, -0.0210, 0.0307, -0.0708, -0.2458, 0.1627],
# [-0.0163, -0.0568, -0.0266, 0.0878, -0.1461, -0.1745, 0.1097, 0.0230, 0.0353, -0.0739, -0.2186, 0.0818],
# [-0.1145, -0.0460, -0.0732, 0.0950, -0.1765, -0.2599, 0.0063, 0.0143, 0.0124, 0.0089, -0.1188, 0.0996]]],
# grad_fn=<TransposeBackward0>)
print(hc)
#(tensor([[[-0.1145, -0.0460, -0.0732, 0.0950, -0.1765, -0.2599]],
# [[ 0.0902, -0.0747, -0.0215, -0.1434, -0.2318, 0.0783]]],
# grad_fn=<StackBackward>),
#tensor([[[-0.2424, -0.1340, -0.1559, 0.3499, -0.3792, -0.5514]],
# [[ 0.1876, -0.1413, -0.0384, -0.2345, -0.4982, 0.1573]]],
# grad_fn=<StackBackward>))
Wie bei normalem LSTM gibt es zwei Ausgänge, "out" und "hc", und "hc" gibt "hc = (h, c)" im Tapple-Format zurück, wie bei normalem LSTM. Ich denke, es gibt zwei Unterschiede zur Ausgabe von normalem LSTM.
h
und c
von hc
werden zurückgegeben.Das Folgende ist eine kurze Erklärung dessen, was diese bedeuten.
(C
wird weggelassen. Ich habe die Einbettungsschicht geschrieben, aber die Einbettungsschicht wird nicht von LSTM erstellt.)
Wie Sie aus der obigen Abbildung sehen können, verbindet jedes Element von "out" jeden verborgenen Ebenenvektor in Vorwärts- und Rückwärtsrichtung. (Die Abmessungen jedes Elements sind also doppelt so groß wie üblich.) Außerdem gibt "h" in "hc = (h, c)" den letzten verborgenen Schichtvektor in Vorwärts- bzw. Rückwärtsrichtung zurück.
Mit anderen Worten
Wird sein. Sie können es aus der Quellcode-Ausgabe des obigen Beispiels lesen, was bedeutet, dass.
print(out[:,-1][:,:6]) #Die erste Hälfte des letzten Elements von out
print(hc[0][0]) #Wert der letzten verborgenen Schicht von Forward-LSTM
#tensor([[-0.1145, -0.0460, -0.0732, 0.0950, -0.1765, -0.2599]], grad_fn=<SliceBackward>)
#tensor([[-0.1145, -0.0460, -0.0732, 0.0950, -0.1765, -0.2599]], grad_fn=<SelectBackward>)
print(out[:,0][:,6:]) #Die hintere Hälfte des ersten Elements von out
print(hc[0][1]) #Der Wert der letzten ausgeblendeten Ebene des Rückwärts-LSTM
#tensor([[ 0.0902, -0.0747, -0.0215, -0.1434, -0.2318, 0.0783]], grad_fn=<SliceBackward>)
#tensor([[ 0.0902, -0.0747, -0.0215, -0.1434, -0.2318, 0.0783]], grad_fn=<SelectBackward>)
Sobald Sie die Ausgabespezifikationen kennen, können Sie nach Belieben kochen. Bei der Erstellung eines Many-to-One-Modells wie der Satzklassifizierung in bidirektionales LSTM scheint es verschiedene Methoden zu geben, z. B. das Kombinieren des zweiten Rückgabewerts von LSTM, das Mitteln und das Aufnehmen des Elementprodukts. Im Fall von Keras scheint es (standardmäßig) auf der Keras-Seite kombiniert zu werden, aber im Fall von PyTorch müssen diese Prozesse anscheinend selbst implementiert werden. Wenn ich beispielsweise Satzklassifizierung nach LSTM als bidirektionales LSTM poste, sieht es wie folgt aus.
class LSTMClassifier(nn.Module):
def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size, batch_size=100):
super(LSTMClassifier, self).__init__()
self.batch_size = batch_size
self.hidden_dim = hidden_dim
self.word_embeddings = nn.Embedding(vocab_size, embedding_dim, padding_idx=0)
self.bilstm = nn.LSTM(embedding_dim, hidden_dim, batch_first=True, bidirectional=True)
#Versteckt, weil es eine Kombination der letzten verborgenen Ebenenvektoren in Vorwärts- und Rückwärtsrichtung empfängt_Double dim
self.hidden2tag = nn.Linear(hidden_dim * 2, tagset_size)
self.softmax = nn.LogSoftmax()
def forward(self, sentence):
embeds = self.word_embeddings(sentence)
_, bilstm_hc = self.bilstm(embeds)
# bilstm_out[0][0]->Letzter versteckter Schichtvektor des Vorwärts-LSTM
# bilstm_out[0][1]->Rückwärts LSTM letzter versteckter Schichtvektor
bilstm_out = torch.cat([bilstm_hc[0][0], bilstm_hc[0][1]], dim=1)
tag_space = self.hidden2tag(bilstm_out)
tag_scores = self.softmax(tag_space.squeeze())
return tag_scores
――Es mag eine Geschichte sein, die in der Welt leicht zu verstehen scheint, aber selbst für einen Moment, wenn Sie sich wie Sie mit Bidirektionalem LSTM mit PyTorch beschäftigen? Ich hoffe, dieser Artikel hilft denen, die denken, dass es Zeit ist, ihn nachzuschlagen. Übrigens wird GRU zu bidirektionaler GRU mit "bidirektional = wahr" wie LSTM. Ich denke, dass es kein Problem mit dem Ausgabeformat gibt, wenn die obigen LSTM-Spezifikationen bekannt sind.
Ende
Recommended Posts