Pytorch: Integrando tensores complejos

Creado en 16 feb. 2017  ·  128Comentarios  ·  Fuente: pytorch/pytorch

Nueva descripción de @ezyang :

El trabajo está en progreso en https://github.com/Roger-luo/pytorch-complex

Principios organizacionales

  • El soporte de tensor complejo es importante para PyTorch, y aceptaremos parches para el núcleo que agregan pequeñas cantidades de código para agregar soporte complejo.
  • Agregar complejo implica escribir una gran cantidad de nuevos núcleos y código: nos gustaría que este código viva inicialmente fuera del repositorio, por lo que es más fácil para las personas iterarlo rápidamente sin tener que pasar por el proceso de revisión del código principal de PyTorch. NO nos comprometeremos a revisar núcleos nuevos grandes a corto plazo, pero eventualmente nos gustaría que todos los núcleos regresen a PyTorch.
  • La biblioteca externa se podrá construir por separado de PyTorch, por lo que podrá mantenerla como un repositorio separado sin tener que fusionarse con PyTorch (y lidiar con muchos conflictos de fusión).

    • PyTorch puede ocasionalmente realizar cambios importantes en la API de C++; Si nos informa sobre esto, haremos todo lo posible para ayudar a resolver estos problemas.

  • Los ganchos necesarios para esto NO se enviarán con PyTorch 1.0, pero se enviarán con una versión lanzada de PyTorch en un futuro no muy lejano.

¿Cómo trabajaré en núcleos complejos?

Así es como se verá el flujo de trabajo en el estado estable.

PyTorch contendrá API de forma nativa para hacer referencia al tipo de d complejo, pero no harán nada de forma predeterminada. PyTorch define torch.complex64 y torch.complex128 en referencia a tensores complejos. Sin embargo, si intenta construir un tensor de esta manera, de forma predeterminada, PyTorch generará un error:

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

@ezyang proporcionó un parche que agrega estos dtypes a PyTorch. https://github.com/pytorch/pytorch/pull/11173

A mediano plazo, fusionaremos la compatibilidad con la funcionalidad básica (como la asignación de un tensor de ceros) para que PyTorch la admita de forma nativa. Un indicador razonable de lo que el soporte es "básico" es el soporte nativo de PyTorch para los medios tensores de CPU (que están extremadamente empobrecidos).

PyTorch publica una interfaz para registrar una implementación de tensores complejos. La implementación hereda de la clase TypeDefault (https://github.com/pytorch/pytorch/pull/11013) y anulará los métodos de esta clase para definir implementaciones de funciones para las que tenemos implementaciones complejas. Se verá algo como esto:

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
  }
  // ...
}

Esta clase anulará exactamente los tipos que se admiten para complejo; todas las demás implementaciones son proporcionadas por TypeDefault y generarán un error de forma predeterminada.

Habrá una lista canónica de métodos admitidos en Type (la interfaz general) como un archivo generado automáticamente que se registra en el repositorio fuente de PyTorch; comunicaremos los cambios de la API mediante diferencias a este archivo. En general, los métodos están en correspondencia uno a uno con sus nombres correspondientes en la interfaz de PyTorch.

En general, cuando utiliza una operación que aún no ha implementado,

ADVERTENCIA: Tenemos la intención de refactorizar Type en un nuevo sistema que también admita el registro abierto de nuevas operaciones (esto obviamente no funciona si tiene una sola superclase que define todos los métodos que posiblemente desee admitir). Por lo tanto, trate de no apegarse demasiado a la estrategia de implementación particular de escribir Type como una subclase.

Para publicar solo operaciones nuevas y complejas, utilizará la API de extensión de C++. La API de extensión de C++ está documentada en https://pytorch.org/tutorials/advanced/cpp_extension.html Esencialmente, puede escribir una función de C++ como:

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

Y luego, la API de extensión de C++ generará un enlace de Python para que invoque esta función desde Python.

Algunas operaciones serán "fáciles" de integrar en PyTorch tal como existe hoy. Por ejemplo, para la implementación de operaciones binarias, probablemente tenga más sentido extender add_kernel en BinaryOpsKernel.cpp para que distribuya tipos complejos (y luego lo obtenga gratis, porque std::complex implementa la adición). Siempre que estos parches sean pequeños y autónomos, prometemos fusionarlos en el momento oportuno.

SIEMPRE debería ser posible desbloquear, simplemente escribiendo una anulación en Tipo en lugar de usar la infraestructura existente, y copiando y pegando liberalmente. ¡Pero evitémoslo cuando es fácil!

Autograd. Siempre que esté trabajando en operaciones que ya tienen fórmulas derivadas definidas para ellas, obtendrá "automáticamente" soporte de graduación automática, siempre que implemente soporte complejo para todas las funciones constituyentes que se invocan en la implementación inversa de derivadas.yaml .

En algunos casos, es posible que necesitemos ajustar las fórmulas de autograduación para que funcionen con números complejos; por ejemplo, el gradiente de 'abs' no es 'grad'. self.sign()'. En estos casos, todo lo que tenemos que hacer es cambiar la fórmula de autograduación de 'abs' a 'abs_backward', que es una función que se puede anular.

Para la retropropagación valorada compleja general, hay algunas referencias:

  1. Las “Redes Neuronales Complejas Valiosas” de Akira.
  2. https://giggleliu.github.io/2018/02/01/complex_bp.html

Generalmente, no necesitaremos modificar el autogrado ya que en la mayoría de los casos solo calculamos las derivadas de una función de valor real (la pérdida).

El plan de trabajo

Muchas de las piezas necesarias ya están en su lugar, pero no se ensamblan de manera integral. Esto es lo que hay que hacer.

  • [X] Codemod TH para no ser real https://github.com/pytorch/pytorch/pull/11163
  • [X] Compatibilidad integrada con los tipos de d torch.complex64 y torch.complex128. https://github.com/pytorch/pytorch/pull/11173
  • [X] Una interfaz para registrar CPUComplexType, etc., para que esta implementación se invoque cuando solicita un tensor complejo con dtype=torch.complex64 o realiza una operación en tensores complejos.
  • [X] Tierra https://github.com/pytorch/pytorch/pull/11013
  • [X] Un ejemplo de extremo a extremo, que incluye un sistema de compilación en funcionamiento, de un programa C++ compilable por separado que se vincula con libtorch y usa la interfaz antes mencionada para implementar una asignación de tensor compleja.

Plan de integración a corto plazo. Estas operaciones son "fáciles" de implementar, por lo que debemos incluirlas en PyTorch lo antes posible.

  • [X] Fábricas de tensores básicos: antorcha.vacío, antorcha.ceros, antorcha.unos
  • [ ] Operaciones binarias de CPU: agregar, sub, mul, div #11641
  • [ ] FFT
  • [ ] ???

Implementación del núcleo:

TODO: generar una lista basada en https://github.com/Roger-luo/TH/blob/master/ChangeLog.md

Otras tareas complejas relacionadas:

  • [ ] Averigüe las reglas de promoción de tipos para tensores complejos e impleméntelas en advanceTypes #11641

Contenido del problema histórico

Comentario original de @PhilippPelz

Me preguntaba si hay interés en incorporar tensores complejos en pytorch.
Para soporte de CPU hay ztorch y escribí z-cutorch (https://github.com/PhilippPelz/z-cutorch) hace un tiempo. Es una bifurcación de cutorch antes de la refactorización de CudaHalfTensor (todavía no tengo el hardware).
Si no es demasiado trabajo, me gustaría integrarlo lentamente con pytorch. Estoy usando matplotlib para trazar a través de fb.ptyhon y resulta un gran dolor cada vez que reinstalo mi sistema (compilando todas las dependencias), además parece que pytorch funcionará pronto en Windows, en el que se ejecuta una de mis PC experimentales.
También necesitaría gradientes complejos, por lo que tarde o temprano también tocaría autograd.
Si bien tf admite tensores complejos per se, parece que muchas operaciones aún no lo admiten (https://github.com/tensorflow/tensorflow/issues/2255), además parece un poco pesado para mis propósitos.

Tal vez alguien podría decir algunas palabras sobre cómo y por dónde empezar con esto, si es una buena idea.

feature complex triaged

Comentario más útil

@sunilkpai , @boeddeker , @Randl ,

Gracias por el informe sobre los derivados complejos. Intentaré seguir eso y volveré sobre esto la próxima semana. Pensé en agregar algunos enlaces aquí y describir el estado del proyecto.

El estado de los números complejos no es oficial y debe agregarse a través de la extensión PyTorch:

Cada extensión contiene dos cosas:

  • Un .cpp que contiene los registros del kernel matemático necesarios.
  • Una carpeta test/ que contiene versiones muy simplificadas de los scripts de prueba de pytorch.
    Busque en los scripts de prueba para ver qué kernels son compatibles (y por qué otros no lo son).

¿Por qué no puedo imprimir un tensor complejo en la consola?

  • El objeto Tensor python tiene un formato de impresión bonito que llama a algunas funciones que no son compatibles.

    • Puede modificar el contenido de tensor.py para omitir el formato de impresión.

    • O simplemente puede convertir los tensores Pytorch en matrices Numpy y luego imprimir.

Estado actual del proyecto:

  • La cobertura de la CPU es bastante buena.

    • Los núcleos se implementan dentro de PyTorch en 'aten/src/ATen/native/cpu/ </li> <li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h

    • La aceleración de Intel AVX256 está en 'aten/src/ATen/cpu/vec256/`



      • @sunilkpai : No conocía la optimización de exp. Esta es la carpeta donde agregas eso.


      • Avísame si te sientes cómodo haciendo el cambio.



  • La cobertura de GPU se limita a operaciones binarias y unarias:

    • Los núcleos se implementan dentro de PyTorch en 'aten/src/ATen/native/cuda/* </li> <li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh

    • Se utilizan los tipos de datos thrust::complex<T> e incluyen los núcleos optimizados.

El desarrollo actual:

  • Esperando a que los núcleos TH basados ​​en C se transfieran a la carpeta ATen de C++.

    • La función rand() es necesaria para trasladar los casos de prueba a las pruebas internas de pytorch.

    • Algunas operaciones de indexación actualmente no están portadas.

    • Actualmente hay 168/1300 núcleos matemáticos (frente a los 230 de octubre) que deben transferirse de TH a ATen.

  • Intentaré agregar compatibilidad con números complejos a medida que estos núcleos estén disponibles en ATen.

--

Todos 128 comentarios

Creo que estaríamos interesados ​​en agregar un soporte opcional para tensores complejos. La mejor manera sería bifurcar y trabajar en las bibliotecas C en torch/lib . Esto debería estar libre de conflictos con el maestro, por lo que puede hacerlo durante mucho tiempo. Una vez que tenga las bibliotecas en un estado utilizable, puede comenzar a escribir los enlaces, y aquí es donde podemos brindarle orientación sobre cómo evitar conflictos en ese momento.

Tengo TH con compilación de tipos complejos. ¿Qué necesito agregar para la integración de python?

@PhilippPelz , ¿te refieres a: https://github.com/facebook/ztorch/tree/master/lib/THZ ? ¿O construiste tu propia bifurcación de TH que permite tipos complejos?

@killeent tiene algunas notas sobre cómo TH está vinculado a Python, puede compartirlas.

En general, para obtener tensores complejos, prefiero THZ, ya que tiene pruebas, etc.

Sin embargo, construir un backend de CUDA para tensores complejos es un esfuerzo bastante grande, ni siquiera hemos comenzado con eso.

Escribí z-cutorch (https://github.com/PhilippPelz/z-cutorch) hace un tiempo. Es una bifurcación de cutorch antes de la refactorización de CudaHalfTensor (todavía no tengo el hardware).

Esto es genial. Supongo que ya has hecho un gran esfuerzo en esa dirección :)

@soumith Hice una bifurcación de TH con tipos complejos. Básicamente, un THGenerateComplexTypes.h + agregó BLAS + LAPACK rutinas, el resto fue casi gratis. Parecía mucho menos trabajo para mí que verificar qué partes de THZ son compatibles y luego copiar y pegar.

Estoy atascado compilando THPP en este momento, descifrando mensajes del compilador como

/home/philipp/projects/pytorch/torch/lib/tmp_install/include/TH/generic/THBlas.h:6:40: error: se esperaba ',' o '...' antes del token '*'
TH_API void THBlas_(swap)(long n, real *, long incx, real *, long incy);

es un poco complicado.

Agradecería ayuda sobre cómo habilitar la integración de python. El backend de CUDA debe ser principalmente copiar y pegar de z-cutorch.

@PhilippPelz aquí hay algunas notas sobre PyTorch wraps TH: https://gist.github.com/killeent/4675635b40b61a45cac2f95a285ce3c0

@killeent gracias, parece muy útil. lib/build_all.sh ahora se está compilando, creo que puedo mirar el directorio csrc.

Esto ahora se ejecuta:

importar antorcha como th
importar numpy como np

a = np.matriz([1+1j,2+2j])
b = np.matriz([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()
imprimir (ath. numpy ())

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

junto con la mayoría de las funciones matemáticas.
Agregaré funciones de conveniencia y ffts en las próximas semanas. Supongo que debe haber pruebas para todo antes de poder fusionar esto. Si conoce a alguien más que esté interesado en tensores complejos y esté dispuesto a contribuir a escribir las pruebas, sería increíble. Este artículo me viene a la mente: Deep Complex Networks , tal vez a esos tipos les interese.
No tendré tiempo para escribir todas las pruebas por mi cuenta.

@PhilippPelz Gracias por sus comentarios. Estoy revisando tu implementación. Y en primer lugar, no estoy seguro acerca de su implementación ger . Algunas funciones blas complejas no están incluidas en su THBlas.c como usted definió GER como zger_ y cger_ en los encabezados generados, pero no hay una función blas con cger_ en el genérico/THBlas.c . Sin embargo, puedo usar tu gemv y algunas otras funciones. Y en mi opinión, ¿quizás deberías agregar .gch a .gitignore? ¿Has empujado todas tus extensiones a tu horquilla? Puedo hacer una solicitud de extracción a su maestro en función de su implementación primero.

Y para DOT supongo que tal vez para vectores complejos, ¿las rutinas dotc para punto son más comunes?

Y sí, si solo usar real será más fácil de implementar, me sentía extraño cuando real es en realidad un complejo...

Y para las pruebas, no vi ninguna prueba anterior para TH. ¿Dónde debo escribir esas pruebas? o simplemente escribimos algunas pruebas de python

Sí, lo siento, veo que es posible que no haya empujado todo lo que se necesita. El lunes lo vuelvo a consultar. Faltan algunas declaraciones, p. zger y cger

Para DOT estoy usando cdotc y zdotc, parece que faltan, actualizaré la próxima semana.

Consulte con los mantenedores de pytorch qué nombres prefieren de verdad. Me gusta más tu versión, solo que todavía no me esforcé.

Sí, pruebas de Python para las matemáticas. Debe cambiarse fácilmente para que la mayoría de las funciones también incluyan verificaciones de números compelx.

¡Genial que también estés investigando esto!

Ok, hice algunos cambios. Las rutinas TH blas ahora están ahí para complejos

@PhilippPelz Acabo de hacer una solicitud de extracción en su repositorio. Y para capas lineales complejas y algunos otros operadores. podría haber muchas operaciones hermitianas (como bp para una capa lineal compleja). ¿Quizás agregar alguna función para un tensor? ¿Has comprobado la parte de THNN?

Sí hermitian es útil. cuda fft está funcionando ahora. cpu fft podría envolverse desde numpy. Todavía no he tocado THNN o THCUNN.

@PhilippPelz He agregado un hermitiano simple en PR. Y podrías revisarlo. Entonces podríamos ver si esos cambios son adecuados y pasar al siguiente paso. ¡Gracias! PD. parece que te perdiste algunos encabezados, también corrijo eso y algunas otras advertencias. Para una función compleja con salida real, ¿debemos devolver un tensor real en lugar de un tensor complejo? Implementé métodos de copia entre tipos complejos y reales, por lo que es posible.

Volveré a basar todos los compromisos después de su revisión.

@PhilippPelz Hola, estoy bastante confundido acerca de la parte THPP que implementaste. ¿Por qué depende del empuje en Traits.hpp ? Esto causará un error al compilar sin cuda. ¿Es posible usar solo comooen Traits.hpp ? No lo he descifrado. ¿Tal vez podrías ofrecer algunas pistas?

@ Roger-luo Sí, también tengo algunos problemas con eso en otros lugares. Los tipos complejos que estamos usando deben ser de complex.h o std::complex. Dado que THPP es el contenedor de C++, tal vez std::complex sea más apropiado. ¿Puedes cambiar eso?

Thrust también está causando problemas exactamente por la misma razón cuando intenta crear extensiones cffi. En este momento estoy haciendo una solución alternativa, pero la forma adecuada sería cambiar el tipo complejo a cuFloatComplex/cuDoubleComplex en THC. para que el compilador cffi no se queje. Solo quiero continuar con la investigación ahora mismo, esto me está quitando demasiado tiempo :( . Si tiene tiempo, hágalo.

Además, crear una extensión cffi con llamadas de kernel personalizadas es bastante engorroso, porque uno siempre necesita crear una biblioteca adicional compilada con nvcc, que luego se vincula a un contenedor cffi. Supongo que no hay otra manera. Se podría usar cffi en modo ABI, pero el sitio web dice "El modo API en su lugar compila un contenedor CPython C que invoca directamente la función de destino. Es, comparativamente, mucho más rápido (y funciona mejor que libffi)".

@PhilippPelz tal vez reinterpret_cast podría ser una solución. Supongo que debería cambiarse a cuComplex y usar reinterpret_cast en THPP. Voy a probar primero...

Sí, supongo que no hay otra forma que reinterpret_cast si quieres que THPP construya también sin cuda instalado.

@PhilippPelz Me gustaría ayudar. ¿Hay alguna lista de tareas pendientes en alguna parte?

THNN y THCUNN deben estar habilitados para tipos complejos. ¿Puedes coordinar con @roger-luo? Además, si nuestro objetivo es la integración con el maestro, las pruebas unitarias deben escribirse para todos los métodos complejos.

@elbamos La mayor parte del trabajo en THNN consistirá en implementar nuevos métodos complejos de retropropagación para cada capa existente. Hay un WIP PR en la bifurcación de Philipp. He enumerado algunas referencias.

@apaszke @soumith @PhilippPelz Y hay dos preguntas:

  • ¿Alguien sabe por qué hay otro archivo GenerateXXXTypes.h en THS ? Se ve igual que los de TH .

  • ¿Para qué sirve el siguiente código en 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);
  }
}

¿Alguna sugerencia sobre la implementación de su versión compleja relacionada? No estoy seguro si la siguiente implementación es correcta...

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>);
  }
}

El decodeUInt128XE anterior se declara como

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);
}

Actualmente uso std::complex<T> en lugar de T _Complex en THPP . No estoy seguro de que esto pueda ser usado por Python todavía. O solo el tipo c T _Complex se puede usar para python. Así que aquí el tipo de dst es std::complex<T> .

Y si estoy en lo correcto para esta implementación, probablemente necesitemos una implementación uint128_t , como https://github.com/calccrypto/uint128_t ? Dado que parece que no todos los compiladores admiten enteros de 128 bits (gcc tiene int128_t y uint128_t).

@PhilippPelz noté que su bifurcación no tiene problemas habilitados. ¿Cuál es el estado de su proyecto? Estoy un poco molesto porque los tensores complejos no están en la hoja de ruta para pytorch

@el3ment He agregado un backend complejo para CPU https://github.com/pytorch/pytorch/pull/4899 Pero aún no se ha revisado... Y no he recibido ningún comentario para mi PR, así que recurrí a usar el lenguaje de programación Julia recientemente...

Le envié un correo electrónico a @PhilippPelz la última vez, supongo que su repositorio todavía está en v0.1 y está ocupado con la tesis hasta septiembre. Y estaba trabajando en el nuevo backend CUDA de v0.3, pero no tengo tiempo para terminar todos estos enlaces solo. Las funciones map/reduce son diferentes de v0.1 con algunas optimizaciones, pero no se pueden convertir de manera trivial para admitir números complejos. Sería feliz si hay alguien que esté dispuesto a ayudar...

Estoy dispuesto a ayudar.

El 10 de abril de 2018, a las 22:52, Rogerluo [email protected] escribió:

@el3ment He agregado un backend complejo para CPU #4899

Le envié un correo electrónico a @PhilippPelz la última vez, supongo que su repositorio todavía está en v0.1 y está ocupado con la tesis hasta septiembre. Y estaba trabajando en el nuevo backend CUDA de v0.3, pero no tengo tiempo para terminar todos estos enlaces solo. Las funciones map/reduce son diferentes de v0.1 con algunas optimizaciones, pero no se pueden convertir de manera trivial para admitir números complejos. Sería feliz si hay alguien que esté dispuesto a ayudar...


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencie el hilo.

@elbamos genial, parece que el equipo de pytorch prefiere una implementación separada. Actualizaré mi bifurcación más tarde para las otras partes más adelante. Pero realmente no tengo tiempo para esto y supongo que deberíamos comenzar a trabajar en ello cuando haya un plan del equipo de pytorch porque sería una gran extensión para pytorch.

Hola, mi código está en algún compromiso después de v0.2

Había visto que había un refactor bastante grande moviendo todo el código de tensor a Aten. Esto significa que uno no puede fusionar fácilmente mi bifurcación en la versión actual y podría haber más trabajo involucrado.

Todavía estoy escribiendo mi doctorado, pero planeaba esperar a 0.4 de todos modos hasta que se publique la fusión de Variable y Tensor. Me temo que podría haber demasiada refactorización para ponerse al día si uno lo hace antes.

@elbamos , si quieres, puedes comenzar a agregar cosas a mi bifurcación, las fusionaré. En tu lugar, simplemente implementaría lo que necesitas para cualquier proyecto que estés haciendo. TH(CU)NN es una interfaz bastante grande y sería una gran carga de trabajo.

@el3ment No tengo tiempo para trabajar en los problemas de otros. Sin embargo, fusionaré cosas si necesita implementar algo que no está allí.

Si solo desea algo que funcione con números complejos de forma inmediata, le recomiendo tensorflow.

También ayudaré si hay problemas de compilación.

Si continúo con el postdoctorado, en algún momento transferiré todo esto a la versión actual. Es realmente triste que facebook no quiera apoyar esto. :((

@PhilippPelz De acuerdo, es realmente triste y, en realidad, tensorflow no es compatible con todos los operadores de la física cuántica ... Comencé a usar Julia y abandoné Python.

@ Roger-luo interesante, ¿estás usando un paquete julia específico o es todo un código escrito por ti mismo?

@PhilippPelz Estoy desarrollando un kit de herramientas cuántico de muchos cuerpos en Julia (desde PyTorch PR), incluye una implementación de red neuronal compleja/real basada en algunos artículos anteriores sobre redes neuronales complejas, y descubrí que es muy fácil de desarrollar con Julia's metaprogramación. Actualmente solo lo puse en QMTK.jl , todavía está trabajando en progreso y no he terminado todo lo que quiero. PyTorch me inspira mucho, pero lo siento mucho por el soporte complejo...

Pero tengo planes para separarlo en un paquete único de red neuronal en el futuro (simplemente no quiero mantener varios repositorios en este momento). Y habrá más personas que se unan al desarrollo desde el Instituto de Física, CAS. Aceptaré PR después de su primera versión etiquetada (que será en unas pocas semanas).

Puedes verlo si estás interesado en su desarrollo.

Sin embargo, si el equipo de PyTorch todavía tiene planes para un soporte complejo en el futuro, estaré dispuesto a ayudar.

Genial, estaré al pendiente!

Hola chicos, siento mucho que no hayamos respondido sobre este problema desde que se abrió.

Aquí hay dos hechos:

  1. Estamos totalmente de acuerdo en que PyTorch necesita un soporte complejo y
  2. No tenemos la mano de obra para llenar adecuadamente la cola larga que necesitarían todas las operaciones complejas. (Para evidencia de esto, mire el apoyo escaso, que está en el maestro y ha estado cojeando).

Desde que se abrió este problema en 2017, han cambiado algunas cosas importantes que pueden simplificar un poco la implementación de soporte complejo. La primera es que ahora tenemos ATen, una biblioteca C++ ergonómica para manipular tensores. Esto significa que no tiene que copiar y pegar franjas gigantes de código TH/THC y esperar que haya hecho todo el recuento manual correctamente, puede escribir código C++ como si fuera Python y se ejecutará rápidamente. En segundo lugar, estamos trabajando en una nueva versión de ATen, llamada C10, que se toma mucho más en serio tener backends abiertos que ATen (que es algo cerrado), lo que debería facilitar el trabajo en soporte complejo, ya que no No implica bifurcar PyTorch, simplemente agregando un nuevo directorio de código.

Entonces, @Roger-luo y @PhilippPelz , nos encantaría contar con su ayuda para hacer realidad el backend complejo, pero realmente nos gustaría encontrar una manera de hacerlo que nos ayude a mantenerlo de manera sostenible en el futuro. Háganos saber lo que piensas.

@ezyang Si no tiene mano de obra, podría intentar mantener la parte del tensor complejo en el futuro, acabo de comenzar mi doctorado (y este es mi año sabático en realidad) y, por lo tanto, no tendré el problema de escribir mi tesis en al menos los últimos años. Pero realmente no puedo seguir contribuyendo sin recibir comentarios del equipo de pytorch. Creo que debería haber una hoja de ruta para esta gran extensión. Y podríamos agregar soporte complejo sin problemas para que sus muchachos no necesiten revisar un gran PR y facilitará los esfuerzos de los desarrolladores en el seguimiento de la rama maestra.

En primer lugar, creo que el principal problema del soporte complejo sería la parte de CUDA. Es bastante fácil admitir la parte de la CPU con ATen o cualquier otra biblioteca, puedo reescribir la parte de la CPU en solo unos días si hay algún comentario. Hay algunos problemas que podrían preocuparme por parte de CUDA, y creo que esto podría conducir a dos enfoques diferentes:

  1. Use float2 , etc. para simular un único valor complejo como lo hace cuComplex en la parte de CUDA.
  2. Use FloatTensor y DoubleTensor existentes para simular un tensor complejo en la parte C++ de ATen.

El motivo del segundo enfoque es que en THC , pytorch usa algunos trucos para acelerar las operaciones de mapa/reducción y no es adecuado para cuComplex trivialmente porque cuComplex es en realidad float2 , pero las funciones __shfl_xxx no admiten de forma nativa float2 . No estoy seguro de cómo simular de manera eficiente dicha función por float2 en este momento.

El segundo enfoque sería más fácil porque ahora no necesitamos preocuparnos por el hardware, y podemos hacer que nuestra nueva extensión compleja funcione en dispositivos antiguos mucho más fácilmente. Sin embargo, esto podría causar cierta sobrecarga debido a la dirección de memoria no contigua.

Además, descubrí que para integrar números complejos en ATen, podríamos tener que manejar cuatro tipos diferentes que en realidad son iguales en el hardware: std::complex , thrust::complex , cuComplex , float2 que a veces puede ser peligroso. (de hecho, encontré este problema el año pasado, y reinterpreter_cast fue la solución).

Sin embargo, personalmente preferiría escribir todo más nativo.

Y creo que probablemente necesitemos un marco de tiempo o una hoja de ruta, y podemos recoger cada pequeña parte y trabajar juntos, así que no tendré que rastrear el maestro yo mismo, lo cual es totalmente imposible...

hubo un ChangeLog cuando estaba tratando de implementar el backend de la CPU, clasifiqué las funciones que deben modificarse para números complejos en el registro. Podríamos escribir un mapa de ruta basado en este registro.

Además, como me acaban de rechazar la visa (por parte de Australia), tengo que empezar un año sabático, si necesitas que alguien siga trabajando en esto, podría solicitar una pasantía.

Pensé mucho en esto durante el último día. Es un poco triste que no pudimos fusionar el esfuerzo de Roger tal como está, pero pensé para mis adentros

"¿Cómo podemos desarrollar un soporte de Tensor complejo y mantener bajos los gastos generales de mantenimiento?"

Esto es lo que estoy presentando como un plan efectivo del objetivo anterior:

  • Los tensores complejos no deberían ser un nuevo tipo de tensor fundamental, como los tensores sparse . Agregar un tipo fundamental provoca una gran cantidad de gastos generales de mantenimiento y cambios transversales. La sobrecarga de mantenimiento no se trata de "¿quién mantiene los bits complejos?", sino más bien de "ahora todos los desarrolladores principales deben ser conscientes de este tipo complejo al realizar cambios fundamentales, cualquier cambio de ATen, etc."

    • En su lugar, siempre deben ser [Forma de tensor x 2] o [2 x Forma de tensor], es decir, el tensor debe tener una dimensión adicional con un tamaño 2.

  • Los tensores complejos deben ser un pequeño archivo/carpeta de ~2k líneas de C++ simple que se construyen sobre la API de ATen Tensor.

    • Por ejemplo, como sugiere https://github.com/pytorch/pytorch/issues/6514 , la multiplicación compleja debe implementarse como torch.stack([real1 * real2 - imag1 * imag2, real1 * imag2 + imag1 * real2], dim = -1) donde real1 = input1[:, :, :, ..., 0]

    • Esto perjudica el rendimiento: sí, no obtendremos tanto rendimiento como si lo pusiéramos todo en línea. Sin embargo, la pregunta es: "¿por cuánto?". Creo que deberíamos apuntar a un rendimiento un 20 % más bajo a cambio de un soporte complejo saludable y con todas las funciones + mantenido.

    • Las funciones complejas más utilizadas pueden comenzar a obtener núcleos dedicados, de modo que cuando el rendimiento se ve afectado en más del 20% en una función de uso frecuente, intervenimos.

Tiene que ser [Tensor Shape x 2] ya que BLAS, cublas y MAGMA esperan sus propios tipos complejos que sean compatibles con bytes para float2. Además, las llamadas de blas, cublas y magma no se pueden manejar en el nivel de python.
No creo que sea solo el 20% para la multiplicación compleja, ¿no tienes 4 operaciones de copia completa además de los cálculos para la parte real e imagen?
De todos modos, todavía estaría feliz si no tuviera que fusionar los cambios del maestro continuamente.

De acuerdo con @PhilippPelz , podríamos perder mucho rendimiento ya que perderemos el soporte complejo de BLAS, cublas y MAGMA. Pero no estoy seguro de eso. Sin embargo, para ser claros, el tensor complejo es algo completamente diferente del tensor disperso , la mayoría de las bibliotecas como scipy.sparse y el SparseArrays de Julia tratan el arreglo disperso como una composición de arreglos multidimensionales fundamentales. Pero nadie trata una matriz multidimensional con un tipo complejo mediante dos matrices reales compuestas ... (nadie aquí me refiero a tensorflow, arrayfire, numpy y Julia). Aunque en MXNet, la FFT se logra mediante una composición de dos tensores reales, de hecho, no admiten complejos... Parece que tensorflow implementó un tipo de datos como un envoltorio alrededor de diferentes tipos de redes, incluidos complex64 y complex128 ver tipos.proto

Acerca de la pérdida de rendimiento

En primer lugar, las funciones basadas en elementos (llamadas a funciones mapear/reducir) no tendrán una gran pérdida de rendimiento (al menos, la memoria para estas operaciones será contigua). Pero creo que primero deberíamos intentar comparar algunas funciones de BLAS, para ver si una composición de FloatTensor tiene un rendimiento similar con Complex64Tensor en GPU, y cuánto perderemos en rendimiento con una proyecto de implementación, como:

  • gemm
  • gemv

Un tensor complejo compuesto sería algo parecido (o simplemente use shared_ptr ):

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

Sin embargo, como mencioné en la desventaja del primer enfoque, funciones como __shfl_xxx también parecen un obstáculo si queremos hacer esto de forma más nativa.

actualmente torch.fft devuelve un solo tensor flotante de forma [dim1, ..., dimN, 2]

@ezyang, ¿cuál es el plazo para el lanzamiento del C10? Eso suena como un punto muy razonable para comenzar a admitir complejos en la rama maestra.

@PhilippPelz Definitivamente no para 0.4. Estamos apuntando internamente a junio, espero que no sea demasiado tiempo de espera.

@ezyang mencionaste a June, ¿conseguiste agregar soporte para números complejos a PyTorch?

Creo que se refería a C10, no a soporte complejo. C10 hará que agregar complejos sea más fácil. Así lo entendí.

Sí, C10 tendrá registro abierto tanto de tipos como de funciones de Tensor. Por lo tanto, agregar un tipo complejo como un paquete separado será mucho más fácil.

¿Hay algún ETA en números complejos? ¿"Mucho más fácil" significa "probablemente se hará rápidamente"?

@themightyoarfish por mucho más fácil, quiero decir que no seremos bloqueados en lo que se puede enviar a pytorch master. No hemos establecido una ETA. Examinaré el trabajo una vez que tengamos un registro abierto en PyTorch.

@soumith , ¿todavía necesita gente para trabajar en este (número complejo)? ¿El equipo de PyTorch admitirá números complejos? Puedo dedicar algún tiempo a trabajar en esto si lo desea en septiembre, ya que mantendré QuCumber (utilizará mucho el número complejo)

@Roger-luo sí. Quería comunicarme con usted una vez que tengamos el registro abierto disponible en el backend de PyTorch, y podamos resolver los detalles.
@ezyang , ¿tendremos registro de tipo abierto para septiembre?

@soumith Genial, a su servicio.

Podemos hacer que suceda. (No tendremos el nuevo sistema "completo" implementado, pero siempre que configuremos las cosas para que sea refactorizable, podemos seguir moviéndolo a medida que ocurren nuevos desarrollos. Será un buen caso de prueba para el nuevo abierto registro. Puedo asegurarme de que esto suceda.)

@ezyang alguna nota por ahora? Podría leerlo antes de trabajar en él. Parece que las cosas han cambiado mucho desde la última vez.

@Roger-luo @PhilippPelz También me gustaría ayudarlo con la implementación de tensores complejos. También lo necesito para mis investigaciones de doctorado..

@alexgomezalanis tal vez podríamos tener un canal para discutir en la holgura, acabo de crear una llamada de canal #complex-numbers . Pero no empezaré a trabajar en él hasta septiembre (todavía necesito trabajar en parte de mi código de Julia...)

Por cierto, parece que ha cambiado mucho desde la última vez. Usaré un poco de tiempo para ponerme al día antes de ponerle las manos encima.

@alexgomezalanis No puedo. primero debe unirse al espacio de trabajo de pytorch en holgura. No puedo encontrarte. Envíe un correo electrónico a la dirección: [email protected] para obtener una invitación.

@Roger-luo @alexgomezalanis Genial volver a ver la vida en el tema del tensor complejo. Puedo ofrecerme para participar también, pero siendo realistas, esto no sucederá hasta finales de septiembre o principios de octubre. En cuanto a algunos comentaristas sobre este tema, el soporte de tensor complejo sería muy útil para mi proyecto de doctorado.

También estaba tratando de salvar mi investigación el año pasado 😏... pero ahora solo quiero revivir mi viejo código 1w+ loc. 🤣 ¡hablemos en Slack!

:) Sí, hablemos en Slack. Acabo de encontrar la invitación en la carpeta de correo.

El complemento de trabajo en progreso (solo para CPU a corto plazo) está aquí: https://github.com/Roger-luo/pytorch-complex

Por favor, siéntase libre de darme un problema y relaciones públicas.

He publicado las notas sobre cómo se llevará a cabo la implementación compleja en la parte superior de este número.

Recientemente comencé a usar PyTorch y me encanta, es mucho más agradable de usar que TensorFlow. Sin embargo, el soporte de tensores complejos es bastante crítico para mi investigación (redes neuronales ópticas). ¿Todavía se está trabajando activamente en esto? Si es así, ¿alguien sabe un marco de tiempo (flojo) para el soporte de tensor complejo?

Estaría feliz de ayudar a trabajar en esto donde pueda, pero soy relativamente nuevo en PyTorch, por lo que todavía no tengo una buena idea de qué tan grande es la tarea de esta característica. Algunos de mis compañeros de laboratorio también han expresado un gran interés en el soporte de tensor complejo (en física, agregar esto podría hacer que Torch sea casi un reemplazo acelerado por GPU para NumPy) y podrían estar dispuestos a ayudar si eso significa obtener soporte complejo en el futuro cercano.

Hola @bencbartlett

Todavía estoy tratando de trabajar en ello lentamente... pero actualmente también soy solo un estudiante (con una situación bastante inestable), lo que significa que no puedo trabajar en esto a tiempo completo, sino solo en mi tiempo libre. (Implementé mi código relacionado con la investigación en Julia del año pasado, lo que significa que solo nuestro paquete heredado necesita un mejor soporte de números complejos de la antorcha).

Si el número complejo es crucial para usted y es urgente tenerlo en la antorcha, le sugiero que intente esto:

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

Es super lento... pero al menos funciona. O tenía una versión C al viejo estilo TH.

Este no será un proyecto pequeño que se puede hacer en unos pocos días. Por lo tanto, no puedo garantizar ningún marco de tiempo específico para un soporte funcional completo con valor complejo en CPU o CUDA.

Sin embargo, me encantaría ayudarlo a trabajar conmigo en esto. Le sugiero que comience tratando de resolver los problemas que publiqué en el repositorio de extensiones. Y no dude en preguntarme a través de Slack, correo electrónico o problema si tiene preguntas (ya que aún no hay muchos documentos).

Desafortunadamente, todavía no tengo acceso a PyTorch Slack. (Envié un correo electrónico dos veces pidiendo una invitación, pero no he recibido respuesta). ¿Alguien podría invitarme? ([email protected])

@Roger-luo Definitivamente revisaré tu bifurcación, pero no puedo prometer que seré de mucha ayuda: mi C ++ está oxidado y, como señalaste, es difícil encontrar tiempo para trabajar en esto como un estudiante. Las utilidades de QuCumber son agradables, pero desafortunadamente no serían muy útiles para mí: hasta que los tensores complejos sean compatibles con GPU o con autograd y torch.nn, no brindan mucha utilidad por encima de lo que puede ofrecer NumPy.

@soumith @ezyang ¡Sería genial recibir más atención sobre esto por parte del equipo de PyTorch! El soporte complejo parece ser una característica importante para una biblioteca general de tensores, es prácticamente esencial en física y específicamente dentro de ML en los últimos años, ha habido un interés creciente en modelos de valores complejos.

El enfoque de @bencbartlett QuCumber se puede usar en GPU con AD... es simplemente súper lento... Quiero decir, si solo quieres ese AD, es posible que puedas usarlo.

Sí, hablando francamente, estoy usando una versión ligeramente modificada de https://github.com/FluxML/Flux.jl y mi propio paquete en Julia para investigación (también necesito AD complejo en GPU con tensores en algunas situaciones ). El paquete de AD source2source Zygote.jl puede hacer AD en tensores complejos, pero se encuentra en una etapa muy temprana que puede tener fallas de segmento. El ecosistema aún no es tan estable en comparación con la antorcha, a veces tengo que piratear un poco esas implementaciones para uso propio... Pero básicamente funciona para lo que necesito para la investigación en física cuántica. También puedo tener tensores complejos en la GPU.

No creo que se necesite soporte de valor complejo para torch.nn , es posible que solo necesitemos agregar algunas definiciones para autograd una vez que el tensor complejo sea funcional, porque cosas como las capas lineales pueden permanecer igual . Y es posible que algunas funciones de activación no tengan una expansión estándar en el espacio de Hilbert... (Puede consultar la publicación del blog de mi colaborador @GiggleLiu )

Para la extensión pytorch-complex, no estoy seguro de cuándo podremos obtener soporte completo con AD en GPU... esto todavía me parece bastante lejano. Diría que la implementación de la CPU pasará por un período que requiere parches en el árbol principal (por ejemplo, promociones de tipo, compatibilidad con simd, etc.), esto también podría estar relacionado con la próxima implementación de ATen en C++ y deshacerse de TH, etc. y luego podremos agregar operadores para tensores complejos más rápido.

Puedo solicitar pasantías en primavera (sobre las cuales le acabo de preguntar a @ezyang ). Así que podría trabajar en esto a tiempo completo durante varios meses antes de comenzar mi doctorado. Vamos a ver.

Mientras tanto, implementé mi propia versión de la multiplicación compleja. Sin embargo, cuando lo perfilo, sucede que una cantidad sustancial de tiempo se destina a: torch._C_._cuda_isDriverSufficient

image

¿Tienes alguna idea de por qué? Si conoce una mejor implementación de la multiplicación compleja, hágamelo saber. De alguna manera, mi versión (aunque optimizada para el número de multiplicaciones: 3 en lugar de 4) parece ser relativamente lenta, por ejemplo, irfft del tensor de salida es 10 veces más rápido que mi multiplicación por elementos. ¿Se admite la multiplicación compleja en el nivel 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)

Estamos tratando de soportarlo desde C++. ver la publicación en la parte superior. Si puede compilar la extensión, debería funcionar para la multiplicación escalar al menos en este momento...

Su implementación es similar a la que tenemos en QuCumber. Podría llamar a muchos subprocesos de GPU adicionales si no llama al kernel cuda correcto para el número complejo. Y es posible que pierda SIMD si no tiene un backend de C++ como soporte en Python.

Le sugiero que ejecute nvprof para obtener más detalles.

@Roger-luo @apaszke @soumith Gracias por este hilo por cierto. Implementé un tensor complejo básico pirateado a partir de la subclasificación de torch.Tensor.

Trato la primera mitad como real, la segunda como imaginaria e implementé mis propias operaciones aritméticas básicas y algunas otras que necesito para mi investigación.

Verifiqué contra Tensorflow y numpy. ¡Los gradientes y todas las operaciones que implementé coinciden con sus resultados!

Solo pretende ser un remanente hasta que PT admita completamente tensores complejos.

Características:

  1. Pruebas implementadas.
  2. Compatible con Pypi (es decir, pip install)
pip install pytorch-complex-tensor

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

¡Gracias @williamFalcon !

¿Alguna actualización de esto? Solo me preguntaba si habrá un plan para integrar el soporte de tipo complejo en pytorch.

Hola, @whmrtm

@ezyang está trabajando en https://github.com/Roger-luo/pytorch-complex/issues/4 O quien esté interesado en esto podría ayudarnos a hacerlo funcionar. Este problema resolverá algunos problemas básicos de transmisión (entonces puede usar muchas funciones después de que se resuelva este). Por favor, siéntase libre de hacer cualquier PR o pedirme que lo agregue como colaborador.

No podré trabajar en nada hasta el verano, tengo que terminar un nuevo lanzamiento para nuestro propio paquete.

Hola, @whmrtm

@ezyang está trabajando en Roger-luo/pytorch-complex#4 O quien esté interesado en esto podría ayudarnos a hacerlo funcionar. Este problema resolverá algunos problemas básicos de transmisión (entonces puede usar muchas funciones después de que se resuelva este). Por favor, siéntase libre de hacer cualquier PR o pedirme que lo agregue como colaborador.

No podré trabajar en nada hasta el verano, tengo que terminar un nuevo lanzamiento para nuestro propio paquete.

Gracias por la actualización, veré qué puedo hacer.

Hola @Roger-luo

¿Puedo acceder al canal de holgura relacionado con el tema de soporte de tensores complejos ([email protected])? Envié un correo electrónico para una invitación, pero no pasó nada todavía. En este momento estoy tratando de averiguar los puntos por donde empezar a contribuir a este problema. Supongo que https://github.com/Roger-luo/pytorch-complex/issues/4 es un punto de entrada actual ahora.

@beconstant sí, ese es el punto de partida, esto debería hacer que funcione alguna función de transmisión, pero no sé por qué arroja un error de promoción de tipo en cuda, estaba funcionando en la CPU. (Aunque no tenemos la intención de admitir cuda en primer lugar, esto provocaría una falla en la compilación)

No puedo enviarte un correo electrónico de invitación (no tengo acceso). Creo que deberías seguir la guía oficial de pytorch para unirte a Slack. Pero siempre podemos discutir en el tema/PR.

@Roger-luo está bien, lo tengo :)

Avísenme si necesitan ayuda. Comenzaré construyendo la versión de pytorch especificada. ¿Algún progreso en pytorch-complex/issues/4 ?

Avísenme si necesitan ayuda. Comenzaré construyendo la versión de pytorch especificada. ¿Algún progreso en pytorch-complex/issues/4 ?

@dylanbespalko Hola, necesito urgentemente que se implemente pytorch en una versión de valor complejo.
Muchas gracias por tus aportes.

Atentamente,
Zellar209

Hola @Zellar209 ,

Tengo la sensación de que @ezyang está trabajando duro en uno de los problemas más importantes ( pytorch-complex/issues/4 ). Tengo un sistema AMD ahora y un sistema Nvidia en 3 semanas que puedo usar para aumentar el soporte de GPU.

Supongo que el problema es que el cambio de promoción del tipo original interrumpe CUDA, siempre que se resuelva ese PR, al menos algunos operadores funcionan en la CPU, todavía no tenemos soporte para CUDA...

En mi humilde opinión, creo que primero deberíamos centrarnos en la CPU y hacer que las cosas funcionen, y luego considerar la GPU.

El soporte solo para CPU está bien. ¿Este tipo de problema de promoción ( pytorch-complex/issues/4 está siendo manejado internamente por fb? ¿Está bien trabajar en él externamente?

Hola @dylanbespalko; Le dije a @Roger-luo que iba a investigarlo (porque probablemente era el mejor ubicado para averiguar cuál es el problema), pero aún no he tenido tiempo de analizarlo. Si desea ver cómo solucionar el problema, estaré encantado de aconsejarle.

Hola @Zellar209 ,

Tengo la sensación de que @ezyang está trabajando duro en uno de los problemas más importantes ( pytorch-complex/issues/4 ). Tengo un sistema AMD ahora y un sistema Nvidia en 3 semanas que puedo usar para aumentar el soporte de GPU.

Sí, no necesito ninguna GPU ahora, estoy usando el sistema MAC. Pero he tenido algunos errores al construir este proyecto.

Hola @Zellar209 , ¿podrías publicar lo que obtienes en el problema de pytorch-complex? Creo que hay algo mal con el nuevo Xcode de Mac, que lo hace difícil de construir. Pero la gente necesitará más mensajes de error para averiguar por qué.

Pregunté sobre el sistema operativo y el mensaje de error, pero no respondió...

Hola @dylanbespalko; Le dije a @Roger-luo que iba a investigarlo (porque probablemente era el mejor ubicado para averiguar cuál es el problema), pero aún no he tenido tiempo de analizarlo. Si desea ver cómo solucionar el problema, estaré encantado de aconsejarle.

Gracias por su pronta respuesta.

1. Cuando ejecuto "python setup.py install" usando gcc (predeterminado), obtengo errores como este:

construyendo la extensión '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: error: opción de línea de comandos no reconocida '-stdlib=libc++'

error: el comando 'gcc' falló con el estado de salida 1

2. Cuando uso clang para compilarlo, los errores son:

En archivo incluido desde src/module. cpp:2 :
En el archivo incluido de src/CPUComplexType.h:60:
src/CPUComplexTypeImpl.h:102:105: advertencia: 'IntList' está en desuso [-Wdeprecated-declarations]
Tensor & CPUComplexType::set_(Tensor & self, Storage source, int64_t storage_offset, tamaños de IntList, pasos de IntList) const {
^
/anaconda3/lib/python3.6/site-packages/torch/include/c10/util/ArrayRef.h:273:7: nota: 'IntList' se ha marcado explícitamente como obsoleto aquí
usando IntList C10_DEPRECATED_USING = ArrayRef;
^
En archivo incluido desde src/module. cpp:2 :
En el archivo incluido de src/CPUComplexType.h:60:
src/CPUComplexTypeImpl.h:105:76: error: ningún miembro llamado 'scalarTypeToDataType' en el espacio de nombres 'at'
fuente automática_ = almacenamiento_comprobado(fuente,"fuente",2, DeviceType::CPU, en::scalarTypeToDataType(CPUComplexTypeInfo::scalar_type));
~~~~^
7 advertencias y 2 errores generados.

error: el comando 'clang' falló con el estado de salida 1

No puedo arreglarlo. ¡Realmente espero que puedas ayudarme!

Hola chicos,

Gracias por tus comentarios. Creo que puedo pasar la semana investigando esto. Hasta ahora, he compilado el complejo pytorch de @ Roger-luo de la siguiente manera:

@ Zellar209 : Adjunté mis variables de entorno que se ejecutan en macOS 10.13.

  1. Elimine la distribución de pytorch existente de la siguiente manera
    conda desinstalar pytorch
    antorcha de desinstalación pip
    pip desinstalar antorcha # ejecutar este comando dos veces
    python setup.py limpio
    Elimine la carpeta torch en la carpeta de paquetes del sitio de python, si existe.
    Cambie el nombre (o elimine) la carpeta de origen de pytorch anterior (algo se refería a ella).

  2. Instale PyTorch revisión 6cb593b88cb0c411690b4957850058329526d87b.

    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. Instalar el complejo 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. Crear un tensor complejo
   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:

Todo lo relacionado con la promoción de tipos para las operaciones de tensor parece estar hecho en c10/core/ScalarType.h
He encontrado el error AT_ERROR("promoteTypes with complex numbers is not handled yet; figure out what the correct rules should be”);
Parece que tengo que agregar una entrada para c8 y c16 dentro de esta tabla.
¿Tiene esto algo que ver con 9515 ? Creo que esto es solo para llamar a funciones numpy.
¿Es ese un buen lugar para empezar?

9515 no está relacionado. Sin embargo, corregir esta ruta de código en ScalarType.h es un buen lugar para comenzar.

Arreglé la ruta de código en ScalarType.h
BinaryOps (add, sub, mul, div) funciona, pero solo si ambos argumentos son tensores.
Algunos otros problemas extraños, pero necesito mirarlo un poco más.

@dylanbespalko He agregado promociones de tipo aquí: https://github.com/pytorch/pytorch/pull/11641

Podría simplemente copiar eso, pero el problema es que esto rompe CUDA de alguna manera.

IIRC, hubo un error de conexión debido a la versión gcc. Tuve algunas soluciones allí.

Ah, gracias @Roger-luo. Estaba mirando los comentarios de #11641 . Haré un mejor trabajo copiando el código mañana.

¿Cómo puedo saber cuándo he roto CUDA cuando no tengo un dispositivo CUDA? ¿Supongo que el CI me lo dirá?

Sí, siempre que envíe un PR, le dirá cuál está roto. Y si todo pasa, entonces podríamos fusionarlo y hacer que todo funcione.

Ok, entonces comenzaré a enviar relaciones públicas para saber cuándo sucederá.

@dylanbespalko Hola, ¿todavía parece haber algunos errores en su entorno?
Si lo solucionas, por favor compártelo con nosotros. Muchas gracias.

Hola chicos,

Intenté hacer varias relaciones públicas después de copiar varias confirmaciones de @Roger-luo. Desafortunadamente, no tengo una GPU CUDA en este momento y las máquinas CI con CUDA no se están inicializando. No puedo recrear la falla de la prueba CUDA en este momento, así que volveré a esto en unas semanas cuando pueda ejecutar localmente en esa GPU. Parece prometedor, al menos.

@ezyang , @Roger-luo

He echado un vistazo al PR # 11641 de Roger:

  • Se compila y se ejecuta en mi máquina CUDA 9.0
  • No se puede construir en máquinas CI que ejecutan CUDA 9.0

También he echado un vistazo a algunos de los desarrollos recientes de PyTorch:

  • Una presentación de @ezyang que describe cómo escribir una extensión C++/CUDA que puede definir un dispositivo/diseño/dtipo personalizado .

    • Un PR más reciente #21964 que "elimina ComplexHooksInterface", pero define una extensión C++ de número complejo ubicada en pytorch/test/cpp_extensions/complex_registration_extension.cpp

Me parece que se está desarrollando una nueva capacidad de extensión "fuera del árbol", que me permitiría investigar el soporte de números complejos sin romper el resto de pytorch. Mi objetivo es:

  1. Definir compatibilidad con CPU compleja sin AVX.
  2. Defina el soporte CUDA complejo usando Thrust.

@ezyang
¿Puede proporcionar una línea de tiempo esperada para esta extensión de dispositivo/diseño/tipo de punto fuera del árbol que presentó? ¿Podemos esperar esta característica en los próximos 3 meses?

@ezyang

¿Puede fusionar la compatibilidad con números complejos en la CPU sin la compatibilidad con AVX/SSE? Planeo enviar lo siguiente en solicitudes de combinación separadas:

  • [ ] Se agregó soporte complejo de núcleos CPU BinaryOp
  • [ ] Se agregó soporte complejo de CPU TensorFactories
  • [ ] Se agregó soporte complejo de CPU FillKernels
  • [ ] Se agregó soporte complejo de núcleos de rango de CPU
  • [ ] Compatibilidad compleja agregada de kernels unarios de CPU
  • [ ] Compatibilidad compleja agregada de núcleos de comparación de CPU
  • [ ] Compatibilidad compleja agregada con núcleos de CPU TensorCompare
  • [ ] Se agregó soporte complejo de núcleos CPU ReduceOp
  • [ ] Se agregó soporte complejo de núcleos CPU PointwiseOps
  • [ ] Compatibilidad compleja agregada con kernels de CPU learnOps
  • [ ] Se agregó soporte complejo de núcleos CPU LinearAlgebraOps
  • [ ] Se agregó soporte complejo de núcleos CPU SpectralOps

Planeo tener esto probado a través de intel/arm cpus en los próximos días.

@ezyang ,

Estoy investigando operaciones como fft() y var() donde la implementación del número complejo debe convertir los datos del tensor en un tensor doble de forma: (complex_shape, 2) . Esto no funciona con ningún método de tensor existente:

  1. 'tensor.to(torch.float64): solo conserva la parte real y devuelve un tensor con la misma forma.
  2. 'tensor.view(new_shape): la nueva forma debe tener el mismo número de elementos.

Obviamente puedo hacer algo ineficiente como:

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]

Obviamente, eso es crear copias, cuando todo lo que necesito es static_cast<double> y cambiar la forma del tensor a (old_shape, 2) . ¿Tiene alguna sugerencia sobre cómo hacer esto?

Además, hay un truco en numpy que te permite hacer esto:

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 capacidad de establecer dtype realmente funciona en esta situación, sin embargo, podría ser impredecible.

Alguna información adicional sobre la interpretación de un número complejo como una matriz de números reales de longitud 2. Lo siguiente es válido en C++11.

Para cualquier puntero a un elemento de una matriz de números complejos p y cualquier índice de matriz válido i, reinterpret_cast(p)[2 i] es la parte real del número complejo p[i], y reinterpret_cast(pag)[2 i + 1] es la parte imaginaria del número complejo p[i]. (Desde C++11)

Creo que esto significa que es posible convertir un tensor_complejo en un tensor_real con forma (forma_compleja, 2) y luego realizar una operación sin llamar a real() y imag() que asignaron nueva memoria.

@dylanbespalko Tenía miedo cuando preguntabas sobre esto :) La garantía std::complex significa que si tienes el puntero de datos std::complex<float>* , puedes convertirlo de forma segura en float* (masculle un alias estricto) y luego páselo a cualquier cosa que esté usando. Si solo necesita implementar fft/var donde puede pasar esta repetición de bajo nivel, será más fácil.

Sin embargo, si necesita volver a ver literalmente un tensor complejo como un tensor flotante, estamos en un aprieto, porque no hay precedentes para esto en PyTorch hoy. Storage dtype siempre ha estado de acuerdo con Tensor dtype. Entonces, si realiza un almacenamiento complejo, no hay forma de revisarlo como un almacenamiento flotante.

Un pensamiento que he tenido es que tal vez deberíamos relajar este invariante. La idea es:

  1. Siempre asignamos almacenamientos como el tipo "no vectorizado" en cuestión. Entonces para un complejoasignamos un tensor flotante.
  2. Se permite que el tipo de tensor no esté de acuerdo con el tipo de almacenamiento, pero solo como una variante vectorizada del tipo subyacente

Sin embargo, no estoy seguro de cuánto código tendríamos que cambiar para que esto suceda.

@ezyang ,

Sí, esto era inevitable...

Si solo necesita implementar fft/var donde puede pasar esta repetición de bajo nivel, será más fácil.

Sí, esto es posible en muchos casos. ¿Puede proporcionar un fragmento de código de cómo interpretar los datos del tensor como un std::vector?

Sin embargo, si necesita volver a ver literalmente un tensor complejo como un tensor flotante,...

Me imagino que es raro ver un tensor usando otro dtype. Implementé un método set_dtype() para Tensor , pero recibí algunos errores. Tampoco actualicé los pasos para reflejar los cambios de forma. No estoy seguro de por qué la configuración de dtype funciona de forma numérica (¿es una coincidencia?), Sin embargo, cuando carga datos en un convertidor de digital a analógico (DAC), a menudo espera que los datos reales/imaginarios se intercalen. Quizás eso motivaría la necesidad de desacoplar el tipo de tensor del tipo de almacenamiento como ha sugerido.

Evitaré hacer esto por ahora. Estoy seguro de que hay otros cuellos de botella de rendimiento para mí.

Sí, esto es posible en muchos casos. ¿Puede proporcionar un fragmento de código de cómo interpretar los datos del tensor como un std::vector?

No es exactamente un std::vector, pero estoy imaginando algo como esto:

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;

Implementé un método set_dtype() para Tensor, pero recibí algunos errores. Tampoco actualicé los pasos para reflejar los cambios de forma.

Sí, probablemente sea una mala idea si no arreglas los pasos. Además, no soy un gran fanático de los tensores que se transmutan en otros tipos de d; mejor hacerlo todo fuera de lugar :)

sin embargo, cuando carga datos en un convertidor de digital a analógico (DAC), a menudo espera que los datos reales/imaginarios se intercalen. Quizás eso motivaría la necesidad de desacoplar el tipo de tensor del tipo de almacenamiento como ha sugerido.

Sí, en última instancia, esto es lo correcto, pero estoy de acuerdo en que es más fácil no hacerlo ahora.

@ezyang ,

Estoy empezando a perder el tiempo con el soporte CUDA de números complejos.

Hay dos opciones binarias compatibles:

  1. cuComplex : muy básico add, sub, mul, div, real, imag support.
  2. empuje::complejo : reemplazo directo de std::complex que admite la asignación de memoria de host y dispositivo.

El contenedor push::complex parece ser el camino a seguir. La API de Thrust::Complex sugiere que los contenedores thrust::complex<T> se pueden asignar en la memoria del host y del dispositivo, mientras que los std::complex<T> solo se pueden asignar en la memoria del host:

__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. ¿Esto sugiere que AT_DISPATCH_COMPLEX_TYPES debería establecer using scalar_t = thrust::complex<double> en lugar de using scalar_t = std::complex<double> ?

  2. ¿Cómo llama Pytorch automáticamente a los equivalentes de CUDA de std::log para tipos de datos reales? ¿Cómo sé que hay un equivalente CUDA de un núcleo matemático?

  1. Creo que la dificultad de usar thrust::complex<double> universalmente para CPU y CUDA es que en realidad no construimos contra empuje si haces una compilación solo de CPU. Supongo que hay un montón de opciones; podríamos lanzar nuestro propio tipo complejo (similar a cómo lanzamos nuestro propio medio tipo), o simplemente reinterpretar el lanzamiento para llegar a la victoria, porque std::complex<> está definido para tener un diseño binario específico. Depende de usted, pero solo reinterpretar el lanzamiento entre los tipos parece más fácil por ahora.
  2. Tenemos sobrecargas matemáticas en THCNumerics.cuh, ¿eso responde a su pregunta?

@iotamudelta ha planteado un problema con el cumplimiento de C++11 en #29547

std::real es solo constexpr de C++14

Si entiendo correctamente std::real() debe ser constexpr para que el compilador hcc pueda compilar la instrucción para __device__ .

Soluciones posibles:

  1. Encuentre otro método o función para convertir complex<double> a double :
  1. Encuentre una manera de envolver la función:

    • La mayoría de las llamadas a std::real se realizan en aten/src/ATen/native/cpu/zmath.h . Ejemplo: reemplace inline con 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)

Esto no se compilará porque todavía hay una llamada anidada a std::real() que no es un constexpr .

3. Si uso std::complex::real() en lugar de std::real() esto parece ser compatible con C++11.

  1. Creo que estás diciendo que no importa lo que haga, este código es UB hasta C++14. ¿Hay alguna otra forma de convertir un std::complex<double> a double que cumpla con sus requisitos?

@iotamudelta , @bddppq , @ezyang ,

He agregado soporte para UnaryOps y BinaryOps complejas en CUDA push::complex API, pero necesito hacer algunas preguntas antes de enviarlo.

Definí una función de plantilla que le permite usar tipos de datos de empuje::complejos cuando se trata de números complejos.
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

Luego en aten/src/ATen/native/cuda/BinaryOpsKernel.cu
Reemplazar:

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;
    });
  });
}

Con:

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;
    });
  });
}

Preguntas

  1. @ezyang : para números no complejos, scalar_t y thrust_t son del mismo tipo. ¿Tal vez podría reemplazar el nombre de la variable thrust_t con algo más amigable para los números no complejos, como scalar_t_c ?
  2. La biblioteca de empuje parece estar ampliamente referenciada en el código:
    a) @bddppq : ¿Hay alguna razón por la que debería usar cuComplex en lugar de thrust::complex ?
    b) @iotamudelta : el empuje de la cadera se eliminó en ROCm2.7. ¿Debería usar hip_complex en su lugar?
    thrust::complex parece admitir más funciones que cuComplex .

Por favor déjame saber lo que piensa.

@iotamudelta

He actualizado la discusión sobre std::real(). ¿Puedes confirmar que std::complex::real() solucionaría el problema.

Hola @dylanbespalko ,

Supongo que de lo que se queja @iotamudelta es que al cast_and_store para tipos complejos le falta un C10_HOST_DEVICE , esto sería un UB si esa ruta de código se ejecuta alguna vez en la GPU.

Actualmente, esta utilidad de conversión dinámica solo se usa en GPU TensorIterator, y solo se usa cuando hay promoción de tipo. Debido a que el complejo no era compatible con la GPU actualmente, cast_and_store para tipos complejos en este momento no tiene el calificador C10_HOST_DEVICE y usa std::real que está totalmente bien para un host. única función. No hay UB aquí porque no se usa y no hay nada de lo que deba preocuparse.

Pero dado que desea agregar soporte de complejo a GPU, y complejo es compatible con la promoción de tipos, como podemos ver en https://github.com/pytorch/pytorch/blob/master/c10/core/ScalarType.h#L398 - L420, debe tener mucho cuidado con esta ruta de código y es posible que deba hacer algunas modificaciones para que funcione:

Por supuesto, debe agregar C10_HOST_DEVICE como lo está haciendo @iotamudelta en https://github.com/pytorch/pytorch/pull/29547 , pero eso no es suficiente, porque simplemente agregue C10_HOST_DEVICE sin otros cambios sigue siendo UB en C++ 11 como lo menciona @iotamudelta , una buena solución podría ser lo que ha mencionado: use std::complex::real() para reemplazar el std::real .

Pero más allá de eso, si observa el archivo https://github.com/pytorch/pytorch/blob/master/c10/util/TypeCast.h , verá dentro fetch_and_cast , hay algo como:

#ifndef C10_HOST_DEVICE
    AT_FORALL_COMPLEX_TYPES(FETCH_AND_CAST_COMPLEX_CASE)
#endif

Esta ruta de código está deshabilitada en GPU. Necesitas habilitarlo y hacer que funcione.

Además, no vi ninguna conversión entre complex<float> y complex<double> dentro fetch_and_cast y cast_and_store . Es posible que también deba agregar la conversión para eso. Asegúrese de probar exhaustivamente la cobertura de estas funciones de todos los tipos de d.

CC: @ezyang y @bddppq

También @dylanbespalko , envíeme un mensaje de correo electrónico si está realizando algún cambio en TypeCast.h en su PR.

Bien, tengo un par de cosas pequeñas que arreglar con torch.real() y torch.imag() en ARM, así que arreglaré TypeCast.h y algunos otros mientras estoy en eso. Voy a cc ustedes en el PR.

Drive by comment: @smessmer nos está moviendo a C++ 14, momento en el cual no será UB. Dado que esto llegará pronto, si la UB no está causando problemas reales, no me preocuparía demasiado por eso.

@ezyang : Es bueno saberlo. La mayoría de las cosas de terceros como Eigen todavía llaman a std::real() muy generosamente.

Para números no complejos, scalar_t y thrust_t son del mismo tipo. ¿Tal vez podría reemplazar el nombre de la variable push_t con algo más amigable con los números no complejos, como escalar_t_c?

No estoy muy seguro, pero scalar_t_c parece un poco menos claro que thrust_t (¿qué significa c todos modos?) Los tipos en cuestión aquí parecen bastante específicos, por lo que parece mejor utilizar un nombre que hable directamente de la intención.

Ok, me quedaré con thrust_t . Si alguien se sumerge en ztype_cuda<>() , debería darse cuenta instantáneamente de que scalar_t es thrust_t para tipos no complejos.

¡Hola a todos! ¡Parece que se está haciendo un buen progreso para agregar soporte complejo a pytorch! ¡Gracias @dylanbespalko por tomar la iniciativa en esto y agregar soporte CUDA también! Desde un alto nivel, me interesa saber cuál es el progreso actual en el soporte complejo. Estoy principalmente interesado en una línea de tiempo aproximada para tener soporte CUDA para agregar y multiplicar tensores complejos (operaciones binarias). ¡Gracias!

Hola @sunilkpai ,

Tengo un PR abierto que debería admitir operaciones binarias y unarias en CUDA: #30295.

Un problema más es con la propagación hacia atrás. Creo que la derivada del complejo abs() se define de manera diferente a los números reales. No estoy seguro de qué hacer al respecto, pero los derivados se definen en tools/autograd/derivatives.yaml

Creo que para números complejos /dz abs(z) = z/abs(z) . Esto también se puede usar para números reales, pero probablemente será más lento que sgn(z)

@dylanbespalko Tal vez las tablas 4.1, 4.2 y 4.3 en mi Informe https://arxiv.org/pdf/1701.00392.pdf puedan ayudarlo a definir las derivadas.

Para las derivadas complejas (cálculo de Wirtinger), hay dos opciones.
Cálculo de la derivada con z o z conjugada.
Personalmente, me gusta más el derivado wrt z conjugado.
Se siente más natural para las operaciones de matriz y la actualización de gradiente no necesita un conjugado.
La definición de ellos es:

  • derivada wrt z por z = x + jy : dJ/dz = dJ/dx -j dJ/dy
  • derivado wrt z.conj por z = x + jy : dJ/dz.conj = dJ/dx + j dJ/dy

De su comentario, mi suposición es que usted calcula la derivada wrt z en este momento.
En este caso la derivada es d abs(z) / d z = z.conj / abs(z) . Cuando toma la otra definición, puede seguir la sugerencia de @Randl .

Déjame saber si debo explicar más. También tengo algunas implementaciones numpy para los derivados complejos.

Otra operación que sería útil (especialmente para proyectos en el espacio de la física que requieren compatibilidad con números complejos) es un controlador para el operador exp() . En tensorflow, tenemos tf.exp(x + iy) = tf.exp(x) * (tf.cos(y) + 1j * tf.sin(y)) . ¿Es esto sencillo de implementar también en pytorch?

@sunilkpai , @boeddeker , @Randl ,

Gracias por el informe sobre los derivados complejos. Intentaré seguir eso y volveré sobre esto la próxima semana. Pensé en agregar algunos enlaces aquí y describir el estado del proyecto.

El estado de los números complejos no es oficial y debe agregarse a través de la extensión PyTorch:

Cada extensión contiene dos cosas:

  • Un .cpp que contiene los registros del kernel matemático necesarios.
  • Una carpeta test/ que contiene versiones muy simplificadas de los scripts de prueba de pytorch.
    Busque en los scripts de prueba para ver qué kernels son compatibles (y por qué otros no lo son).

¿Por qué no puedo imprimir un tensor complejo en la consola?

  • El objeto Tensor python tiene un formato de impresión bonito que llama a algunas funciones que no son compatibles.

    • Puede modificar el contenido de tensor.py para omitir el formato de impresión.

    • O simplemente puede convertir los tensores Pytorch en matrices Numpy y luego imprimir.

Estado actual del proyecto:

  • La cobertura de la CPU es bastante buena.

    • Los núcleos se implementan dentro de PyTorch en 'aten/src/ATen/native/cpu/ </li> <li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h

    • La aceleración de Intel AVX256 está en 'aten/src/ATen/cpu/vec256/`



      • @sunilkpai : No conocía la optimización de exp. Esta es la carpeta donde agregas eso.


      • Avísame si te sientes cómodo haciendo el cambio.



  • La cobertura de GPU se limita a operaciones binarias y unarias:

    • Los núcleos se implementan dentro de PyTorch en 'aten/src/ATen/native/cuda/* </li> <li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh

    • Se utilizan los tipos de datos thrust::complex<T> e incluyen los núcleos optimizados.

El desarrollo actual:

  • Esperando a que los núcleos TH basados ​​en C se transfieran a la carpeta ATen de C++.

    • La función rand() es necesaria para trasladar los casos de prueba a las pruebas internas de pytorch.

    • Algunas operaciones de indexación actualmente no están portadas.

    • Actualmente hay 168/1300 núcleos matemáticos (frente a los 230 de octubre) que deben transferirse de TH a ATen.

  • Intentaré agregar compatibilidad con números complejos a medida que estos núcleos estén disponibles en ATen.

--

para tu información Con respecto a los derivados complejos, tuvimos una larga discusión en Julia y su implementación ahora está en ChainRules (ver también: http://www.juliadiff.org/ChainRules.jl/dev/api.html#ChainRulesCore.Wirtinger) y Zygote ahora . En general, las personas solo necesitan
\partial L/\partial adjoint(z) como el gradiente (por definición, es la dirección de disminución más rápida), pero la derivada es diferente a \partial L/\partial z , se debe agregar una interfaz adicional, si queremos un soporte completo para el número complejo AD . Para conocer las reglas detalladas, puede verificar qué se implementa en ChainRules o Zygote/lib (dado que solo hay reglas genéricas, no hay reglas separadas para números complejos para la mayoría de los operadores, pase hacia atrás para cosas como matmul están escritos en una definición genérica, por ejemplo adjoint(A) * B )

¿Por qué no puedo imprimir un tensor complejo en la consola?
El objeto Tensor python tiene un formato de impresión bonito que llama a algunas funciones que no son compatibles.
Puede modificar el contenido de tensor.py para omitir el formato de impresión.
O simplemente puede convertir los tensores Pytorch en matrices Numpy y luego imprimir.

Creo que arreglé al menos parte de la impresión en https://github.com/Roger-luo/pytorch-complex para la depuración, etc. en primer lugar, no estoy seguro de si esto ayudaría ya que el maestro ha cambiado mucho en el pasado año. Puedes tomarlo si es útil, no voy a trabajar más en esto.

@dylanbespalko Soy relativamente inexperto con las partes internas de pytorch, ¡aunque he comenzado a aprender! Posiblemente podría intentar este cambio, aunque según lo que veo en aten/src/ATen/cpu/vec256/* , no estoy seguro de si es necesario dado que el comportamiento predeterminado de std::exp(std::complex) es exactamente lo que mencioné. en mi comentario anterior: vea las notas de https://en.cppreference.com/w/cpp/numeric/complex/exp . Tampoco estoy seguro de cómo se traduce esto en la implementación de estas operaciones en CUDA (que actualmente parece estar limitado a real, imag, conj y angle).

@sunilkpai ,

He agregado el soporte AVX para exp() usando la ecuación provista.

También noté que algunas cosas se rompieron debido a algunos cambios recientes en PyTorch. Los he arreglado en #30871.

@dylanbespalko

¿Existe un cronograma para la migración de TH a ATen?
¿Hay alguna manera en que pueda contribuir, dado que no estoy bien versado en el funcionamiento interno de pytorch?

Encontré una fórmula para la retropropagación del complejo svd en el arxiv y podría implementarla, si me muestran dónde

¡Gracias por tu trabajo!

@Jakob-Unfried

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

Los núcleos TH se implementan en C y hay poco interés en agregar soporte complejo allí debido a todos los problemas inherentes de conteo de referencias. Puede seguir el progreso en aten/src/ATen/native/native_functions.yaml donde se registra cada kernel:

Busque legacy::cpu::_th y divida ese número por 3 para obtener el número de núcleos TH antiguos.
Busque legacy::cpu::_thnn y divida ese número por 3 para obtener la cantidad de núcleos de red neuronal TH antiguos.

Cada núcleo se registra típicamente de 3 maneras diferentes:
1. Núcleo regular y = agregar (a, b)
2. Kernel en el lugar a = add_(a, b)
3. Núcleo de salida add_out(a, b, out=y)
La implementación real siempre está en el Kernel de salida y los otros 2 llaman a esa función.

Los núcleos nn tienden a ser más fáciles de portar porque tienen menos núcleos dependientes. Por lo tanto, si puede portar los núcleos en el orden inverso al que fueron implementados, entonces hará menos trabajo en general.

Comprobación del problema de seguimiento de la portabilidad https://github.com/pytorch/pytorch/issues/24507 , también cc @VitalyFedyunin

Aquí hay una actualización de estado sobre el soporte de números complejos según lo solicitado en #32437. Estoy de vuelta trabajando en el soporte relacionado con la CPU hoy.

Soporte de autograduación

  • No he tenido mucho tiempo para esto.
  • Se implementaron angle() , real() , imag() , conj() .
  • abs() requerirá una implementación separada para números complejos. (ver notas de @boeddeker y @Randl arriba)

soporte de hardware

El soporte de números complejos se implementa actualmente fuera del árbol. Esto es lo que eso significa:

Código en el árbol

  • Las operaciones matemáticas en realidad se implementan en el árbol (dentro del código fuente de PyTorch).
  • Ninguna de las pruebas de Pytorch en el árbol valida el soporte de números complejos (por lo que las cosas tienden a fallar).
  • PyTorch está migrando de TH a ATen (#24507).
    - Los núcleos matemáticos implementados en TH no admiten números complejos.
    - Solo los núcleos implementados en ATen pueden admitir números complejos.
  • Debe instalar una extensión PyTorch para habilitar el dtype complejo.

Código fuera del árbol

  • Se implementan varias extensiones de PyTorch y se pueden internalizar fácilmente en el futuro:
  • Cada extensión tiene cuatro archivos de importancia:

    • setup.py: crea e instala la extensión pytorch.

    • [CPU/CUDA/FPGA]ComplexType.cpp: registro del kernel matemático similar a CPUType.cpp o CUDAType.cpp

    • test/test_torch.py: Casos de prueba muy desagradables que indican qué núcleos están funcionando, limitados por el soporte de ATen.

    • test/test_autograd.py: Probando la funcionalidad de autogradación.

Extensiones de PyTorch fuera del árbol

  1. cpu_strided_complex
    - [x] Copia del núcleo
    - [ ] TensorFactories (th_random, th_uniforme, th_normal)
    - [x] RangoFábricas
    - [x] BinaryOpKernals
    - [x] UnaryOpKernels
    - [x] CompararOpKernels
    - [x] PowKernels
    - [x] ReduceOpKernels (th_min, th_max, algunas normas no calculan conjugado complejo)
    - [ ] IndexOpKernels (th_masked_select_bool, th_set, th_gather, th_cat)
    - [x] PointwiseOps
    - [x] Operaciones Lerp
    - [ ] BlasOps (th_mv, th_mm, th_fmod, th_eig)
    - [ ] LinpackOps (usa Blas)
    - [ ] SpectralOps (compatible, pero necesita trabajo)

    1. cuda_strided_complex



      • [x] Copiar núcleo


      • [ ] TensorFactories (ver problemas de CPU)


      • [x] BinaryOpKernals


      • [x] UnaryOpKernels (Todo: agregar ángulo, real, imag, conj)


      • [ ] CompareOpKernels (Todo)


      • [ ] ReduceOpKernels (mensajes de error relacionados con WARP_SIZE)


      • La GPU se vuelve difícil más allá de los cálculos puntuales, sin embargo, se pueden admitir funciones adicionales



  2. vitis_strided_complex

    • Xilinx Vitis es una plataforma de síntesis de alto nivel de FPGA que admite servidores y dispositivos integrados.

    • Lanzado en octubre de 2019 (probable compatibilidad limitada para dispositivos).

    • Combina SDAccel (servidor) con VIvado HLS (integrado).

    • [ ] BinaryOpKernals (finales de enero)

    • [ ] UnaryOpKernels (finales de enero)

    • [ ] ReduceOpKernels (finales de febrero).

    • La FPGA agregó recientemente objetos vectorizados compatibles con la clase de plantilla Vec256 PyTorch

    • Vec256 fue cómo se agregó soporte complejo a la CPU y parece ser la forma más natural de implementar espacios tensoriales $C$ o $R^N$

Más actualizaciones por venir sobre este problema: https://github.com/pytorch/pytorch/issues/33152

Esto puede o no merecer un tema aparte, pero agradecería ver y pensar que actualmente es prácticamente más importante tener en la documentación algo que explique 'cómo funciona pytorch con números complejos en este momento'. también conocido como suma, multiplicación, algún tipo de norma, no puede tener pesos complejos, etc. todo lo cual se puede resumir en unas pocas líneas de documentación que explican cuál es el comportamiento actual previsto de alto nivel.

Esto puede o no merecer un tema aparte, pero agradecería ver y pensar que actualmente es prácticamente más importante tener en la documentación algo que explique 'cómo funciona pytorch con números complejos en este momento'. también conocido como suma, multiplicación, algún tipo de norma, no puede tener pesos complejos, etc. todo lo cual se puede resumir en unas pocas líneas de documentación que explican cuál es el comportamiento actual previsto de alto nivel.

Hola @redwrasse gracias por los comentarios! actualmente tenemos una nota para números complejos que habla sobre algunos fundamentos de antorcha y funciones complejas admitidas para tensores complejos en maestro
(la mayoría de los cuales están incluidos en la versión 1.6) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex. ¿Puedes compartir qué otras funciones te interesan? feliz de hablar más sobre nuestro soporte actual y cuál es el plan para los próximos lanzamientos.

Esto puede o no merecer un tema aparte, pero agradecería ver y pensar que actualmente es prácticamente más importante tener en la documentación algo que explique 'cómo funciona pytorch con números complejos en este momento'. también conocido como suma, multiplicación, algún tipo de norma, no puede tener pesos complejos, etc. todo lo cual se puede resumir en unas pocas líneas de documentación que explican cuál es el comportamiento actual previsto de alto nivel.

Hola @redwrasse gracias por los comentarios! actualmente tenemos una nota para números complejos que habla sobre algunos fundamentos de antorcha y funciones complejas admitidas para tensores complejos en maestro
(la mayoría de los cuales están incluidos en la versión 1.6) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex. ¿Puedes compartir qué otras funciones te interesan? feliz de hablar más sobre nuestro soporte actual y cuál es el plan para los próximos lanzamientos.

Gracias @anjali411 , es genial ver esta documentación, no la conocía anteriormente. Creo que lo que se necesita es al frente y al centro unas pocas líneas 'estado actual de soporte para redes neuronales complejas', pero déjame repasarlo...

Las personas que estén interesadas en autogradación compleja, pueden estar interesados ​​en https://github.com/pytorch/pytorch/issues/41857 que trata sobre qué convención seguirá PyTorch (JAX o TF).

¿Fue útil esta página
0 / 5 - 0 calificaciones