Rozszerzenia dostawcy interfejsu Neural Networks API (NNAPI), wprowadzone w Androidzie 10, to zbiory operacji i typów danych zdefiniowanych przez dostawcę. Na urządzeniach z NNHAL w wersji 1.2 lub nowszej sterowniki mogą zapewniać niestandardowe operacje przyspieszone sprzętowo, obsługując odpowiednie rozszerzenia dostawców. Rozszerzenia dostawcy nie modyfikują działania dotychczasowych operacji.
Rozszerzenia dostawcy stanowią bardziej uporządkowaną alternatywę dla operacji OEM i typów danych, które zostały wycofane w Androidzie 10. Więcej informacji znajdziesz w artykule Obsługa OEM i typy danych.
Lista dozwolonych rozszerzeń
Rozszerzeń dostawcy mogą używać tylko wyraźnie określone aplikacje na Androida i natywne pliki binarne na partycjach /product
, /vendor
, /odm
i /data
.
Aplikacje ani natywne pliki binarne znajdujące się na partycji /system
nie mogą używać rozszerzeń dostawców.
Lista aplikacji i plików binarnych na Androida, które mogą korzystać z rozszerzeń dostawcy NNAPI, jest przechowywana w /vendor/etc/nnapi_extensions_app_allowlist
. Każdy wiersz pliku zawiera nowy wpis. Element może być ścieżką do pliku binarnego z prefiksem ukośnika (/), np. /data/foo
, lub nazwą pakietu aplikacji na Androida, np. com.foo.bar
.
Lista dozwolonych jest wymuszana przez bibliotekę współdzieloną NNAPI. Ta biblioteka chroni przed przypadkowym użyciem, ale nie przed celowym obchodzeniem zabezpieczeń przez aplikację bezpośrednio za pomocą interfejsu HAL sterownika NNAPI.
Definicja rozszerzenia z danymi o dostawcy
Dostawca tworzy i utrzymuje plik nagłówka z definicją rozszerzenia. Pełny przykład definicji rozszerzenia znajdziesz tutaj: example/fibonacci/FibonacciExtension.h
.
Każde rozszerzenie musi mieć unikalną nazwę, która zaczyna się od odwrotnej nazwy domeny 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 tego, który jest stosowany 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 spoza rozszerzenia i typów operandów z innych rozszerzeń. W przypadku użycia typu operandu z innego rozszerzenia sterownik musi je obsługiwać.
Rozszerzenia mogą też deklarować niestandardowe struktury, które będą towarzyszyć operatorom rozszerzeń.
/**
* Quantization parameters for {@link EXAMPLE_TENSOR}.
*/
typedef struct ExampleTensorParams {
double scale;
int64_t zeroPoint;
} ExampleTensorParams;
Używanie rozszerzeń w klientach NNAPI
Plik runtime/include/NeuralNetworksExtensions.h
(C API) zapewnia obsługę rozszerzenia w czasie wykonywania. W tej sekcji znajdziesz omówienie interfejsu C API.
Aby sprawdzić, czy urządzenie obsługuje rozszerzenie, użyj narzędzia 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 argumentu ANeuralNetworksModel_getExtensionOperandType
w celu uzyskania typu argumentu 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);
Opcjonalnie możesz użyć parametru ANeuralNetworksModel_setOperandExtensionData
, aby powiązać 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 utworzyć model z operacją rozszerzenia, użyj funkcji ANeuralNetworksModel_getExtensionOperationType
, aby uzyskać typ operacji, a potem wywołaj funkcję 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
. 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,
},
},
}
Z 32 bitów używanych do identyfikowania typów i operacji najwyższe
Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
bity to rozszerzenie
prefiks, a najniższe
Model::ExtensionTypeEncoding::LOW_BITS_TYPE
bity reprezentują typ lub operację rozszerzenia.
Podczas obsługi operacji lub typu operandu sterownik musi sprawdzić prefiks rozszerzenia. Jeśli prefiks rozszerzenia ma wartość różną od 0, typ operacji lub operand jest typem rozszerzenia. Jeśli wartość to 0
, operacja lub typ argumentu nie jest typem rozszerzenia.
Aby zmapować prefiks na nazwę rozszerzenia, wyszukaj go tutaj: model.extensionNameToPrefix
.
Mapowanie prefiksu na nazwę rozszerzenia jest w przypadku danego modelu mapowaniem jeden-do-jednego (jednoznacznym). Różne wartości prefiksów mogą odpowiadać tej samej nazwie rozszerzenia w różnych modelach.
Sterownik musi sprawdzać poprawność operacji i typów danych rozszerzenia, ponieważ środowisko uruchomieniowe NNAPI nie może sprawdzać poprawności poszczególnych operacji i typów danych rozszerzenia.
operandy rozszerzenia mogą mieć powiązane dane w elemencie operand.extraParams.extension
, który środowisko wykonawcze traktuje jako nieprzetworzony obiekt blob danych o dowolnym rozmiarze.
Typy danych i działania OEM
NNAPI zawiera operacje i typy danych OEM, aby umożliwić producentom urządzeń udostępnianie niestandardowych funkcji związanych z sterownikami. Te operacje i typy danych są używane tylko przez aplikacje OEM. Semantyka operacji OEM i typów danych zależy od producenta OEM i może się w dowolnym momencie zmienić. Operacje i typy danych OEM są kodowane za pomocą znaczników OperationType::OEM_OPERATION
, OperandType::OEM
i OperandType::TENSOR_OEM_BYTE
.