Anbietererweiterungen

NNAPI-Anbietererweiterungen (Neural Networks API), die im Android 10 Sammlungen von anbieterdefinierten Vorgängen und Datentypen. Auf Geräten mit NN HAL 1.2 oder höher können Treiber benutzerdefinierte hardwarebeschleunigte Operationen durch entsprechende Anbietererweiterungen unterstützt. Durch die Anbietererweiterungen ändert sich vorhandenen Vorgängen.

Diese Erweiterungen 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. Apps und native Binärdateien, die sich in der Partition /system befinden, können den Anbieter nicht verwenden Erweiterungen.

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 über die HAL-Schnittstelle des NNAPI-Treibers eine Anwendung erstellen.

Definition der Anbietererweiterung

Der Anbieter erstellt und verwaltet eine Headerdatei mit der Definition der Erweiterung. 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 beginnt des Zulieferunternehmens.

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 Operandtyp verwendet werden, einschließlich Nichterweiterungs-Operanden und Operandtypen von anderen Erweiterungen. Wenn Sie einen Operandentyp aus eine andere Erweiterung haben, muss der Treiber die andere Erweiterung unterstützen.

Erweiterungen können auch benutzerdefinierte Strukturen deklarieren, die zu Erweiterungsoperanden gehören.

/**
 * 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 enthält eine Übersicht ü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.
    ...
}

Um ein Modell mit einem Erweiterungsoperanden zu erstellen, verwenden Sie ANeuralNetworksModel_getExtensionOperandType um den Operandentyp zu erhalten und 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);

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

Um ein Modell mit einem Erweiterungsvorgang zu erstellen, verwenden Sie ANeuralNetworksModel_getExtensionOperationType um den Vorgangstyp zu erhalten und 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);

Erweiterungsunterstützung zu einem NNAPI-Treiber hinzufügen

Treiber melden unterstützte Erweiterungen über das IDevice::getSupportedExtensions . Die zurückgegebene Liste muss einen Eintrag enthalten, der alle unterstützten .

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, ist die hohe Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX Bits ist die Erweiterung Präfix und der niedrige Model::ExtensionTypeEncoding::LOW_BITS_TYPE Bits stehen für den Typ oder Vorgang der Erweiterung.

Bei der Verarbeitung eines Vorgangs- oder Operandentyps muss der Treiber die Erweiterung überprüfen. . Wenn das Erweiterungspräfix einen Wert ungleich null hat, wird die Operation oder der Operand Typ ist ein Erweiterungstyp. Wenn der Wert 0 ist, ist der Operation- oder Operandentyp ist kein Erweiterungstyp.

Wenn Sie das Präfix einem Erweiterungsnamen zuordnen möchten, suchen Sie es unter model.extensionNameToPrefix Die Zuordnung vom Präfix zum Erweiterungsnamen erfolgt 1:1-Übereinstimmung (Bijektion) für ein bestimmtes Modell. Unterschiedliche Präfixwerte können dem Erweiterungsnamen in verschiedenen Modellen.

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

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

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 von OEM Vorgang und Datentypen sind OEM-spezifisch und können sich jederzeit ändern. Der OEM Vorgang und Datentypen werden mit OperationType::OEM_OPERATION, OperandType::OEM und OperandType::TENSOR_OEM_BYTE.