Rozszerzenia dostawcy interfejsu API sieci neuronowych (NNAPI), wprowadzone w systemie Android 10, to kolekcje 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 odpowiednie rozszerzenia dostawców. Rozszerzenia dostawcy nie zmieniają zachowania istniejących operacji.
Rozszerzenia dostawcy zapewniają bardziej ustrukturyzowaną alternatywę dla operacji OEM i typów danych, które zostały przestarzałe w systemie Android 10. Aby uzyskać więcej informacji, zobacz Operacje OEM i typy danych .
Lista dozwolonych używania rozszerzeń
Rozszerzenia dostawcy mogą być używane tylko przez jawnie określone aplikacje dla systemu Android i natywne pliki binarne na partycjach /product
, /vendor
, /odm
i /data
. Aplikacje i natywne pliki binarne znajdujące się na partycji /system
nie mogą używać rozszerzeń dostawcy.
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żdy wiersz 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 z biblioteki współużytkowanej środowiska wykonawczego NNAPI. Ta biblioteka chroni przed przypadkowym użyciem, ale nie przed celowym obejściem przez aplikację bezpośrednio przy użyciu interfejsu HAL sterownika NNAPI.
Definicja rozszerzenia dostawcy
Dostawca tworzy i utrzymuje plik nagłówkowy z definicją rozszerzenia. 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 nazwy domeny wstecznej dostawcy.
const char EXAMPLE_EXTENSION_NAME[] = "com.example.my_extension";
Nazwa działa jako przestrzeń nazw dla operacji i typów danych. NNAPI używa tej nazwy do rozróżniania rozszerzeń dostawców.
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 typów operandów bez rozszerzenia i typów operandów z innych rozszerzeń. W przypadku używania typu operandu z innego rozszerzenia sterownik musi obsługiwać inne 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;
Korzystanie z rozszerzeń w klientach NNAPI
Plik runtime/include/NeuralNetworksExtensions.h
(C API) zapewnia obsługę rozszerzeń środowiska uruchomieniowego. 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
, aby uzyskać typ 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 skojarzyć dodatkowe dane z operandem rozszerzenia.
ExampleTensorParams params{
.scale = 0.5,
.zeroPoint = 128,
};
CHECK_EQ(ANeuralNetworksModel_setOperandExtensionData(model, operandIndex, ¶ms, sizeof(params)),
ANEURALNETWORKS_NO_ERROR);
Aby zbudować model z operacją rozszerzenia, użyj ANeuralNetworksModel_getExtensionOperationType
, aby uzyskać typ 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);
Dodawanie obsługi rozszerzeń do sterownika NNAPI
Sterowniki zgłaszają obsługiwane rozszerzenia za pomocą metody IDevice::getSupportedExtensions
. Zwracana 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, wysokie bity Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
są prefiksem rozszerzenia, a niskie bity Model::ExtensionTypeEncoding::LOW_BITS_TYPE
reprezentują typ lub operację rozszerzenia.
Podczas obsługi operacji lub typu operandu sterownik musi sprawdzić prefiks rozszerzenia. Jeśli prefiks rozszerzenia ma wartość niezerową, operacja lub typ operandu jest typem rozszerzenia. Jeśli wartość wynosi 0
, typ operacji lub operandu nie jest typem rozszerzenia.
Aby zamapować prefiks na nazwę rozszerzenia, wyszukaj go w model.extensionNameToPrefix
. Mapowanie z przedrostka na nazwę rozszerzenia jest korespondencją jeden do jednego (bijection) dla danego modelu. Różne wartości prefiksów mogą odpowiadać tej samej nazwie rozszerzenia w różnych modelach.
Sterownik musi sprawdzać poprawność operacji rozszerzeń i typów danych, ponieważ środowisko uruchomieniowe NNAPI nie może sprawdzać poprawności określonych operacji rozszerzeń i typów danych.
Operandy rozszerzeń mogą mieć skojarzone dane w operand.extraParams.extension
, które środowisko uruchomieniowe traktuje jako obiekt BLOB danych surowych o dowolnym rozmiarze.
Operacja OEM i typy danych
NNAPI ma operację OEM i typy danych OEM, aby umożliwić producentom urządzeń dostarczanie niestandardowych funkcji specyficznych dla sterownika. Te operacje i typy danych są używane tylko przez aplikacje OEM. Semantyka operacji 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
.