178 votes

PyTorch - contigu ()

J'allais à travers cet exemple de la LSTM modèle de langue sur github (lien). Ce qu'il fait, en général, est assez clair pour moi. Mais j'ai encore des difficultés à comprendre ce qu'appelant contiguous() , ce qui se produit plusieurs fois dans le code.

Par exemple, dans la ligne de 74/75 du code de l'entrée et des séquences cibles de la LSTM sont créés. De données (stockées dans ids) est en 2 dimensions où la première dimension est la taille du lot.

for i in range(0, ids.size(1) - seq_length, seq_length):
    # Get batch inputs and targets
    inputs = Variable(ids[:, i:i+seq_length])
    targets = Variable(ids[:, (i+1):(i+1)+seq_length].contiguous())

Donc, comme un simple exemple, lors de l'utilisation de la taille des lots 1 et seq_length 10 inputs et targets ressemble à ceci:

inputs Variable containing:
0     1     2     3     4     5     6     7     8     9
[torch.LongTensor of size 1x10]

targets Variable containing:
1     2     3     4     5     6     7     8     9    10
[torch.LongTensor of size 1x10]

Donc, en général, ma question est, qu'est - contiguous() et pourquoi en ai-je besoin?

De plus je ne comprends pas pourquoi la méthode est appelée pour la séquence cible et mais pas la séquence d'entrée que les deux variables sont composés des mêmes données.

Comment pourrait - targets être uncontiguous et inputs encore être contigus?

EDIT: J'ai essayé de laisser de côté les appelant contiguous(), mais cela conduit à un message d'erreur lors du calcul de la perte.

RuntimeError: invalid argument 1: input is not contiguous at .../src/torch/lib/TH/generic/THTensor.c:231

Alors, évidemment, appelant contiguous() dans cet exemple est nécessaire.

(Pour garder cette lisibles j'ai évité de poster le code complet ici, il peut être trouvé en utilisant le GitHub lien ci-dessus.)

Merci à l'avance!

314voto

ShitalShah Points 2213

Il y a quelques opérations sur les Tenseurs en PyTorch qui ne change pas vraiment le contenu du tenseur, mais seulement la façon de convertir les indices de tenseur des octets de l'emplacement. Ces opérations comprennent:

narrow(), view(), expand() et transpose()

Par exemple: lorsque vous appelez transpose(), PyTorch ne pas générer de nouvelles tenseur avec la nouvelle mise en page, c'est juste modifie les méta-informations dans Tenseur objet, afin de compenser et de la foulée sont de nouveau en forme. Le tenseur transposé et tenseur d'origine sont en effet le partage de la mémoire!

x = torch.randn(3,2)
y = torch.transpose(x, 0, 1)
x[0, 0] = 42
print(y[0,0])
# prints 42

C'est là que le concept de contiguë vient dans. Ci-dessus x est contigus, mais y n'est pas parce que sa mémoire mise en page est différente de celle d'un tenseur de même forme, fabriqué à partir de zéro. Notez que le mot "ligne" est un peu trompeuse, car ce n'est pas que le contenu du tenseur s'étale autour d'déconnecté des blocs de mémoire. Ici octets sont toujours attribuées dans un bloc de mémoire, mais l'ordre des éléments est différent!

Lorsque vous appelez contiguous(), il est en fait une copie de tenseur de l'ordre des éléments serait même que si le tenseur de même forme créée à partir de zéro.

Normalement, vous n'avez pas besoin de vous inquiéter à ce sujet. Si PyTorch attend contiguë tenseur mais si ses pas, puis vous obtiendrez RuntimeError: input is not contiguous , puis il suffit d'ajouter un appel à l' contiguous().

43voto

patapouf_ai Points 6350

À partir de la pytorch de la documentation:

contiguë() → Tenseur

Returns a contiguous tensor containing the same data as self 

tenseur. Si l'auto tenseur est contiguë, cette fonction renvoie l'auto tenseur.

contiguous signifie ici contigus en mémoire. Si l' contiguous fonction n'affecte pas votre cible tenseur du tout, il a juste fait en sorte qu'il est stocké dans un espace contigu de mémoire.

21voto

avatar Points 61

tenseur.contiguë() permet de créer une copie du tenseur, et l'élément de la copie est stockée dans la mémoire contiguë façon. La zone contiguë() la fonction est généralement nécessaire lorsque nous avons d'abord transpose() un tenseur et puis remodeler () il. Tout d'abord, nous allons créer une zone contiguë de tenseur:

aaa = torch.Tensor( [[1,2,3],[4,5,6]] )
print(aaa.stride())
print(aaa.is_contiguous())
#(3,1)
#True

La foulée() return (3,1) signifie que: lorsque l'on déplace le long de la première dimension de chaque étape (ligne par ligne), nous avons besoin de passer 3 étapes dans la mémoire. Lorsque vous déplacez le long de la deuxième dimension (colonne par colonne), nous devons aller de l'étape 1 dans la mémoire. Cela indique que les éléments du tenseur sont stockés de manière contiguë.

Maintenant, nous essayons d'appliquer viennent fonctions pour le tenseur:

bbb = aaa.transpose(0,1)
print(bbb.stride())
print(bbb.is_contiguous())

ccc = aaa.narrow(1,1,2)   ## equivalent to matrix slicing aaa[:,1:3]
print(ccc.stride())
print(ccc.is_contiguous())


ddd = aaa.repeat(2,1 )   # The first dimension repeat once, the second dimension repeat twice
print(ddd.stride())
print(ddd.is_contiguous())

## expand is different from repeat  if a tensor has a shape [d1,d2,1], it can only be expanded using "expand(d1,d2,d3)", which
## means the singleton dimension is repeated d3 times
eee = aaa.unsqueeze(2).expand(2,3,3)
print(eee.stride())
print(eee.is_contiguous())

fff = aaa.unsqueeze(2).repeat(1,1,8).view(2,-1,2)
print(fff.stride())
print(fff.is_contiguous())

#(1, 3)
#False
#(3, 1)
#False
#(3, 1)
#True
#(3, 1, 0)
#False
#(24, 2, 1)
#True

Ok, on peut trouver que transpose(), étroit() et du tenseur de découpage, et d'élargir() fera l'généré tenseur ne sont pas contigus. Il est intéressant de noter, répéter() et afficher() ne discontinus. Alors maintenant, la question est: qu'advient-il si j'utilise un discontinus tenseur?

La réponse est-elle le point de vue() la fonction ne peut pas être appliqué à un discontinus tenseur. C'est probablement parce que la vue() exige que le tenseur d'être stockés de manière contiguë afin qu'elle puisse faire rapide remodeler en mémoire. e.g:

bbb.view(-1,3)

nous vous obtenez le message d'erreur:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-63-eec5319b0ac5> in <module>()
----> 1 bbb.view(-1,3)

RuntimeError: invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view(). at /pytorch/aten/src/TH/generic/THTensor.cpp:203

Pour résoudre ce problème, ajoutez simplement contigus() pour un discontinus tenseur, pour créer contiguë copier et ensuite appliquer l'affichage()

bbb.contiguous().view(-1,3)
#tensor([[1., 4., 2.],
        [5., 3., 6.]])

11voto

p. vignesh Points 81

Comme dans la réponse précédente, contigous () alloue des blocs de mémoire contigus , il sera utile lorsque nous passons tenseur à du code backend c ou c ++ dans lequel les tenseurs sont transmis en tant que pointeurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X