L'API Android Neural Networks (NNAPI) è un'API di Android C progettata per l'esecuzione per il machine learning sui dispositivi Android. NNAPI è progettato per fornire un livello di funzionalità di base per framework di machine learning come TensorFlow Lite e Caffe2, che creano e addestrano le reti neurali. L'API è disponibile Su tutti i dispositivi Android con Android 8.1 (livello API 27) o versioni successive.
NNAPI supporta l'inferenza applicando i dati dei dispositivi Android ai addestrati e definiti dagli sviluppatori. Esempi di inferenza includono la classificazione immagini, prevedendo il comportamento degli utenti e selezionando risposte appropriate query di ricerca.
L'inferenza on-device offre molti vantaggi:
- Latenza: non è necessario inviare una richiesta tramite una connessione di rete, attendere una risposta. Ad esempio, ciò può risultare fondamentale per le applicazioni video che elaborano i fotogrammi successivi provenienti da una videocamera.
- Disponibilità: l'applicazione viene eseguita anche al di fuori della copertura di rete.
- Velocità: nuovo hardware specifico per l'elaborazione della rete neurale. offre un calcolo molto più veloce rispetto a una CPU per uso generico, da sola.
- Privacy: i dati rimangono sul dispositivo Android.
- Costo: non è necessaria alcuna server farm quando tutti i calcoli vengono eseguiti il dispositivo Android.
Ci sono anche dei compromessi che uno sviluppatore deve tenere presente:
- Utilizzo del sistema: la valutazione delle reti neurali comporta il consumo della batteria potrebbe aumentare. Dovresti prendere in considerazione monitorare lo stato della batteria se questo è un problema per la tua app, in particolare per calcoli a lunga esecuzione.
- Dimensioni dell'applicazione: presta attenzione alle dimensioni dei modelli. I modelli possono e occupano più megabyte di spazio. Se raggruppa nell'APK modelli di grandi dimensioni avere un impatto eccessivo sui tuoi utenti, ti consigliamo di scaricare dopo l'installazione dell'app, utilizzando modelli più piccoli o eseguendo i calcoli nel cloud. NNAPI non fornisce funzionalità per l'esecuzione nel cloud.
Consulta le Esempio di API Android Neural Networks per un esempio di come utilizzare NNAPI.
Informazioni sul runtime dell'API Neural Networks
NNAPI è pensato per essere chiamato da librerie, framework e strumenti di machine learning che consentono agli sviluppatori di addestrare i propri modelli al di fuori del dispositivo ed eseguirne il deployment su Android dispositivi mobili. Le app in genere non usano direttamente NNAPI, ma usare framework di machine learning di livello più alto. Questi framework a loro volta potrebbero utilizzare NNAPI per eseguire operazioni di inferenza con accelerazione hardware sui dispositivi supportati.
In base ai requisiti di un'app e alle funzionalità hardware di un Android dispositivo mobile, il runtime della rete neurale di Android può distribuire tra i diversi processori disponibili sul dispositivo, inclusi i carichi di lavoro hardware di rete neurale, GPU (Graphics Processing Unit) e segnale digitale processori (DSP).
Per i dispositivi Android privi di un driver del fornitore specializzato, il runtime NNAPI che esegue le richieste sulla CPU.
La figura 1 mostra l'architettura di sistema generale per NNAPI.
.Modello di programmazione dell'API Neural Networks
Per eseguire calcoli utilizzando NNAPI, devi prima creare una grafico che definisce i calcoli da eseguire. Questo grafico di calcolo, combinato con i tuoi dati di input (ad esempio, le ponderazioni e i bias tramandati da una framework di machine learning), costituisce il modello per la valutazione del runtime NNAPI.
NNAPI utilizza quattro astrazioni principali:
- Modello: un grafico computazionale delle operazioni matematiche e della costante.
valori appresi durante un processo di addestramento. Queste operazioni sono specifiche
neurali profonde. Includono le immagini bidimensionali (2D)
convoluzione,
logistica
(sigmoidale)
l'attivazione,
lineare rettificato
(ReLU) e altro ancora. La creazione di un modello è un'operazione sincrona.
Una volta creato correttamente, può essere riutilizzato in thread e compilazioni.
In NNAPI, un modello è rappresentato come
ANeuralNetworksModel
in esecuzione in un'istanza Compute Engine. - Compilation: rappresenta una configurazione per la compilazione di un modello NNAPI in
codice di livello inferiore. La creazione di una compilazione è un'operazione sincrona. Una volta
creato correttamente, può essere riutilizzato in più thread ed esecuzioni. Nella
NNAPI, ogni compilazione è rappresentata come
ANeuralNetworksCompilation
in esecuzione in un'istanza Compute Engine. - Memoria: rappresenta la memoria condivisa, i file mappati in memoria e la memoria simile.
buffer. L'utilizzo di un buffer di memoria consente al runtime NNAPI di trasferire i dati ai driver
in modo più efficiente. Un'app di solito crea un buffer di memoria condiviso
contiene tutti i tensori necessari per definire un modello. Puoi anche usare le opzioni
buffer per archiviare gli input e gli output di un'istanza di esecuzione. In NNAPI,
ogni buffer è rappresentato da una
ANeuralNetworksMemory
in esecuzione in un'istanza Compute Engine. Esecuzione: interfaccia per applicare un modello NNAPI a un set di input e a raccogliere i risultati. L'esecuzione può essere eseguita in modo sincrono o asincrono.
Per l'esecuzione asincrona, più thread di poter attendere durante la stessa esecuzione. Al termine dell'esecuzione, tutti i thread vengono rilasciate.
In NNAPI, ogni esecuzione è rappresentata come
ANeuralNetworksExecution
in esecuzione in un'istanza Compute Engine.
La figura 2 mostra il flusso di programmazione di base.
.Il resto di questa sezione descrive i passaggi per configurare il modello NNAPI per eseguire il calcolo, compilare il modello ed eseguire il modello compilato.
Fornire accesso ai dati di addestramento
I dati su bias e ponderazioni addestrati sono probabilmente archiviati in un file. Per fornire il
dal runtime NNAPI con accesso efficiente a questi dati, crea un
ANeuralNetworksMemory
richiamando l'istanza
ANeuralNetworksMemory_createFromFd()
e passando il descrittore del file di dati aperto. Inoltre
specifica i flag di protezione della memoria e un offset in cui la regione della memoria condivisa
inizia nel file.
// Create a memory buffer from the file that contains the trained data
ANeuralNetworksMemory* mem1 = NULL;
int fd = open("training_data", O_RDONLY);
ANeuralNetworksMemory_createFromFd(file_size, PROT_READ, fd, 0, &mem1);
Anche se in questo esempio ne utilizziamo solo uno
ANeuralNetworksMemory
Ad esempio per tutti i pesi, è possibile utilizzare
ANeuralNetworksMemory
istanza per più file.
Usa buffer hardware nativi
Puoi usare buffer hardware nativi
per input, output e valori operando costanti. In alcuni casi,
L'acceleratore NNAPI può accedere
AHardwareBuffer
senza che il driver debba copiare i dati. AHardwareBuffer
ne ha molti
configurazioni diverse e non tutti gli acceleratori NNAPI possono supportare tutte
queste configurazioni. A causa di questo limite, fai riferimento ai vincoli
elencato in
Documentazione di riferimento di ANeuralNetworksMemory_createFromAHardwareBuffer
e testa in anticipo sui dispositivi di destinazione per garantire compilazioni ed esecuzioni
che utilizzano AHardwareBuffer
si comportano come previsto, utilizzando
assegnazione del dispositivo per specificare l'acceleratore.
Per consentire al runtime NNAPI di accedere a un oggetto AHardwareBuffer
, crea un'istanza
ANeuralNetworksMemory
richiamando l'istanza
ANeuralNetworksMemory_createFromAHardwareBuffer
e passando la funzione
AHardwareBuffer
, come mostrato nel seguente esempio di codice:
// Configure and create AHardwareBuffer object AHardwareBuffer_Desc desc = ... AHardwareBuffer* ahwb = nullptr; AHardwareBuffer_allocate(&desc, &ahwb); // Create ANeuralNetworksMemory from AHardwareBuffer ANeuralNetworksMemory* mem2 = NULL; ANeuralNetworksMemory_createFromAHardwareBuffer(ahwb, &mem2);
Quando NNAPI non deve più accedere all'oggetto AHardwareBuffer
, libera il token
istanza ANeuralNetworksMemory
corrispondente:
ANeuralNetworksMemory_free(mem2);
Nota:
- Puoi utilizzare la modalità
AHardwareBuffer
solo per l'intero buffer; non puoi utilizzarla un parametroARect
. - Il runtime NNAPI non verrà svuotato del buffer. Devi assicurarti che i buffer di input e output siano accessibile prima di pianificare l'esecuzione.
- Non è disponibile assistenza per di sincronizzazione dei descrittori dei file di recinto.
- Per un
AHardwareBuffer
con i formati e le parti d'uso specifici del fornitore, dipende dall'implementazione determinare se il client o il conducente sono responsabili dello svuotamento .
Modello
Un modello è l'unità fondamentale di calcolo in NNAPI. Ogni modello è definito da uno o più operandi e operazioni.
Operandi
Gli operanti sono oggetti di dati utilizzati per definire il grafico. Sono inclusi gli input e gli output del modello, i nodi intermedi che contengono i dati passa da un'operazione all'altra e le costanti che vengono trasmesse queste operazioni.
Ai modelli NNAPI è possibile aggiungere due tipi di operandi: scalari e tensori.
Uno scalare rappresenta un singolo valore. NNAPI supporta i valori scalari nei campi booleani, rappresentazione in virgola mobile a 16 bit, in virgola mobile a 32 bit, numero intero a 32 bit e senza segno Formati interi a 32 bit.
La maggior parte delle operazioni in NNAPI prevede l'uso di tensori. I tensori sono array n-dimensionali. NNAPI supporta tensori con rappresentazione in virgola mobile a 16 bit, in virgola mobile a 32 bit e a 8 bit quantizzato, quantizzato a 16 bit, numero intero a 32 bit e 8 bit valori booleani.
Ad esempio, la figura 3 rappresenta un modello con due operazioni: una addizione seguito da una moltiplicazione. Il modello prende un tensore di input e ne produce uno tensore di output.
.Il modello sopra riportato ha sette operandi. Questi operandi sono identificati implicitamente l'indice dell'ordine in cui vengono aggiunti al modello. Il primo operando aggiunto ha un indice pari a 0, al secondo un indice di 1 e così via. Operativi 1, 2, 3, e 5 sono operandi costanti.
L'ordine in cui vengono aggiunti gli operandi non è importante. Ad esempio, il modello l'operando di output potrebbe essere il primo aggiunto. La parte importante è utilizzare quando si fa riferimento a un operando.
Gli operando hanno dei tipi. Questi valori vengono specificati quando vengono aggiunti al modello.
Un operando non può essere utilizzato sia come input che come output di un modello.
Ogni operando deve essere un input del modello, una costante o l'operando di output di esattamente un'operazione.
Per ulteriori informazioni sull'uso degli operandi, vedi Scopri di più sugli operandi.
Fasi operative
Un'operazione specifica i calcoli da eseguire. Ogni operazione consiste di questi elementi:
- un tipo di operazione (ad esempio addizione, moltiplicazione, convoluzione),
- un elenco di indici degli operandi utilizzati dall'operazione per l'input e
- un elenco di indici degli operandi utilizzati dall'operazione per l'output.
L'ordine in questi elenchi è importante; vedi il Riferimento API NNAPI per gli input previsti e output di ogni tipo di operazione.
Devi aggiungere al modello gli operandi prodotti o consumati da un'operazione prima di aggiungere l'operazione.
L'ordine in cui vengono aggiunte le operazioni non è importante. NNAPI si basa sulle delle dipendenze stabilite dal grafico di calcolo di operandi e operazioni determinano l'ordine in cui vengono eseguite le operazioni.
Le operazioni supportate da NNAPI sono riassunte nella tabella seguente:
Problema noto nel livello API 28: al superamento
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
tensori al
ANEURALNETWORKS_PAD
disponibile su Android 9 (livello API 28) e versioni successive,
l'output da NNAPI potrebbe non corrispondere a quello del machine learning di livello superiore
di base, come
TensorFlow Lite. Tu
deve passare solo
ANEURALNETWORKS_TENSOR_FLOAT32
Il problema viene risolto in Android 10 (livello API 29) e versioni successive.
Creare modelli
Nell'esempio seguente, creiamo il modello a due operazioni che si trova in figura 3.
Per creare il modello, segui questi passaggi:
Chiama il
ANeuralNetworksModel_create()
per definire un modello vuoto.ANeuralNetworksModel* model = NULL; ANeuralNetworksModel_create(&model);
Aggiungi gli operandi al modello chiamando
ANeuralNetworks_addOperand()
I tipi di dati vengono definiti utilizzandoANeuralNetworksOperandType
struttura dei dati.// In our example, all our tensors are matrices of dimension [3][4] ANeuralNetworksOperandType tensor3x4Type; tensor3x4Type.type = ANEURALNETWORKS_TENSOR_FLOAT32; tensor3x4Type.scale = 0.f; // These fields are used for quantized tensors tensor3x4Type.zeroPoint = 0; // These fields are used for quantized tensors tensor3x4Type.dimensionCount = 2; uint32_t dims[2] = {3, 4}; tensor3x4Type.dimensions = dims;
// We also specify operands that are activation function specifiers ANeuralNetworksOperandType activationType; activationType.type = ANEURALNETWORKS_INT32; activationType.scale = 0.f; activationType.zeroPoint = 0; activationType.dimensionCount = 0; activationType.dimensions = NULL;
// Now we add the seven operands, in the same order defined in the diagram ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 0 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 1 ANeuralNetworksModel_addOperand(model, &activationType); // operand 2 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 3 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 4 ANeuralNetworksModel_addOperand(model, &activationType); // operand 5 ANeuralNetworksModel_addOperand(model, &tensor3x4Type); // operand 6Per gli operandi che hanno valori costanti, come ponderazioni e bias che ottenuta da un processo di addestramento, utilizza
ANeuralNetworksModel_setOperandValue()
eANeuralNetworksModel_setOperandValueFromMemory()
funzioni.Nell'esempio seguente, impostiamo valori costanti dal file di dati di addestramento corrispondente al buffer di memoria creato in Fornisci l'accesso a: dati di addestramento.
// In our example, operands 1 and 3 are constant tensors whose values were // established during the training process const int sizeOfTensor = 3 * 4 * 4; // The formula for size calculation is dim0 * dim1 * elementSize ANeuralNetworksModel_setOperandValueFromMemory(model, 1, mem1, 0, sizeOfTensor); ANeuralNetworksModel_setOperandValueFromMemory(model, 3, mem1, sizeOfTensor, sizeOfTensor);
// We set the values of the activation operands, in our example operands 2 and 5 int32_t noneValue = ANEURALNETWORKS_FUSED_NONE; ANeuralNetworksModel_setOperandValue(model, 2, &noneValue, sizeof(noneValue)); ANeuralNetworksModel_setOperandValue(model, 5, &noneValue, sizeof(noneValue));Per ogni operazione che vuoi calcolare nel grafico diretto, aggiungi il metodo operativa al tuo modello richiamando
ANeuralNetworksModel_addOperation()
personalizzata.Come parametri per questa chiamata, l'app deve fornire:
- il tipo di operazione
- il conteggio dei valori di input
- l'array degli indici per gli operandi di input
- il conteggio dei valori di output
- l'array degli indici per gli operandi di output
Tieni presente che non è possibile utilizzare un operando sia per l'input che per l'output dello stesso operativa.
// We have two operations in our example // The first consumes operands 1, 0, 2, and produces operand 4 uint32_t addInputIndexes[3] = {1, 0, 2}; uint32_t addOutputIndexes[1] = {4}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3, addInputIndexes, 1, addOutputIndexes);
// The second consumes operands 3, 4, 5, and produces operand 6 uint32_t multInputIndexes[3] = {3, 4, 5}; uint32_t multOutputIndexes[1] = {6}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_MUL, 3, multInputIndexes, 1, multOutputIndexes);Identificare quali operandi il modello deve trattare come input e output chiamando il
ANeuralNetworksModel_identifyInputsAndOutputs()
personalizzata.// Our model has one input (0) and one output (6) uint32_t modelInputIndexes[1] = {0}; uint32_t modelOutputIndexes[1] = {6}; ANeuralNetworksModel_identifyInputsAndOutputs(model, 1, modelInputIndexes, 1 modelOutputIndexes);
Facoltativamente, puoi specificare se
ANEURALNETWORKS_TENSOR_FLOAT32
può essere calcolata con un intervallo o una precisione più bassi di quelli Formato in virgola mobile IEEE 754 a 16 bit richiamandoANeuralNetworksModel_relaxComputationFloat32toFloat16()
.Chiama il numero
ANeuralNetworksModel_finish()
per finalizzare la definizione del modello. Se non ci sono errori, restituisce un codice risultato diANEURALNETWORKS_NO_ERROR
ANeuralNetworksModel_finish(model);
Una volta creato un modello, puoi compilarlo un numero qualsiasi di volte ed eseguire ogni una qualsiasi numero di volte.
Flusso di controllo
Per incorporare il flusso di controllo in un modello NNAPI, segui questi passaggi:
Crea i sottografici di esecuzione corrispondenti (
then
eelse
grafici secondari per un'istruzioneIF
,condition
ebody
sottografi per un cicloWHILE
) come modelliANeuralNetworksModel*
autonomi:ANeuralNetworksModel* thenModel = makeThenModel(); ANeuralNetworksModel* elseModel = makeElseModel();
Creare operandi che fanno riferimento a questi modelli all'interno del modello contenente flusso di controllo:
ANeuralNetworksOperandType modelType = { .type = ANEURALNETWORKS_MODEL, }; ANeuralNetworksModel_addOperand(model, &modelType); // kThenOperandIndex ANeuralNetworksModel_addOperand(model, &modelType); // kElseOperandIndex ANeuralNetworksModel_setOperandValueFromModel(model, kThenOperandIndex, &thenModel); ANeuralNetworksModel_setOperandValueFromModel(model, kElseOperandIndex, &elseModel);
Aggiungi l'operazione del flusso di controllo:
uint32_t inputs[] = {kConditionOperandIndex, kThenOperandIndex, kElseOperandIndex, kInput1, kInput2, kInput3}; uint32_t outputs[] = {kOutput1, kOutput2}; ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_IF, std::size(inputs), inputs, std::size(output), outputs);
Compilation
Il passaggio di compilazione determina su quali processori verrà eseguito il modello e chiede ai conducenti corrispondenti di prepararsi per l'esecuzione. Questo potrebbe includi la generazione di un codice macchina specifico per i processori del tuo modello in cui verrà eseguita.
Per compilare un modello, segui questi passaggi:
Chiama il
ANeuralNetworksCompilation_create()
per creare una nuova istanza di compilazione.// Compile the model ANeuralNetworksCompilation* compilation; ANeuralNetworksCompilation_create(model, &compilation);
Facoltativamente, puoi utilizzare l'assegnazione dispositivo per e scegliere i dispositivi su cui eseguire.
Puoi anche influenzare il compromesso tra l'autonomia della batteria la velocità di utilizzo e di esecuzione. Puoi farlo chiamando
ANeuralNetworksCompilation_setPreference()
// Ask to optimize for low power consumption ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);
Le preferenze che puoi specificare includono:
ANEURALNETWORKS_PREFER_LOW_POWER
: Preferisci l'esecuzione in modo da ridurre al minimo il consumo della batteria. Ciò è auspicabile per compilation che vengono eseguite spesso.ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER
: Preferisci restituire una singola risposta il più rapidamente possibile, anche se questo causa un maggiore consumo di energia. Questa è l'impostazione predefinita.ANEURALNETWORKS_PREFER_SUSTAINED_SPEED
: Preferisci massimizzare la velocità effettiva dei frame successivi, ad esempio quando l'elaborazione di fotogrammi successivi provenienti dalla fotocamera.
Facoltativamente, puoi configurare la memorizzazione nella cache di compilazione richiamando
ANeuralNetworksCompilation_setCaching
// Set up compilation caching ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);
Utilizza
getCodeCacheDir()
percacheDir
. Il valoretoken
specificato deve essere univoco per ogni modello all'interno di per l'applicazione.Finalizza la definizione della compilazione richiamando
ANeuralNetworksCompilation_finish()
Se non ci sono errori, questa funzione restituisce il codice risultatoANEURALNETWORKS_NO_ERROR
ANeuralNetworksCompilation_finish(compilation);
Rilevamento e assegnazione dei dispositivi
Sui dispositivi con Android 10 (livello API 29) e versioni successive, NNAPI fornisce che consentono alle librerie e alle app di framework di machine learning di ottenere informazioni sui dispositivi disponibili e specificare quelli da utilizzare dell'esecuzione. Se fornisci informazioni sui dispositivi disponibili consenti alle app di ricevere la versione esatta dei driver trovata su un dispositivo per evitare incompatibilità. Consentendo alle app di specificare quali dispositivi devono eseguire diverse sezioni di un modello, le app possono essere ottimizzate per Android su cui vengono distribuiti.
Rilevamento dispositivi
Utilizza le funzionalità di
ANeuralNetworks_getDeviceCount
per ottenere il numero di dispositivi disponibili. Per ogni dispositivo, usa
ANeuralNetworks_getDevice
per impostare un'istanza ANeuralNetworksDevice
su un riferimento a quel dispositivo.
Una volta ottenuto il riferimento di un dispositivo, puoi trovare ulteriori informazioni sulla il dispositivo utilizzando le seguenti funzioni:
ANeuralNetworksDevice_getFeatureLevel
ANeuralNetworksDevice_getName
ANeuralNetworksDevice_getType
ANeuralNetworksDevice_getVersion
Assegnazione dispositivo
Utilizza le funzionalità di
ANeuralNetworksModel_getSupportedOperationsForDevices
per scoprire quali operazioni di un modello possono essere eseguite su dispositivi specifici.
Per controllare quali acceleratori utilizzare per l'esecuzione, richiama
ANeuralNetworksCompilation_createForDevices
al posto di ANeuralNetworksCompilation_create
.
Utilizza l'oggetto ANeuralNetworksCompilation
risultante, come di consueto.
La funzione restituisce un errore se il modello fornito contiene operazioni che
non supportata dai dispositivi selezionati.
Se vengono specificati più dispositivi, il runtime è responsabile della distribuzione il lavoro sui vari dispositivi.
Analogamente ad altri dispositivi, l'implementazione della CPU NNAPI è rappresentata da un
ANeuralNetworksDevice
con il nome nnapi-reference
e il tipo
ANEURALNETWORKS_DEVICE_TYPE_CPU
. Durante la chiamata
ANeuralNetworksCompilation_createForDevices
, l'implementazione della CPU non è
utilizzata per gestire i casi di errore per la compilazione e l'esecuzione del modello.
È responsabilità di un'applicazione partizionare un modello in modelli secondari che
possono essere eseguiti sui dispositivi specificati. Applicazioni che non richiedono l'esecuzione di operazioni manuali
il partizionamento deve continuare a chiamare la più semplice
ANeuralNetworksCompilation_create
di utilizzare tutti i dispositivi disponibili (inclusa la CPU) per accelerare
un modello di machine learning. Se non è stato possibile supportare completamente il modello dai dispositivi specificati
tramite ANeuralNetworksCompilation_createForDevices
,
ANEURALNETWORKS_BAD_DATA
viene restituito.
Partizionamento del modello
Quando sono disponibili più dispositivi per il modello, il tempo di esecuzione NNAPI
distribuisce equamente il lavoro tra i vari dispositivi. Ad esempio, se più di un dispositivo era
forniti a ANeuralNetworksCompilation_createForDevices
, tutti i valori specificati
verranno prese in considerazione durante l'assegnazione del lavoro. Tieni presente che, se il dispositivo CPU
non è presente nell'elenco, l'esecuzione della CPU verrà disabilitata. Quando utilizzi ANeuralNetworksCompilation_create
verranno presi in considerazione tutti i dispositivi disponibili, inclusa la CPU.
La distribuzione viene effettuata selezionandolo dall'elenco dei dispositivi disponibili, per
delle operazioni nel modello, il dispositivo che supporta l'operazione e
dichiarando le migliori prestazioni, ovvero i tempi di esecuzione più rapidi
il consumo energetico minimo, in base alla preferenza di esecuzione specificata
il cliente. Questo algoritmo di partizionamento non tiene conto di possibili
causate dall'IO tra i diversi processori; pertanto, quando
specificare più processori (in modo esplicito quando si utilizza
ANeuralNetworksCompilation_createForDevices
o in modo implicito utilizzando
ANeuralNetworksCompilation_create
) è importante profilare il risultato
un'applicazione.
Per comprendere come il modello è stato partizionato da NNAPI, controlla
Log di Android per un messaggio (a livello di INFO con tag ExecutionPlan
):
ModelBuilder::findBestDeviceForEachOperation(op-name): device-index
op-name
è il nome descrittivo dell'operazione nel grafico e
device-index
è l'indice del dispositivo candidato nell'elenco dei dispositivi.
Questo elenco è l'input fornito a ANeuralNetworksCompilation_createForDevices
o, se usi ANeuralNetworksCompilation_createForDevices
, l'elenco di dispositivi
restituiti durante l'iterazione su tutti i dispositivi utilizzando ANeuralNetworks_getDeviceCount
e
ANeuralNetworks_getDevice
.
Il messaggio (a livello di INFO con il tag ExecutionPlan
):
ModelBuilder::partitionTheWork: only one best device: device-name
Questo messaggio indica che l'intero grafico è stato accelerato sul dispositivo.
device-name
.
Esecuzione
Il passaggio di esecuzione applica il modello a un insieme di input e archivia output di calcolo in uno o più buffer o spazi di memoria utente che l'app sono state assegnate.
Per eseguire un modello compilato, segui questi passaggi:
Chiama il
ANeuralNetworksExecution_create()
per creare una nuova istanza di esecuzione.// Run the compiled model against a set of inputs ANeuralNetworksExecution* run1 = NULL; ANeuralNetworksExecution_create(compilation, &run1);
Specifica dove l'app legge i valori di input per il calcolo. La tua app Può leggere i valori di input da un buffer utente o da uno spazio di memoria allocato chiamando
ANeuralNetworksExecution_setInput()
oANeuralNetworksExecution_setInputFromMemory()
rispettivamente.// Set the single input to our sample model. Since it is small, we won't use a memory buffer float32 myInput[3][4] = { ...the data... }; ANeuralNetworksExecution_setInput(run1, 0, NULL, myInput, sizeof(myInput));
Specifica dove l'app scrive i valori di output. L'app può scrivere valori di output in un un buffer utente o uno spazio di memoria allocato, richiamando
ANeuralNetworksExecution_setOutput()
oppureANeuralNetworksExecution_setOutputFromMemory()
rispettivamente.// Set the output float32 myOutput[3][4]; ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
Pianifica l'avvio dell'esecuzione chiamando il metodo
ANeuralNetworksExecution_startCompute()
personalizzata. Se non ci sono errori, questa funzione restituisce il codice risultatoANEURALNETWORKS_NO_ERROR
// Starts the work. The work proceeds asynchronously ANeuralNetworksEvent* run1_end = NULL; ANeuralNetworksExecution_startCompute(run1, &run1_end);
Chiama il
ANeuralNetworksEvent_wait()
per attendere il completamento dell'esecuzione. Se l'esecuzione fosse riuscita, questa funzione restituisce un codice risultatoANEURALNETWORKS_NO_ERROR
L'attesa può essere eseguita su un thread diverso da quello che avvia l'esecuzione.// For our example, we have no other work to do and will just wait for the completion ANeuralNetworksEvent_wait(run1_end); ANeuralNetworksEvent_free(run1_end); ANeuralNetworksExecution_free(run1);
Facoltativamente, puoi applicare un set di input diverso al modello compilato utilizzando la stessa istanza di compilazione per creare
ANeuralNetworksExecution
in esecuzione in un'istanza Compute Engine.// Apply the compiled model to a different set of inputs ANeuralNetworksExecution* run2; ANeuralNetworksExecution_create(compilation, &run2); ANeuralNetworksExecution_setInput(run2, ...); ANeuralNetworksExecution_setOutput(run2, ...); ANeuralNetworksEvent* run2_end = NULL; ANeuralNetworksExecution_startCompute(run2, &run2_end); ANeuralNetworksEvent_wait(run2_end); ANeuralNetworksEvent_free(run2_end); ANeuralNetworksExecution_free(run2);
Esecuzione sincrona
L'esecuzione asincrona richiede del tempo per generare e sincronizzare i thread. Inoltre, la latenza può essere molto variabile, con la latenza più lunga ritardi fino a 500 microsecondi tra il momento in cui un thread viene avvisato e l'ora che alla fine viene associata a un core della CPU.
Per migliorare la latenza, puoi invece indirizzare un'applicazione alla creazione di un modello
di inferenza al runtime. La chiamata viene restituita solo una volta che un'inferenza è
anziché tornare una volta avviata l'inferenza. Invece
delle chiamate
ANeuralNetworksExecution_startCompute
per una chiamata di inferenza asincrona al runtime, l'applicazione chiama
ANeuralNetworksExecution_compute
per effettuare una chiamata sincrona al runtime. Una chiamata a
ANeuralNetworksExecution_compute
non accetta ANeuralNetworksEvent
e
non è associato a una chiamata a ANeuralNetworksEvent_wait
.
Esecuzioni burst
Sui dispositivi con Android 10 (livello API 29) e versioni successive, la NNAPI supporta i burst
esecuzioni
ANeuralNetworksBurst
. Le esecuzioni a raffica sono una sequenza di esecuzioni della stessa compilazione
che si verificano in rapida successione, come quelle che
operano sui fotogrammi di una videocamera
o campioni audio successivi. L'utilizzo di ANeuralNetworksBurst
oggetti potrebbe
comportano esecuzioni più rapide, poiché indicano agli acceleratori che le risorse potrebbero
essere riutilizzato tra un'esecuzione e l'altra e che gli acceleratori rimangano in
prestazioni elevate per tutta la durata del burst.
ANeuralNetworksBurst
introduce solo una piccola modifica nella normale esecuzione
del tuo percorso di apprendimento. Per creare un oggetto burst
ANeuralNetworksBurst_create
,
come mostrato nello snippet di codice riportato di seguito:
// Create burst object to be reused across a sequence of executions ANeuralNetworksBurst* burst = NULL; ANeuralNetworksBurst_create(compilation, &burst);
Le esecuzioni di burst sono sincrone. Tuttavia, invece di utilizzare
ANeuralNetworksExecution_compute
eseguire ogni inferenza, accoppia le varie
ANeuralNetworksExecution
oggetti con lo stesso ANeuralNetworksBurst
nelle chiamate alla funzione
ANeuralNetworksExecution_burstCompute
// Create and configure first execution object // ... // Execute using the burst object ANeuralNetworksExecution_burstCompute(execution1, burst); // Use results of first execution and free the execution object // ... // Create and configure second execution object // ... // Execute using the same burst object ANeuralNetworksExecution_burstCompute(execution2, burst); // Use results of second execution and free the execution object // ...
Libera l'oggetto ANeuralNetworksBurst
con
ANeuralNetworksBurst_free
quando non è più necessario.
// Cleanup ANeuralNetworksBurst_free(burst);
Code di comandi asincroni ed esecuzione protetta
In Android 11 e versioni successive, NNAPI supporta un modo aggiuntivo per programmare
dell'esecuzione asincrona tramite
ANeuralNetworksExecution_startComputeWithDependencies()
. Quando utilizzi questo metodo, l'esecuzione attende tutti i casi
eventi da segnalare prima di iniziare la valutazione. Una volta che l'esecuzione è
e gli output sono pronti per essere utilizzati, l'evento restituito
segnalato.
A seconda dei dispositivi che gestiscono l'esecuzione, l'evento potrebbe essere supportato da una
sincronizza recinto. Tu
deve chiamare
ANeuralNetworksEvent_wait()
attendere l'evento e recuperare le risorse utilizzate dall'esecuzione. Tu
puoi importare le barriere di sincronizzazione in un oggetto evento utilizzando
ANeuralNetworksEvent_createFromSyncFenceFd()
,
ed esportare le barriere di sincronizzazione da un oggetto evento utilizzando
ANeuralNetworksEvent_getSyncFenceFd()
.
Output con dimensioni dinamiche
Per supportare modelli in cui la dimensione dell'output dipende dall'input
ovvero dove non è possibile determinare la dimensione al momento dell'esecuzione del modello
volta, usa
ANeuralNetworksExecution_getOutputOperandRank
e
ANeuralNetworksExecution_getOutputOperandDimensions
.
Il seguente esempio di codice mostra come eseguire questa operazione:
// Get the rank of the output uint32_t myOutputRank = 0; ANeuralNetworksExecution_getOutputOperandRank(run1, 0, &myOutputRank); // Get the dimensions of the output std::vector<uint32_t> myOutputDimensions(myOutputRank); ANeuralNetworksExecution_getOutputOperandDimensions(run1, 0, myOutputDimensions.data());
Pulizia
Il passaggio di pulizia gestisce la liberazione delle risorse interne utilizzate per calcolo.
// Cleanup ANeuralNetworksCompilation_free(compilation); ANeuralNetworksModel_free(model); ANeuralNetworksMemory_free(mem1);
Gestione degli errori e fallback della CPU
In caso di errore durante il partizionamento, se un driver non riesce a compilare (pezzo di un) modello, o se un driver non riesce a eseguire un modello compilato (pezzo di un), NNAPI potrebbe ricorrere alla propria implementazione della CPU operazioni.
Se il client NNAPI contiene versioni ottimizzate dell'operazione (ad esempio, (ad es. TFLite) potrebbe essere vantaggioso disabilitare il fallback della CPU Gestire gli errori con l'implementazione delle operazioni ottimizzate del client.
In Android 10, se la compilazione viene eseguita con
ANeuralNetworksCompilation_createForDevices
, la CPU di riserva verrà disattivata.
In Android P, l'esecuzione della NNAPI torna alla CPU in caso di errore dell'esecuzione sul driver.
Questo vale anche per Android 10 quando ANeuralNetworksCompilation_create
invece
di ANeuralNetworksCompilation_createForDevices
.
La prima esecuzione torna indietro per quella singola partizione e, se ancora un errore, esegue un nuovo tentativo dell'intero modello sulla CPU.
Se il partizionamento o la compilazione non riesce, l'intero modello verrà provato sulla CPU.
In alcuni casi alcune operazioni non sono supportate sulla CPU e, in questi casi, la compilazione o l'esecuzione delle situazioni non riuscirà, invece di tornare indietro.
Anche dopo la disabilitazione della riserva della CPU, il modello potrebbero continuare a eseguire operazioni
pianificati sulla CPU. Se la CPU è nell'elenco dei processori forniti
a ANeuralNetworksCompilation_createForDevices
ed è l'unico
che supporta queste operazioni o che dichiara di essere il più
del rendimento per queste operazioni, verrà scelto come primario (non di riserva)
esecutore.
Per assicurarti che non venga eseguita l'esecuzione della CPU, utilizza ANeuralNetworksCompilation_createForDevices
escludendo nnapi-reference
dall'elenco dei dispositivi.
A partire da Android P, è possibile disattivare i video di riserva in fase di esecuzione su
La build di DEBUG viene creata impostando la proprietà debug.nn.partition
su 2.
Domini di memoria
In Android 11 e versioni successive, NNAPI supporta i domini di memoria che forniscono l'allocatore interfacce per memorie opache. Ciò consente alle applicazioni di trasmettere contenuti nativi le memorie di tutte le esecuzioni, in modo che NNAPI non copia o trasforma i dati inutilmente quando si eseguono esecuzioni consecutive sullo stesso driver.
La caratteristica del dominio di memoria è destinata ai tensori che sono per lo più interni il driver e che non necessitano di accesso frequente al lato client. Esempi di Questi tensori includono i tensori di stato nei modelli in sequenza. Per tensori che necessitano un accesso frequente alla CPU sul lato client, usa invece pool di memoria condivisi.
Per allocare una memoria opaca, segui questi passaggi:
Chiama il
ANeuralNetworksMemoryDesc_create()
per creare un nuovo descrittore della memoria:// Create a memory descriptor ANeuralNetworksMemoryDesc* desc; ANeuralNetworksMemoryDesc_create(&desc);
Specifica tutti i ruoli di input e output previsti richiamando
ANeuralNetworksMemoryDesc_addInputRole()
eANeuralNetworksMemoryDesc_addOutputRole()
.// Specify that the memory may be used as the first input and the first output // of the compilation ANeuralNetworksMemoryDesc_addInputRole(desc, compilation, 0, 1.0f); ANeuralNetworksMemoryDesc_addOutputRole(desc, compilation, 0, 1.0f);
Se vuoi, specifica le dimensioni della memoria richiamando
ANeuralNetworksMemoryDesc_setDimensions()
// Specify the memory dimensions uint32_t dims[] = {3, 4}; ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
Finalizza la definizione del descrittore richiamando
ANeuralNetworksMemoryDesc_finish()
ANeuralNetworksMemoryDesc_finish(desc);
Alloca tutti i ricordi che ti servono passando il descrittore a
ANeuralNetworksMemory_createFromDesc()
// Allocate two opaque memories with the descriptor ANeuralNetworksMemory* opaqueMem; ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
Libera il descrittore della memoria quando non ti serve più.
ANeuralNetworksMemoryDesc_free(desc);
Il client può utilizzare solo l'oggetto ANeuralNetworksMemory
creato con
ANeuralNetworksExecution_setInputFromMemory()
o
ANeuralNetworksExecution_setOutputFromMemory()
in base ai ruoli
specificato nell'oggetto ANeuralNetworksMemoryDesc
. Offset e lunghezza
devono essere impostati su 0 per indicare che viene utilizzata l'intera memoria. Il cliente
può anche impostare o estrarre in modo esplicito i contenuti della memoria utilizzando
ANeuralNetworksMemory_copy()
Puoi creare ricordi opachi con ruoli di dimensioni o ranking non specificati.
In tal caso, la creazione della memoria potrebbe non riuscire a causa
ANEURALNETWORKS_OP_FAILED
se non è supportato dalla struttura
conducente. Il cliente è invitato a implementare la logica di fallback allocando una
buffer sufficientemente grande, supportato da AHardwareBuffer
in modalità BLOB o Ashmem.
Quando NNAPI non ha più bisogno di accedere all'oggetto di memoria opaco, libera il
istanza ANeuralNetworksMemory
corrispondente:
ANeuralNetworksMemory_free(opaqueMem);
Misurare le prestazioni
Puoi valutare le prestazioni della tua app misurando il tempo di esecuzione oppure la profilazione.
Tempo di esecuzione
Se vuoi determinare il tempo totale di esecuzione attraverso il runtime, puoi usare
l'API synchronous activation e misura il tempo impiegato dalla chiamata. Quando
Vuoi determinare il tempo totale di esecuzione attraverso un livello inferiore del software
stack, puoi utilizzare
ANeuralNetworksExecution_setMeasureTiming
e
ANeuralNetworksExecution_getDuration
per ottenere:
- su un acceleratore (non nel driver, che viene eseguito sull'host di elaborazione).
- tempo di esecuzione nel driver, incluso il tempo sull'acceleratore.
Il tempo di esecuzione nel driver esclude l'overhead come quello del runtime e l'IPC necessario affinché il runtime comunichi con il driver.
Queste API misurano la durata tra il lavoro inviato e quello completato piuttosto che il tempo dedicato dal conducente o dall'acceleratore all'esecuzione un'inferenza artificiale, eventualmente interrotta dal cambio di contesto.
Ad esempio, se inizia l'inferenza 1, il conducente smette di lavorare per eseguire l'inferenza 2, riprende e completa l'inferenza 1, il tempo di esecuzione l'inferenza 1 includerà il tempo in cui il lavoro è stato interrotto per eseguire l'inferenza 2.
Queste informazioni sulle tempistiche possono essere utili per un deployment di produzione di per raccogliere dati di telemetria da usare offline. Puoi usare i dati sulle tempistiche per modificare l'app per migliorare le prestazioni.
Quando utilizzi questa funzionalità, tieni presente quanto segue:
- La raccolta delle informazioni sulle tempistiche potrebbe comportare un costo in termini di prestazioni.
- Solo il conducente è in grado di calcolare il tempo trascorso da solo o sul dell'acceleratore, escluso il tempo trascorso nel runtime NNAPI e in IPC.
- Puoi utilizzare queste API solo con un
ANeuralNetworksExecution
che è stato creato conANeuralNetworksCompilation_createForDevices
connumDevices = 1
. - Nessun conducente deve essere in grado di segnalare le informazioni sulle tempistiche.
Profila la tua applicazione con Android Systrace
A partire da Android 10, NNAPI genera automaticamente systrace che che puoi utilizzare per profilare la tua applicazione.
L'origine NNAPI viene fornita con un'utilità parse_systrace
per elaborare
systrace generati dalla tua applicazione e genera una visualizzazione tabella che mostra
il tempo trascorso nelle diverse fasi del ciclo di vita del modello (creazione di un'istanza,
preparazione, esecuzione e cessazione della compilazione) e i diversi livelli
diverse applicazioni. I livelli in cui l'applicazione è divisa sono:
Application
: il codice principale dell'applicazioneRuntime
: runtime NNAPIIPC
: la comunicazione tra processi tra il runtime NNAPI e il driver codiceDriver
: processo del driver dell'acceleratore.
Genera i dati dell'analisi di profilazione
Supponendo che tu abbia controllato la struttura di origine AOSP alla pagina $ANDROID_BUILD_TOP e utilizzando l'esempio di classificazione delle immagini TFLite come applicazione di destinazione, puoi generare i dati di profilazione NNAPI con seguenti passaggi:
- Avvia il sistema Android con il seguente comando:
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py -o trace.html -a org.tensorflow.lite.examples.classification nnapi hal freq sched idle load binder_driver
Il parametro -o trace.html
indica che le tracce verranno
scritto nel trace.html
. Durante la profilazione della propria applicazione è necessario
sostituisci org.tensorflow.lite.examples.classification
con il nome del processo
specificate nel file manifest dell'app.
Questa operazione manterrà occupata una delle console della shell, non eseguire il comando
in background poiché è in attesa interattiva della terminazione di un enter
.
- Dopo aver avviato il raccoglitore systrace, avvia l'app ed esegui benchmark.
Nel nostro caso puoi avviare l'app Classificazione delle immagini da Android Studio. oppure direttamente dall'interfaccia utente del telefono di test, se l'app è già installata. Per generare alcuni dati NNAPI devi configurare l'app in modo che utilizzi NNAPI selezionando NNAPI come dispositivo di destinazione nella finestra di dialogo di configurazione dell'app.
Al termine del test, termina la systrace premendo
enter
su terminale della console attivo dal passaggio 1.Esegui l'utilità
systrace_parser
per generare statistiche cumulative:
$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html
Il parser accetta i seguenti parametri:
- --total-times
: mostra il tempo totale trascorso in un livello, incluso il tempo
spesi in attesa dell'esecuzione su una chiamata a un livello sottostante
- --print-detail
: visualizza tutti gli eventi raccolti da systrace
- --per-execution
: visualizza solo l'esecuzione e le relative sottofasi
(come tempi di esecuzione) anziché statistiche per tutte le fasi
- --json
: produce l'output in formato JSON
Di seguito è riportato un esempio di output:
===========================================================================================================================================
NNAPI timing summary (total time, ms wall-clock) Execution
----------------------------------------------------
Initialization Preparation Compilation I/O Compute Results Ex. total Termination Total
-------------- ----------- ----------- ----------- ------------ ----------- ----------- ----------- ----------
Application n/a 19.06 1789.25 n/a n/a 6.70 21.37 n/a 1831.17*
Runtime - 18.60 1787.48 2.93 11.37 0.12 14.42 1.32 1821.81
IPC 1.77 - 1781.36 0.02 8.86 - 8.88 - 1792.01
Driver 1.04 - 1779.21 n/a n/a n/a 7.70 - 1787.95
Total 1.77* 19.06* 1789.25* 2.93* 11.74* 6.70* 21.37* 1.32* 1831.17*
===========================================================================================================================================
* This total ignores missing (n/a) values and thus is not necessarily consistent with the rest of the numbers
L'analizzatore sintattico potrebbe non riuscire se gli eventi raccolti non rappresentano un numero completo traccia dell'applicazione. In particolare, potrebbe non riuscire se vengono generati eventi systrace per indicare la fine di una sezione sono presenti nella traccia senza un evento di inizio della sezione. Questo di solito accade se alcuni eventi di una sessione di profilazione generata all'avvio del raccoglitore systrace. In questo caso, sarà necessario eseguire nuovamente la profilazione.
Aggiungi statistiche per il codice dell'applicazione all'output di systrace_parser
L'applicazione parse_systrace è basata sul sistema Android systrace integrato funzionalità. Puoi aggiungere tracce per operazioni specifiche nella tua app utilizzando il metodo API systrace (per Java , per applicazioni native ) con i nomi degli eventi personalizzati.
Per associare gli eventi personalizzati alle fasi del ciclo di vita dell'applicazione: anteponi al nome dell'evento una delle seguenti stringhe:
[NN_LA_PI]
: evento a livello di applicazione per l'inizializzazione[NN_LA_PP]
: evento a livello di applicazione per la preparazione[NN_LA_PC]
: evento a livello di applicazione per la compilazione[NN_LA_PE]
: evento a livello di applicazione per l'esecuzione
Ecco un esempio di come modificare l'esempio di classificazione delle immagini TFLite
di codice aggiungendo una sezione runInferenceModel
per la fase Execution
e
Application
contenente un'altra sezione preprocessBitmap
che
non verranno considerate nelle tracce NNAPI. La sezione runInferenceModel
sarà
parte degli eventi systrace elaborati dall'analizzatore sintattico di systrace nnapi:
Kotlin
/** Runs inference and returns the classification results. */ fun recognizeImage(bitmap: Bitmap): List{ // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap") convertBitmapToByteBuffer(bitmap) Trace.endSection() // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel") long startTime = SystemClock.uptimeMillis() runInference() long endTime = SystemClock.uptimeMillis() Trace.endSection() ... return recognitions }
Java
/** Runs inference and returns the classification results. */ public ListrecognizeImage(final Bitmap bitmap) { // This section won’t appear in the NNAPI systrace analysis Trace.beginSection("preprocessBitmap"); convertBitmapToByteBuffer(bitmap); Trace.endSection(); // Run the inference call. // Add this method in to NNAPI systrace analysis. Trace.beginSection("[NN_LA_PE]runInferenceModel"); long startTime = SystemClock.uptimeMillis(); runInference(); long endTime = SystemClock.uptimeMillis(); Trace.endSection(); ... Trace.endSection(); return recognitions; }
Qualità del servizio
In Android 11 e versioni successive, NNAPI consente una migliore qualità del servizio (QoS) consentendo a un'applicazione di indicare le priorità relative dei suoi modelli, la quantità massima di tempo prevista per la preparazione di un determinato modello e la quantità massima di tempo previsto per completare un determinato calcolo. Android 11 introduce anche codici risultato NNAPI aggiuntivi che consentono alle applicazioni di comprendere errori come la mancata esecuzione le scadenze.
Imposta la priorità di un carico di lavoro
Per impostare la priorità di un carico di lavoro NNAPI, chiama
ANeuralNetworksCompilation_setPriority()
prima di chiamare ANeuralNetworksCompilation_finish()
.
Imposta scadenze
Le candidature possono fissare scadenze sia per la compilazione del modello che per l'inferenza.
- Per impostare il timeout della compilazione, richiama
ANeuralNetworksCompilation_setTimeout()
prima di chiamareANeuralNetworksCompilation_finish()
. - Per impostare il timeout dell'inferenza, richiama
ANeuralNetworksExecution_setTimeout()
prima di avviare la compilazione.
Ulteriori informazioni sugli operandi
La seguente sezione tratta argomenti avanzati sull'utilizzo degli operandi.
Tensori quantizzati
Un tensore quantizzato è un modo compatto per rappresentare un array n-dimensionale di valori con rappresentazione in virgola mobile.
NNAPI supporta i tensori quantizzati asimmetrici a 8 bit. Per questi tensori, il valore di ogni cella è rappresentato da un numero intero a 8 bit. Associato al tensore è una scala e un valore di punto zero. Queste vengono usate per convertire i dati a 8 bit numeri interi nei valori con rappresentazione in virgola mobile rappresentati.
La formula è:
(cellValue - zeroPoint) * scale
dove il valore zeroPoint è un numero intero a 32 bit e la scala è in virgola mobile a 32 bit il valore in punti.
Rispetto ai tensori con valori in virgola mobile a 32 bit, i tensori quantizzati a 8 bit presentano due vantaggi:
- La tua applicazione è più piccola, poiché i pesi addestrati occupano un quarto delle dimensioni di tensori a 32 bit.
- I calcoli spesso possono essere eseguiti più velocemente. Ciò è dovuto all'importo inferiore di dati che devono essere recuperati dalla memoria e l'efficienza dei processori come le DSP nelle calcoli di numeri interi.
Sebbene sia possibile convertire un modello in virgola mobile in uno quantizzato, i nostri ha dimostrato che si ottengono risultati migliori addestrando un modello un modello di machine learning. In effetti, la rete neurale impara a compensare una maggiore granularità di ogni valore. Per ogni tensore quantizzato, la scala I valori zeroPoint vengono determinati durante il processo di addestramento.
In NNAPI, definisci i tipi di tensori quantizzati impostando il campo del tipo di
ANeuralNetworksOperandType
struttura dei dati
ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
.
Devi specificare anche la scala e il valore zeroPoint del tensore nei dati
alla struttura del centro di costo.
Oltre ai tensori quantizzati asimmetrici a 8 bit, NNAPI supporta quanto segue:
ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
che puoi utilizzare per rappresentare i pesi Operazioni diCONV/DEPTHWISE_CONV/TRANSPOSED_CONV
.ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
che puoi utilizzare per lo stato internoQUANTIZED_16BIT_LSTM
.ANEURALNETWORKS_TENSOR_QUANT8_SYMM
che può essere un input perANEURALNETWORKS_DEQUANTIZE
.
Operatori facoltativi
Alcune operazioni, ad esempio
ANEURALNETWORKS_LSH_PROJECTION
,
prendono gli operandi facoltativi. Per indicare nel modello che l'operando facoltativo è
omesso, richiamare
ANeuralNetworksModel_setOperandValue()
, passando NULL
per il buffer e 0 per la lunghezza.
Se la decisione sulla presenza o meno dell'operando varia per ogni
di esecuzione, indichi che l'operando viene omesso utilizzando
ANeuralNetworksExecution_setInput()
o
ANeuralNetworksExecution_setOutput()
, passando NULL
per il buffer e 0 per la lunghezza.
Tensori di ranking sconosciuto
Android 9 (livello API 28) ha introdotto operandi del modello di dimensioni sconosciute, ma rango noto (il numero di dimensioni). Introduzione di Android 10 (livello API 29) tensori di rango sconosciuto, come mostrato in ANeuralNetworksOperandType.
Benchmark NNAPI
Il benchmark NNAPI è disponibile su AOSP nei seguenti paesi: platform/test/mlts/benchmark
(app benchmark) e platform/test/mlts/models
(modelli e set di dati).
Il benchmark valuta la latenza e l'accuratezza e confronta i driver con lo stesso il lavoro svolto utilizzando Tensorflow Lite in esecuzione sulla CPU, per gli stessi modelli e set di dati.
Per utilizzare il benchmark:
Collega un dispositivo Android di destinazione al computer, apri una finestra del terminale e assicurati che il dispositivo sia raggiungibile tramite ADB.
Se sono connessi più dispositivi Android, esporta quelli di destinazione.
ANDROID_SERIAL
variabile di ambiente.Vai alla directory di origine di primo livello Android.
Esegui questi comandi:
lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available ./test/mlts/benchmark/build_and_run_benchmark.sh
Al termine dell'esecuzione di un benchmark, i relativi risultati verranno presentati come pagina HTML passato a
xdg-open
.
Log NNAPI
NNAPI genera utili informazioni diagnostiche nei log di sistema. Per analizzare i log, utilizza il comando logcat utilità.
Attiva il logging NNAPI dettagliato per fasi o componenti specifici impostando il valore
debug.nn.vlog
(con adb shell
) al seguente elenco di valori,
separate da spazi, due punti o virgole:
model
: creazione del modellocompilation
: generazione del piano di esecuzione del modello e della compilazioneexecution
: esecuzione del modellocpuexe
: esecuzione di operazioni utilizzando l'implementazione della CPU NNAPImanager
: informazioni relative a estensioni NNAPI, interfacce disponibili e funzionalitàall
o1
: tutti gli elementi precedenti
Ad esempio, per abilitare il logging dettagliato completo, utilizza il comando
adb shell setprop debug.nn.vlog all
. Per disabilitare il logging dettagliato, utilizza il comando
adb shell setprop debug.nn.vlog '""'
.
Una volta abilitato, il logging dettagliato genera voci di log a livello di INFO con un tag impostato sul nome della fase o del componente.
Oltre ai messaggi controllati da debug.nn.vlog
, i componenti dell'API NNAPI forniscono
altre voci di log a vari livelli, ognuna con uno specifico tag di log.
Per ottenere un elenco dei componenti, cerca nella struttura di origine utilizzando la seguente espressione:
grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"
Al momento questa espressione restituisce i seguenti tag:
- Strumento per la creazione di burst
- Callback
- Builder di compilazione
- CpuExecutor
- ExecutionBuilder
- Controller ExecutionBurst
- ExecutionBurstServer
- Piano di esecuzione
- Pilota Fibonacci
- Grafico
- Wrapperforma indicizzata
- IonWatcher
- Manager
- Memoria
- Utili memoria
- Metamodello
- Informazioni sull'argomento del modello
- Creazione modelli
- Reti neurali
- Risolto con le operazioni
- Fasi operative
- Utili operativi
- Informazioni sul pacchetto
- TokenHasher
- Gestore tipi
- Utili
- ConvalidaHal
- Interfacce con più versioni
Per controllare il livello dei messaggi di log mostrati da logcat
, usa
la variabile di ambiente ANDROID_LOG_TAGS
.
Per visualizzare l'insieme completo dei messaggi di log NNAPI e disabilitare eventuali altri messaggi, imposta ANDROID_LOG_TAGS
su
le seguenti:
BurstBuilder:V Callbacks:V CompilationBuilder:V CpuExecutor:V ExecutionBuilder:V ExecutionBurstController:V ExecutionBurstServer:V ExecutionPlan:V FibonacciDriver:V GraphDump:V IndexedShapeWrapper:V IonWatcher:V Manager:V MemoryUtils:V Memory:V MetaModel:V ModelArgumentInfo:V ModelBuilder:V NeuralNetworks:V OperationResolver:V OperationsUtils:V Operations:V PackageInfo:V TokenHasher:V TypeManager:V Utils:V ValidateHal:V VersionedInterfaces:V *:S.
Puoi impostare ANDROID_LOG_TAGS
utilizzando il seguente comando:
export ANDROID_LOG_TAGS=$(grep -R 'define LOG_TAG' | awk -F '"' '{ print $2 ":V" }' | sort -u | egrep -v "Sample|FileTag|test" | xargs echo -n; echo ' *:S')
Tieni presente che questo è solo un filtro che si applica a logcat
. Devi ancora
imposta la proprietà debug.nn.vlog
su all
per generare informazioni di log dettagliate.