Extensions de fournisseur

Extensions de fournisseur de l'API Neural Networks (NNAPI), introduites dans Android 10, sont d'opérations et de types de données définis par le fournisseur. Sur les appareils exécutant NN HAL 1.2 ou version ultérieure, les pilotes peuvent fournir des opérations personnalisées avec accélération matérielle la prise en charge des extensions des fournisseurs correspondants. Les extensions de fournisseur ne modifient pas le comportement des opérations existantes.

Les extensions de fournisseur offrent une alternative plus structurée aux opérations OEM et de données, qui ont été abandonnés dans Android 10. Pour en savoir plus, consultez Opération et types de données OEM.

Liste d'autorisation d'utilisation des extensions

Les extensions de fournisseur ne peuvent être utilisées que par les applications Android et les applications Android explicitement spécifiées binaires natifs sur les partitions /product, /vendor, /odm et /data. Les applications et les binaires natifs situés sur la partition /system ne peuvent pas utiliser le fournisseur .

La liste des applications et binaires Android autorisés à utiliser les extensions de fournisseur NNAPI est stocké dans /vendor/etc/nnapi_extensions_app_allowlist. Chaque ligne du fichier contient une nouvelle entrée. Une entrée peut être un chemin binaire natif précédé de une barre oblique (/), par exemple /data/foo, ou le nom d'un package d'application Android, pour Exemple : com.foo.bar.

La liste d'autorisation est appliquée à partir de la bibliothèque partagée de l'environnement d'exécution NNAPI. Cette bibliothèque protège contre l'utilisation accidentelle, mais pas contre le contournement délibéré une application directement à l'aide de l'interface HAL du pilote NNAPI.

Définition de l'extension du fournisseur

Le fournisseur crée et gère un fichier d'en-tête avec la définition de l'extension. A un exemple complet de définition d'extension se trouve dans example/fibonacci/FibonacciExtension.h

Chaque extension doit avoir un nom unique qui commence par le nom de domaine inversé du fournisseur.

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

Le nom sert d'espace de noms pour les opérations et les types de données. La NNAPI utilise cette pour distinguer les extensions de fournisseur.

Les opérations et les types de données sont déclarés d'une manière semblable à celles 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,
};

Une opération d'extension peut utiliser n'importe quel type d'opérande, y compris les opérandes sans extension et les types d'opérandes d'autres extensions. Lorsque vous utilisez un type d'opérande une autre extension, le pilote doit être compatible avec l'autre extension.

Les extensions peuvent également déclarer des structures personnalisées pour accompagner les opérandes d'extension.

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

Utiliser des extensions dans les clients NNAPI

La runtime/include/NeuralNetworksExtensions.h (API C) prend en charge l'extension d'exécution. Cette section fournit de l'API C.

Pour vérifier si un appareil est compatible avec une extension, utilisez ANeuralNetworksDevice_getExtensionSupport

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

Pour créer un modèle avec un opérande d'extension, utilisez ANeuralNetworksModel_getExtensionOperandType pour obtenir le type d'opérande et appeler 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);

Vous pouvez également utiliser ANeuralNetworksModel_setOperandExtensionData pour associer des données supplémentaires à un opérande d'extension.

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

Pour créer un modèle avec une opération d'extension, utilisez ANeuralNetworksModel_getExtensionOperationType pour obtenir le type d'opération et appeler 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);

Ajouter la prise en charge des extensions à un pilote NNAPI

Les pilotes signalent les extensions compatibles via la IDevice::getSupportedExtensions . La liste renvoyée doit contenir une entrée décrivant chaque élément compatible .

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

Sur les 32 bits utilisés pour identifier les types et les opérations, le haut Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX bits est l'extension préfixe et la valeur Model::ExtensionTypeEncoding::LOW_BITS_TYPE bits représentent le type ou l'opération de l'extension.

Lors du traitement d'une opération ou d'un type d'opérande, le pilote doit vérifier l'extension . Si le préfixe d'extension a une valeur non nulle, l'opération ou l'opérande type est un type d'extension. Si la valeur est 0, le type d'opération ou d'opérande n'est pas un type d'extension.

Pour mapper le préfixe à un nom d'extension, recherchez-le dans model.extensionNameToPrefix Le mappage entre le préfixe et le nom de l'extension est une correspondance (bijection) pour un modèle donné. Différentes valeurs de préfixe peuvent correspondre au le même nom d'extension dans différents modèles.

Le pilote doit valider les opérations d'extension et les types de données, car la NNAPI l'environnement d'exécution ne peut pas valider des opérations d'extension spécifiques et des types de données spécifiques.

Les opérandes d'extension peuvent avoir des données associées dans operand.extraParams.extension que l'environnement d'exécution traite comme un blob de données brutes de taille arbitraire.

Opération et types de données OEM

NNAPI dispose d'une opération OEM et de types de données OEM aux fabricants de périphériques de fournir des fonctionnalités personnalisées et spécifiques au pilote. Ces et les types de données ne sont utilisés que par les applications OEM. La sémantique des produits OEM et les types de données sont spécifiques aux OEM et peuvent changer à tout moment. L'OEM et les types de données sont encodés à l'aide de OperationType::OEM_OPERATION, OperandType::OEM et OperandType::TENSOR_OEM_BYTE.