Je suis confus au sujet de la méthode view()
dans l'extrait de code suivant.
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*5*5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()
Ma confusion concerne la ligne suivante.
x = x.view(-1, 16*5*5)
Que fait la tensor.view()
fonction? J'ai vu son utilisation à de nombreux endroits, mais je ne comprends pas comment il interprète ses paramètres.
Que se passe-t-il si je donne des valeurs négatives comme paramètres à la view()
fonction? Par exemple, que se passe-t-il si j'appelle tensor_variable.view(1, 1, -1)
,?
Quelqu'un peut-il expliquer le principe principal de la view()
fonction avec quelques exemples?
reshape
dans PyTorch?!Faisons quelques exemples, du plus simple au plus difficile.
La
view
méthode renvoie un tenseur avec les mêmes données que leself
tenseur (ce qui signifie que le tenseur renvoyé a le même nombre d'éléments), mais avec une forme différente. Par exemple:En supposant que ce
-1
n'est pas l'un des paramètres, lorsque vous les multipliez ensemble, le résultat doit être égal au nombre d'éléments dans le tenseur. Si vous faites:,a.view(3, 3)
cela soulèvera unRuntimeError
car la forme (3 x 3) n'est pas valide pour une entrée avec 16 éléments. En d'autres termes: 3 x 3 n'est pas égal à 16 mais 9.Vous pouvez utiliser l'
-1
un des paramètres que vous transmettez à la fonction, mais une seule fois. Tout ce qui se passe, c'est que la méthode fera le calcul pour vous sur la façon de remplir cette dimension. Par exemple,a.view(2, -1, 4)
est équivalent àa.view(2, 2, 4)
. [16 / (2 x 4) = 2]Notez que le tenseur retourné partage les mêmes données . Si vous modifiez la "vue", vous modifiez les données du tenseur d'origine:
Maintenant, pour un cas d'utilisation plus complexe. La documentation indique que chaque nouvelle dimension de vue doit être soit un sous-espace d'une dimension d'origine, soit uniquement s'étendre sur d, d + 1, ..., d + k qui satisfont à la condition de contiguïté suivante qui, pour tout i = 0,. .., k - 1, foulée [i] = foulée [i + 1] x taille [i + 1] . Sinon,
contiguous()
doit être appelé avant que le tenseur ne puisse être visualisé. Par exemple:Notez que pour
a_t
, foulée [0]! = Foulée [1] x taille [1] depuis 24! = 2 x 3la source
torch.Tensor.view()
En termes simples,
torch.Tensor.view()
qui est inspiré parnumpy.ndarray.reshape()
ounumpy.reshape()
, crée une nouvelle vue du tenseur, tant que la nouvelle forme est compatible avec la forme du tenseur d'origine.Comprenons cela en détail à l'aide d'un exemple concret.
Avec ce tenseur
t
de forme(18,)
, de nouvelles vues ne peuvent être créées que pour les formes suivantes:(1, 18)
ou de manière équivalente(1, -1)
ou ou équivalente ou ou équivalente ou ou équivalente ou ou équivalente ou ou équivalente ou(-1, 18)
(2, 9)
(2, -1)
(-1, 9)
(3, 6)
(3, -1)
(-1, 6)
(6, 3)
(6, -1)
(-1, 3)
(9, 2)
(9, -1)
(-1, 2)
(18, 1)
(18, -1)
(-1, 1)
Comme nous pouvons déjà l'observer à partir des tuples de forme ci-dessus, la multiplication des éléments du tuple de forme (par exemple
2*9
,3*6
etc.) doit toujours être égale au nombre total d'éléments dans le tenseur d'origine (18
dans notre exemple).Une autre chose à observer est que nous avons utilisé un
-1
dans l'un des endroits de chacun des tuples de forme. En utilisant a-1
, nous sommes paresseux dans le calcul nous-mêmes et déléguons plutôt la tâche à PyTorch pour faire le calcul de cette valeur pour la forme lors de la création de la nouvelle vue . Une chose importante à noter est que nous pouvons seulement utiliser un seul-1
dans le tuple de forme. Les valeurs restantes doivent être fournies explicitement par nous. Sinon PyTorch se plaindra en lançant unRuntimeError
:Ainsi, avec toutes les formes mentionnées ci-dessus, PyTorch retournera toujours une nouvelle vue du tenseur d'origine
t
. Cela signifie essentiellement qu'il modifie simplement les informations de foulée du tenseur pour chacune des nouvelles vues demandées.Vous trouverez ci-dessous quelques exemples illustrant comment les foulées des tenseurs sont modifiées à chaque nouvelle vue .
Maintenant, nous allons voir les progrès pour les nouvelles vues :
Voilà donc la magie de la
view()
fonction. Il modifie simplement les enjambées du tenseur (d'origine) pour chacune des nouvelles vues , tant que la forme de la nouvelle vue est compatible avec la forme d'origine.Une autre chose intéressante que l'on peut observer à partir des tuples de foulées est que la valeur de l'élément en 0 ème position est égale à la valeur de l'élément en 1 ère position du tuple de forme.
Ceci est dû au fait:
la foulée
(6, 1)
dit que pour passer d'un élément à l'élément suivant le long de la 0 ème dimension, il faut sauter ou faire 6 pas. (c.-à-d. pour aller de0
à6
, il faut faire 6 étapes.) Mais pour passer d'un élément à l'élément suivant dans la 1ère dimension, nous n'avons besoin que d'une seule étape (par exemple pour aller de2
à3
).Ainsi, les informations de pas sont au cœur de l'accès aux éléments depuis la mémoire pour effectuer le calcul.
torch.reshape ()
Cette fonction retournerait une vue et est exactement la même que celle utilisée
torch.Tensor.view()
tant que la nouvelle forme est compatible avec la forme du tenseur d'origine. Sinon, il en retournera une copie.Cependant, les notes de
torch.reshape()
préviennent que:la source
Je l'ai trouvé
x.view(-1, 16 * 5 * 5)
équivalent àx.flatten(1)
, où le paramètre 1 indique que le processus d'aplatissement commence à partir de la 1ère dimension (pas d'aplatissement de la dimension `` échantillon '') Comme vous pouvez le voir, cette dernière utilisation est sémantiquement plus claire et plus facile à utiliser, donc je préférezflatten()
.la source
Vous pouvez lire
-1
comme nombre dynamique de paramètres ou "n'importe quoi". Pour cette raison, il ne peut y avoir qu'un seul paramètre-1
dansview()
.Si vous le demandez
x.view(-1,1)
, la forme du tenseur sortira en[anything, 1]
fonction du nombre d'éléments dansx
. Par exemple:Sortira:
la source
weights.reshape(a, b)
retournera un nouveau tenseur avec les mêmes données que les poids de taille (a, b) car il copie les données dans une autre partie de la mémoire.weights.resize_(a, b)
renvoie le même tenseur avec une forme différente. Cependant, si la nouvelle forme entraîne moins d'éléments que le tenseur d'origine, certains éléments seront supprimés du tenseur (mais pas de la mémoire). Si la nouvelle forme produit plus d'éléments que le tenseur d'origine, les nouveaux éléments ne seront pas initialisés en mémoire.weights.view(a, b)
renverra un nouveau tenseur avec les mêmes données que les poids de taille (a, b)la source
J'ai vraiment aimé les exemples de @Jadiel de Armas.
Je voudrais ajouter un petit aperçu de la façon dont les éléments sont ordonnés pour .view (...)
la source
Essayons de comprendre la vue par les exemples suivants:
-1 comme valeur d'argument est un moyen facile de calculer la valeur de disons x à condition que nous connaissions les valeurs de y, z ou l'inverse en cas de 3d et pour 2d à nouveau un moyen facile de calculer la valeur de disons x à condition que nous connaître les valeurs de y ou vice versa ..
la source