Anbietererweiterungen

Anbietererweiterungen der Neural Networks API (NNAPI), die in Android 10 eingeführt wurden, sind Sammlungen von vom Anbieter definierten Vorgängen und Datentypen. Auf Geräten mit NN HAL 1.2 oder höher können Treiber benutzerdefinierte hardwarebeschleunigte Vorgänge bereitstellen, indem sie entsprechende Anbietererweiterungen unterstützen. Anbietererweiterungen ändern das Verhalten vorhandener Vorgänge nicht.

Anbietererweiterungen sind eine strukturiertere Alternative zum OEM-Betrieb und Datentypen, die in Android 10 eingestellt wurden. Weitere Informationen finden Sie unter OEM-Betrieb und -Datentypen:

Zulassungsliste für die Nutzung von Erweiterungen

Anbietererweiterungen können nur von explizit angegebenen Android-Apps und native Binärdateien in den Partitionen /product, /vendor, /odm und /data. Für Apps und native Binärdateien auf der Partition /system können keine Anbietererweiterungen verwendet werden.

Eine Liste der Android-Apps und -Binärprogramme, die NNAPI-Anbietererweiterungen verwenden dürfen, ist gespeichert in /vendor/etc/nnapi_extensions_app_allowlist. Jede Zeile der Datei enthält einen neuen Eintrag. Ein Eintrag kann ein nativer Binärpfad mit dem Präfix einen Schrägstrich (/), z. B. /data/foo oder der Name eines Android-App-Pakets für Beispiel: com.foo.bar.

Die Zulassungsliste wird von der gemeinsam genutzten NNAPI-Laufzeitbibliothek erzwungen. Diese Bibliothek vor versehentlicher Nutzung, aber nicht vor absichtlicher Umgehung durch eine Anwendung direkt über die HAL-Schnittstelle des NNAPI-Treibers.

Definition der Anbietererweiterung

Der Anbieter erstellt und verwaltet eine Headerdatei mit der Erweiterungsdefinition. A Ein vollständiges Beispiel für eine Erweiterungsdefinition finden Sie unter example/fibonacci/FibonacciExtension.h

Jede Erweiterung muss einen eindeutigen Namen haben, der mit dem umgekehrten Domainnamen des Anbieters beginnt.

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

Der Name dient als Namespace für Vorgänge und Datentypen. Die NNAPI verwendet diese Namen, um zwischen den Anbietererweiterungen zu unterscheiden.

Vorgänge und Datentypen werden ähnlich wie die in 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,
};

Für einen Erweiterungsvorgang kann jeder Operandentyp verwendet werden, einschließlich Operandentypen, die nicht zu Erweiterungen gehören, und Operandentypen aus anderen Erweiterungen. Wenn Sie einen Operandentyp aus einer anderen Erweiterung verwenden, muss der Treiber die andere Erweiterung unterstützen.

Erweiterungen können auch benutzerdefinierte Strukturen für Erweiterungsoperanden deklarieren.

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

Erweiterungen in NNAPI-Clients verwenden

Die runtime/include/NeuralNetworksExtensions.h (C API) unterstützt Laufzeiterweiterung. Dieser Abschnitt bietet einen Überblick über die C API.

Wenn Sie prüfen möchten, ob ein Gerät eine Erweiterung unterstützt, verwenden Sie ANeuralNetworksDevice_getExtensionSupport

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

Wenn Sie ein Modell mit einem Erweiterungsoperanden erstellen möchten, verwenden Sie ANeuralNetworksModel_getExtensionOperandType, um den Operandentyp abzurufen, und rufen Sie ANeuralNetworksModel_addOperand auf.

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);

Optional: ANeuralNetworksModel_setOperandExtensionData , um zusätzliche Daten mit einem Erweiterungsoperanden zu verknüpfen.

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

Wenn Sie ein Modell mit einem Erweiterungsvorgang erstellen möchten, verwenden Sie ANeuralNetworksModel_getExtensionOperationType, um den Vorgangstyp abzurufen, und rufen Sie ANeuralNetworksModel_addOperation auf.

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);

Erweiterungsunterstützung für einen NNAPI-Treiber hinzufügen

Treiber melden unterstützte Erweiterungen über das IDevice::getSupportedExtensions . Die zurückgegebene Liste muss einen Eintrag für jede unterstützte Erweiterung enthalten.

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

Von den 32 Bits, die zum Identifizieren von Typen und Vorgängen verwendet werden, sind die hohen Bits Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX das Prefix der Erweiterung und die niedrigen Bits Model::ExtensionTypeEncoding::LOW_BITS_TYPE der Typ oder Vorgang der Erweiterung.

Beim Umgang mit einem Vorgang oder Operandentyp muss der Treiber das Erweiterungspräfix prüfen. Wenn das Erweiterungspräfix einen Wert ungleich Null hat, ist der Vorgangs- oder Operandentyp ein Erweiterungstyp. Wenn der Wert 0 ist, ist der Vorgang oder Operand kein Erweiterungstyp.

Wenn Sie das Präfix einem Erweiterungsnamen zuordnen möchten, suchen Sie in model.extensionNameToPrefix danach. Die Zuordnung vom Präfix zum Erweiterungsnamen ist für ein bestimmtes Modell eine Eins-zu-eins-Beziehung (Bijektion). Unterschiedliche Präfixwerte können demselben Erweiterungsnamen in verschiedenen Modellen entsprechen.

Der Treiber muss Erweiterungsvorgänge und Datentypen validieren, da die NNAPI Laufzeit bestimmte Erweiterungsvorgänge und Datentypen nicht validieren kann.

Erweiterungsoperanden können Daten in operand.extraParams.extension haben, die von der Laufzeit als Rohdaten-Blob beliebiger Größe behandelt werden.

OEM-Betrieb und Datentypen

NNAPI bietet einen OEM-Betrieb und OEM-Datentypen, Geräteherstellern zur Bereitstellung benutzerdefinierter, treiberspezifischer Funktionen. Diese Vorgang und Datentypen werden nur von OEM-Anwendungen verwendet. Die Semantik der OEM-Funktionen und -Datentypen ist OEM-spezifisch und kann sich jederzeit ändern. Der OEM Vorgang und Datentypen werden mit OperationType::OEM_OPERATION, OperandType::OEM und OperandType::TENSOR_OEM_BYTE.