Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Extensiones de proveedores

Las extensiones de proveedor de la API de redes neuronales (NNAPI), introducidas en Android 10, son colecciones de operaciones y tipos de datos definidos por el proveedor. En los dispositivos que ejecutan NN HAL 1.2 o superior, los controladores pueden proporcionar operaciones personalizadas aceleradas por hardware al admitir las extensiones de los proveedores correspondientes. Las extensiones de proveedor no modifican el comportamiento de las operaciones existentes.

Las extensiones de proveedor brindan una alternativa más estructurada a los tipos de datos y operaciones de OEM, que quedaron obsoletos en Android 10. Para obtener más información, consulte Tipos de datos y operaciones de OEM .

Lista de permisos de uso de extensiones

Las extensiones de proveedor solo pueden ser utilizadas por aplicaciones de Android explícitamente especificadas y binarios nativos en las particiones /product , /vendor , /odm y /data . Las aplicaciones y los binarios nativos ubicados en la partición /system no pueden usar extensiones de proveedor.

En /vendor/etc/nnapi_extensions_app_allowlist se almacena una lista de aplicaciones y binarios de Android que pueden usar extensiones de proveedor de /vendor/etc/nnapi_extensions_app_allowlist . Cada línea del archivo contiene una nueva entrada. Una entrada puede ser una ruta binaria nativa que tiene como prefijo una barra (/), por ejemplo, /data/foo , o el nombre de un paquete de aplicación de Android, por ejemplo, com.foo.bar .

La lista de permitidos se aplica desde la biblioteca compartida en tiempo de ejecución de NNAPI. Esta biblioteca protege contra el uso accidental, pero no contra la elusión deliberada por parte de una aplicación que utiliza directamente la interfaz HAL del controlador NNAPI.

Definición de extensión de proveedor

El proveedor crea y mantiene un archivo de encabezado con la definición de extensión. Puede encontrar un ejemplo completo de una definición de extensión en example/fibonacci/FibonacciExtension.h .

Cada extensión debe tener un nombre único que comience con el nombre de dominio inverso del proveedor.

const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";

El nombre actúa como un espacio de nombres para operaciones y tipos de datos. La NNAPI usa este nombre para distinguir entre extensiones de proveedores.

Las operaciones y los tipos de datos se declaran de forma similar a los de runtime/include/NeuralNetworks.h .

enum {
    /**
     * A custom scalar type.
     */
    EXAMPLE_SCALAR = 0,

    /**
     * A custom tensor type.
     *
     * Attached to this tensor is {@link ExampleTensorParams}.
     */
    EXAMPLE_TENSOR = 1,
};

enum {
    /**
     * Computes example function.
     *
     * Inputs:
     * * 0: A scalar of {@link EXAMPLE_SCALAR}.
     *
     * Outputs:
     * * 0: A tensor of {@link EXAMPLE_TENSOR}.
     */
    EXAMPLE_FUNCTION = 0,
};

Una operación de extensión puede usar cualquier tipo de operando, incluidos los tipos de operandos sin extensión y los tipos de operandos de otras extensiones. Cuando se utiliza un tipo de operando de otra extensión, el controlador debe admitir la otra extensión.

Las extensiones también pueden declarar estructuras personalizadas para acompañar a los operandos de extensión.

/**
 * Quantization parameters for {@link EXAMPLE_TENSOR}.
 */
typedef struct ExampleTensorParams {
    double scale;
    int64_t zeroPoint;
} ExampleTensorParams;

Usar extensiones en clientes NNAPI

El archivo runtime/include/NeuralNetworksExtensions.h (C API) proporciona compatibilidad con la extensión en tiempo de ejecución. Esta sección proporciona una descripción general de la API de C.

Para comprobar si un dispositivo admite una extensión, utilice ANeuralNetworksDevice_getExtensionSupport .

bool isExtensionSupported;
CHECK_EQ(ANeuralNetworksDevice_getExtensionSupport(device, EXAMPLE_EXTENSION_NAME,
                                                   &isExtensionSupported),
         ANEURALNETWORKS_NO_ERROR);
if (isExtensionSupported) {
    // The device supports the extension.
    ...
}

Para construir un modelo con un operando de extensión, use ANeuralNetworksModel_getExtensionOperandType para obtener el tipo de operando y llame a ANeuralNetworksModel_addOperand .

int32_t type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperandType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_TENSOR, &type),
         ANEURALNETWORKS_NO_ERROR);
ANeuralNetworksOperandType operandType{
        .type = type,
        .dimensionCount = dimensionCount,
        .dimensions = dimensions,
};
CHECK_EQ(ANeuralNetworksModel_addOperand(model, &operandType), ANEURALNETWORKS_NO_ERROR);

Opcionalmente, use ANeuralNetworksModel_setOperandExtensionData para asociar datos adicionales con un operando de extensión.

ExampleTensorParams params{
        .scale = 0.5,
        .zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, &params, sizeof(params)),
         ANEURALNETWORKS_NO_ERROR);

Para construir un modelo con una operación de extensión, use ANeuralNetworksModel_getExtensionOperationType para obtener el tipo de operación y llame a ANeuralNetworksModel_addOperation .

ANeuralNetworksOperationType type;
CHECK_EQ(ANeuralNetworksModel_getExtensionOperationType(model, EXAMPLE_EXTENSION_NAME, EXAMPLE_FUNCTION,
                                                        &type),
         ANEURALNETWORKS_NO_ERROR);
CHECK_EQ(ANeuralNetworksModel_addOperation(model, type, inputCount, inputs, outputCount, outputs),
         ANEURALNETWORKS_NO_ERROR);

Adición de soporte de extensión a un controlador NNAPI

Los controladores informan sobre las extensiones admitidas a través del método IDevice::getSupportedExtensions . La lista devuelta debe contener una entrada que describa cada extensión admitida.

Extension {
    .name = EXAMPLE_EXTENSION_NAME,
    .operandTypes = {
        {
            .type = EXAMPLE_SCALAR,
            .isTensor = false,
            .byteSize = 8,
        },
        {
            .type = EXAMPLE_TENSOR,
            .isTensor = true,
            .byteSize = 8,
        },
    },
}

De los 32 bits utilizados para identificar tipos y operaciones, los bits altos Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX son el prefijo de extensión y los bits bajos Model::ExtensionTypeEncoding::LOW_BITS_TYPE representan el tipo u operación de la extensión.

Al manejar una operación o un tipo de operando, el controlador debe verificar el prefijo de extensión. Si el prefijo de extensión tiene un valor distinto de cero, la operación o el tipo de operando es un tipo de extensión. Si el valor es 0 , la operación o el tipo de operando no es un tipo de extensión.

Para asignar el prefijo a un nombre de extensión, búsquelo en model.extensionNameToPrefix . El mapeo del prefijo al nombre de la extensión es una correspondencia uno a uno (biyección) para un modelo dado. Diferentes valores de prefijo pueden corresponder al mismo nombre de extensión en diferentes modelos.

El controlador debe validar las operaciones de extensión y los tipos de datos porque el tiempo de ejecución de NNAPI no puede validar operaciones de extensión y tipos de datos particulares.

Los operandos de extensión pueden tener datos asociados en operand.extraParams.extension , que el tiempo de ejecución trata como un blob de datos sin procesar de tamaño arbitrario.

Operación OEM y tipos de datos

NNAPI tiene una operación OEM y tipos de datos OEM para permitir que los fabricantes de dispositivos proporcionen una funcionalidad personalizada y específica del controlador. Estos tipos de datos y operaciones solo los utilizan las aplicaciones OEM. La semántica del funcionamiento del OEM y los tipos de datos son específicos del OEM y pueden cambiar en cualquier momento. La operación OEM y los tipos de datos se codifican mediante OperationType::OEM_OPERATION , OperandType::OEM y OperandType::TENSOR_OEM_BYTE .