Rozszerzenia dostawcy

Rozszerzenia dostawcy interfejsu Neural Networks API (NNAPI) wprowadzone w systemie Android 10 to zbiory operacji i typów danych zdefiniowanych przez dostawcę. Na urządzeniach z systemem NN HAL 1.2 lub nowszym sterowniki mogą zapewniać niestandardowe operacje przyspieszane sprzętowo, obsługując rozszerzenia odpowiednich dostawców. Rozszerzenia dostawców nie modyfikują zachowania istniejących operacji.

Rozszerzenia dostawców zapewniają bardziej uporządkowaną alternatywę dla operacji i typów danych OEM, które zostały przestarzałe w systemie Android 10. Aby uzyskać więcej informacji, zobacz Działanie OEM i typy danych .

Lista dozwolonych zastosowań rozszerzeń

Rozszerzenia dostawcy mogą być używane tylko przez wyraźnie określone aplikacje na Androida i natywne pliki binarne na partycjach /product , /vendor , /odm i /data . Aplikacje i natywne pliki binarne znajdujące się na partycji /system nie mogą korzystać z rozszerzeń dostawców.

Lista aplikacji i plików binarnych na Androida, które mogą korzystać z rozszerzeń dostawców NNAPI, jest przechowywana w /vendor/etc/nnapi_extensions_app_allowlist . Każda linia pliku zawiera nowy wpis. Wpis może być natywną ścieżką binarną poprzedzoną ukośnikiem (/), na przykład /data/foo lub nazwą pakietu aplikacji dla systemu Android, na przykład com.foo.bar .

Lista dozwolonych jest wymuszana na podstawie udostępnionej biblioteki środowiska wykonawczego NNAPI. Ta biblioteka chroni przed przypadkowym użyciem, ale nie przed celowym obejściem przez aplikację bezpośrednio korzystającą z interfejsu HAL sterownika NNAPI.

Definicja rozszerzenia dostawcy

Dostawca tworzy i utrzymuje plik nagłówkowy z definicją rozszerzenia. Kompletny przykład definicji rozszerzenia można znaleźć w example/fibonacci/FibonacciExtension.h .

Każde rozszerzenie musi mieć unikalną nazwę rozpoczynającą się od odwrotnej nazwy domeny dostawcy.

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

Nazwa pełni rolę przestrzeni nazw dla operacji i typów danych. NNAPI używa tej nazwy do rozróżnienia rozszerzeń dostawców.

Operacje i typy danych deklarowane są w sposób podobny do tych w 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,
};

Operacja rozszerzająca może używać dowolnego typu operandu, w tym typów operandów nierozszerzających i typów operandów z innych rozszerzeń. W przypadku używania typu operandu z innego rozszerzenia sterownik musi obsługiwać to drugie rozszerzenie.

Rozszerzenia mogą również deklarować niestandardowe struktury towarzyszące operandom rozszerzeń.

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

Użyj rozszerzeń w klientach NNAPI

Plik runtime/include/NeuralNetworksExtensions.h (C API) zapewnia obsługę rozszerzeń środowiska wykonawczego. Ta sekcja zawiera omówienie interfejsu API języka C.

Aby sprawdzić, czy urządzenie obsługuje rozszerzenie, użyj ANeuralNetworksDevice_getExtensionSupport .

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

Aby zbudować model z operandem rozszerzenia, użyj ANeuralNetworksModel_getExtensionOperandType w celu uzyskania typu operandu i wywołaj 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);

Opcjonalnie użyj ANeuralNetworksModel_setOperandExtensionData , aby powiązać dodatkowe dane z operandem rozszerzenia.

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

Aby zbudować model z operacją rozszerzenia, użyj ANeuralNetworksModel_getExtensionOperationType w celu uzyskania typu operacji i wywołaj 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);

Dodaj obsługę rozszerzeń do sterownika NNAPI

Sterowniki zgłaszają obsługiwane rozszerzenia za pomocą metody IDevice::getSupportedExtensions . Zwrócona lista musi zawierać wpis opisujący każde obsługiwane rozszerzenie.

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

Spośród 32 bitów używanych do identyfikacji typów i operacji, wyższe bity Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX to przedrostek rozszerzenia, a niższe bity Model::ExtensionTypeEncoding::LOW_BITS_TYPE reprezentują typ lub działanie rozszerzenia.

Podczas obsługi operacji lub typu argumentu sterownik musi sprawdzić przedrostek rozszerzenia. Jeśli przedrostek rozszerzenia ma wartość różną od zera, typem operacji lub argumentu jest typ rozszerzenia. Jeśli wartość wynosi 0 , typ operacji lub operandu nie jest typem rozszerzenia.

Aby zmapować przedrostek na nazwę rozszerzenia, wyszukaj go w model.extensionNameToPrefix . Odwzorowanie przedrostka na nazwę rozszerzenia jest korespondencją jeden do jednego (bijekcją) dla danego modelu. Różne wartości przedrostka mogą odpowiadać tej samej nazwie rozszerzenia w różnych modelach.

Sterownik musi sprawdzić poprawność operacji rozszerzeń i typów danych, ponieważ środowisko wykonawcze NNAPI nie może sprawdzić określonych operacji rozszerzeń i typów danych.

Operandy rozszerzeń mogą mieć powiązane dane w operand.extraParams.extension , które środowisko wykonawcze traktuje jako obiekt typu blob surowych danych o dowolnym rozmiarze.

Działanie OEM i typy danych

NNAPI obsługuje operacje OEM i typy danych OEM, aby umożliwić producentom urządzeń zapewnianie niestandardowych funkcji specyficznych dla sterowników. Te operacje i typy danych są używane tylko przez aplikacje OEM. Semantyka działania OEM i typy danych są specyficzne dla OEM i mogą ulec zmianie w dowolnym momencie. Operacja OEM i typy danych są kodowane przy użyciu OperationType::OEM_OPERATION , OperandType::OEM i OperandType::TENSOR_OEM_BYTE .