4
votes

Existe-t-il une méthode dans Pytorch pour compter le nombre de valeurs uniques d'une manière qui peut être propagée en retour?

Étant donné le tenseur suivant (qui est le résultat d'un réseau [notez le grad_fn]):

ll = torch.zeros((1,243))
for x in xx:
    ll[0,x.long()] += 1

Que nous définirons comme:

tvtensor = torch.tensor([i for i in range(243)]).unsqueeze(1).repeat(1,xx.shape[0]).float().requires_grad_(True)
(xx==tvtensor).sum(dim=1)

Je voudrais définir une opération qui compte le nombre d'occurrences de chaque valeur de telle manière que l'opération produise le tenseur suivant:

tt = []
for i in range(243):
    tt.append((xx == i).unsqueeze(0))
torch.cat(tt,dim=0).sum(dim=1)

c'est-à-dire qu'il y a 2 zéros, 1 un, 2 treize, etc ... le nombre total de valeurs possibles est défini en amont, mais dans cet exemple est 243

Jusqu'à présent, j'ai essayé les approches suivantes, qui produisent avec succès le tenseur souhaité, mais ne le font pas d'une manière qui permette de calculer les gradients à travers le réseau:

Tentative 1

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

Tentative 2

xx = torch.tensor([121., 241., 125.,   1., 108., 238., 125., 121.,  13., 117., 121., 229.,
        161.,  13.,   0., 202., 161., 121., 121.,   0., 121., 121., 242., 125.]).requires_grad_(True)

EDIT: tentative ajoutée

Tentative 3

- Je ne m'attendais pas vraiment à ce que cela soutienne un accessoire, mais je pensais que j'essaierais quand même

tensor([121., 241., 125.,   1., 108., 238., 125., 121.,  13., 117., 121., 229.,
        161.,  13.,   0., 202., 161., 121., 121.,   0., 121., 121., 242., 125.],
       grad_fn=<MvBackward>)

Toute aide est appréciée

EDIT: Comme demandé, l'objectif final est le suivant:

J'utilise une technique pour calculer la similitude structurelle entre deux séquences temporelles. L'un est réel et l'autre est généré. La technique est décrite dans cet article ( https://link.springer.com/chapter/10.1007/978-3-642-02279-1_33 ) où une série chronologique est convertie en une séquence de mots de code et la distribution de mots de code (similaire à la façon dont Bag of Words est utilisé en PNL) est utilisé pour représenter la série chronologique. Deux séries sont considérées comme similaires lorsque les deux distributions de signaux sont similaires. C'est à cela que sert le tenseur des statistiques de comptage.

Ce que l'on souhaite, c'est pouvoir construire une fonction de perte qui consomme ce tenseur et mesure la distance entre le signal réel et le signal généré (la norme euclidienne sur les données du domaine temporel directement ne fonctionne pas bien et cette approche revendique de meilleurs résultats), de sorte qu'elle peut mettre à jour le générateur de manière appropriée.


0 commentaires

3 Réponses :


2
votes

Vous ne pourrez pas faire cela car une opération unique est simplement non différentiable.

De plus, seuls les tenseurs à floating peuvent avoir un gradient tel qu'il est défini uniquement pour le domaine des nombres réels, pas pour les entiers.

Pourtant, il peut y avoir une autre façon différentiable de faire ce que vous voulez réaliser, mais c'est une question différente.


2 commentaires

Ok donc cela explique pourquoi les tentatives ne fonctionnent pas car elles utilisent des opérateurs de comparaison qui, comme elles produisent des entiers, n'auraient pas de grad_fn? Y a-t-il une manière différentiable dont vous pouvez penser qui pourrait produire un résultat équivalent?


Cela dépend de votre objectif final (je suppose qu'il ne prend pas de valeurs uniques). Comme décrit ci-dessus, on pourrait combiner des fonctions différentiables afin d'obtenir une approximation de ce que vous recherchez. Cependant, ce n'est généralement pas nécessaire et vous pourrez peut-être éviter la non-différentiabilité d'une manière ou d'une autre. Veuillez décrire votre objectif final, nous pourrons peut-être vous aider.



0
votes

L'opération "uniquify" n'est pas différentiable, mais il peut y avoir des moyens d'y remédier, par exemple, en écrivant un opérateur personnalisé, ou par une combinaison intelligente d'opérateurs différentiables.

Cependant, vous devez vous poser cette question: quel est selon vous le gradient d'une telle opération? Ou, à un niveau supérieur, qu'essayez-vous de réaliser avec cette opération?


3 commentaires

La sortie fait partie d'un modèle génératif, et le tenseur que j'ai montré ci-dessus est le résultat d'une opération qui est une version codifiée d'un signal généré. Les statistiques de comptage représentent le nombre d'occurrences de différents codes au sein du signal généré et sont dans ce cadre représentatives de la qualité du signal. Par conséquent, les gradients que je recherche seraient par rapport aux changements dans le nombre d'occurrences de chaque valeur dans le tenseur.


Je ne suis pas sûr de comprendre votre cas d'utilisation, mais cela ne semble pas être quelque chose avec des dégradés bien définis. Ce que vous décrivez, ce ne sont que des différences, c'est-à-dire des dérivés dans le cas discret, et je ne pense pas que ce soit quelque chose d'optimisable par le type de techniques offertes par les bibliothèques DL traditionnelles.


J'imagine que vous essayez de contraindre le modèle à générer un certain nombre de signaux spécifiques. Dans ce cas, vous souhaiterez peut-être explorer certains modèles de génération contrôlée. Je ne suis pas un expert en la matière, mais une idée générale pourrait être d'attacher un classificateur à la sortie et de lui demander de classer le nombre d'occurrences pour un signal spécifique, puis d'ajouter un terme de pénalité basé sur la prédiction.



0
votes

Je le ferais avec unique méthode unique :

entrez la description de l'image ici

si vous voulez compter les occurrences, vous devez ajouter le paramètre return_counts=True

entrez la description de l'image ici

Je l'ai fait dans la version 1.3.1

entrez la description de l'image ici


0 commentaires