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 NN HAL 1.2 lub nowszym sterowniki mogą udostępniać niestandardowe operacje przyspieszane sprzętowo, obsługując odpowiednie rozszerzenia dostawcy. Rozszerzenia dostawcy nie modyfikują działania istniejących operacji.
Rozszerzenia dostawcy stanowią bardziej uporządkowaną alternatywę dla operacji i typów danych OEM, które zostały wycofane w Androidzie 10. Więcej informacji znajdziesz w artykule Operacje i typy danych OEM.
Lista dozwolonych zastosowań rozszerzeń
Rozszerzenia dostawcy mogą być używane tylko przez wyraźnie określone aplikacje na Androida i natywne pliki binarne w partycjach /product, /vendor, /odm i /data.
Aplikacje i natywne pliki binarne znajdujące się w partycji /system nie mogą korzystać z rozszerzeń dostawcy.
Lista aplikacji i plików binarnych na Androida, które mogą korzystać z rozszerzeń dostawcy NNAPI, jest przechowywana w pliku /vendor/etc/nnapi_extensions_app_allowlist. Każdy wiersz pliku zawiera nowy wpis. Wpis może być ścieżką do natywnego pliku binarnego, która zaczyna się od ukośnika (/), np. /data/foo, lub nazwą pakietu aplikacji na Androida, np. com.foo.bar.
Lista dozwolonych jest egzekwowana przez bibliotekę współdzieloną środowiska wykonawczego NNAPI. Ta biblioteka chroni przed przypadkowym użyciem, ale nie przed celowym obchodzeniem przez aplikację, która bezpośrednio korzysta z interfejsu HAL sterownika NNAPI.
Definicja rozszerzenia dostawcy
Dostawca tworzy i utrzymuje plik nagłówkowy z definicją rozszerzenia. Pełny przykład definicji rozszerzenia znajdziesz w pliku
example/fibonacci/FibonacciExtension.h.
Każde rozszerzenie musi mieć unikalną nazwę, która zaczyna się od nazwy domeny dostawcy w odwrotnej kolejności.
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ń 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 typów operandów innych niż rozszerzenia i typów operandów z innych rozszerzeń. Jeśli używasz typu operandu z innego rozszerzenia, sterownik musi obsługiwać to rozszerzenie.
Rozszerzenia mogą też deklarować struktury niestandardowe, które będą towarzyszyć operandów 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ń w czasie działania. W tej sekcji znajdziesz omówienie 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
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 możesz użyć
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
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ą
IDevice::getSupportedExtensions
metody. 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 pierwsze
Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX
bity to
prefiks rozszerzenia, a ostatnie
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ść inną niż zero, operacja lub typ operandu jest typem rozszerzenia. Jeśli wartość to 0, operacja lub typ operandu nie jest typem rozszerzenia.
Aby zmapować prefiks na nazwę rozszerzenia, wyszukaj go w
model.extensionNameToPrefix.
Mapowanie prefiksu na nazwę rozszerzenia jest w przypadku danego modelu jednoznaczne (bijekcja). W różnych modelach różne wartości prefiksu mogą odpowiadać tej samej nazwie rozszerzenia.
Sterownik musi weryfikować operacje i typy danych rozszerzenia, ponieważ środowisko wykonawcze NNAPI nie może weryfikować konkretnych operacji i typów danych rozszerzenia.
Operandy rozszerzeń mogą mieć powiązane dane w
operand.extraParams.extension,
które środowisko wykonawcze traktuje jako nieprzetworzony blok danych o dowolnym rozmiarze.
Operacje i typy danych OEM
NNAPI ma operacje i typy danych OEM, które umożliwiają producentom urządzeń udostępnianie niestandardowych funkcji specyficznych dla sterownika. Te operacje i typy danych są używane tylko przez aplikacje OEM. Semantyka operacji i typów danych OEM jest specyficzna dla OEM i może się zmienić w dowolnym momencie. Operacje i typy danych OEM są kodowane za pomocą OperationType::OEM_OPERATION, OperandType::OEM i OperandType::TENSOR_OEM_BYTE.