1
votes

Pour une condition donnée, obtenir des indices de valeurs dans le tenseur 2D A, utiliser ceux-ci pour indexer un tenseur 3D B

Pour un tenseur 2D donné, je veux récupérer tous les indices dont la valeur est 1 . Je m'attendais à pouvoir utiliser simplement torch.nonzero (a == 1) .squeeze () , ce qui renverrait tensor ([1, 3, 2]) . Cependant, à la place, torch.nonzero (a == 1) renvoie un tenseur 2D (ça va), avec deux valeurs par ligne (ce n'est pas ce à quoi je m'attendais). Les indices renvoyés doivent ensuite être utilisés pour indexer la deuxième dimension (index 1) d'un tenseur 3D, renvoyant à nouveau un tenseur 2D.

tensor([[0, 1],
        [1, 3],
        [2, 2]])

Évidemment, cela ne fonctionne pas, conduisant à uneRunTimeError:

RuntimeError: argument non valide 4: le tenseur d'index doit avoir le même dimensions comme tenseur d'entrée à C: \ w \ 1 \ s \ windows \ pytorch \ aten \ src \ TH / generic / THTensorEvenMoreMath.cpp: 453

Il semble que idxs ne soit pas ce à quoi je m'attendais, et je ne peux pas non plus l'utiliser comme je le pensais. idxs est

import torch

a = torch.Tensor([[12, 1, 0, 0],
                  [4, 9, 21, 1],
                  [10, 2, 1, 0]])

b = torch.rand(3, 4, 8)

print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])

idxs = torch.nonzero(a == 1)
print('idxs_size', idxs.size())
# idxs_size torch.Size([3, 2])

print(b.gather(1, idxs))

mais en lisant le documentation Je ne comprends pas pourquoi je récupère également les indices de ligne dans le tenseur résultant. Maintenant, je sais que je peux obtenir les idx corrects en découpant idxs [:, 1] mais je ne peux quand même pas utiliser ces valeurs comme indices pour le tenseur 3D car la même erreur qu'avant est soulevée. Est-il possible d'utiliser le tenseur d'indices 1D pour sélectionner des éléments dans une dimension donnée?


0 commentaires

4 Réponses :


0
votes
import torch

a = torch.Tensor([[12, 1, 0, 0],
                  [4, 9, 21, 1],
                  [10, 2, 1, 0]])

b = torch.rand(3, 4, 8)

print('a_size', a.size())
# a_size torch.Size([3, 4])
print('b_size', b.size())
# b_size torch.Size([3, 4, 8])

#idxs = torch.nonzero(a == 1, as_tuple=True)
idxs = torch.nonzero(a == 1)
#print('idxs_size', idxs.size())

print(torch.index_select(b,1,idxs[:,1]))

4 commentaires

Non pas du tout. Tout d'abord z est un tuple, ensuite mon objectif final est d'obtenir un index de b .


ouais désolé, je n'ai pas complètement lu votre question correctement ... pour votre dernière question "Est-il possible d'utiliser le tenseur d'indices 1D pour sélectionner des éléments à travers une dimension donnée", fait torch.index_select (b, 1, idxs [ :, 1]) vous donne ce dont vous avez besoin?


Non. Cela renverra une matrice 3D. J'attends un 2D. Voyez ma réponse.


ah, gotcha. Je suis content que vous l'aillez compris. sidenote assurez-vous simplement qu'il n'y a pas 2 1 consécutifs



0
votes

En supposant que les trois dimensions de b sont batch_size x sequence_length x features (bxsx feats), les résultats attendus peuvent être obtenus comme suit.

import torch

a = torch.Tensor([[12, 1, 0, 0],
                  [4, 9, 21, 1],
                  [10, 2, 1, 0]])

b = torch.rand(3, 4, 8)
print(b.size())
# b x s x feats
idxs = torch.nonzero(a == 1)[:, 1]
print(idxs.size())
# b
c = b[torch.arange(b.size(0)), idxs]
print(c.size())
# b x feats


0 commentaires

1
votes

Vous pouvez simplement les découper et les passer en tant qu'indices comme dans:

# adv. indexing using a tuple of lists
In [213]: a[([1,], [2,])] 
Out[213]: tensor([21.])

# adv. indexing using a tuple of tuples
In [215]: a[((1,), (2,))]  
Out[215]: tensor([21.])

# adv. indexing using a tuple of tensors
In [214]: a[(torch.tensor([1,]), torch.tensor([2,]))] 
Out[214]: tensor([21.])

Alternativement, une approche encore plus simple et ma préférée serait d'utiliser simplement torch.where () puis indexez directement dans le tenseur b comme dans:

   In [212]: a[(1, 2)] 
   Out[212]: tensor(21.)

Un peu plus d'explications sur l'approche ci-dessus d'utilisation de torch.where () : Il fonctionne sur la base du concept de indexation avancée . Autrement dit, lorsque nous indexons dans le tenseur en utilisant un tuple d'objets de séquence tels que tuple de tenseurs, tuple de listes, tuple de tuples, etc.

# some input tensor
In [207]: a  
Out[207]: 
tensor([[12.,  1.,  0.,  0.],
        [ 4.,  9., 21.,  1.],
        [10.,  2.,  1.,  0.]])

Pour le découpage de base, nous aurait besoin d'un tuple d'indices entiers:

In [196]: b[torch.where(a == 1)]  
Out[196]: 
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
        [0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
        [0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])

Pour obtenir la même chose en utilisant l'indexation avancée, nous aurions besoin d'un tuple d'objets de séquence:

In [193]: idxs = torch.nonzero(a == 1)     
In [194]: c = b[idxs[:, 0], idxs[:, 1]]  

In [195]: c   
Out[195]: 
tensor([[0.3411, 0.3944, 0.8108, 0.3986, 0.3917, 0.1176, 0.6252, 0.4885],
        [0.5698, 0.3140, 0.6525, 0.7724, 0.3751, 0.3376, 0.5425, 0.1062],
        [0.7780, 0.4572, 0.5645, 0.5759, 0.5957, 0.2750, 0.6429, 0.1029]])


2 commentaires

J'aime beaucoup la simplicité de votre deuxième suggestion. Pourriez-vous expliquer un peu plus pourquoi cela fonctionne? Puisque torch.where (a == 1) renvoie un tuple. Comment trancher un tenseur avec un tuple fonctionne-t-il comme ça?


@BramVanroy a ajouté quelques explications :)



0
votes

En complément de la solution de @ kmario23, vous pouvez toujours obtenir les mêmes résultats que

b[torch.nonzero(a==1,as_tuple=True)]


0 commentaires