Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

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.

Extensiones de proveedor proporcionan una alternativa más estructurado para operación y datos OEM tipos, que estaban en desuso en Android 10. Para más información, véase la operación y datos OEM tipos .

Lista de permisos de uso de extensiones

Vendedor extensiones sólo pueden ser utilizados por aplicaciones de Android explícitamente especificados y binarios nativos en la /product , /vendor , /odm , y /data particiones. Aplicaciones y binarios nativos ubicados en el /system partición no pueden utilizar las extensiones de los proveedores.

Una lista de aplicaciones de Android y los binarios permite el uso de extensiones de proveedores NNAPI se almacena en /vendor/etc/nnapi_extensions_app_allowlist . Cada línea del archivo contiene una nueva entrada. Una entrada puede ser una ruta nativa binario que se prefija con una barra (/), por ejemplo, /data/foo , o un nombre de un paquete de aplicaciones para 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. Un ejemplo completo de una definición de extensión se puede encontrar 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.

Operaciones y tipos de datos se declaran de una manera 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 runtime/include/NeuralNetworksExtensions.h (C API) de archivos proporciona apoyo a la extensión de tiempo de ejecución. Esta sección proporciona una descripción general de la API de C.

Para comprobar si un dispositivo soporta una extensión, el uso 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, el uso ANeuralNetworksModel_getExtensionOperandType para obtener el tipo de operando y llamada 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, utilizar ANeuralNetworksModel_setOperandExtensionData a los datos adicionales asociados con una extensión operando.

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, el uso ANeuralNetworksModel_getExtensionOperationType para obtener el tipo de operación y la llamada 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 conductores reportan extensiones soportadas a través de la IDevice::getSupportedExtensions método. 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 los tipos y las operaciones, el alto Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX bits es el prefijo de extensión y el bajo Model::ExtensionTypeEncoding::LOW_BITS_TYPE los bits representa el tipo o el funcionamiento 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 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.

Operandos extensión puede tener asociado datos en operand.extraParams.extension , que los trata de tiempo de ejecución como una gota de datos en bruto 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. El funcionamiento y tipos de datos de OEM se codifican utilizando OperationType::OEM_OPERATION , OperandType::OEM , y OperandType::TENSOR_OEM_BYTE .