Pytorch: Intégration de tenseurs complexes

Créé le 16 févr. 2017  ·  128Commentaires  ·  Source: pytorch/pytorch

Nouvelle description de @ezyang :

Le travail est en cours sur https://github.com/Roger-luo/pytorch-complex

Principes d'organisation

  • La prise en charge de tenseurs complexes est importante pour PyTorch, et nous accepterons des correctifs au cœur qui ajoutent de petites quantités de code pour rendre l'ajout d'une prise en charge complexe.
  • L'ajout de complexe implique l'écriture de beaucoup de nouveaux noyaux et de code : nous aimerions que ce code vive initialement hors du référentiel, afin qu'il soit plus facile pour les gens d'itérer rapidement dessus sans avoir à passer par le processus de révision du code principal de PyTorch. Nous ne nous engagerons PAS à examiner de nouveaux noyaux volumineux à court terme, mais nous aimerions éventuellement que tous les noyaux reviennent sur PyTorch.
  • La bibliothèque externe pourra être construite séparément de PyTorch, vous pourrez donc la conserver en tant que référentiel séparé sans avoir à fusionner avec PyTorch (et gérer de nombreux conflits de fusion).

    • PyTorch peut occasionnellement apporter des modifications avec rupture dans l'API C++ ; si vous les portez à notre attention, nous ferons tout notre possible pour aider à résoudre ces problèmes.

  • Les crochets nécessaires pour cela ne seront PAS livrés avec PyTorch 1.0, mais ils seront livrés avec une version publiée de PyTorch dans un avenir pas trop lointain.

Comment vais-je travailler sur des noyaux complexes ?

Voici à quoi ressemblera le flux de travail à l'état stable.

PyTorch contiendra nativement des API pour faire référence au dtype complexe, mais elles ne feront rien par défaut. PyTorch définit torch.complex64 et torch.complex128 en référence aux tenseurs complexes. Cependant, si vous essayez de construire un tenseur de cette manière, par défaut, PyTorch générera une erreur :

>>> torch.zeros({2,2}, dtype=torch.complex64)
RuntimeError: complex64 not supported by PyTorch

@ezyang a fourni un patch qui ajoute ces dtypes à PyTorch. https://github.com/pytorch/pytorch/pull/11173

À moyen terme, nous fusionnerons la prise en charge des fonctionnalités de base (comme l'allocation d'un tenseur de zéros) pour qu'elles soient prises en charge par PyTorch de manière native. Un indicateur raisonnable de ce que le support est "de base" est le support natif de PyTorch pour les demi-tenseurs CPU (qui sont extrêmement pauvres).

PyTorch édite une interface pour enregistrer une implémentation de tenseurs complexes. L'implémentation hérite de la classe TypeDefault (https://github.com/pytorch/pytorch/pull/11013) et remplacera les méthodes de cette classe pour définir des implémentations de fonctions pour lesquelles nous avons des implémentations complexes. Cela ressemblera à ceci :

struct CPUComplexFloatType final : public TypeDefault {
  virtual Tensor add(const Tensor & self, const Tensor & other, Scalar alpha=1) const override {
    // Your implementation of add for complex tensors
  }
  // ...
}

Cette classe remplacera exactement les types pris en charge pour complex ; toutes les autres implémentations sont fournies par TypeDefault et généreront une erreur par défaut.

Il y aura une liste canonique des méthodes prises en charge sur Type (l'interface globale) sous la forme d'un fichier généré automatiquement qui est archivé dans le référentiel source PyTorch ; nous communiquerons les changements d'API par diffs à ce fichier. En général, les méthodes sont en correspondance biunivoque avec leurs noms correspondants dans l'interface PyTorch.

En général, lorsque vous utilisez une opération que vous n'avez pas encore implémentée,

AVERTISSEMENT : Nous avons l'intention de refactoriser Type dans un nouveau système qui prend également en charge l'enregistrement ouvert des nouvelles opérations (cela ne fonctionne évidemment pas si vous avez une seule superclasse qui définit toutes les méthodes que vous pourriez éventuellement vouloir prendre en charge). Ainsi, essayez de ne pas trop vous attacher à la stratégie d'implémentation particulière consistant à écrire Type en tant que sous-classe.

Pour publier de nouvelles opérations uniquement complexes, vous utiliserez l'API d'extension C++. L'API d'extension C++ est documentée sur https://pytorch.org/tutorials/advanced/cpp_extension.html Essentiellement, vous pouvez écrire une fonction C++ comme :

at::Tensor imag(at::Tensor z) {
  ...
}

Et puis l'API d'extension C++ générera une liaison Python afin que vous invoquiez cette fonction à partir de Python.

Certaines opérations seront « faciles » à intégrer dans PyTorch tel qu'il existe aujourd'hui. Par exemple, pour l'implémentation d'opérations binaires, il est probablement plus logique d'étendre add_kernel dans BinaryOpsKernel.cpp afin qu'il soit distribué sur des types complexes (et vous l'obtenez ensuite gratuitement, car std::complex implémente l'addition). Tant que ces correctifs sont petits et autonomes, nous promettons de les fusionner en temps opportun.

Il devrait TOUJOURS être possible de débloquer, en écrivant simplement un remplacement sur Type au lieu d'utiliser l'infrastructure existante, et en faisant un copier-coller libéral. Mais évitons-le quand c'est facile !

Autograd. Tant que vous travaillez sur des opérations pour lesquelles des formules dérivées ont déjà été définies, vous obtiendrez "automatiquement" la prise en charge d'autograd, tant que vous implémentez une prise en charge complexe de toutes les fonctions constitutives qui sont invoquées dans l'implémentation inverse à partir de derives.yaml .

Dans certains cas, nous devrons peut-être ajuster les formules autograd afin qu'elles fonctionnent pour les nombres complexes ; par exemple, le gradient de 'abs' n'est pas 'grad . self.sign()'. Dans ces cas, tout ce que nous devons faire est de corriger en amont la modification de la formule autograd de 'abs' en 'abs_backward', qui est une fonction qui peut être remplacée.

Pour la rétro-propagation générale à valeur complexe, il existe quelques références :

  1. « Réseaux de neurones à valeurs complexes » d'Akira.
  2. https://giggleliu.github.io/2018/02/01/complex_bp.html

Généralement, nous n'aurons pas besoin de modifier l'autograd puisque dans la plupart des cas nous ne calculons que les dérivées d'une fonction à valeurs réelles (la perte).

Plan de travail

Bon nombre des éléments nécessaires sont en place aujourd'hui, mais ils ne sont pas assemblés de bout en bout. Voici ce qu'il faut faire.

  • [X] Codemod TH à pas ifdef réel https://github.com/pytorch/pytorch/pull/11163
  • [X] Prise en charge intégrée des types torch.complex64 et torch.complex128. https://github.com/pytorch/pytorch/pull/11173
  • [X] Une interface pour enregistrer CPUComplexType, etc., afin que cette implémentation soit invoquée lorsque vous demandez un tenseur complexe avec dtype=torch.complex64 ou effectuez une opération sur des tenseurs complexes.
  • [X] Terrain https://github.com/pytorch/pytorch/pull/11013
  • [X] Un exemple de bout en bout, y compris un système de construction fonctionnel, d'un programme C++ compilable séparément qui se lie à libtorch et utilise l'interface susmentionnée pour implémenter une allocation de tenseur complexe.

Plan d'intégration à court terme. Ces opérations sont "faciles" à mettre en œuvre, et nous devrions donc les intégrer dans PyTorch dès que possible.

  • [X] Fabriques de tenseurs de base : torch.empty, torch.zeros, torch.ones
  • [ ] Opérations binaires du processeur : add, sub, mul, div #11641
  • [ ] FFT
  • [ ] ???

Implémentation du noyau :

TODO : Générer une liste basée sur https://github.com/Roger-luo/TH/blob/master/ChangeLog.md

Autres tâches connexes complexes :

  • [ ] Déterminez les règles de promotion de type pour les tenseurs complexes et implémentez-les dans promoteTypes #11641

Contenu historique du problème

Commentaire original de @PhilippPelz

Je me demandais s'il y avait un intérêt à incorporer des tenseurs complexes dans pytorch.
Pour le support CPU, il y a ztorch et j'ai écrit z-cutorch ( https://github.com/PhilippPelz/z-cutorch ) il y a quelque temps. C'est un fork off cutorch avant le refactoring pour CudaHalfTensor (je n'ai pas encore le matériel).
Si ce n'est pas trop de travail, j'aimerais l'intégrer lentement avec pytorch. J'utilise matplotlib pour tracer via fb.ptyhon et cela s'avère être une énorme douleur chaque fois que je réinstalle mon système (en compilant toutes les dépendances), et il semble que pytorch fonctionnera bientôt sous Windows, sur lequel fonctionne l'un de mes PC d'expérimentation.
J'aurais aussi besoin de dégradés complexes, donc tôt ou tard je toucherais aussi à l'autograd.
Alors que tf prend en charge les tenseurs complexes en soi, il semble que de nombreuses opérations ne le prennent pas encore en charge (https://github.com/tensorflow/tensorflow/issues/2255), et cela semble un peu lourd pour mes besoins.

Peut-être que quelqu'un pourrait dire quelques mots sur comment et par où commencer, si c'est une bonne idée.

feature complex triaged

Commentaire le plus utile

@sunilkpai , @boeddeker , @Randl ,

Merci pour le rapport sur les dérivés complexes. Je vais essayer de suivre ça et j'y reviendrai la semaine prochaine. J'ai pensé ajouter quelques liens ici et décrire l'état du projet.

Le statut des nombres complexes est pris en charge de manière non officielle et doit être ajouté via l'extension PyTorch :

Chaque extension contient deux éléments :

  • Un .cpp qui contient tous les enregistrements nécessaires du noyau mathématique.
  • Un dossier test/ qui contient des versions très simplifiées des scripts de test pytorch.
    Regardez dans les scripts de test pour voir quels noyaux sont pris en charge (et pourquoi d'autres ne le sont pas).

Pourquoi ne puis-je pas imprimer un tenseur complexe sur la console ?

  • L'objet python Tensor a une mise en forme jolie qui appelle certaines fonctions non prises en charge.

    • Vous pouvez modifier le contenu de tensor.py pour contourner le formatage d'impression.

    • Ou, vous pouvez simplement convertir les tenseurs Pytorch en tableaux Numpy, puis imprimer.

Statut actuel du projet :

  • La couverture CPU est assez bonne.

    • Les noyaux sont implémentés dans PyTorch sous 'aten/src/ATen/native/cpu/ </li> <li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h

    • L'accélération Intel AVX256 est sous 'aten/src/ATen/cpu/vec256/`



      • @sunilkpai : Je ne connaissais pas l'optimisation d'exp. C'est le dossier où vous ajoutez cela.


      • Faites-moi savoir si vous êtes à l'aise pour faire le changement.



  • La couverture GPU est limitée aux opérations binaires et unaires :

    • Les noyaux sont implémentés dans PyTorch sous 'aten/src/ATen/native/cuda/* </li> <li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh

    • Les types de données thrust::complex<T> sont utilisés et ils incluent les noyaux optimisés.

Développement actuel :

  • En attente du portage des noyaux TH basés sur C dans le dossier C++ ATen.

    • La fonction rand() est nécessaire pour porter les cas de test vers les tests internes de pytorch.

    • Certaines opérations d'indexation ne sont actuellement pas portées.

    • Il y a actuellement 168/1300 noyaux mathématiques (contre 230 en octobre) qui doivent être portés de TH vers ATen.

  • J'essaierai d'ajouter le support des nombres complexes au fur et à mesure que ces noyaux seront disponibles dans ATen.

--

Tous les 128 commentaires

Je pense que nous serions intéressés par l'ajout d'un support optionnel pour les tenseurs complexes. La meilleure façon serait de bifurquer et de travailler sur les bibliothèques C dans torch/lib . Cela devrait être sans conflit avec le maître, vous pouvez donc le faire pendant longtemps. Une fois que vous obtenez les bibliothèques dans un état utilisable, vous pouvez commencer à écrire les liaisons, et c'est là que nous pouvons fournir des conseils sur la façon d'éviter les conflits à ce moment-là.

J'ai TH avec des types complexes compilant. Que dois-je ajouter pour l'intégration python ?

@PhilippPelz voulez-vous dire comme : https://github.com/facebook/ztorch/tree/master/lib/THZ ? ou avez-vous construit votre propre fork de TH qui permet des types complexes ?

@killeent a quelques notes sur la façon dont TH est lié à Python, il peut les partager.

En général, pour obtenir des Tensors complexes, je préférerais THZ, car il a des tests, etc.

Construire un backend CUDA pour les Tensors complexes est cependant un effort assez important, nous n'avons même pas commencé là-dessus.

J'ai écrit z-cutorch ( https://github.com/PhilippPelz/z-cutorch ) il y a quelque temps. C'est un fork off cutorch avant le refactoring pour CudaHalfTensor (je n'ai pas encore le matériel).

C'est bien. Je suppose que vous avez déjà fait un gros effort dans cette direction :)

@soumith j'ai fait un fork de TH avec des types complexes. Fondamentalement, un THGenerateComplexTypes.h + a ajouté des routines BLAS + LAPACK, le reste était presque gratuit. Cela me semblait beaucoup moins de travail que de vérifier quelles parties de THZ sont compatibles, puis de copier-coller.

Je suis coincé avec la compilation THPP en ce moment, en découvrant les messages du compilateur comme

/home/philipp/projects/pytorch/torch/lib/tmp_install/include/TH/generic/THBlas.h:6:40 : erreur : attendu ',' ou '...' avant le jeton '*'
TH_API void THBlas_(swap)(long n, real *, long incx, real *, long incy);

est un peu délicat.

J'apprécierais de l'aide sur la façon d'activer l'intégration de python. Le backend CUDA devrait être principalement un copier-coller de z-cutorch.

@PhilippPelz voici quelques notes sur les wraps PyTorch TH : https://gist.github.com/killeent/4675635b40b61a45cac2f95a285ce3c0

@killeent merci, semble très utile. lib/build_all.sh est en train de compiler, je pense que je peux regarder le répertoire csrc.

Cela fonctionne maintenant :

importer la torche comme e
importer numpy en tant que np

a = np.tableau([1+1j,2+2j])
b = np.tableau([3+3j,4+4j])
ath = th.from_numpy(a)
bth = th.from_numpy(b)
ath_cuda = ath.cuda()
ath_cuda += bth.cuda()
ath = ath_cuda.cpu()
impression(ath.numpy())

Sortie : [ 4.+4.j 6.+6.j]

ainsi que la plupart des fonctions mathématiques.
J'ajouterai des fonctions pratiques et des FFT au cours des prochaines semaines. Je suppose qu'il doit y avoir des tests pour tout avant de pouvoir fusionner cela. Si vous connaissez quelqu'un d'autre qui s'intéresse aux tenseurs complexes et qui serait prêt à contribuer à l'écriture des tests, ce serait génial. Cet article me vient à l'esprit : Deep Complex Networks , peut-être que ces gars-là seraient intéressés.
Je n'aurai pas le temps d'écrire tous les tests par moi-même.

@PhilippPelz Merci pour vos commentaires. Je vérifie votre implémentation. Et tout d'abord, je ne suis pas sûr de votre implémentation ger . Certaines fonctions blas complexes ne sont pas incluses dans votre THBlas.c comme vous avez défini GER comme zger_ et cger_ dans les en-têtes de génération, mais il n'y a pas de fonction blas avec cger_ dans le générique/THBlas.c . Cependant, je peux utiliser votre gemv et quelques autres fonctions. Et IMO peut-être devriez-vous ajouter .gch à .gitignore ? Avez-vous poussé toutes vos extensions à votre fourche? Je peux d'abord faire une demande d'extraction à votre maître en fonction de votre implémentation.

Et pour DOT , je suppose que peut-être pour les vecteurs complexes, les routines dotc pour les points sont plus courantes ?

Et oui, si utiliser simplement real sera plus facile à mettre en œuvre, je me sentais juste bizarre quand real est en fait un complexe...

Et pour les tests, je n'ai vu aucun test précédent pour TH. Où dois-je écrire ces tests ? ou nous écrivons simplement des tests python

Oui, désolé, je vois que je n'ai peut-être pas poussé tout ce qui est nécessaire. Je vais re-vérifier lundi. Certaines déclarations manquent, par ex. zger et cger

Pour DOT, j'utilise cdotc et zdotc, ils semblent manquer, je mettrai à jour la semaine prochaine.

Vérifiez auprès des mainteneurs de pytorch quelle dénomination ils préfèrent pour de vrai. J'aime plus votre version, mais je n'ai pas encore fait l'effort.

Oui, des tests python pour les trucs mathématiques. Devrait être facilement modifié pour la plupart des fonctions afin d'inclure également les vérifications de nombre compelx.

Cool que vous vous penchiez aussi là-dessus !

Ok, j'ai poussé quelques changements. Les routines TH blas sont là maintenant pour les complexes

@PhilippPelz Je viens de faire une demande d'extraction à votre référentiel. Et pour les couches linéaires complexes et quelques autres opérateurs. il pourrait y avoir beaucoup d'opérations hermitiennes (comme bp pour une couche linéaire complexe). Peut-être ajouter une fonction pour un tenseur? Avez-vous vérifié la partie THNN?

Oui hermitian est utile. cuda fft fonctionne maintenant. cpu fft pourrait être enveloppé à partir de numpy. Je n'ai pas encore touché THNN ou THCUNN.

@PhilippPelz J'ai ajouté un simple hermitien dans le PR. Et pourriez-vous le revoir. Nous pourrions donc voir si ces changements sont appropriés et passer à l'étape suivante. Merci! PS. il semble que vous ayez manqué certains en-têtes, je corrige également cela et quelques autres avertissements. Pour une fonction complexe avec une sortie réelle, devrions-nous retourner un tenseur réel plutôt qu'un tenseur complexe ? J'ai implémenté des méthodes de copie entre des types complexes et réels, c'est donc possible.

Je rebaserai tous les commits après votre examen.

@PhilippPelz Salut, je suis assez confus à propos de la partie THPP que vous avez implémentée. Pourquoi dépend-il de la poussée dans Traits.hpp ?. Cela provoquera une erreur lors de la compilation sans cuda. Est-il possible d'utiliser uniquement commeouen Traits.hpp ? Je n'ai pas compris. Peut-être pourriez-vous proposer des indices ?

@Roger-luo Oui, j'ai aussi des problèmes avec ça ailleurs. Les types complexes que nous utilisons doivent provenir soit de complex.h, soit de std::complex. Puisque THPP est le wrapper C++, peut-être que std::complex est plus approprié. Pouvez-vous s'il vous plaît changer cela?

Thrust pose également des problèmes pour la même raison lorsque vous essayez de créer des extensions cffi. En ce moment, je fais une solution de contournement, mais la bonne façon serait de changer le type complexe en cuFloatComplex/cuDoubleComplex dans THC. pour que le compilateur cffi ne se plaigne pas. Je veux juste poursuivre mes recherches maintenant, cela me prend beaucoup trop de temps :( . Si vous avez le temps, faites-le s'il vous plaît.

De plus, la construction d'une extension cffi avec des appels de noyau personnalisés est assez lourde, car il faut toujours créer une bibliothèque supplémentaire compilée avec nvcc, qui est ensuite liée à un wrapper cffi. Je suppose qu'il n'y a pas d'autre moyen. On pourrait utiliser cffi en mode ABI, mais le site Web indique "Le mode API compile à la place un wrapper CPython C qui invoque directement la fonction cible. Il est, comparativement, massivement plus rapide (et fonctionne mieux que libffi ne le peut jamais)."

@PhilippPelz peut-être reinterpret_cast pourrait être une solution ? Je suppose qu'il devrait être changé en cuComplex et utiliser reinterpret_cast dans THPP. Je vais d'abord essayer...

Oui, je suppose qu'il n'y a pas d'autre moyen que reinterpret_cast si vous voulez que THPP se construise également sans cuda installé.

@PhilippPelz J'aimerais aider. Existe-t-il une liste de tâches quelque part ?

THNN et THCUNN doivent être activés pour les types complexes. Pouvez-vous vous coordonner avec @roger-luo ? De plus, si nous visons l'intégration avec master, des tests unitaires doivent être écrits pour toutes les méthodes complexes.

@elbamos La plupart du travail dans THNN consistera à implémenter de nouvelles méthodes de rétropropagation complexes pour chaque couche existante. Il y a un WIP PR dans la fourchette de Philipp. J'ai listé quelques références.

@apaszke @soumith @PhilippPelz Et il y a deux questions :

  • est-ce que quelqu'un sait pourquoi il y a un autre fichier GenerateXXXTypes.h dans THS ? Cela ressemble à ceux de TH .

  • A quoi sert le code suivant dans byte_order.cpp ?

void THP_decodeFloatBuffer(float* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
  for (size_t i = 0; i < len; i++) {
    union { uint32_t x; float f; };
    x = (order == THP_BIG_ENDIAN ? decodeUInt32BE(src) : decodeUInt32LE(src));
    dst[i] = f;
    src += sizeof(float);
  }
}

void THP_decodeDoubleBuffer(double* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
  for (size_t i = 0; i < len; i++) {
    union { uint64_t x; double d; };
    x = (order == THP_BIG_ENDIAN ? decodeUInt64BE(src) : decodeUInt64LE(src));
    dst[i] = d;
    src += sizeof(double);
  }
}

Des suggestions sur la mise en œuvre de sa version complexe associée ? Je ne sais pas si l'implémentation suivante est correcte...

void THP_decodeZFloatBuffer(std::complex<float>* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
  for (size_t i = 0; i < len; i++) {
    union { uint64_t x; std::complex<float> cf;};
    x = (order == THP_BIG_ENDIAN ? decodeUInt64BE(src) : decodeUInt64LE(src));
    dst[i] = cf;
    src += sizeof(std::complex<float>);
  }
}

void THP_decodeDoubleBuffer(std::complex<double>* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
  for (size_t i = 0; i < len; i++) {
    union { uint128_t x; std::complex<double> df;};
    x = (order == THP_BIG_ENDIAN ? decodeUInt128BE(src) : decodeUInt128LE(src));
    dst[i] = df;
    src += sizeof(std::complex<double>);
  }
}

Le decodeUInt128XE précédent est déclaré comme

static inline uint128_t decodeUInt128LE(const uint8_t *data) {
  return (((uint128_t)data[ 0])<<  0) | (((uint128_t)data[ 1])<<  8)|
         (((uint128_t)data[ 2])<< 16) | (((uint128_t)data[ 3])<< 24)|
         (((uint128_t)data[ 4])<< 32) | (((uint128_t)data[ 5])<< 40)|
         (((uint128_t)data[ 6])<< 48) | (((uint128_t)data[ 7])<< 56)|
         (((uint128_t)data[ 8])<< 64) | (((uint128_t)data[ 9])<< 72)|
         (((uint128_t)data[10])<< 80) | (((uint128_t)data[11])<< 88)|
         (((uint128_t)data[12])<< 96) | (((uint128_t)data[13])<<104)|
         (((uint128_t)data[14])<<112) | (((uint128_t)data[15])<<120);
}

static inline uint128_t decodeUInt128BE(const uint8_t *data) {
  return (((uint128_t)data[15])<<  0) | (((uint128_t)data[14])<<  8)|
         (((uint128_t)data[13])<< 16) | (((uint128_t)data[12])<< 24)|
         (((uint128_t)data[11])<< 32) | (((uint128_t)data[10])<< 40)|
         (((uint128_t)data[ 9])<< 48) | (((uint128_t)data[ 8])<< 56)|
         (((uint128_t)data[ 7])<< 64) | (((uint128_t)data[ 6])<< 72)|
         (((uint128_t)data[ 5])<< 80) | (((uint128_t)data[ 4])<< 88)|
         (((uint128_t)data[ 3])<< 96) | (((uint128_t)data[ 2])<<104)|
         (((uint128_t)data[ 1])<<112) | (((uint128_t)data[ 0])<<120);
}

J'utilise actuellement std::complex<T> au lieu de T _Complex dans THPP . Je ne suis pas sûr que cela puisse encore être utilisé par Python. Ou seul le type c T _Complex est utilisable pour python. Donc ici le type de dst est std::complex<T> .

Et si j'ai raison pour cette implémentation, nous avons probablement besoin d'une implémentation uint128_t , comme https://github.com/calccrypto/uint128_t ? Comme il semble que tous les compilateurs ne prennent pas en charge les entiers 128 bits (gcc a un int128_t et un uint128_t).

@PhilippPelz j'ai remarqué que votre fork n'avait pas de problèmes activés - quel est l'état de votre projet ? je suis un peu déçu que les tenseurs complexes ne soient pas sur la feuille de route pour pytorch

@el3ment J'ai ajouté un backend complexe pour CPU https://github.com/pytorch/pytorch/pull/4899 Mais il n'est pas encore revu... Et je n'ai reçu aucun commentaire pour mon PR, donc je me suis tourné vers l'utilisation le langage de programmation Julia récemment...

J'ai envoyé un e-mail à @PhilippPelz la dernière fois, je suppose que son dépôt est toujours sous la v0.1 et qu'il est occupé pour sa thèse jusqu'en septembre ? Et je travaillais sur le nouveau backend CUDA de la v0.3, mais je n'ai pas le temps de terminer tous ces liens seul. Les fonctions map/reduce sont différentes de la v0.1 avec quelques optimisations mais elles ne peuvent pas être trivialement converties pour prendre en charge les nombres complexes. Je serais heureux s'il y a quelqu'un qui veut m'aider...

Je suis prêt à aider.

Le 10 avril 2018 à 22h52, Rogerluo [email protected] a écrit :

@el3ment j'ai ajouté un backend complexe pour le CPU #4899

J'ai envoyé un e-mail à @PhilippPelz la dernière fois, je suppose que son dépôt est toujours sous la v0.1 et qu'il est occupé pour sa thèse jusqu'en septembre ? Et je travaillais sur le nouveau backend CUDA de la v0.3, mais je n'ai pas le temps de terminer tous ces liens seul. Les fonctions map/reduce sont différentes de la v0.1 avec quelques optimisations mais elles ne peuvent pas être trivialement converties pour prendre en charge les nombres complexes. Je serais heureux s'il y a quelqu'un qui veut m'aider...


Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub ou désactivez le fil de discussion.

@elbamos cool, il semble que l'équipe pytorch préfère une implémentation séparée. Je mettrai à jour ma fourche plus tard pour les autres parties plus tard. Mais je n'ai vraiment pas le temps là-dessus et je suppose que nous devrions commencer à travailler dessus quand il y aura un plan de l'équipe pytorch car ce serait une grosse extension pour pytorch.

Salut, mon code est sur un commit après la v0.2

J'avais vu qu'il y avait un assez gros refactor déplaçant tout le code tenseur dans Aten. Cela signifie que l'on ne peut pas facilement fusionner mon fork dans la version actuelle et qu'il peut y avoir plus de travail à faire.

Je suis toujours en train de rédiger mon doctorat, mais je prévoyais d'attendre la version 0.4 de toute façon jusqu'à ce que la fusion de Variable et Tensor soit publiée. Je crains qu'il n'y ait trop de refactoring en cours pour le rattraper si on le fait plus tôt.

@elbamos si vous le souhaitez, vous pouvez commencer à ajouter des éléments à mon fork, je le fusionnerai. À votre place, je mettrais simplement en œuvre ce dont vous avez besoin pour n'importe quel projet que vous faites. TH(CU)NN est une interface assez grande et représenterait une énorme charge de travail.

@el3ment Je n'ai pas le temps de travailler sur les problèmes des autres. Cependant, je fusionnerai des éléments si vous avez besoin d'implémenter quelque chose qui n'est pas là.

Si vous voulez juste quelque chose qui fonctionne avec des nombres complexes prêts à l'emploi, je recommanderais fortement tensorflow.

J'aiderai aussi s'il y a des problèmes de compilation.

Si je continue avec le postdoc, je porterai tout cela sur la version actuelle à un moment donné. C'est vraiment triste que Facebook ne veuille pas soutenir cela. :((

@PhilippPelz D'accord, c'est vraiment triste et en fait tensorflow ne supporte pas tous les opérateurs de la physique quantique... J'ai commencé à utiliser Julia et j'ai abandonné python.

@Roger-luo intéressant, utilisez-vous un package julia spécifique ou s'agit-il uniquement de code auto-écrit?

@PhilippPelz Je développe une boîte à outils quantique à plusieurs corps à Julia (depuis ce PyTorch PR), elle comprend une implémentation de réseau neuronal complexe/réel basée sur des articles précédents sur le réseau neuronal complexe, et j'ai trouvé qu'il est si facile à développer avec Julia métaprogrammation. Je viens de le mettre actuellement dans QMTK.jl , il est toujours en cours et je n'ai pas fini tout ce que je veux. PyTorch m'inspire beaucoup en effet, mais vraiment désolé pour le support complexe...

Mais j'ai l'intention de le séparer en un seul paquet de réseau neuronal à l'avenir (je ne veux tout simplement pas maintenir plusieurs dépôts pour le moment). Et il y aura plus de personnes rejoignant le développement de l'Institut de physique, CAS. J'accepterai les PR après sa première version taguée (qui sera dans quelques semaines).

Vous pouvez le regarder si vous êtes intéressé par son développement.

Si l'équipe PyTorch a encore des plans pour un support complexe à l'avenir, je serai prêt à aider.

Cool, je vais surveiller ça !

Hé les gars, désolé que nous n'ayons pas répondu à ce problème depuis son ouverture.

Voici deux faits :

  1. Nous sommes absolument d'accord que PyTorch a besoin d'un support complexe, et
  2. Nous n'avons pas la main-d'œuvre pour remplir adéquatement la longue traîne dont toutes les opérations complexes auraient besoin. (Pour preuve de cela, regardez le support clairsemé, qui est en maître et qui boitait.)

Depuis que ce problème a été ouvert en 2017, quelques éléments importants ont changé, ce qui peut simplifier un peu la mise en œuvre d'une prise en charge complexe. La première est que nous avons maintenant ATen, une bibliothèque C++ ergonomique pour manipuler les tenseurs. Cela signifie que vous n'avez pas à copier-coller des pans entiers de code TH/THC et en espérant que vous avez bien compris tout le recomptage manuel, vous pouvez écrire du code C++ comme s'il s'agissait de Python et il fonctionnera rapidement. Deuxièmement, nous travaillons sur une nouvelle version d'ATen, appelée C10, qui est beaucoup plus sérieuse pour avoir des backends ouverts qu'ATen (qui est une chose fermée) ce qui devrait faciliter le travail sur un support complexe, car il ne serait pas Cela implique en fait de bifurquer PyTorch, en ajoutant simplement un nouveau répertoire de code.

Donc, @Roger-luo et @PhilippPelz , nous aimerions avoir votre aide pour faire du backend complexe une réalité, mais nous aimerions vraiment trouver un moyen de le faire qui nous aide à le maintenir durablement dans le futur. Dites-nous ce que vous en pensez.

@ezyang Si vous manquez de main-d'oeuvre, je pourrais essayer de maintenir la partie tenseur complexe à l'avenir, je viens de commencer mon doctorat (et c'est mon année de césure en fait), et donc je n'aurai pas de problème pour écrire ma thèse en ces dernières années au moins. Mais je ne peux vraiment pas continuer à contribuer sans aucun retour de l'équipe pytorch. Je pense qu'il devrait y avoir une feuille de route pour cette grande extension. Et nous pourrions ajouter un support complexe en douceur afin que vos gars n'aient pas besoin de revoir un grand PR et cela facilitera les efforts des développeurs sur le suivi de la branche principale.

Premièrement, je pense que le principal problème concernant le support complexe serait la partie CUDA. Il est assez facile de prendre en charge la partie CPU avec ATen ou toute autre bibliothèque, je peux réécrire la partie CPU en quelques jours seulement s'il y a des commentaires. Il y a quelques problèmes que je pourrais concerner pour la partie CUDA, et je pense que cela pourrait conduire à deux approches différentes :

  1. Utilisez float2 , etc. pour simuler une seule valeur complexe comme cuComplex dans la partie CUDA.
  2. Utilisez FloatTensor et DoubleTensor existants pour simuler un tenseur complexe dans la partie C++ d'ATen.

La raison de la deuxième approche est que dans THC , pytorch utilise quelques astuces pour accélérer les opérations de mappage/réduction et il ne convient pas trivialement à cuComplex car cuComplex est en fait float2 , mais les fonctions __shfl_xxx ne prennent pas nativement en charge float2 . Je ne sais pas comment simuler efficacement une telle fonction pour float2 pour le moment.

La deuxième approche serait plus facile car nous n'avons plus besoin de nous soucier du matériel, et nous pouvons rendre notre nouvelle extension complexe plus facile à utiliser sur les anciens appareils. Cependant, cela peut entraîner une surcharge en raison d'une adresse mémoire non contiguë.

De plus, j'ai découvert que pour intégrer un nombre complexe dans ATen, nous devions peut-être gérer quatre types différents qui sont en fait les mêmes sur le matériel : std::complex , thrust::complex , cuComplex , float2 qui peut parfois être dangereux. (en fait, j'ai rencontré ce problème l'année dernière, et reinterpreter_cast était la solution).

Personnellement, je préférerais écrire tout ce qui est plus natif.

Et je pense que nous avons probablement besoin d'un calendrier ou d'une feuille de route, et nous pouvons prendre chaque petite partie et travailler ensemble afin que je n'aie pas besoin de suivre moi-même ce qui est totalement impossible...

il y avait un ChangeLog lorsque j'essayais d'implémenter le backend CPU, j'ai classé les fonctions à modifier pour les nombres complexes dans le journal. Nous pourrions écrire une feuille de route basée sur ce journal.

De plus, comme mon visa vient d'être rejeté (par l'Australie), je dois commencer une année sabbatique, si vous avez besoin que quelqu'un continue à travailler là-dessus, je pourrais postuler pour un stage.

J'y ai beaucoup réfléchi le dernier jour. C'est un peu triste que nous n'ayons pas pu fusionner les efforts de Roger tels quels, mais je me suis dit

"comment pouvons-nous créer un support Tensor complexe, tout en maintenant de faibles frais généraux de maintenance ?"

Voici ce que je présente comme un plan efficace à partir de l'objectif ci-dessus :

  • Les tenseurs complexes ne devraient pas être un nouveau type de tenseur fondamental, comme les tenseurs sparse . L'ajout d'un type fondamental entraîne de nombreux frais généraux de maintenance et des modifications transversales. La surcharge de maintenance ne concerne pas "qui maintient les bits complexes ?", mais plutôt "maintenant, tous les développeurs de base doivent être conscients de ce type complexe lorsqu'ils effectuent des modifications fondamentales, des modifications ATen, etc."

    • Au lieu de cela, ils doivent toujours être [Tensor Shape x 2] ou [2 x TensorShape], c'est-à-dire que le Tensor doit avoir une dimension supplémentaire avec une taille 2.

  • Les tenseurs complexes doivent être un petit fichier/dossier d'environ 2 000 lignes de C++ simple construit sur l'API ATen Tensor.

    • Par exemple, comme le suggère https://github.com/pytorch/pytorch/issues/6514 , la multiplication complexe doit être implémentée comme torch.stack([real1 * real2 - imag1 * imag2, real1 * imag2 + imag1 * real2], dim = -1)real1 = input1[:, :, :, ..., 0]

    • Cela nuit aux performances : oui, nous n'obtiendrons pas autant de performances que si nous incorporions tout. Cependant, la question est: "de combien?". Je pense que nous devrions viser des performances inférieures de 20 % en échange d'un support complexe sain et complet + maintenu.

    • Les fonctions complexes les plus utilisées peuvent commencer à recevoir des noyaux dédiés, de sorte que là où les performances subissent un impact supérieur à 20 % sur une fonction fréquemment utilisée, nous intervenons.

Il doit être [Tensor Shape x 2] car BLAS, cublas et MAGMA attendent tous leurs propres types complexes qui sont compatibles en octets avec float2. De plus, les appels blas, cublas et magma ne peuvent pas être gérés au niveau python.
Je ne pense pas que ce sera seulement 20% pour la multiplication complexe, n'avez-vous pas 4 opérations de copie complète en plus des calculs pour la partie réelle et imag ?
Quoi qu'il en soit, je serais toujours heureux si je n'avais pas à fusionner continuellement les modifications du maître.

D'accord avec @PhilippPelz , nous pourrions perdre beaucoup de performances car nous perdrons le support complexe de BLAS, cublas et MAGMA. Mais je n'en suis pas sûr. Cependant, pour être clair, le complexe Tensor est quelque chose de complètement différent de sparse tensor , la plupart des bibliothèques comme scipy.sparse et Julia's SparseArrays traitent le sparse array comme une composition de tableaux multidimensionnels fondamentaux. Mais personne ne traite un tableau multidimensionnel avec un type complexe en combinant deux tableaux réels ... (personne ici, je veux dire tensorflow, arrayfire, numpy et Julia). Bien que dans MXNet, la FFT soit effectivement réalisée par une composition de deux tenseurs réels, ils ne prennent pas en charge les complexes ... Il semble que tensorflow ait implémenté un DataType en tant que wrapper autour de différents types de réseaux, notamment complex64 et complex128 voir types.proto

À propos de la perte de performances

Premièrement, les fonctions élément par élément (appels de fonctions map/reduce) n'auront pas de perte de performances importante (au moins, la mémoire pour ces opérations sera contiguë). Mais je pense que nous devrions d'abord essayer de comparer certaines fonctions BLAS, pour voir si une composition de FloatTensor a des performances similaires avec Complex64Tensor sur GPU, et combien nous perdrons sur les performances avec un projet de mise en œuvre, comme :

  • gemm
  • gemv

Un tenseur complexe composé ressemblerait à quelque chose (ou utilisez simplement shared_ptr ):

class ComplexTensor {
    FloatTensor *real;
    FloatTensor *imag;
};

Cependant, comme je l'ai mentionné dans l'inconvénient de la première approche, des fonctions comme __shfl_xxx semblent également être un obstacle si nous voulons faire cela de manière plus native.

actuellement torch.fft renvoie un seul tenseur flottant de forme [dim1, ..., dimN, 2]

@ezyang quel est le calendrier de la sortie du C10 ? Cela semble être un point très raisonnable pour commencer à prendre en charge le complexe dans la branche principale.

@PhilippPelz Certainement pas pour 0.4. Nous ciblons juin en interne, espérons que cela ne soit pas trop long à attendre.

@ezyang vous avez mentionné June, avez-vous réussi à ajouter la prise en charge des nombres complexes à PyTorch ?

Je pense qu'il voulait dire C10, pas un support complexe. C10 va faciliter l'ajout de complexes. C'est comme ça que je l'ai compris.

Oui, C10 aura un enregistrement ouvert des types et des fonctions Tensor. Ainsi, l'ajout d'un type complexe en tant que package séparé sera beaucoup plus facile.

Y a-t-il une ETA sur les nombres complexes ? Est-ce que « beaucoup plus facile » signifie « ce sera probablement fait rapidement » ?

@themightyoarfish par beaucoup plus facile, je veux dire qu'on ne sera pas bloqué sur ce qui peut être poussé vers pytorch master. Nous n'avons pas fixé d'ETA. Je délimiterai le travail une fois que nous aurons obtenu une inscription ouverte à PyTorch.

@soumith avez-vous encore besoin de personnes pour travailler sur ce (numéro complexe) ? L'équipe PyTorch prendra-t-elle en charge les nombres complexes ? Je peux passer du temps à travailler dessus si vous le souhaitez en septembre, car je maintiendrai QuCumber (il utilisera fortement les nombres complexes)

@Roger-luo oui. Je voulais vous contacter une fois que nous aurons l'enregistrement ouvert disponible dans le backend PyTorch, et nous pourrons régler les détails.
@ezyang aurons-nous un enregistrement de type ouvert d'ici septembre ?

@soumith Cool, à votre service.

Nous pouvons y arriver. (Nous n'aurons pas le nouveau système "complet" en place, mais tant que nous configurons les choses de manière à ce qu'il soit refactorisable, nous pouvons continuer à le faire avancer au fur et à mesure que de nouveaux développements se produisent. Ce sera un bon cas de test pour le nouveau système ouvert inscription. Je peux m'assurer que cela se produit.)

@ezyang des notes maintenant? Je pourrais le lire avant de travailler dessus. Il semble que les choses aient beaucoup changé depuis la dernière fois.

@Roger-luo @PhilippPelz J'aimerais également vous aider dans la mise en œuvre de tenseurs complexes. J'en ai aussi besoin pour mes recherches de doctorat.

@alexgomezalanis peut-être pourrions-nous avoir un canal pour discuter sur le mou, je viens de créer un appel de canal #complex-numbers . Mais je ne commencerai pas à travailler dessus avant septembre (j'ai encore besoin de travailler sur une partie de mon code Julia...)

BTW, il semble beaucoup changé depuis la dernière fois. Je vais mettre un peu de temps à me rattraper avant de mettre la main dessus.

@alexgomezalanis je ne peux pas. vous devez d'abord rejoindre l'espace de travail de pytorch sur Slack. Je n'arrive pas à te trouver. Veuillez envoyer un e-mail à l'adresse : [email protected] pour recevoir une invitation.

@Roger-luo @alexgomezalanis Super de revoir la vie sur la question complexe du tenseur. Je peux également proposer de m'impliquer, mais en réalité, cela n'arrivera pas avant fin septembre / début octobre. Comme pour de nombreux commentateurs sur cette question, un support de tenseur complexe serait très utile pour mon projet de doctorat.

J'essayais aussi de sauver mes recherches l'année dernière 😏… mais maintenant je veux juste redonner vie à mon ancien code loc 1w+. 🤣 discutons sur slack !

:) Ouais, parlons de mou. Je viens de trouver l'invitation dans le dossier courrier.

Le plugin work in progress (pour CPU uniquement à court terme) est ici : https://github.com/Roger-luo/pytorch-complex

N'hésitez pas à me donner un problème et un PR.

J'ai posté les notes sur la complexité de la mise en œuvre en haut de ce numéro.

J'ai récemment commencé à utiliser PyTorch et je l'adore - c'est tellement plus agréable à utiliser que TensorFlow. Cependant, le support de tenseurs complexes est assez critique pour mes recherches (réseaux de neurones optiques). Est-ce que cela est toujours activement travaillé? Si oui, est-ce que quelqu'un connaît un délai (lâche) pour un support de tenseur complexe ?

Je serais heureux d'aider à travailler là-dessus là où je le peux, mais je suis relativement nouveau sur PyTorch, donc je n'ai pas encore une bonne idée de l'ampleur de cette fonctionnalité. Certains de mes camarades de laboratoire ont également exprimé un vif intérêt pour la prise en charge complexe des tenseurs (en physique, l'ajout de cela pourrait faire de Torch presque un remplacement accéléré par GPU pour NumPy) et pourraient également être disposés à aider si cela signifie obtenir une prise en charge complexe dans le futur proche.

Salut @bencbartlett

J'essaie toujours d'y travailler lentement.... mais je ne suis actuellement qu'un étudiant (avec une situation assez instable), ce qui signifie que je ne peux pas travailler sur ce temps plein mais seulement sur mon temps libre. (J'implémente mon code lié à la recherche dans Julia de l'année dernière, ce qui signifie que seul notre ancien package a besoin d'une meilleure prise en charge des nombres complexes par Torch.).

Si le nombre complexe est crucial pour vous et qu'il est urgent de l'avoir en torche, je vous suggérerais d'essayer ceci :

https://github.com/PIQuIL/QuCumber/blob/master/qucumber/utils/cplx.py

C'est super lent... mais ça marche au moins. Ou j'avais une version C dans l'ancien style TH.

Ce ne sera pas un petit projet qui peut être fait en quelques jours. Par conséquent, je ne peux garantir aucun délai spécifique pour un support fonctionnel complet avec une valeur complexe sur CPU ou CUDA.

J'aimerais cependant vous aider à travailler avec moi sur ce sujet. Je vous suggère de commencer par essayer de résoudre les problèmes que j'ai publiés dans le référentiel d'extension. Et n'hésitez pas à me demander par slack ou par e-mail ou par problème si vous avez des questions (car il n'y a pas encore beaucoup de docs).

Je n'ai malheureusement pas encore accès à PyTorch Slack. (J'ai envoyé deux e-mails demandant une invitation, mais je n'ai pas eu de réponse.) Quelqu'un pourrait-il m'inviter ? ([email protected])

@Roger-luo Je vais certainement jeter un œil à votre fork, mais je ne peux pas promettre que je serai d'une grande aide - mon C++ est rouillé et comme vous l'avez souligné, il est difficile de trouver le temps de travailler dessus en tant que élève. Les utilitaires QuCumber sont sympas, mais malheureusement ils ne me seraient pas très utiles : jusqu'à ce que les tenseurs complexes soient pris en charge par le GPU ou pris en charge par autograd et torch.nn, ils ne fournissent pas beaucoup d'utilité au-delà de ce que NumPy peut offrir.

@soumith @ezyang Ce serait formidable d'avoir plus d'attention à ce sujet de la part de l'équipe PyTorch ! La prise en charge complexe semble être une fonctionnalité importante pour une bibliothèque de tenseurs générale, elle est pratiquement essentielle en physique, et en particulier dans ML au cours des dernières années, il y a eu un intérêt croissant pour les modèles à valeurs complexes.

L'approche de @bencbartlett QuCumber peut être utilisée sur GPU avec AD ... c'est juste super lent ... Je veux dire si vous voulez juste cet AD, vous pourrez peut-être l'utiliser.

Ouais, franchement, j'utilise une version légèrement modifiée de https://github.com/FluxML/Flux.jl et mon propre package dans Julia pour la recherche (j'ai besoin d'une AD complexe sur GPU avec des tenseurs dans certaines situations également ). Le package AD source2source Zygote.jl peut faire de l'AD sur des tenseurs complexes, mais il en est à un stade très précoce qui peut avoir des défauts de segment. L'écosystème n'est pas si stable encore comparé à Torch, je dois parfois pirater un peu ces implémentations pour mon usage personnel... Mais cela fonctionne essentiellement pour ce dont j'ai besoin pour la recherche en physique quantique. Je peux aussi avoir des tenseurs complexes sur GPU.

Je ne pense pas que le support de valeur complexe pour torch.nn soit nécessaire, nous pourrions juste avoir besoin d'ajouter quelques définitions pour autograd une fois que le tenseur complexe est fonctionnel, car des choses comme les couches linéaires peuvent rester les mêmes . Et certaines fonctions d'activation peuvent ne pas avoir d'extension standard dans l'espace Hilbert... (Vous pouvez consulter le blog de mon collaborateur @GiggleLiu )

Pour l'extension pytorch-complex, je ne sais pas quand pourrons-nous obtenir un support complet avec AD sur GPU... cela me semble encore assez loin. Je dirais que l'implémentation du processeur passera par une période qui nécessite des correctifs dans l'arborescence principale (par exemple, les promotions de type, le support simd, etc.), cela pourrait également être lié à la prochaine implémentation ATen en C++ et se débarrasser de TH, etc., et ensuite nous pourrons ajouter plus rapidement des opérateurs pour des tenseurs complexes.

Je peux postuler pour des stages au printemps (ce que je viens de demander à @ezyang ). Je pourrais donc peut-être y travailler à plein temps pendant plusieurs mois avant de commencer mon doctorat. Voyons voir.

En attendant, j'ai implémenté ma propre version de la multiplication complexe. Cependant, lorsque je le profile, il arrive qu'un temps substantiel soit consacré à : torch._C_._cuda_isDriverSufficient

image

Avez-vous une idée pourquoi? Si vous connaissez une meilleure implémentation de la multiplication complexe, faites-le moi savoir. D'une manière ou d'une autre, ma version (même optimisée pour le nombre de multiplications : 3 au lieu de 4) semble être relativement lente, par exemple irfft du tenseur de sortie est 10 X plus rapide que ma multiplication élément par élément. La multiplication complexe est-elle prise en charge au niveau C++ de PyTorch ?

def complex_mul(x, y, out):
    uavc = x[..., 0] * (y[..., 0] + y[..., 1])
    out[..., 0] = uavc - (x[..., 0] + x[..., 1]) * y[..., 1]
    out[..., 1] = (x[..., 1] - x[..., 0]) * y[..., 0] + uavc
def test_complex_mul_out_tensor(self):
        N, C, H, W, I = 128, 3, 32, 32, 2
        K = 16  # number of filter banks
        repetitions = 1000
        dtype = torch.float
        if torch.cuda.is_available():
            device = torch.device("cuda")
        else:
            device = torch.device("cpu")
        x = torch.randn(N, 1, C, H, W, I, dtype=dtype, device=device)
        y = torch.randn(K, C, H, W, I, dtype=dtype, device=device)
        start_mul_time = time.time()
        out = torch.empty(N, K, C, H, W, I, dtype=dtype, device=device)
        for _ in range(repetitions):
            complex_mul(x, y, out)
        print("multiplication time: ", time.time() - start_mul_time)

Nous essayons de le supporter à partir de C++. voir le post en haut. Si vous pouvez compiler l'extension, cela devrait fonctionner pour la multiplication scalaire au moins pour le moment...

Votre implémentation est similaire à celle que nous avons dans QuCumber. Il peut appeler beaucoup de threads GPU supplémentaires si vous n'appelez pas le bon noyau cuda pour le nombre complexe. Et vous risquez de perdre SIMD si vous n'avez pas de backend C++ comme support dans Python.

Je vous suggère d'exécuter nvprof pour obtenir plus de détails.

@Roger-luo @apaszke @soumith Merci pour ce fil d'ailleurs. J'ai implémenté un tenseur complexe de base piraté à partir de la sous-classe torch.Tensor.

Je traite la première moitié comme réelle, la seconde comme imaginaire et j'ai mis en œuvre mes propres opérations arithmétiques de base et quelques autres dont j'ai besoin pour mes recherches.

J'ai vérifié contre Tensorflow et numpy. Les gradients et toutes les opérations que j'ai implémentées correspondent à leurs sorties !

C'est juste destiné à rester jusqu'à ce que PT prenne pleinement en charge les tenseurs complexes.

Caractéristiques:

  1. Tests mis en œuvre.
  2. Pypi pris en charge (c'est-à-dire: installation pip)
pip install pytorch-complex-tensor

https://github.com/williamFalcon/pytorch-complex-tensor

Merci @williamFalcon !

Une mise à jour celle-ci ? Je me demande simplement s'il y aura un plan pour intégrer le support de type complexe dans le pytorch.

Salut, @whmrtm

@ezyang travaille sur https://github.com/Roger-luo/pytorch-complex/issues/4 Ou toute personne intéressée par cela pourrait nous aider à le faire fonctionner. Ce problème résoudra un problème de diffusion de base (vous pourrez alors utiliser de nombreuses fonctions une fois celui-ci résolu). N'hésitez pas à faire des relations publiques ou à me demander de vous ajouter en tant que collaborateur.

Je ne pourrai pas travailler sur quoi que ce soit avant l'été, je dois terminer une nouvelle version pour notre propre package.

Salut, @whmrtm

@ezyang travaille sur Roger-luo/pytorch-complex#4 Ou toute personne intéressée par cela pourrait nous aider à le faire fonctionner. Ce problème résoudra un problème de diffusion de base (vous pourrez alors utiliser de nombreuses fonctions une fois celui-ci résolu). N'hésitez pas à faire des relations publiques ou à me demander de vous ajouter en tant que collaborateur.

Je ne pourrai pas travailler sur quoi que ce soit avant l'été, je dois terminer une nouvelle version pour notre propre package.

Merci pour la mise à jour, je vais voir ce que je peux faire.

Salut @Roger-luo

Puis-je accéder au canal slack lié au sujet de support des tenseurs complexes ([email protected]) ? J'ai envoyé un mail pour une invitation mais rien ne s'est encore passé. En ce moment, j'essaie de trouver des points par où commencer à contribuer à ce problème. Je suppose que https://github.com/Roger-luo/pytorch-complex/issues/4 est un point d'entrée actuel maintenant ?

@beconstant oui, c'est le point de départ, cela devrait faire fonctionner certaines fonctions de diffusion, mais je ne sais pas pourquoi cela génère une erreur de promotion de type sur cuda, cela fonctionnait sur le processeur. (Bien que nous n'ayons pas l'intention de prendre en charge cuda en premier lieu, cela entraînerait un échec de la construction)

Je ne peux pas vous envoyer d'e-mail d'invitation (je n'y ai pas accès). Je pense que vous devriez suivre le guide officiel de pytorch pour rejoindre le mou. Mais on peut toujours en discuter dans le numéro/PR.

@Roger-luo ok, j'ai compris :)

Faites-moi savoir si vous avez besoin d'aide. Je vais commencer par construire la version de pytorch spécifiée. Des progrès sur pytorch-complex/issues/4 ?

Faites-moi savoir si vous avez besoin d'aide. Je vais commencer par construire la version de pytorch spécifiée. Des progrès sur pytorch-complex/issues/4 ?

@dylanbespalko Bonjour, j'ai un besoin urgent de pytorch implémenté dans la version à valeur complexe.
Merci beaucoup pour vos contributions.

Cordialement,
Zellar209

Salut @Zellar209 ,

J'ai l'impression que @ezyang travaille dur sur l'un des plus gros problèmes ( pytorch-complex/issues/4 ). J'ai maintenant un système AMD et un système Nvidia dans 3 semaines que je peux utiliser pour augmenter le support GPU.

Je suppose que le problème concerne à peu près le changement de promotion de type d'origine qui casse CUDA, tant que ce PR est résolu, il a au moins quelques opérateurs qui fonctionnent sur le CPU, nous n'avons pas encore de support CUDA du tout ...

À mon humble avis, je pense que nous devrions nous concentrer sur le processeur et faire en sorte que les choses fonctionnent d'abord, puis envisager le GPU plus tard.

La prise en charge du processeur uniquement est correcte. Ce problème de promotion de type ( pytorch-complex/issues/4 est-il géré en interne par fb ? Est-il acceptable d'y travailler en externe ?

Bonjour @dylanbespalko ; J'ai dit à @Roger-luo que j'allais l'examiner (parce que j'étais probablement le mieux placé pour comprendre quel est le problème), mais je n'ai pas encore eu le temps de l'examiner. Si vous voulez jeter un coup d'œil sur la façon de résoudre le problème, je serais heureux de vous conseiller.

Salut @Zellar209 ,

J'ai l'impression que @ezyang travaille dur sur l'un des plus gros problèmes ( pytorch-complex/issues/4 ). J'ai maintenant un système AMD et un système Nvidia dans 3 semaines que je peux utiliser pour augmenter le support GPU.

Oui, je n'ai plus besoin de GPU maintenant, j'utilise le système MAC. Mais j'ai eu quelques erreurs lors de la construction de ce projet.

Salut @ Zellar209 , pourriez-vous publier ce que vous obtenez dans le problème de pytorch-complex ? Je pense qu'il y a quelque chose qui ne va pas avec le nouveau Xcode de Mac, ce qui le rend difficile à construire. Mais les gens auront besoin de plus de messages d'erreur pour comprendre pourquoi.

J'ai posé une question sur le système d'exploitation et le message d'erreur, mais vous n'avez pas répondu ...

Bonjour @dylanbespalko ; J'ai dit à @Roger-luo que j'allais l'examiner (parce que j'étais probablement le mieux placé pour comprendre quel est le problème), mais je n'ai pas encore eu le temps de l'examiner. Si vous voulez jeter un coup d'œil sur la façon de résoudre le problème, je serais heureux de vous conseiller.

Merci pour votre première réponse.

1. Lorsque j'exécute "python setup.py install" en utilisant gcc (par défaut), j'obtiens des erreurs comme celle-ci :

construire l'extension 'torch_complex.cpp'
gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/anaconda3/include -arch x86_64 -I/anaconda3/include -arch x86_64 -I/ anaconda3/lib/python3.6/site-packages/torch/include -I/anaconda3/lib/python3.6/site-packages/torch/include/torch/csrc/api/include -I/anaconda3/lib/python3. 6/site-packages/torch/include/TH -I/anaconda3/lib/python3.6/site-packages/torch/include/THC -I/anaconda3/include/python3.6m -c src/module.cpp -o build/temp.macosx-10.7-x86_64-3.6/src/module.o -g -stdlib=libc++ -std=c++11 -DTORCH_API_INCLUDE_EXTENSION_H -DTORCH_EXTENSION_NAME=cpp
gcc : erreur : option de ligne de commande non reconnue '-stdlib=libc++'

erreur : la commande 'gcc' a échoué avec l'état de sortie 1

2. Lorsque j'utilise clang pour le compiler, les erreurs sont :

Dans le fichier inclus à partir de src/module. cpp:2 :
Dans le fichier inclus à partir de src/CPUComplexType.h:60 :
src/CPUComplexTypeImpl.h:102:105 : avertissement : 'IntList' est obsolète [-Wdeprecated-declarations]
Tensor & CPUComplexType ::set_(Tensor & self, Storage source, int64_t storage_offset, IntList sizes, IntList strides) const {
^
/anaconda3/lib/python3.6/site-packages/torch/include/c10/util/ArrayRef.h:273:7 : remarque : 'IntList' a été explicitement marqué obsolète ici
en utilisant IntList C10_DEPRECATED_USING = ArrayRef;
^
Dans le fichier inclus à partir de src/module. cpp:2 :
Dans le fichier inclus à partir de src/CPUComplexType.h:60 :
src/CPUComplexTypeImpl.h:105:76 : erreur : aucun membre nommé 'scalarTypeToDataType' dans l'espace de noms 'at'
auto source_ =checked_storage(source,"source",2, DeviceType::CPU, at::scalarTypeToDataType(CPUComplexTypeInfo ::scalar_type));
~~~~^
7 avertissements et 2 erreurs générés.

erreur : la commande 'clang' a échoué avec l'état de sortie 1

Je ne peux pas le réparer. J'espère vraiment que tu peux m'aider!

Salut les gars,

Merci pour vos commentaires. Je pense que je peux passer la semaine à étudier ça. Jusqu'à présent, j'ai compilé le complexe pytorch de @Roger-luo comme suit :

@Zellar209 : J'ai joint mes variables d'environnement fonctionnant sous macOS 10.13.

  1. Supprimer la distribution pytorch existante comme suit
    conda désinstaller pytorch
    torche de désinstallation de pip
    pip uninstall torch # exécutez cette commande deux fois
    python setup.py propre
    Supprimez le dossier torche dans le dossier python site-packages s'il existe.
    Renommez (ou supprimez) le dossier source précédent de pytorch (quelque chose y faisait référence).

  2. Installez la révision 6cb593b88cb0c411690b4957850058329526d87b de PyTorch.

    git clone [email protected]:pytorch/pytorch.git
    git checkout 6cb593b88cb0c411690b4957850058329526d87b
    git submodule update --init —recursive
    export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../“}
    MACOSX_DEPLOYMENT_TARGET=10.13 CC=clang CXX=clang++ python setup.py develop
    python
>>> import torch
  1. Installer le complexe pytorch
    python setup.py install
    python setup.py build
    python setup.py test
    # ERROR: test (unittest.loader._FailedTest)
    # ERROR: test_scalar_binary_op (tests.test_tensor.TestComplexTensor)
  1. Créer un tenseur complexe
   from torch_complex import torch
   a = torch.ones(3, dtype=torch.complex128)
   a*a  
   RuntimeError: promoteTypes with complex numbers is not handled yet; figure out what the correct rules should be

@ezyang , @Roger-luo :

Tout pour la promotion de type pour les opérations tensorielles semble être fait dans c10/core/ScalarType.h
J'ai trouvé l'erreur AT_ERROR("promoteTypes with complex numbers is not handled yet; figure out what the correct rules should be”);
Il semble que je doive ajouter une entrée pour c8 et c16 à l'intérieur de ce tableau.
Cela a-t-il quelque chose à voir avec 9515 ? Je pense que c'est juste pour appeler des fonctions numpy.
Est-ce un bon point de départ ?

9515 est sans rapport. Cependant, la correction de ce chemin de code dans ScalarType.h est un bon point de départ.

J'ai corrigé le chemin de code dans ScalarType.h
BinaryOps (add, sub, mul, div) fonctionne, mais seulement si les deux arguments sont des Tensors.
Quelques autres problèmes étranges, mais je dois y regarder un peu plus.

@dylanbespalko J'ai ajouté des promotions de type ici : https://github.com/pytorch/pytorch/pull/11641

Vous pouvez simplement copier cela, mais le problème est que cela casse CUDA d'une manière ou d'une autre.

IIRC, il y avait un bogue de fil dû à la version de gcc. J'avais quelques solutions de contournement là-bas.

Ah, merci @Roger-luo. Je regardais les commentaires de #11641 . Je ferai un meilleur travail de copie du code demain.

Comment savoir quand j'ai cassé CUDA alors que je n'ai pas d'appareil CUDA ? Je suppose que le CI me le dira ?

Oui, tant que vous soumettez un PR, il vous dira lequel est cassé. Et si tout passe, alors nous pourrions simplement fusionner cela et faire fonctionner les choses.

Ok, alors je commencerai à soumettre des PR afin que je sache quand cela se produira.

@dylanbespalko Bonjour, il semble encore des erreurs dans votre environnement ?
Si vous le corrigez, partagez-le avec nous. Merci beaucoup.

Salut les gars,

J'ai essayé de faire plusieurs relations publiques après avoir copié plusieurs commits de @Roger-luo. Malheureusement, je n'ai pas de GPU CUDA pour le moment et les machines CI avec un CUDA ne s'initialisent pas. Je ne peux pas recréer l'échec du test CUDA pour le moment, donc j'y reviendrai dans quelques semaines lorsque je pourrai exécuter localement sur ce GPU. Semble prometteur, au moins.

@ezyang , @Roger-luo

J'ai jeté un œil au PR #11641 de Roger :

  • Il se construit et s'exécute sur ma machine CUDA 9.0
  • Il ne parvient pas à créer sur des machines CI qui exécutent CUDA 9.0

J'ai également jeté un coup d'œil à certains des développements récents de PyTorch :

  • Une présentation de @ezyang décrivant comment écrire une extension C++/CUDA qui peut définir un device/layout/dtype personnalisé .

    • Un PR #21964 plus récent qui "supprime ComplexHooksInterface", mais définit une extension C++ de nombre complexe située dans pytorch/test/cpp_extensions/complex_registration_extension.cpp

Il me semble qu'une nouvelle capacité d'extension "hors arbre" est en cours de développement, ce qui me permettrait d'étudier la prise en charge des nombres complexes sans casser le reste de pytorch. Mon objectif est de :

  1. Définissez la prise en charge du processeur complexe sans AVX.
  2. Définissez la prise en charge de CUDA complexe à l'aide de Thrust.

@ezyang
Pouvez-vous fournir un calendrier prévu pour cette extension de périphérique/disposition/dtype hors arbre que vous avez présentée ? Pouvons-nous nous attendre à cette fonctionnalité dans les 3 prochains mois ?

@ezyang

Êtes-vous capable de fusionner la prise en charge des nombres complexes sur le processeur sans prise en charge AVX/SSE ? Je prévois de soumettre les éléments suivants dans des demandes de fusion distinctes :

  • [ ] Ajout de la prise en charge complexe des noyaux CPU BinaryOp
  • [ ] Ajout du support complexe de CPU TensorFactories
  • [ ] Ajout du support complexe de CPU FillKernels
  • [ ] Ajout de la prise en charge complexe des noyaux CPU Range
  • [ ] Ajout de la prise en charge complexe des noyaux CPU Unary
  • [ ] Ajout de la prise en charge complexe des noyaux CPU Compare
  • [ ] Ajout de la prise en charge complexe des noyaux CPU TensorCompare
  • [ ] Ajout de la prise en charge complexe des noyaux CPU ReduceOp
  • [ ] Ajout de la prise en charge complexe des noyaux CPU PointwiseOps
  • [ ] Ajout de la prise en charge complexe des noyaux CPU learpOps
  • [ ] Ajout de la prise en charge complexe des noyaux CPU LinearAlgebraOps
  • [ ] Ajout de la prise en charge complexe des noyaux CPU SpectralOps

Je prévois de le tester sur les processeurs Intel / Arm dans les prochains jours.

@ezyang ,

Je regarde des opérations comme fft() et var() où l'implémentation des nombres complexes doit convertir les données du tenseur en un double tenseur de forme : (complex_shape, 2) . Cela ne fonctionne avec aucune méthode de tenseur existante :

  1. 'tensor.to(torch.float64) : ne garde que la partie réelle et renvoie un tenseur de même forme.
  2. 'tensor.view(new_shape) : la nouvelle forme doit avoir le même nombre d'éléments.

Évidemment, je peux faire quelque chose d'inefficace comme:

def to_float(tensor):
    return th.stack((tensor.real().type(th.float64), tensor.imag().type(th.float64)), -1)

def to_complex(tensor):
    tensor = tensor.type(th.complex128) 
    return tensor[..., 0] + 1j*tensor[..., 1]

Évidemment, il s'agit de créer des copies, alors que tout ce dont j'ai besoin est de static_cast<double> et de changer la forme du tenseur en (old_shape, 2) . Avez-vous des suggestions sur la façon de procéder?

De plus, il existe un hack dans numpy qui vous permet de faire ceci :

a = np.array([1 + 1j], dtype=np.complex128)
a.dtype = np.float64  ## This works

a = torch.tensor([1 + 1j], dtype=torch.complex128)
a.dtype = torch.float64  ## This does not work

La possibilité de définir dtype fonctionne vraiment dans cette situation, mais cela peut être imprévisible.

Quelques informations supplémentaires concernant l'interprétation d'un nombre complexe comme un tableau de nombres réels de longueur 2. Ce qui suit est valide en C++11.

Pour tout pointeur vers un élément d'un tableau de nombres complexes p et tout index de tableau valide i, reinterpret_cast(p)[2 i] est la partie réelle du nombre complexe p[i], et reinterpret_cast(p)[2 i + 1] est la partie imaginaire du nombre complexe p[i]. (Depuis C++11)

Je pense que cela signifie qu'il est possible de convertir un complex_tensor en un real_tensor avec une forme (complex_shape, 2), puis d'effectuer une opération sans appeler real() et imag() qui allouent une nouvelle mémoire.

@dylanbespalko J'avais peur quand vous poseriez des questions à ce sujet :) La garantie std::complex signifie que si vous avez le pointeur de données std::complex<float>* , vous pouvez le lancer en toute sécurité dans float* (aliasing strict marmonnant) puis transmettez-le à n'importe quelle fft chose que vous utilisez. Si vous avez seulement besoin d'implémenter fft/var où vous pouvez transmettre ce représentant de bas niveau, ce sera plus facile.

Cependant, si vous avez besoin de revoir littéralement un tenseur complexe comme un tenseur flottant, nous sommes un peu dans le pétrin, car il n'y a pas de précédent pour cela dans PyTorch aujourd'hui. Storage dtype a toujours été d'accord avec Tensor dtype. Donc, si vous créez un stockage complexe, il n'y a aucun moyen de le considérer comme un stockage flottant.

Une pensée que j'ai eue est peut-être que nous devrions assouplir cet invariant. L'idée est :

  1. Nous allouons toujours les stockages en tant que type "non vectorisé" en question. Alors pour un complexenous allouons un tenseur flottant.
  2. Le type de tenseur est autorisé à être en désaccord avec le type de stockage, mais uniquement en tant que variante vectorisée du type sous-jacent

Je ne sais pas combien de code nous devrions changer pour que cet invariant se produise.

@ezyang ,

Oui c'était inévitable...

Si vous avez seulement besoin d'implémenter fft/var où vous pouvez transmettre ce représentant de bas niveau, ce sera plus facile.

Oui, cela est possible dans de nombreux cas. Êtes-vous en mesure de fournir un extrait de code expliquant comment interpréter les données de tenseur en tant que std :: vector ?

Cependant, si vous avez besoin de revoir littéralement un tenseur complexe comme un tenseur flottant, ....

J'imagine qu'il est rare de voir un tenseur en utilisant un autre dtype. J'ai implémenté une méthode set_dtype() pour Tensor , mais j'ai eu quelques erreurs. Je n'ai pas non plus mis à jour les foulées pour refléter les changements de forme. Je ne sais pas pourquoi la définition de dtype fonctionne dans numpy (est-ce une coïncidence ?), Cependant, lorsque vous téléchargez des données sur un convertisseur numérique-analogique (DAC), il s'attend souvent à ce que les données réelles/imaginaires soient entrelacées. Cela motiverait peut-être la nécessité de découpler le type de tenseur du type de stockage, comme vous l'avez suggéré.

Je vais éviter de le faire pour l'instant. Je suis sûr qu'il y a d'autres goulots d'étranglement de performance pour moi.

Oui, cela est possible dans de nombreux cas. Êtes-vous en mesure de fournir un extrait de code expliquant comment interpréter les données de tenseur en tant que std :: vector ?

Pas exactement un std :: vector, mais j'imagine quelque chose comme ça :

Tensor complex_tensor;
assert(complex_tensor.is_contiguous());
std::complex<float>* cp = complex_tensor.data_ptr<std::complex<float>>();
float* fp = reinterpret_cast<float*>(cp);
auto num_floats = complex_tensor.numel() * 2;

J'ai implémenté une méthode set_dtype() pour Tensor, mais j'ai eu quelques erreurs. Je n'ai pas non plus mis à jour les foulées pour refléter les changements de forme.

Ouais, c'est probablement une mauvaise idée si vous ne corrigez pas aussi les foulées. De plus, je ne suis pas un grand fan des tenseurs se transmutant en d'autres dtypes ; mieux vaut tout faire hors de propos :)

cependant, lorsque vous téléchargez des données vers un convertisseur numérique-analogique (DAC), il s'attend souvent à ce que les données réelles/imaginaires soient entrelacées. Cela motiverait peut-être la nécessité de découpler le type de tenseur du type de stockage, comme vous l'avez suggéré.

Oui, en fin de compte, c'est la bonne chose à faire, mais je suis d'accord qu'il est plus facile de ne pas le faire maintenant.

@ezyang ,

Je commence à jouer avec le support CUDA des nombres complexes.

Il existe deux options compatibles binaires :

  1. cuComplex : Prise en charge très basique des add, sub, mul, div, real, imag.
  2. push::complex : remplace std::complex qui prend en charge l'allocation de mémoire à l'hôte et au périphérique.

Le conteneur push::complex semble être la voie à suivre. L' API Thrust::Complex suggère que les conteneurs thrust::complex<T> peuvent être alloués sur la mémoire de l'hôte et de l'appareil, tandis que les std::complex<T> ne peuvent être alloués que sur la mémoire de l'hôte :

__host__ __device__     thrust::complex< T >::complex (const complex< T > &z)  //thrust container
__host__    thrust::complex< T >::complex (const std::complex< T > &z) //stl container.
  1. Cela suggère-t-il que AT_DISPATCH_COMPLEX_TYPES devrait définir using scalar_t = thrust::complex<double> au lieu de using scalar_t = std::complex<double> ?

  2. Comment Pytorch appelle-t-il automatiquement les équivalents CUDA de std::log pour les types de données réels ? Comment puis-je savoir qu'il existe un équivalent CUDA d'un noyau mathématique ?

  1. Je pense que la difficulté avec l'utilisation universelle de thrust::complex<double> pour le CPU et CUDA est que nous ne construisons pas réellement contre la poussée si vous faites une construction uniquement CPU. Je suppose qu'il y a un tas d'options; nous pourrions lancer notre propre type complexe (similaire à la façon dont nous roulons notre propre demi-type), ou vous pouvez simplement réinterpréter lancer votre chemin vers la victoire, car std::complex<> est défini pour avoir une disposition binaire spécifique. C'est à vous de voir, mais se contenter de réinterpréter le casting entre les types semble plus simple pour l'instant.
  2. Nous avons des surcharges mathématiques dans THCNumerics.cuh, cela répond-il à votre question ?

@iotamudelta a soulevé un problème avec la conformité C++11 dans #29547

std :: real est uniquement constexpr à partir de C++ 14

Si je comprends bien, le std::real() doit être un constexpr pour que le compilateur hcc puisse compiler l'instruction pour __device__ .

Solutions possibles:

  1. Trouvez une autre méthode ou fonction pour convertir complex<double> en double :
  1. Trouvez un moyen d'encapsuler la fonction :

    • La plupart des appels à std::real sont effectués en aten/src/ATen/native/cpu/zmath.h . Exemple : remplacez inline par constexpr :

      inline VALUE_TYPE real_impl (SCALAR_TYPE z) ->
      constexpr VALUE_TYPE real_impl (SCALAR_TYPE z)

      inline std::complex<float> real_impl <std::complex<float>> (std::complex<float> z) -> constexpr std::complex<float> real_impl <std::complex<float>> (std::complex<float> z)

      inline std::complex<float> real_impl <std::complex<double>> (std::complex<float> z) -> constexpr std::complex<float> real_impl <std::complex<double>> (std::complex<float> z)

Cela ne compilera pas car il y a toujours un appel imbriqué à std::real() qui n'est pas un constexpr .

3. Si j'utilise std::complex::real() au lieu de std::real() cela semble être conforme à C++11.

  1. Je pense que vous dites que peu importe ce que je fais, ce code est UB jusqu'à C++14. Existe-t-il un autre moyen de convertir un std::complex<double> en double qui répondrait à vos besoins ?

@iotamudelta , @bddppq , @ezyang ,

J'ai ajouté la prise en charge des UnaryOps et BinaryOps complexes sur l'API CUDA poussée :: complexe, mais je dois poser quelques questions avant de le soumettre.

J'ai défini une fonction de modèle qui vous permet d'utiliser des types de données push :: complexes lorsque vous traitez des nombres complexes.
aten/src/ATen/native/cuda/zmath.cuh

#pragma once

#include <complex>
#include <thrust/complex.h>

namespace at { namespace native {
namespace {

template <typename TYPE>
struct ztype_cuda {
  using value_t = TYPE; // Complex template type
  using thrust_t = TYPE; // Equivalent thrust type
};

template <>
struct ztype_cuda<std::complex<float>> {
  using value_t = float;
  using thrust_t = thrust::complex<float>;
};

template <>
struct ztype_cuda<std::complex<double>> {
  using value_t = double;
  using thrust_t = thrust::complex<double>;
};

} // end namespace
}} //end at::native

Puis dans aten/src/ATen/native/cuda/BinaryOpsKernel.cu
Remplacer:

void add_kernel_cuda(TensorIterator& iter, Scalar alpha_scalar) {
  AT_DISPATCH_ALL_TYPES_AND2(kHalf, kBool, iter.common_dtype(), "add_cuda/sub_cuda", [&]() {
    auto alpha = alpha_scalar.to<scalar_t>();
    gpu_kernel_with_scalars(iter, [alpha]GPU_LAMBDA(scalar_t a, scalar_t b) -> scalar_t {
      return a + alpha * b;
    });
  });
}

Avec:

void add_kernel_cuda(TensorIterator& iter, Scalar alpha_scalar) {
  AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2(kHalf, kBool, iter.dtype(), "add_cuda/sub_cuda", [&]() {
    using thrust_t = typename ztype_cuda<scalar_t>::thrust_t;
    auto alpha = thrust_t(alpha_scalar.to<scalar_t>());
    gpu_kernel_with_scalars(iter, [alpha]GPU_LAMBDA(thrust_t a, thrust_t b) -> thrust_t {
      return a + alpha * b;
    });
  });
}

Des questions

  1. @ezyang : Pour les nombres non complexes, scalar_t et thrust_t sont du même type. Peut-être que je pourrais remplacer le nom de la variable thrust_t par quelque chose de plus convivial pour les nombres non complexes, comme scalar_t_c ?
  2. La bibliothèque de poussée semble largement référencée dans le code :
    a) @bddppq : Y a-t-il une raison pour laquelle je devrais utiliser cuComplex au lieu de thrust::complex ?
    b) @iotamudelta : la poussée de la hanche a été supprimée dans ROCm2.7. Dois-je utiliser hip_complex la place ?
    push::complex semble prendre en charge plus de fonctionnalités que cuComplex .

S'il vous plait, faite moi part de votre avis.

@iotamudelta

J'ai mis à jour la discussion sur std :: real(). Pouvez-vous confirmer que std :: complexe::real() résoudrait le problème.

Salut @dylanbespalko ,

Je suppose que @iotamudelta se plaint du fait que cast_and_store pour les types complexes manque un C10_HOST_DEVICE , ce serait un UB si ce chemin de code est exécuté sur le GPU.

Actuellement, cet utilitaire de diffusion dynamique n'est utilisé que dans GPU TensorIterator, et il n'est utilisé qu'en cas de promotion de type. Pour la raison que le complexe n'était pas pris en charge sur le GPU actuellement, cast_and_store pour les types complexes n'a pas le qualificatif C10_HOST_DEVICE et utilise std::real ce qui est tout à fait OK pour un hôte- seule fonction. Il n'y a pas d'UB ici car il n'est pas utilisé et vous n'avez rien à craindre.

Mais puisque vous souhaitez ajouter la prise en charge du complexe au GPU, et que le complexe est pris en charge par la promotion de type, comme nous pouvons le voir dans https://github.com/pytorch/pytorch/blob/master/c10/core/ScalarType.h#L398 - L420, vous devez être très prudent sur ce chemin de code et il y a quelques modifications que vous devrez peut-être faire pour le faire fonctionner :

Bien sûr, vous devez ajouter C10_HOST_DEVICE comme le fait @iotamudelta dans https://github.com/pytorch/pytorch/pull/29547 , mais cela ne suffit pas, car il suffit d'ajouter C10_HOST_DEVICE sans autres changements est toujours UB sur C++11 comme mentionné par @iotamudelta , une bonne solution pourrait être ce que vous avez mentionné : utilisez std::complex::real() pour remplacer le std::real .

Mais au-delà de cela, si vous regardez le fichier https://github.com/pytorch/pytorch/blob/master/c10/util/TypeCast.h , vous verrez à l'intérieur fetch_and_cast , il y a quelque chose comme :

#ifndef C10_HOST_DEVICE
    AT_FORALL_COMPLEX_TYPES(FETCH_AND_CAST_COMPLEX_CASE)
#endif

Ce chemin de code est désactivé sur le GPU. Vous devez l'activer et le faire fonctionner.

De plus, je n'ai vu aucune conversion entre complex<float> et complex<double> à l'intérieur fetch_and_cast et cast_and_store . Vous devrez peut-être également ajouter la conversion pour cela. Assurez-vous de tester soigneusement la couverture de ces fonctions de tous les dtypes.

cc : @ezyang et @bddppq

Aussi @dylanbespalko , s'il vous plaît contactez-moi si vous apportez des modifications à TypeCast.h dans votre PR.

OK, j'ai quelques petites choses à régler avec torch.real() et torch.imag() sur ARM, donc je vais régler TypeCast.h et quelques autres pendant que j'y suis. Je vous mettrai en copie sur le PR.

Drive by comment : @smessmer nous fait passer à C++14, auquel cas ce ne sera plus UB. Comme cela arrive bientôt, si l'UB ne cause pas de vrais problèmes, je ne m'en soucierais pas trop.

@ezyang : Bon à savoir. La plupart des trucs tiers comme Eigen appellent toujours std::real() très généreusement.

Pour les nombres non complexes, scalar_t et thrust_t sont du même type. Peut-être pourrais-je remplacer le nom de variable thrust_t par quelque chose de plus convivial pour les nombres non complexes, comme scalar_t_c ?

Je ne suis pas trop sûr, mais scalar_t_c semble un peu moins clair que thrust_t (que veut dire c toute façon ?) Les types en question ici semblent assez spécifiques, il semble donc préférable d'utiliser un nom qui parle directement de l'intention.

Ok, je m'en tiendrai à thrust_t . Si quelqu'un plonge dans ztype_cuda<>() , il devrait immédiatement comprendre que scalar_t est thrust_t pour les types non complexes.

Salut à tous! Il semble que de bons progrès soient réalisés vers l'ajout d'un support complexe à pytorch ! Merci @dylanbespalko d'avoir pris l'initiative à ce sujet et d'avoir également ajouté le support CUDA ! D'un haut niveau, je suis intéressé de savoir quel est l'état d'avancement actuel du support complexe ? Je suis surtout intéressé par un calendrier approximatif pour la prise en charge de CUDA pour l'ajout et la multiplication de tenseurs complexes (opérations binaires). Merci!

Bonjour @sunilkpai ,

J'ai un PR ouvert qui devrait prendre en charge les opérations binaires et unaires sur CUDA : #30295.

Un autre problème concerne la propagation vers l'arrière. Je pense que la dérivée du complexe abs() est définie différemment des nombres réels. Je ne sais pas quoi faire à ce sujet, mais les dérivés sont définis dans tools/autograd/derivatives.yaml

Je pense que pour les nombres complexes /dz abs(z) = z/abs(z) . Cela peut également être utilisé pour les nombres réels, mais sera probablement plus lent que sgn(z)

@dylanbespalko Peut-être que les tableaux 4.1, 4.2 et 4.3 de mon rapport https://arxiv.org/pdf/1701.00392.pdf peuvent vous aider à définir les dérivés.

Pour les dérivées complexes (calcul de Wirtinger), il existe deux options.
Calcul de la dérivée par rapport à z ou z conjugué.
Personnellement, j'aime davantage la dérivée wrt z conjuguée.
Cela semble plus naturel pour les opérations matricielles et la mise à jour du gradient n'a pas besoin de conjugué.
La définition de ceux-ci est :

  • dérivée par rapport z pour z = x + jy : dJ/dz = dJ/dx -j dJ/dy
  • dérivée par rapport z.conj pour z = x + jy : dJ/dz.conj = dJ/dx + j dJ/dy

D'après votre commentaire, mon hypothèse est que vous calculez la dérivée par rapport z pour le moment.
Dans ce cas, la dérivée est d abs(z) / d z = z.conj / abs(z) . Lorsque vous prenez l'autre définition, vous pouvez suivre la suggestion de @Randl .

Faites-moi savoir si je dois expliquer plus. J'ai aussi quelques implémentations numpy pour les dérivées complexes.

Une autre opération qui serait utile (en particulier pour les projets dans l'espace physique nécessitant la prise en charge de nombres complexes) est un gestionnaire pour l'opérateur exp() . Dans tensorflow, nous avons tf.exp(x + iy) = tf.exp(x) * (tf.cos(y) + 1j * tf.sin(y)) . Est-ce aussi simple à implémenter dans pytorch?

@sunilkpai , @boeddeker , @Randl ,

Merci pour le rapport sur les dérivés complexes. Je vais essayer de suivre ça et j'y reviendrai la semaine prochaine. J'ai pensé ajouter quelques liens ici et décrire l'état du projet.

Le statut des nombres complexes est pris en charge de manière non officielle et doit être ajouté via l'extension PyTorch :

Chaque extension contient deux éléments :

  • Un .cpp qui contient tous les enregistrements nécessaires du noyau mathématique.
  • Un dossier test/ qui contient des versions très simplifiées des scripts de test pytorch.
    Regardez dans les scripts de test pour voir quels noyaux sont pris en charge (et pourquoi d'autres ne le sont pas).

Pourquoi ne puis-je pas imprimer un tenseur complexe sur la console ?

  • L'objet python Tensor a une mise en forme jolie qui appelle certaines fonctions non prises en charge.

    • Vous pouvez modifier le contenu de tensor.py pour contourner le formatage d'impression.

    • Ou, vous pouvez simplement convertir les tenseurs Pytorch en tableaux Numpy, puis imprimer.

Statut actuel du projet :

  • La couverture CPU est assez bonne.

    • Les noyaux sont implémentés dans PyTorch sous 'aten/src/ATen/native/cpu/ </li> <li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h

    • L'accélération Intel AVX256 est sous 'aten/src/ATen/cpu/vec256/`



      • @sunilkpai : Je ne connaissais pas l'optimisation d'exp. C'est le dossier où vous ajoutez cela.


      • Faites-moi savoir si vous êtes à l'aise pour faire le changement.



  • La couverture GPU est limitée aux opérations binaires et unaires :

    • Les noyaux sont implémentés dans PyTorch sous 'aten/src/ATen/native/cuda/* </li> <li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh

    • Les types de données thrust::complex<T> sont utilisés et ils incluent les noyaux optimisés.

Développement actuel :

  • En attente du portage des noyaux TH basés sur C dans le dossier C++ ATen.

    • La fonction rand() est nécessaire pour porter les cas de test vers les tests internes de pytorch.

    • Certaines opérations d'indexation ne sont actuellement pas portées.

    • Il y a actuellement 168/1300 noyaux mathématiques (contre 230 en octobre) qui doivent être portés de TH vers ATen.

  • J'essaierai d'ajouter le support des nombres complexes au fur et à mesure que ces noyaux seront disponibles dans ATen.

--

POUR VOTRE INFORMATION. En ce qui concerne les dérivés complexes, nous avons eu une longue discussion dans Julia et son implémentation est maintenant dans ChainRules (voir aussi : http://www.juliadiff.org/ChainRules.jl/dev/api.html#ChainRulesCore.Wirtinger ) et Zygote maintenant . Généralement, les gens n'ont besoin que
\partial L/\partial adjoint(z) comme gradient (par définition c'est la direction de décroissance la plus rapide), mais la dérivée est différente comme \partial L/\partial z , une interface supplémentaire doit être ajoutée, si nous voulons un support complet pour le nombre complexe AD . Pour les règles détaillées, vous pouvez vérifier ce qui est implémenté dans ChainRules ou Zygote/lib (puisqu'il n'y a que des règles génériques, il n'y a pas de règles distinctes pour les nombres complexes pour la plupart des opérateurs, passe en arrière pour les choses comme matmul sont écrits dans une définition générique, par exemple adjoint(A) * B )

Pourquoi ne puis-je pas imprimer un tenseur complexe sur la console ?
L'objet python Tensor a une mise en forme jolie qui appelle certaines fonctions non prises en charge.
Vous pouvez modifier le contenu de tensor.py pour contourner le formatage d'impression.
Ou, vous pouvez simplement convertir les tenseurs Pytorch en tableaux Numpy, puis imprimer.

Je pense avoir corrigé au moins une partie de l'impression dans https://github.com/Roger-luo/pytorch-complex pour le débogage, etc. en premier lieu, je ne sais pas si cela aiderait car le maître a beaucoup changé dans le passé an. Vous pouvez juste le prendre s'il est utile, je ne vais plus travailler dessus.

@dylanbespalko Je suis relativement inexpérimenté avec les composants internes de pytorch, même si j'ai commencé à apprendre ! Je pourrais peut-être tenter ce changement, mais d'après ce que je vois dans aten/src/ATen/cpu/vec256/* , je ne sais pas si c'est nécessaire étant donné que le comportement par défaut de std::exp(std::complex) est exactement ce que j'ai mentionné dans mon commentaire précédent : voir les notes de https://en.cppreference.com/w/cpp/numeric/complex/exp . Je ne sais pas non plus comment cela se traduit par la mise en œuvre de ces opérations dans CUDA (qui semble actuellement limité à réel, imag, conj et angle ?).

@sunilkpai ,

J'ai ajouté le support AVX pour exp() en utilisant l'équation fournie.

J'ai également remarqué que certaines choses étaient cassées en raison de changements récents dans PyTorch. Je les ai corrigés dans #30871.

@dylanbespalko

Existe-t-il un délai pour le portage de TH vers ATen ?
Existe-t-il un moyen de contribuer, étant donné que je ne connais pas bien le fonctionnement interne de pytorch?

J'ai trouvé une formule pour la rétropropagation du complexe svd sur l'arxiv et je pourrais l'implémenter, si vous me montrez où

Merci pour votre travail !

@Jakob-Unfried

https://github.com/pytorch/pytorch/wiki/TH-to-ATen-porting-guide

Les noyaux TH sont implémentés en C et il y a peu d'intérêt à y ajouter un support complexe en raison de tous les problèmes de comptage de références inhérents. Vous pouvez suivre la progression dans aten/src/ATen/native/native_functions.yaml où chaque noyau est enregistré :

Recherchez legacy::cpu::_th et divisez ce nombre par 3 pour obtenir le nombre d'anciens noyaux TH.
Recherchez legacy::cpu::_thnn et divisez ce nombre par 3 pour obtenir le nombre d'anciens noyaux de réseau de neurones TH.

Chaque noyau est généralement enregistré de 3 manières différentes :
1. Noyau régulier y = add(a, b)
2. Noyau en place a = add_(a, b)
3. Noyau de sortie add_out(a, b, out=y)
L'implémentation réelle est toujours dans le noyau de sortie et les 2 autres appellent cette fonction.

Les noyaux nn ont tendance à être plus faciles à porter car ils ont moins de noyaux dépendants. Par conséquent, si vous pouvez porter les noyaux dans l'ordre inverse de leur implémentation, vous effectuerez moins de travail global.

Vérification du problème de suivi du portage https://github.com/pytorch/pytorch/issues/24507 , également cc @VitalyFedyunin

Voici une mise à jour de l'état de la prise en charge des nombres complexes comme demandé dans #32437. Je travaille de nouveau sur le support lié au CPU aujourd'hui.

Assistance Autograd

  • Je n'ai pas eu beaucoup de temps pour ça.
  • angle() , real() , imag() , conj() ont tous été implémentés.
  • abs() nécessitera une implémentation distincte pour les nombres complexes. (voir les notes de @boeddeker et @Randl ci-dessus)

Assistance matérielle

La prise en charge des nombres complexes est actuellement implémentée hors de l'arborescence. Voici ce que cela signifie :

Code dans l'arborescence

  • Les opérations mathématiques sont en fait implémentées dans l'arborescence (à l'intérieur du code source PyTorch).
  • Aucun des tests Pytorch dans l'arborescence ne valide la prise en charge des nombres complexes (donc les choses ont tendance à casser).
  • PyTorch migre de TH vers ATen (#24507).
    - Les noyaux mathématiques implémentés dans TH ne prennent pas en charge les nombres complexes.
    - Seuls les noyaux implémentés dans ATen peuvent supporter les nombres complexes.
  • Vous devez installer une extension PyTorch pour activer le dtype complexe.

Code hors arbre

  • Plusieurs extensions PyTorch sont implémentées et peuvent être facilement internalisées à l'avenir :
  • Chaque extension a quatre fichiers d'importance :

    • setup.py : compilez et installez l'extension pytorch.

    • [CPU/CUDA/FPGA]ComplexType.cpp : enregistrement du noyau mathématique similaire à CPUType.cpp ou CUDAType.cpp

    • test/test_torch.py : cas de test très laids qui indiquent quels noyaux fonctionnent, limités par le support ATen.

    • test/test_autograd.py : test de la fonctionnalité autograd.

Extensions PyTorch hors arbre

  1. cpu_strider_complex
    - [x] Copier le noyau
    - [ ] TensorFactories (th_random, th_uniform, th_normal)
    - [x] Usines de Gamme
    - [x] BinaryOpKernals
    - [x] UnaryOpKernels
    - [x] CompareOpKernels
    - [x] PowKernels
    - [x] ReduceOpKernels (th_min, th_max, certaines normes ne calculent pas le conjugué complexe)
    - [ ] IndexOpKernels (th_masked_select_bool, th_set, th_gather, th_cat)
    - [x] opérations ponctuelles
    - [x] Opérations Lerp
    - [ ] BlasOps (th_mv, th_mm, th_fmod, th_eig)
    - [ ] LinpackOps (utilise Blas)
    - [ ] SpectralOps (supporté, mais nécessite des travaux)

    1. cuda_strider_complex



      • [x] Copier le noyau


      • [ ] TensorFactories (voir problèmes de processeur)


      • [x] BinaryOpKernals


      • [x] UnaryOpKernels (Todo : ajouter angle, réel, imag, conj)


      • [ ] CompareOpKernels (À faire)


      • [ ] ReduceOpKernels (Messages d'erreur concernant WARP_SIZE)


      • Le GPU devient difficile au-delà des calculs ponctuels, mais des fonctions supplémentaires peuvent être prises en charge



  2. vitis_strider_complex

    • Xilinx Vitis est une plate-forme de synthèse de haut niveau FPGA qui prend en charge les serveurs et les périphériques embarqués.

    • Sorti en octobre 2019 (prise en charge probablement limitée des appareils).

    • Combine SDAccel (serveur) avec VIvado HLS (intégré).

    • [ ] BinaryOpKernals (fin janvier)

    • [ ] UnaryOpKernels (fin janvier)

    • [ ] ReduceOpKernels (fin février).

    • Les objets vectorisés récemment ajoutés par FPGA prennent en charge de manière similaire la classe de modèles Vec256 PyTorch

    • Vec256 était la façon dont la prise en charge complexe a été ajoutée au CPU et cela semble être la manière la plus naturelle d'implémenter des espaces tensoriels $C$ ou $R^N$

Plus de mises à jour à venir sur ce problème : https://github.com/pytorch/pytorch/issues/33152

Cela peut ou non mériter un problème séparé, mais j'apprécierais de voir et je pense qu'il est pratiquement plus important actuellement d'avoir dans la documentation quelque chose qui explique "comment pytorch fonctionne avec les nombres complexes en ce moment". aka peut faire l'addition, la multiplication, une sorte de norme, ne peut pas avoir de poids complexes, etc. tout cela peut être résumé par quelques lignes de documentation expliquant quel est le comportement actuel prévu de haut niveau.

Cela peut ou non mériter un problème séparé, mais j'apprécierais de voir et je pense qu'il est pratiquement plus important actuellement d'avoir dans la documentation quelque chose qui explique "comment pytorch fonctionne avec les nombres complexes en ce moment". aka peut faire l'addition, la multiplication, une sorte de norme, ne peut pas avoir de poids complexes, etc. tout cela peut être résumé par quelques lignes de documentation expliquant quel est le comportement actuel prévu de haut niveau.

Salut @redwrasse merci pour les commentaires ! nous avons actuellement une note pour les nombres complexes qui parle de certains principes fondamentaux de la torche et des fonctions complexes prises en charge pour les tenseurs complexes sur master
(dont la plupart sont inclus dans la version 1.6) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex. Pouvez-vous partager quelles autres fonctions vous intéressent ? heureux de parler davantage de notre support actuel et du plan pour les prochaines versions.

Cela peut ou non mériter un problème séparé, mais j'apprécierais de voir et je pense qu'il est pratiquement plus important actuellement d'avoir dans la documentation quelque chose qui explique "comment pytorch fonctionne avec les nombres complexes en ce moment". aka peut faire l'addition, la multiplication, une sorte de norme, ne peut pas avoir de poids complexes, etc. tout cela peut être résumé par quelques lignes de documentation expliquant quel est le comportement actuel prévu de haut niveau.

Salut @redwrasse merci pour les commentaires ! nous avons actuellement une note pour les nombres complexes qui parle de certains principes fondamentaux de la torche et des fonctions complexes prises en charge pour les tenseurs complexes sur master
(dont la plupart sont inclus dans la version 1.6) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex. Pouvez-vous partager quelles autres fonctions vous intéressent ? heureux de parler davantage de notre support actuel et du plan pour les prochaines versions.

Merci @ anjali411 , c'est super de voir cette documentation, je n'en étais pas au courant auparavant. Je pense que ce qu'il faut, c'est avant et au centre quelques lignes "l'état actuel de la prise en charge des réseaux de neurones complexes", mais laissez-moi passer en revue ...

Les personnes intéressées par l'autograd complexe peuvent être intéressées par https://github.com/pytorch/pytorch/issues/41857 qui aborde la convention que PyTorch suivra (JAX ou TF).

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

SeparateReality picture SeparateReality  ·  3Commentaires

rajarshd picture rajarshd  ·  3Commentaires

eliabruni picture eliabruni  ·  3Commentaires

bartvm picture bartvm  ·  3Commentaires

a1363901216 picture a1363901216  ·  3Commentaires