Controladores de la API de Neural Networks

En esta página, se proporciona una descripción general sobre cómo implementar un controlador de la API de Neural Networks (NNAPI). Para obtener más detalles, consulta la documentación que se encuentra en los archivos de definición de HAL en hardware/interfaces/neuralnetworks. Puedes encontrar una implementación de controlador de ejemplo en frameworks/ml/nn/driver/sample.

Para obtener más información sobre la API de Neural Networks, consulta API de Neural Networks.

HAL de redes neuronales

La HAL de redes neuronales (NN) define una abstracción de los distintos dispositivos, como las unidades de procesamiento de gráficos (GPU) y los procesadores de señal digital (DSP), que se encuentran en un producto (por ejemplo, un teléfono o una tablet). Los controladores de estos dispositivos deben cumplir con el HAL de NN. La interfaz se especifica en los archivos de definición de la HAL en hardware/interfaces/neuralnetworks.

El flujo general de la interfaz entre el framework y un controlador se muestra en la Figura 1.

Flujo de redes neuronales

Figura 1: Flujo de redes neuronales

Inicialización

Durante la inicialización, el framework consulta al controlador sus capacidades con IDevice::getCapabilities_1_3. La estructura @1.3::Capabilities incluye todos los tipos de datos y representa el rendimiento no relajado con un vector.

Para determinar cómo asignar procesamientos a los dispositivos disponibles, el framework usa las capacidades para comprender qué tan rápido y con eficiencia energética cada controlador puede realizar una ejecución. Para proporcionar esta información, el controlador debe proporcionar números de rendimiento estandarizados basados en la ejecución de cargas de trabajo de referencia.

Para determinar los valores que muestra el controlador en respuesta a IDevice::getCapabilities_1_3, usa la app comparativa de la NNAPI para medir el rendimiento de los tipos de datos correspondientes. Se recomiendan los modelos asr_float y tts_float de MobileNet v1 y v2 para medir el rendimiento de los valores de punto flotante de 32 bits, y los modelos cuantificados de MobileNet v1 y v2 se recomiendan para valores cuantificados de 8 bits. Para obtener más información, consulta el Conjunto de pruebas de aprendizaje automático de Android.

En Android 9 y versiones anteriores, la estructura Capabilities incluye información de rendimiento del controlador solo para tensores quantizados y de punto flotante, y no incluye tipos de datos escalares.

Como parte del proceso de inicialización, el framework puede consultar más información con IDevice::getType, IDevice::getVersionString, IDevice:getSupportedExtensions y IDevice::getNumberOfCacheFilesNeeded.

Entre los reinicios del producto, el framework espera que todas las consultas que se describen en esta sección siempre informen los mismos valores para un controlador determinado. De lo contrario, una app que use ese controlador puede mostrar un rendimiento reducido o un comportamiento incorrecto.

Compilación

El framework determina qué dispositivos usar cuando recibe una solicitud de una app. En Android 10, las apps pueden descubrir y especificar los dispositivos de los que elige el framework. Para obtener más información, consulta Descubrimiento y asignación de dispositivos.

En el momento de la compilación del modelo, el framework envía el modelo a cada controlador candidato llamando a IDevice::getSupportedOperations_1_3. Cada controlador muestra un array de booleanos que indican qué operaciones del modelo son compatibles. Un controlador puede determinar que no puede admitir una operación determinada por varios motivos. Por ejemplo:

  • El controlador no admite el tipo de datos.
  • El controlador solo admite operaciones con parámetros de entrada específicos. Por ejemplo, un controlador puede admitir operaciones de convolución de 3x3 y 5x5, pero no de 7x7.
  • El controlador tiene restricciones de memoria que le impiden controlar gráficos o entradas grandes.

Durante la compilación, la entrada, la salida y los operandos internos del modelo, como se describe en OperandLifeTime, pueden tener dimensiones o clasificaciones desconocidas. Para obtener más información, consulta Forma de salida.

El framework le indica a cada controlador seleccionado que se prepare para ejecutar un subconjunto del modelo llamando a IDevice::prepareModel_1_3. Luego, cada controlador compila su subconjunto. Por ejemplo, un conductor podría generar un código o crear una copia reordenada de los pesos. Debido a que puede haber una cantidad significativa de tiempo entre la compilación del modelo y la ejecución de las solicitudes, no se deben asignar recursos, como grandes fragmentos de memoria del dispositivo, durante la compilación.

Si la operación es exitosa, el controlador mostrará un controlador @1.3::IPreparedModel. Si el controlador muestra un código de falla cuando prepara su subconjunto del modelo, el framework ejecuta el modelo completo en la CPU.

Para reducir el tiempo que se usa para la compilación cuando se inicia una app, un controlador puede almacenar en caché los artefactos de compilación. Para obtener más información, consulta Almacenamiento en caché de compilación.

Ejecución

Cuando una app le pide al framework que ejecute una solicitud, este llama al método HAL IPreparedModel::executeSynchronously_1_3 de forma predeterminada para realizar una ejecución síncrona en un modelo preparado. Una solicitud también se puede ejecutar de forma asíncrona con los métodos execute_1_3 o executeFenced (consulta Ejecución cercada), o con una ejecución en ráfaga.

Las llamadas de ejecución síncronas mejoran el rendimiento y reducen la sobrecarga de subprocesos en comparación con las llamadas asíncronas, ya que el control se devuelve al proceso de la app solo después de que se completa la ejecución. Esto significa que el controlador no necesita un mecanismo independiente para notificar al proceso de la app que se completó una ejecución.

Con el método execute_1_3 asíncrono, el control vuelve al proceso de la app después de que se inicia la ejecución, y el controlador debe notificar al framework cuando se completa la ejecución con @1.3::IExecutionCallback.

El parámetro Request que se pasa al método execute enumera los operandos de entrada y salida que se usan para la ejecución. La memoria que almacena los datos del operando debe usar el orden de filas principales, con la primera dimensión iterando la más lenta y sin relleno al final de ninguna fila. Para obtener más información sobre los tipos de operandos, consulta Operandos.

En el caso de los controladores NN HAL 1.2 o posteriores, cuando se completa una solicitud, el estado del error, la forma de salida y la información de tiempo se muestran al framework. Durante la ejecución, la salida o los operandos internos del modelo pueden tener una o más dimensiones o clasificaciones desconocidas. Cuando al menos un operando de salida tiene una dimensión o clasificación desconocida, el controlador debe mostrar información de salida de tamaño dinámico.

En el caso de los controladores con NN HAL 1.1 o anterior, solo se muestra el estado de error cuando se completa una solicitud. Las dimensiones de los operandos de entrada y salida deben especificarse en su totalidad para que la ejecución se complete correctamente. Los operandos internos pueden tener una o más dimensiones desconocidas, pero deben tener una clasificación especificada.

En el caso de las solicitudes del usuario que abarcan varios controladores, el framework es responsable de reservar la memoria intermedia y de secuenciar las llamadas a cada controlador.

Se pueden iniciar varias solicitudes en paralelo en el mismo @1.3::IPreparedModel. El controlador puede ejecutar solicitudes en paralelo o serializar las ejecuciones.

El framework puede pedirle al controlador que mantenga más de un modelo preparado. Por ejemplo, prepara el modelo m1, prepara m2, ejecuta la solicitud r1 en m1, ejecuta r2 en m2, ejecuta r3 en m1, ejecuta r4 en m2, libera (se describe en Limpieza) m1 y libera m2.

Para evitar una primera ejecución lenta que podría generar una experiencia del usuario deficiente (por ejemplo, un tartamudeo en el primer fotograma), el controlador debe realizar la mayoría de las inicializaciones en la fase de compilación. La inicialización en la primera ejecución debe limitarse a acciones que afectan negativamente el estado del sistema cuando se realizan antes, como reservar grandes búferes temporales o aumentar la frecuencia de reloj de un dispositivo. Es posible que los controladores que solo pueden preparar una cantidad limitada de modelos simultáneos deban realizar su inicialización en la primera ejecución.

En Android 10 o versiones posteriores, en los casos en que se ejecuten varias ejecuciones con el mismo modelo preparado en rápida sucesión, el cliente puede optar por usar un objeto de ráfaga de ejecución para comunicarse entre los procesos de la app y del controlador. Para obtener más información, consulta Ejecuciones en ráfaga y colas de mensajes rápidos.

Para mejorar el rendimiento de varias ejecuciones seguidas con rapidez, el controlador puede retener los búferes temporales o aumentar la frecuencia del reloj. Se recomienda crear un subproceso de perro guardián para liberar recursos si no se crean solicitudes nuevas después de un período determinado.

Forma de la salida

Para las solicitudes en las que uno o más operandos de salida no tienen todas las dimensiones especificadas, el controlador debe proporcionar una lista de formas de salida que contengan la información de dimensión para cada operando de salida después de la ejecución. Para obtener más información sobre las dimensiones, consulta OutputShape.

Si una ejecución falla debido a un búfer de salida de poco tamaño, el controlador debe indicar qué operandos de salida no tienen suficiente tamaño de búfer en la lista de formas de salida, y debe informar tanta información dimensional como sea posible, con cero para las dimensiones desconocidas.

Tiempos

En Android 10, una app puede solicitar el tiempo de ejecución si especificó un solo dispositivo para usar durante el proceso de compilación. Para obtener más información, consulta MeasureTiming y Descubrimiento y asignación de dispositivos. En este caso, un controlador NN HAL 1.2 debe medir la duración de la ejecución o informar UINT64_MAX (para indicar que la duración no está disponible) cuando ejecuta una solicitud. El controlador debe minimizar cualquier penalización de rendimiento que resulte de medir la duración de la ejecución.

El controlador informa las siguientes duraciones en microsegundos en la estructura Timing:

  • Tiempo de ejecución en el dispositivo: No incluye el tiempo de ejecución en el controlador, que se ejecuta en el procesador del host.
  • Tiempo de ejecución en el controlador: Incluye el tiempo de ejecución en el dispositivo.

Estas duraciones deben incluir el tiempo en el que se suspende la ejecución, por ejemplo, cuando otras tareas la interrumpen o cuando está esperando que un recurso esté disponible.

Cuando no se le pidió al controlador que mida la duración de la ejecución o cuando hay un error de ejecución, el controlador debe informar las duraciones como UINT64_MAX. Incluso cuando se le solicita al controlador que mida la duración de la ejecución, puede informar UINT64_MAX para el tiempo en el dispositivo, el tiempo en el controlador o ambos. Cuando el controlador informa ambas duraciones como un valor distinto de UINT64_MAX, el tiempo de ejecución en el controlador debe ser igual o superior al tiempo en el dispositivo.

Ejecución cercada

En Android 11, NNAPI permite que las ejecuciones esperen una lista de controladores sync_fence y, de manera opcional, muestren un objeto sync_fence, que se señala cuando se completa la ejecución. Esto reduce la sobrecarga de modelos de secuencia pequeños y los casos de uso de transmisión. La ejecución cercada también mejora la eficiencia de la interoperabilidad con otros componentes que pueden indicar o esperar sync_fence. Para obtener más información sobre sync_fence, consulta Framework de sincronización.

En una ejecución cercada, el framework llama al método IPreparedModel::executeFenced para iniciar una ejecución vallada y asíncrona en un modelo preparado con un vector de vallas de sincronización que esperar. Si la tarea asíncrona finaliza antes de que se muestre la llamada, se puede mostrar un controlador vacío para sync_fence. También se debe mostrar un objeto IFencedExecutionCallback para permitir que el framework consulte el estado del error y la información sobre la duración.

Una vez que se completa una ejecución, se pueden consultar los siguientes dos valores de tiempo que miden la duración de la ejecución a través de IFencedExecutionCallback::getExecutionInfo.

  • timingLaunched: Es la duración desde el momento en que se llama a executeFenced hasta el momento en que executeFenced indica el syncFence que se muestra.
  • timingFenced: Duración desde el momento en que se indican todos los cierres de sincronización que espera la ejecución hasta que executeFenced indica el syncFence que se muestra.

Flujo de control

En el caso de los dispositivos que ejecutan Android 11 o versiones posteriores, NNAPI incluye dos operaciones de flujo de control, IF y WHILE, que toman otros modelos como argumentos y los ejecutan de forma condicional (IF) o repetida (WHILE). Para obtener más información sobre cómo implementar esto, consulta Flujo de control.

Calidad de servicio

En Android 11, la NNAPI incluye una calidad de servicio (QoS) mejorada, ya que permite que una app indique las prioridades relativas de sus modelos, el tiempo de espera máximo para que se prepare un modelo y el tiempo de espera máximo para que se complete una ejecución. Para obtener más información, consulta Calidad de servicio.

Limpieza

Cuando una app termina de usar un modelo preparado, el framework actualiza su referencia al objeto @1.3::IPreparedModel. Cuando ya no se hace referencia al objeto IPreparedModel, se destruye automáticamente en el servicio de controlador que lo creó. En este momento, se pueden reclamar recursos específicos del modelo en la implementación del destructor del controlador. Si el servicio del controlador desea que el objeto IPreparedModel se destruya automáticamente cuando el cliente ya no lo necesita, no debe contener ninguna referencia al objeto IPreparedModel después de que se muestre el objeto IPreparedeModel a través de IPreparedModelCallback::notify_1_3.

Uso de CPU

Se espera que los controladores usen la CPU para configurar los procesamientos. Los controladores no deben usar la CPU para realizar cálculos de grafos, ya que eso interfiere con la capacidad del framework para asignar el trabajo correctamente. El controlador debe informar las partes que no puede controlar al framework y dejar que este se encargue del resto.

El framework proporciona una implementación de CPU para todas las operaciones de la NNAPI, excepto las que define el proveedor. Para obtener más información, consulta Extensiones de proveedores.

Las operaciones que se introdujeron en Android 10 (nivel de API 29) solo tienen una implementación de CPU de referencia para verificar que las pruebas de CTS y VTS sean correctas. Se prefieren las implementaciones optimizadas incluidas en los frameworks de aprendizaje automático para dispositivos móviles en lugar de la implementación de la CPU de NNAPI.

Funciones de utilidad

La base de código de NNAPI incluye funciones de utilidad que pueden usar los servicios de controladores.

El archivo frameworks/ml/nn/common/include/Utils.h contiene varias funciones de utilidad, como las que se usan para el registro y la conversión entre diferentes versiones de HAL de NN.

  • VLogging: VLOG es una macro wrapper alrededor del LOG de Android que solo registra el mensaje si se configura la etiqueta adecuada en la propiedad debug.nn.vlog. Se debe llamar a initVLogMask() antes de cualquier llamada a VLOG. La macro VLOG_IS_ON se puede usar para verificar si VLOG está habilitada actualmente, lo que permite omitir el código de registro complicado si no es necesario. El valor de la propiedad debe ser uno de los siguientes:

    • Es una cadena vacía que indica que no se debe realizar ningún registro.
    • El token 1 o all, que indica que todo el registro se debe realizar.
    • Una lista de etiquetas, delimitadas por espacios, comas o dos puntos, que indican qué registro se debe realizar. Las etiquetas son compilation, cpuexe, driver, execution, manager y model.
  • compliantWithV1_*: Muestra true si un objeto NN HAL se puede convertir al mismo tipo de una versión de HAL diferente sin perder información. Por ejemplo, llamar a compliantWithV1_0 en un V1_2::Model muestra false si el modelo incluye tipos de operaciones que se introdujeron en NN HAL 1.1 o NN HAL 1.2.

  • convertToV1_*: Convierte un objeto NN HAL de una versión a otra. Se registra una advertencia si la conversión provoca una pérdida de información (es decir, si la versión nueva del tipo no puede representar por completo el valor).

  • Funciones: Las funciones nonExtensionOperandPerformance y update se pueden usar para ayudar a compilar el campo Capabilities::operandPerformance.

  • Consultar propiedades de tipos: isExtensionOperandType, isExtensionOperationType, nonExtensionSizeOfData, nonExtensionOperandSizeOfData, nonExtensionOperandTypeIsScalar y tensorHasUnspecifiedDimensions

El archivo frameworks/ml/nn/common/include/ValidateHal.h contiene funciones de utilidad para validar que un objeto NN HAL es válido según la especificación de su versión de HAL.

  • validate*: Muestra true si el objeto NN HAL es válido según la especificación de su versión de HAL. Los tipos de OEM y de extensión no se validan. Por ejemplo, validateModel muestra false si el modelo contiene una operación que hace referencia a un índice de operando que no existe o una operación que no es compatible con esa versión de HAL.

El archivo frameworks/ml/nn/common/include/Tracing.h contiene macros para simplificar la adición de información de seguimiento del sistema al código de redes neuronales. Para ver un ejemplo, consulta las invocaciones de macro NNTRACE_* en el controlador de muestra.

El archivo frameworks/ml/nn/common/include/GraphDump.h contiene una función de utilidad para volcar el contenido de un Model en forma gráfica con fines de depuración.

  • graphDump: Escribe una representación del modelo en formato Graphviz (.dot) en la transmisión especificada (si se proporciona) o en el logcat (si no se proporciona ninguna transmisión).

Validación

Para probar tu implementación de NNAPI, usa las pruebas de VTS y CTS incluidas en el framework de Android. El VTS ejecuta los controladores directamente (sin usar el framework), mientras que el CTS los ejercita de forma indirecta a través del framework. Estos prueban cada método de la API y verifican que todas las operaciones compatibles con los controladores funcionen correctamente y proporcionen resultados que cumplan con los requisitos de precisión.

Los requisitos de precisión en CTS y VTS para NNAPI son los siguientes:

  • Punto flotante: abs(expected - actual) <= atol + rtol  * abs(expected); en el que:

    • Para fp32, atol = 1e-5f, rtol = 5.0f * 1.1920928955078125e-7
    • Para fp16, atol = rtol = 5.0f * 0.0009765625f
  • Cuantizada: ordenada por una (excepto por mobilenet_quantized, que tiene una diferencia de tres)

  • Booleano: concordancia exacta

Una forma en que el CTS prueba la NNAPI es generar gráficos pseudoaleatorios fijos que se usan para probar y comparar los resultados de ejecución de cada controlador con la implementación de referencia de la NNAPI. En el caso de los controladores con NN HAL 1.2 o versiones posteriores, si los resultados no cumplen con los criterios de precisión, CTS informa un error y volca un archivo de especificación para el modelo con errores en /data/local/tmp para la depuración. Para obtener más detalles sobre los criterios de precisión, consulta TestRandomGraph.cpp y TestHarness.h.

Prueba de Fuzz

El propósito de las pruebas de fuzz es encontrar fallas, aserciones, violaciones de memoria o un comportamiento general no definido en el código en prueba debido a factores como entradas inesperadas. Para las pruebas de fuzzing de la NNAPI, Android usa pruebas basadas en libFuzzer, que son eficaces en el fuzzing porque usan la cobertura de línea de casos de prueba anteriores para generar nuevas entradas aleatorias. Por ejemplo, libFuzzer favorece los casos de prueba que se ejecutan en líneas de código nuevas. Esto reduce mucho el tiempo que tardan las pruebas en encontrar el código problemático.

Para realizar pruebas de fuzzing a fin de validar la implementación de tu controlador, modifica frameworks/ml/nn/runtime/test/android_fuzzing/DriverFuzzTest.cpp en la utilidad de prueba libneuralnetworks_driver_fuzzer que se encuentra en el AOSP para incluir el código de tu controlador. Para obtener más información sobre las pruebas de fuzz de NNAPI, consulta frameworks/ml/nn/runtime/test/android_fuzzing/README.md.

Seguridad

Debido a que los procesos de la app se comunican directamente con el proceso de un controlador, estos deben validar los argumentos de las llamadas que reciben. VTS verifica esta validación. El código de validación se encuentra en frameworks/ml/nn/common/include/ValidateHal.h.

Los controladores también deben garantizar que las apps no puedan interferir con otras cuando se usa el mismo dispositivo.

Paquete de pruebas de aprendizaje automático de Android

El Conjunto de pruebas de aprendizaje automático (MLTS) de Android es una comparativa de la NNAPI incluida en los CTS y el VTS para validar la precisión de los modelos reales en dispositivos de proveedores. Las comparativas evalúan la latencia y la precisión, y comparan los resultados de los controladores con los resultados que se obtienen con TF Lite que se ejecuta en la CPU, para el mismo modelo y conjuntos de datos. Esto garantiza que la precisión de un controlador no sea peor que la implementación de referencia de la CPU.

Los desarrolladores de la plataforma de Android también usan MLTS para evaluar la latencia y precisión de los controladores.

Las comparativas de la NNAPI se pueden encontrar en dos proyectos en AOSP:

Modelos y conjuntos de datos

Las comparativas de la NNAPI usan los siguientes modelos y conjuntos de datos.

  • Los números de punto flotante y u8 de MobileNetV1 se cuantizan en diferentes tamaños y se ejecutan en un subconjunto pequeño (1, 500 imágenes) de Open Images Dataset v4.
  • MobileNetV2 con números de punto flotante y u8 cuantificados en diferentes tamaños se ejecuta en un subconjunto pequeño (1,500 imágenes) del conjunto de datos de Open Images v4.
  • Modelo acústico basado en la memoria a largo plazo (LSTM) para la conversión de texto a voz, que se ejecuta en un subconjunto pequeño del conjunto CMU Arctic.
  • Modelo acústico basado en LSTM para el reconocimiento de voz automático, que se ejecuta en un subconjunto pequeño del conjunto de datos de LibriSpeech.

Para obtener más información, consulta platform/test/mlts/models.

Pruebas de esfuerzo

El paquete de pruebas de aprendizaje automático de Android incluye una serie de pruebas de fallas para validar la resiliencia de los controladores en condiciones de uso intensivo o en casos excepcionales de comportamiento de los clientes.

Todas las pruebas de fallas proporcionan las siguientes funciones:

  • Detección de bloqueo: Si se bloquea el cliente NNAPI durante una prueba, esta última fallará con el motivo de falla HANG, y el conjunto de pruebas pasará a la siguiente prueba.
  • Detección de fallas del cliente de la NNAPI: Las pruebas sobreviven a las fallas del cliente y las pruebas fallan con el motivo de falla CRASH.
  • Detección de fallas del controlador: Las pruebas pueden detectar una falla del controlador que provoca una falla en una llamada a la NNAPI. Ten en cuenta que puede haber fallas en los procesos del controlador que no provoquen fallas en la NNAPI ni hagan que la prueba falle. Para cubrir este tipo de fallas, se recomienda ejecutar el comando tail en el registro del sistema para detectar errores o fallas relacionados con el controlador.
  • Segmentación de todos los aceleradores disponibles: Las pruebas se ejecutan en todos los controladores disponibles.

Todas las pruebas de falla tienen los siguientes cuatro resultados posibles:

  • SUCCESS: La ejecución se completó sin errores.
  • FAILURE: Se produjo un error en la ejecución. Por lo general, se produce por una falla cuando se prueba un modelo, lo que indica que el controlador no pudo compilar ni ejecutar el modelo.
  • HANG: El proceso de prueba dejó de responder.
  • CRASH: Se produjo una falla en el proceso de prueba.

Para obtener más información sobre las pruebas de esfuerzo y una lista completa de las pruebas de fallas, consulta platform/test/mlts/benchmark/README.txt.

Usa MLTS

Para usar la MLTS, haz lo siguiente:

  1. Conecta un dispositivo de destino a tu estación de trabajo y asegúrate de que se pueda acceder a él a través de adb. Exporta la variable de entorno ANDROID_SERIAL del dispositivo de destino si hay más de un dispositivo conectado.
  2. cd al directorio del código fuente de nivel superior de Android.

    source build/envsetup.sh
    lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available.
    ./test/mlts/benchmark/build_and_run_benchmark.sh
    

    Cuando finaliza la ejecución de la comparativa, los resultados se presentan como una página HTML y se pasan a xdg-open.

Para obtener más información, consulta platform/test/mlts/benchmark/README.txt.

Versiones de HAL de redes neuronales

En esta sección, se describen los cambios introducidos en las versiones de HAL de Android y redes neuronales.

Android 11

Android 11 presenta el NN HAL 1.3, que incluye los siguientes cambios notables.

  • Compatibilidad con la cuantización de 8 bits con firma en la NNAPI. Agrega el tipo de operando TENSOR_QUANT8_ASYMM_SIGNED. Los controladores con el HAL de NN 1.3 que admiten operaciones con cuantificación sin firmar también deben admitir las variantes firmadas de esas operaciones. Cuando se ejecutan versiones firmadas y sin firmar de la mayoría de las operaciones cuantificadas, los controladores deben producir los mismos resultados hasta un desplazamiento de 128. Existen cinco excepciones a este requisito: CAST, HASHTABLE_LOOKUP, LSH_PROJECTION, PAD_V2 y QUANTIZED_16BIT_LSTM. La operación QUANTIZED_16BIT_LSTM no admite operandos con signo, y las otras cuatro operaciones admiten la cuantización con firma, pero no requiere que los resultados sean los mismos.
  • Compatibilidad con ejecuciones con barreras en las que el framework llama al método IPreparedModel::executeFenced para iniciar una ejecución asíncrona con barreras en un modelo preparado con un vector de barreras de sincronización para esperar. Para obtener más información, consulta Ejecución protegida.
  • Compatibilidad con el flujo de control Agrega las operaciones IF y WHILE, que toman otros modelos como argumentos y los ejecutan de forma condicional (IF) o repetida (WHILE). Para obtener más información, consulta Flujo de control.
  • Una calidad de servicio (QoS) mejorada, ya que las apps pueden indicar las prioridades relativas de sus modelos, la cantidad máxima de tiempo que se espera para que un modelo esté preparado y el tiempo máximo esperado para que se complete una ejecución Para obtener más información, consulta Calidad del servicio.
  • Compatibilidad con dominios de memoria que proporcionan interfaces asignables para búferes administrados por controladores. Esto permite pasar memorias nativas del dispositivo entre ejecuciones, lo que evita la transformación y la copia innecesarias de datos entre ejecuciones consecutivas en el mismo controlador. Para obtener más información, consulta Dominios de memoria.

Android 10

Android 10 presenta NN HAL 1.2, que incluye los siguientes cambios notables:

  • La estructura Capabilities incluye todos los tipos de datos, incluidos los tipos de datos escalares, y representa el rendimiento no relajado con un vector en lugar de campos nombrados.
  • Los métodos getVersionString y getType permiten que el framework recupere el tipo de dispositivo (DeviceType) y la información de la versión. Consulta Detección y asignación de dispositivos.
  • Se llama al método executeSynchronously de forma predeterminada para realizar una ejecución de forma síncrona. El método execute_1_2 le indica al framework que realice una ejecución de forma asíncrona. Consulta Ejecución.
  • El parámetro MeasureTiming para executeSynchronously, execute_1_2 y la ejecución en ráfaga especifica si el controlador debe medir la duración de la ejecución. Los resultados se informan en la estructura Timing. Consulta Tiempo.
  • Compatibilidad con ejecuciones en las que uno o más operandos de salida tienen una dimensión o clasificación desconocida. Consulta Forma de salida.
  • Compatibilidad con extensiones de proveedor, que son colecciones de operaciones y tipos de datos definidos por el proveedor. El controlador informa las extensiones compatibles a través del método IDevice::getSupportedExtensions. Consulta Extensiones de proveedores.
  • Es la capacidad de un objeto de ráfaga para controlar un conjunto de ejecuciones de ráfaga con colas de mensajes rápidas (FMQ) para comunicarse entre los procesos de la app y el controlador, lo que reduce la latencia. Consulta Ejecuciones en ráfaga y colas de mensajes rápidos.
  • Compatibilidad con AHardwareBuffer para permitir que el controlador realice ejecuciones sin copiar datos. Consulta AHardwareBuffer.
  • Se mejoró la compatibilidad con el almacenamiento en caché de artefactos de compilación para reducir el tiempo que se usa para la compilación cuando se inicia una app. Consulta Almacenamiento en caché de compilación.

Android 10 presenta los siguientes tipos de operandos y operaciones.

  • Tipos de operandos

    • ANEURALNETWORKS_BOOL
    • ANEURALNETWORKS_FLOAT16
    • ANEURALNETWORKS_TENSOR_BOOL8
    • ANEURALNETWORKS_TENSOR_FLOAT16
    • ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
    • ANEURALNETWORKS_TENSOR_QUANT16_SYMM
    • ANEURALNETWORKS_TENSOR_QUANT8_SYMM
    • ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
  • Operaciones

    • ANEURALNETWORKS_ABS
    • ANEURALNETWORKS_ARGMAX
    • ANEURALNETWORKS_ARGMIN
    • ANEURALNETWORKS_AXIS_ALIGNED_BBOX_TRANSFORM
    • ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM
    • ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_RNN
    • ANEURALNETWORKS_BOX_WITH_NMS_LIMIT
    • ANEURALNETWORKS_CAST
    • ANEURALNETWORKS_CHANNEL_SHUFFLE
    • ANEURALNETWORKS_DETECTION_POSTPROCESSING
    • ANEURALNETWORKS_EQUAL
    • ANEURALNETWORKS_EXP
    • ANEURALNETWORKS_EXPAND_DIMS
    • ANEURALNETWORKS_GATHER
    • ANEURALNETWORKS_GENERATE_PROPOSALS
    • ANEURALNETWORKS_GREATER
    • ANEURALNETWORKS_GREATER_EQUAL
    • ANEURALNETWORKS_GROUPED_CONV_2D
    • ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT
    • ANEURALNETWORKS_INSTANCE_NORMALIZATION
    • ANEURALNETWORKS_LESS
    • ANEURALNETWORKS_LESS_EQUAL
    • ANEURALNETWORKS_LOG
    • ANEURALNETWORKS_LOGICAL_AND
    • ANEURALNETWORKS_LOGICAL_NOT
    • ANEURALNETWORKS_LOGICAL_OR
    • ANEURALNETWORKS_LOG_SOFTMAX
    • ANEURALNETWORKS_MAXIMUM
    • ANEURALNETWORKS_MINIMUM
    • ANEURALNETWORKS_NEG
    • ANEURALNETWORKS_NOT_EQUAL
    • ANEURALNETWORKS_PAD_V2
    • ANEURALNETWORKS_POW
    • ANEURALNETWORKS_PRELU
    • ANEURALNETWORKS_QUANTIZE
    • ANEURALNETWORKS_QUANTIZED_16BIT_LSTM
    • ANEURALNETWORKS_RANDOM_MULTINOMIAL
    • ANEURALNETWORKS_REDUCE_ALL
    • ANEURALNETWORKS_REDUCE_ANY
    • ANEURALNETWORKS_REDUCE_MAX
    • ANEURALNETWORKS_REDUCE_MIN
    • ANEURALNETWORKS_REDUCE_PROD
    • ANEURALNETWORKS_REDUCE_SUM
    • ANEURALNETWORKS_RESIZE_NEAREST_NEIGHBOR
    • ANEURALNETWORKS_ROI_ALIGN
    • ANEURALNETWORKS_ROI_POOLING
    • ANEURALNETWORKS_RSQRT
    • ANEURALNETWORKS_SELECT
    • ANEURALNETWORKS_SIN
    • ANEURALNETWORKS_SLICE
    • ANEURALNETWORKS_SPLIT
    • ANEURALNETWORKS_SQRT
    • ANEURALNETWORKS_TILE
    • ANEURALNETWORKS_TOPK_V2
    • ANEURALNETWORKS_TRANSPOSE_CONV_2D
    • ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_LSTM
    • ANEURALNETWORKS_UNIDIRECTIONAL_SEQUENCE_RNN

Android 10 introduce actualizaciones para muchas de las operaciones existentes. Las actualizaciones se relacionan principalmente con lo siguiente:

  • Compatibilidad con el diseño de memoria NCHW
  • Compatibilidad con tensores con una clasificación diferente de 4 en operaciones de softmax y normalización
  • Compatibilidad con convoluciones dilatadas
  • Compatibilidad con entradas con cuantización mixta en ANEURALNETWORKS_CONCATENATION

En la siguiente lista, se muestran las operaciones que se modifican en Android 10. Para obtener información detallada sobre los cambios, consulta OperationCode en la documentación de referencia de la NNAPI.

  • ANEURALNETWORKS_ADD
  • ANEURALNETWORKS_AVERAGE_POOL_2D
  • ANEURALNETWORKS_BATCH_TO_SPACE_ND
  • ANEURALNETWORKS_CONCATENATION
  • ANEURALNETWORKS_CONV_2D
  • ANEURALNETWORKS_DEPTHWISE_CONV_2D
  • ANEURALNETWORKS_DEPTH_TO_SPACE
  • ANEURALNETWORKS_DEQUANTIZE
  • ANEURALNETWORKS_DIV
  • ANEURALNETWORKS_FLOOR
  • ANEURALNETWORKS_FULLY_CONNECTED
  • ANEURALNETWORKS_L2_NORMALIZATION
  • ANEURALNETWORKS_L2_POOL_2D
  • ANEURALNETWORKS_LOCAL_RESPONSE_NORMALIZATION
  • ANEURALNETWORKS_LOGISTIC
  • ANEURALNETWORKS_LSH_PROJECTION
  • ANEURALNETWORKS_LSTM
  • ANEURALNETWORKS_MAX_POOL_2D
  • ANEURALNETWORKS_MEAN
  • ANEURALNETWORKS_MUL
  • ANEURALNETWORKS_PAD
  • ANEURALNETWORKS_RELU
  • ANEURALNETWORKS_RELU1
  • ANEURALNETWORKS_RELU6
  • ANEURALNETWORKS_RESHAPE
  • ANEURALNETWORKS_RESIZE_BILINEAR
  • ANEURALNETWORKS_RNN
  • ANEURALNETWORKS_ROI_ALIGN
  • ANEURALNETWORKS_SOFTMAX
  • ANEURALNETWORKS_SPACE_TO_BATCH_ND
  • ANEURALNETWORKS_SPACE_TO_DEPTH
  • ANEURALNETWORKS_SQUEEZE
  • ANEURALNETWORKS_STRIDED_SLICE
  • ANEURALNETWORKS_SUB
  • ANEURALNETWORKS_SVDF
  • ANEURALNETWORKS_TANH
  • ANEURALNETWORKS_TRANSPOSE

Android 9

El NN HAL 1.1 se introdujo en Android 9 y, además, incluye los siguientes cambios notables:

  • IDevice::prepareModel_1_1 incluye un parámetro ExecutionPreference. Un conductor puede usar esto para ajustar su preparación, sabiendo que la app prefiere conservar la batería o ejecutará el modelo en llamadas sucesivas rápidas.
  • Se agregaron nueve operaciones nuevas: BATCH_TO_SPACE_ND, DIV, MEAN, PAD, SPACE_TO_BATCH_ND, SQUEEZE, STRIDED_SLICE, SUB, TRANSPOSE.
  • En una app, se puede especificar que los cálculos de número de punto flotante de 32 bits se puedan ejecutar con un rango de número de punto flotante de 16 bits o con la precisión si configuras Model.relaxComputationFloat32toFloat16 como true. La estructura Capabilities tiene el campo adicional relaxedFloat32toFloat16Performance para que el controlador pueda informar su rendimiento relajado al framework.

Android 8.1

La HAL de redes neuronales inicial (1.0) se lanzó en Android 8.1. Para obtener más información, consulta /neuralnetworks/1.0/.