[PYTHON] J'ai vérifié les spécifications de sortie du LSTM bidirectionnel de PyTorch

introduction

Lors de la déclaration d'un LSTM lors de l'utilisation de LSTM bidirectionnel dans PyTorch, comme dans la référence LSTM (https://pytorch.org/docs/stable/nn.html?highlight=lstm#torch.nn.LSTM) C'est OK juste de spécifier bidirectional = True pour, et c'est très facile à gérer (Keras est OK juste pour entourer LSTM avec Bidrectional). Cependant, en regardant la référence, je ne pense pas qu'il y ait beaucoup de mention de la sortie de la fabrication du LSTM bidirectionnel. Même si je l'ai recherché sur Google, je ne pouvais pas comprendre les spécifications de sortie de Bidirectional LSTM dans PyTorch, je vais donc le résumer ici.

référence

  1. Bidirectional recurrent neural networks
  2. Understanding Bidirectional RNN in PyTorch

Confirmation des spécifications

Comme vous pouvez le voir à partir des références 1 et 2, vous pouvez voir que les RNN et LSTM bidirectionnels sont très simples parce que les RNN et LSTM dans les directions avant et arrière se chevauchent.

Je vais en fait l'utiliser pour le moment.

import torch
import torch.nn as nn

#5 dimensions intégrées pour chaque série
#La taille de la couche cachée de la couche LSTM est de 6
# batch_first=Vrai pour le format d'entrée(batch_size, vocab_size, embedding_dim)Je fais
# bidrectional=Déclarez le LSTM bidirectionnel avec True
bilstm = nn.LSTM(5, 6, batch_first=True, bidirectional=True)

#Taille du lot 1
#La longueur de la série est de 4
#Le nombre de dimensions intégrées de chaque série est de 5
#Générer un tenseur comme
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]]])

#Comme un LSTM normal, il a deux sorties, donc il reçoit les deux.
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>))

Comme avec le LSTM normal, il y a deux sorties, ʻout et hc, et hc renvoie hc = (h, c) ʻen format tapple comme avec le LSTM normal. Je pense qu'il y a deux différences par rapport à la sortie du LSTM normal.

―― La dimension de chaque élément de ʻout` n'est pas la taille de la dimension de la couche cachée de LSTM (6 cette fois), mais le double de la taille (12 cette fois).

Ce qui suit est une brève explication de ce que cela signifie.

(«C» est omis. J'ai écrit la couche Embedding, mais la couche Embedding n'est pas effectuée par LSTM.)

image.png

image.png

Comme vous pouvez le voir sur la figure ci-dessus, chaque élément de ʻoutconnecte chaque vecteur de couche caché dans les directions avant et arrière. (Ainsi, les dimensions de chaque élément sont deux fois plus grandes que d'habitude.) De plus,h in hc = (h, c)` renvoie le dernier vecteur de calque caché dans les directions avant et arrière respectivement.

En d'autres termes

--La première moitié du dernier élément de ʻoutcorrespond àh [0]quandhc = (h, c)`

Sera. Vous pouvez le lire à partir de la sortie du code source de l'exemple ci-dessus, ce qui signifie que.

print(out[:,-1][:,:6]) #La première moitié du dernier élément de out
print(hc[0][0])        #Valeur de la dernière couche cachée du LSTM avant
#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:]) #La moitié arrière du premier élément de out
print(hc[0][1])       #La valeur de la dernière couche cachée du LSTM arrière
#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>)

Une fois que vous connaissez les spécifications de sortie, vous pouvez cuisiner comme vous le souhaitez, Lors de la création d'un modèle plusieurs à un tel que la classification de phrases dans le LSTM bidirectionnel, il semble y avoir diverses méthodes telles que la combinaison de la deuxième valeur de retour de LSTM, la moyenne et la prise du produit d'élément. Dans le cas de Keras, il semble qu'il sera combiné côté Keras (par défaut), mais dans le cas de PyTorch, il semble que ces processus doivent être implémentés par vous-même. Par exemple, si je publie Classification des phrases par LSTM en tant que LSTM bidirectionnel, cela ressemblera à ce qui suit.

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)
        #Caché car il reçoit une combinaison des derniers vecteurs de calque cachés dans les directions avant et arrière_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]->Dernier vecteur de couche caché du LSTM avant
        # bilstm_out[0][1]->Dernier vecteur de couche caché LSTM arrière
        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

en conclusion

――C'est peut-être une histoire qui semble facile à comprendre dans le monde, mais même pour un moment lorsque vous traitez avec le LSTM bidirectionnel avec PyTorch comme vous? J'espère que cet article aidera ceux qui pensent qu'il est temps de le rechercher. Au fait, GRU devient GRU bidirectionnel avec bidirectional = True comme LSTM. Je pense qu'il n'y a pas de problème avec le format de sortie si les spécifications LSTM ci-dessus sont connues.

fin

Recommended Posts

J'ai vérifié les spécifications de sortie du LSTM bidirectionnel de PyTorch
J'ai vérifié le contenu du volume du docker
J'ai vérifié la liste des touches de raccourci de Jupyter
J'ai vérifié la période de rétention de session de django
J'ai vérifié la vitesse de traitement de la numpy unidimensionnelle
J'ai vérifié les versions de Blender et Python
J'ai vérifié le système d'exploitation et le shell par défaut de docker-machine
Filtrer la sortie de tracemalloc
Keras Je veux obtenir la sortie de n'importe quelle couche !!
J'ai étudié le mécanisme de connexion flask!
Je veux sortir le début du mois prochain avec Python
J'ai vérifié l'état d'utilisation du parking à partir d'images satellite.
J'ai vérifié l'image de l'Université des sciences sur Twitter avec Word2Vec.
J'ai vérifié le montant de la taxe sur les cadeaux
J'ai vérifié le nombre de magasins fermés et ouverts dans tout le pays par Corona
Je veux sortir froidement sur la console
J'ai essayé le serveur asynchrone de Django 3.0
Je ne connaissais pas les bases de Python
Le modèle de projet Python auquel je pense.
Décodage du modèle LSTM de Keras.
Sortie du nombre de cœurs de processeur en Python
J'ai essayé la fonction de tableau croisé dynamique des pandas
J'ai recherché dans la bibliothèque l'utilisation de l'API Gracenote
J'ai essayé l'analyse par grappes de la carte météo
J'ai lu l'implémentation de range (Objects / rangeobject.c)
Paramètre pour afficher le journal de l'exécution de cron
J'ai résolu le problème le plus profond d'Hiroshi Yuki.
J'ai essayé de corriger la forme trapézoïdale de l'image
Lire la sortie du sous-processus, ouvrir en temps réel
Essayez Progate Free Edition [Python I]
J'ai touché certaines des nouvelles fonctionnalités de Python 3.8 ①
Sortie sous la forme d'un tableau python
J'ai lu et implémenté les variantes de UKR
Je souhaite personnaliser l'apparence de zabbix
J'ai vérifié le calendrier supprimé dans le calendrier de l'Avent Qiita 2016
J'ai essayé d'utiliser le filtre d'image d'OpenCV
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
[SLAYER] J'ai essayé de confirmer l'âme d'acier en visualisant les paroles de slash metal [Word Cloud]