Posit AI Weblog: antorcha fuera de la caja



Para bien o para mal, vivimos en un mundo en constante cambio. Centrándose en el mejor, un ejemplo destacado es la abundancia, así como la rápida evolución del software program que nos ayuda a lograr nuestros objetivos. Sin embargo, con esa bendición viene un desafío. Tenemos que ser capaces de realmente usar esas nuevas características, instale esa nueva biblioteca, integre esa técnica novedosa en nuestro paquete.

Con torch, hay tantas cosas que podemos lograr tal como están, solo una pequeña fracción de las cuales se ha insinuado en este weblog. Pero si hay algo de lo que estar seguro, es que nunca, nunca, habrá una falta de demanda de más cosas que hacer. Aquí hay tres escenarios que vienen a la mente.

  • cargue un modelo previamente entrenado que se haya definido en Python (sin tener que portar manualmente todo el código)

  • modificar un módulo de crimson neuronal, para incorporar algún refinamiento algorítmico novedoso (sin incurrir en el costo de rendimiento de ejecutar el código personalizado en R)

  • hacer uso de una de las muchas bibliotecas de extensión disponibles en el ecosistema PyTorch (con el menor esfuerzo de codificación posible)

Esta publicación ilustrará cada uno de estos casos de uso en orden. Desde un punto de vista práctico, esto constituye un paso gradual de la perspectiva del usuario a la del desarrollador. Pero detrás de escena, son realmente los mismos componentes básicos que los impulsan a todos.

Facilitadores: torchexport y Torchscript

El paquete R torchexport y (del lado de PyTorch) TorchScript operan en escalas muy diferentes y juegan roles muy diferentes. Sin embargo, ambos son importantes en este contexto, e incluso diría que el actor de “menor escala” (torchexport) es el componente verdaderamente esencial, desde el punto de vista de un usuario de R. En parte, eso se debe a que figura en los tres escenarios, mientras que TorchScript solo está involucrado en el primero.

torchexport: administra la “pila de tipos” y se ocupa de los errores

En R torch, la profundidad de la “pila de tipos” es vertiginosa. El código orientado al usuario está escrito en R; la funcionalidad de bajo nivel está empaquetada en libtorchuna biblioteca compartida de C++ en la que confían torch así como PyTorch. El mediador, como suele ser el caso, es Rcpp. Sin embargo, ahí no es donde termina la historia. Debido a las incompatibilidades del compilador específico del sistema operativo, tiene que haber una capa intermedia adicional que actúe bidireccionalmente que elimine todos los tipos de C++ en un lado del puente (Rcpp o libtorch, resp.), dejando solo punteros de memoria sin procesar, y los vuelve a agregar en el otro. Al last, lo que resulta es una pila de llamadas bastante compleja. Como puede imaginar, existe una necesidad concomitante de un manejo de errores de nivel adecuado y cuidadosamente ubicado, asegurándose de que al last se le presente al usuario información útil.

Ahora, lo que vale para torch se aplica a cada extensión del lado R que agrega código personalizado o llama a bibliotecas C++ externas. Aquí es donde torchexport entra. Como autor de una extensión, todo lo que necesita hacer es escribir una pequeña fracción del código requerido en normal; el resto será generado por torchexport. Volveremos a esto en los escenarios dos y tres.

TorchScript: Permite la generación de código “sobre la marcha”

Ya nos hemos encontrado con TorchScript en un publicación anterior, aunque desde un ángulo diferente, y destacando un conjunto diferente de términos. En esa publicación, mostramos cómo puedes entrenar un modelo en R y rastro lo que da como resultado una representación intermedia optimizada que luego se puede guardar y cargar en un entorno diferente (posiblemente sin R). Allí, el enfoque conceptual estaba en el agente que habilita este flujo de trabajo: el compilador Simply-in-time (JIT) de PyTorch que genera la representación en cuestión. Rápidamente mencionamos que en el lado de Python, hay otra forma de invocar el JIT: no en un modelo “vivo” instanciado, sino en código de definición de modelo con secuencias de comandos. Es esa segunda vía, en consecuencia denominada secuencias de comandosque es relevante en el contexto precise.

Aunque la creación de scripts no está disponible en R (a menos que el código del script esté escrito en Python), aún nos beneficiamos de su existencia. Cuando las bibliotecas de extensión del lado de Python usan TorchScript (en lugar del código regular de C++), no necesitamos agregar enlaces a las funciones respectivas en el lado R (C++). En cambio, PyTorch se encarga de todo.

Esto, aunque completamente transparente para el usuario, es lo que permite el escenario uno. En (Python) TorchVision, los modelos previamente entrenados proporcionados a menudo harán uso de operadores especiales (dependientes del modelo). Gracias a que se han creado scripts, no necesitamos agregar un enlace para cada operador, y mucho menos volver a implementarlos en el lado R.

Habiendo esbozado algunas de las funciones subyacentes, ahora presentamos los escenarios en sí.

Escenario uno: Cargue un modelo preentrenado de TorchVision

Tal vez ya haya utilizado uno de los modelos preentrenados que TorchVision pone a su disposición: un subconjunto de estos se ha portado manualmente a torchvision, el paquete R. Pero hay más de ellos: un lote más. Muchos usan operadores especializados, que rara vez se necesitan fuera del contexto de algún algoritmo. Parecería ser de poca utilidad crear contenedores R para esos operadores. Y, por supuesto, la aparición continua de nuevos modelos requeriría esfuerzos continuos de portabilidad, de nuestra parte.

Por suerte, existe una solución elegante y eficaz. Toda la infraestructura necesaria está configurada por el paquete delgado y dedicado torchvisionlib. (Puede darse el lujo de ser esbelto debido al uso liberal de TorchScript por parte de Python, como se explicó en la sección anterior. Pero para el usuario, cuya perspectiva estoy tomando en este escenario, estos detalles no necesitan importar).

Una vez que haya instalado y cargado torchvisionlibpuede elegir entre un número impresionante de modelos relacionados con el reconocimiento de imágenes. El proceso, entonces, es doble:

  1. Creas una instancia del modelo en Python, guion y guárdelo.

  2. Cargas y usas el modelo en R.

Aquí está el primer paso. Tenga en cuenta cómo, antes de la secuencia de comandos, ponemos el modelo en eval modo, asegurándose así de que todas las capas muestren un comportamiento de tiempo de inferencia.

library(torchvisionlib)

mannequin <- torch::jit_load("fcn_resnet50.pt")

En este punto, puede usar el modelo para obtener predicciones o incluso integrarlo como un bloque de construcción en una arquitectura más grande.

Escenario dos: implementar un módulo personalizado

¿No sería maravilloso si todos los algoritmos nuevos y bien recibidos, todas las variantes novedosas prometedoras de un tipo de capa o, mejor aún, el algoritmo que tiene en mente para revelar al mundo en su próximo artículo ya estuvieran implementados en torch?

Bien quizás; pero tal vez no La solución mucho más sostenible es hacer que sea razonablemente fácil extender torch en paquetes pequeños y dedicados que tienen un propósito claro y son rápidos de instalar. El paquete proporciona un recorrido detallado y práctico del proceso. lltm. Este paquete tiene un toque recursivo. Al mismo tiempo, es una instancia de C++ torch extensión, y sirve como un tutorial que muestra cómo crear dicha extensión.

El propio README explica cómo se debe estructurar el código y por qué. Si estás interesado en cómo torch en sí mismo ha sido diseñado, esta es una lectura esclarecedora, independientemente de si planea o no escribir una extensión. Además de ese tipo de información detrás de escena, el LÉAME tiene instrucciones paso a paso sobre cómo proceder en la práctica. De acuerdo con el propósito del paquete, el código fuente también está ricamente documentado.

Como ya se insinuó en la sección “Facilitadores”, la razón por la que me atrevo a escribir “hazlo razonablemente fácil” (refiriéndose a la creación de un torch extensión) es torchexport, el paquete que genera automáticamente código C++ relacionado con la conversión y el manejo de errores en varias capas en la “pila de tipos”. Por lo normal, encontrará que la cantidad de código generado automáticamente supera significativamente la del código que escribió usted mismo.

Escenario tres: interfaz para las extensiones de PyTorch integradas en el código C++

Es todo menos inconceivable que, algún día, te encuentres con una extensión de PyTorch que desearías que estuviera disponible en R. En caso de que esa extensión estuviera escrita en Python (exclusivamente), la traducirías a R “a mano”, haciendo uso de cualquier funcionalidad aplicable torch proporciona. A veces, sin embargo, esa extensión contendrá una mezcla de código Python y C++. Luego, deberá vincularse a la funcionalidad de C++ de bajo nivel de una manera análoga a cómo torch se une a libtorch – y ahora, todos los requisitos de escritura descritos anteriormente se aplicarán a su extensión de la misma manera.

De nuevo, es torchexport que viene al rescate. Y aquí, también, la lltm README todavía se aplica; es solo que en lugar de escribir su código personalizado, agregará enlaces a funciones de C++ proporcionadas externamente. Hecho esto, tendrás torchexport crear todo el código de infraestructura requerido.

Una especie de plantilla se puede encontrar en el torchsparse paquete (actualmente en desarrollo). Las funciones en csrc/src/torchsparse.cpp todos llaman a PyTorch Escasocon declaraciones de funciones que se encuentran en ese proyecto csrc/disperso.h.

Una vez que esté integrando con el código C++ externo de esta manera, puede surgir una pregunta adicional. Tome un ejemplo de torchsparse. En el archivo de encabezado, notará tipos de retorno como std::tuple<torch::Tensor, torch::Tensor>, <torch::Tensor, torch::Tensor, <torch::non-obligatory<torch::Tensor>>, torch::Tensor>> … y más. En R torch (la capa de C++) tenemos torch::Tensory tenemos torch::non-obligatory<torch::Tensor>, también. Pero no tenemos un tipo personalizado para cada posible std::tuple podrías construir. Así como tener base torch proporcionar todo tipo de funcionalidad especializada y específica del dominio no es sostenible, tiene poco sentido que intente prever todo tipo de tipos que alguna vez estarán en demanda.

En consecuencia, los tipos deben definirse en los paquetes que los necesitan. Cómo hacer esto exactamente se explica en el torchexport Tipos personalizados viñeta. Cuando se utiliza un tipo personalizado de este tipo, torchexport necesita que se le diga cómo se deben nombrar los tipos generados, en varios niveles. Es por esto que en tales casos, en lugar de un escueto //((torch::export))verá líneas como / ((torch::export(register_types=c("tensor_pair", "TensorPair", "void*", "torchsparse::tensor_pair")))). La viñeta explica esto en detalle.

Que sigue

“Qué sigue” es una forma común de finalizar una publicación, reemplazando, por ejemplo, “Conclusión” o “Resumiendo”. Pero aquí, hay que tomarlo literalmente. Esperamos hacer todo lo posible para que el uso, la interfaz y la extensión torch tan sin esfuerzo como sea posible. Por lo tanto, infórmenos sobre cualquier dificultad que esté enfrentando o problemas en los que incurra. Solo crea un problema en antorchaexportación, lltm, antorchao cualquier repositorio que parezca aplicable.

¡Como siempre, gracias por leer!

Foto por Antonino Visalli en Unsplash

Related Articles

Cómo implementar Django en Heroku: un tutorial de Pydantic, parte 3

Esta es la tercera entrega de una serie sobre cómo...

Comments

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Same Category

spot_img

Stay in touch!

Follow our Instagram