Rozszerzenia dostawców

Rozszerzenia dostawcy interfejsu Neural Networks API (NNAPI) wprowadzone w Androida 10 zbiory działań i typów danych zdefiniowanych przez dostawcę. Na urządzeniach z systemem NN HAL w wersji 1.2 lub nowszej, sterowniki mogą umożliwiać niestandardowe przyspieszanie sprzętowe operacji przez obsługujące odpowiednie rozszerzenia dostawców. Rozszerzenia dostawców nie modyfikują dla obecnych operacji.

Rozszerzenia dla dostawców zapewniają bardziej uporządkowaną alternatywę dla działalności OEM. typy danych, które zostały wycofane w Androidzie 10. Więcej informacji: Operacje OEM i typy danych.

Lista dozwolonych na temat użycia rozszerzeń

Rozszerzenia dostawców mogą być używane tylko przez wyraźnie określone aplikacje na Androida 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 usług dostawcy rozszerzeń.

Lista aplikacji na Androida i plików binarnych, które mogą używać rozszerzeń dostawcy NNAPI, to przechowywane w /vendor/etc/nnapi_extensions_app_allowlist. Każdy wiersz w pliku zawiera nowy wpis. Wpis może być natywną ścieżką binarną poprzedzoną prefiksem ukośnik (/), np. /data/foo, lub nazwa pakietu aplikacji na Androida, na przykład na przykład com.foo.bar.

Lista dozwolonych jest egzekwowana z zasobów wspólnych środowiska wykonawczego NNAPI. Ta biblioteka chroni przed przypadkowym użyciem, ale nie przed bezpośrednio przez interfejs HAL sterownika NNAPI.

Definicja rozszerzenia dostawcy

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

Każde rozszerzenie musi mieć unikalną nazwę, która zaczyna się od odwrotnej nazwy domeny od dostawcy.

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

Jest to przestrzeń nazw operacji i typów danych. NNAPI używa tego nazwy pozwalającej rozróżnić rozszerzenia dostawcy.

Operacje i typy danych są deklarowane 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 rozszerzenia może używać dowolnego typu operandu, w tym operandu bez rozszerzenia i typy operandów z innych rozszerzeń. W przypadku użycia typu operandu z argumentu z innego rozszerzenia, sterownik musi je obsługiwać.

Rozszerzenia mogą też deklarować niestandardowe struktury, aby towarzyszyć operandom rozszerzenia.

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

Używanie rozszerzeń w klientach NNAPI

runtime/include/NeuralNetworksExtensions.h (C API) zapewnia obsługę rozszerzenia środowiska wykonawczego. W tej sekcji znajdziesz o C API.

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

Możesz też użyć funkcji 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 utworzyć model z operacją rozszerzenia, użyj funkcji ANeuralNetworksModel_getExtensionOperationType w celu uzyskania typu operacji i wywołania 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);

Dodawanie obsługi rozszerzenia do sterownika NNAPI

Sterowniki zgłaszają obsługiwane rozszerzenia w IDevice::getSupportedExtensions . Zwrócona lista musi zawierać wpis opisujący każdą obsługiwaną listę .

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

Z 32 bitów używanych do identyfikowania typów i operacji wysoka Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX bity to rozszerzenie prefiks i niski Model::ExtensionTypeEncoding::LOW_BITS_TYPE. liczba bitów oznacza typ lub operację rozszerzenia.

Podczas obsługi operacji lub typu operandu sterownik musi sprawdzić rozszerzenie . Jeśli prefiks rozszerzenia ma wartość inną niż zero, operacja lub operand jest typem rozszerzenia. Jeśli wartość to 0, jest to typ operacji lub argumentu. Nie jest typem rozszerzenia.

Aby zmapować prefiks na nazwę rozszerzenia, wyszukaj go w model.extensionNameToPrefix Mapowanie z prefiksu na nazwę rozszerzenia jest korespondencją „jeden do jednego”. (bijekcjom) danego modelu. Różne wartości prefiksów mogą odpowiadać z tą samą nazwą rozszerzenia w różnych modelach.

Sterownik musi zweryfikować operacje rozszerzenia i typy danych, ponieważ NNAPI środowisko wykonawcze nie może sprawdzać konkretnych operacji rozszerzeń ani typów danych.

operandy rozszerzenia mogą mieć powiązane dane w operand.extraParams.extension, które środowisko wykonawcze traktuje jako nieprzetworzony obiekt blob danych o dowolnym rozmiarze.

Działania i typy danych OEM

NNAPI ma placówkę OEM i typy danych OEM, które pozwalają na producentów urządzeń, by udostępnić im niestandardowe funkcje dostosowane do konkretnych sterowników. Te operacji i typów danych są używane tylko przez aplikacje OEM. Semantyka OEM działania i typy danych są typowe dla OEM i mogą się zmienić w dowolnym momencie. OEM operacji i typów danych są kodowane za pomocą OperationType::OEM_OPERATION, OperandType::OEM i OperandType::TENSOR_OEM_BYTE.