Android 神經(jīng)網(wǎng)絡(luò)API

Neural Networks API

NNAPI是一個(gè) Android C API,專門為在移動(dòng)設(shè)備上對(duì)機(jī)器學(xué)習(xí)運(yùn)行計(jì)算密集型運(yùn)算而設(shè)計(jì)。 NNAPI 旨在為構(gòu)建和訓(xùn)練神經(jīng)網(wǎng)絡(luò)的更高級(jí)機(jī)器學(xué)習(xí)框架(例如 (TensorFlow Lite、Caffe2 或其他)提供一個(gè)基礎(chǔ)的功能層。 API 適用于運(yùn)行 Android 8.1(API 級(jí)別 27)或更高版本的所有設(shè)備

NNAPI 支持通過以下方式進(jìn)行推理:將 Android 設(shè)備中的數(shù)據(jù)應(yīng)用到先前訓(xùn)練的開發(fā)者定義模型。 推理的示例包括分類圖像、預(yù)測(cè)用戶行為以及選擇對(duì)搜索查詢的適當(dāng)響應(yīng)。

設(shè)備上推理具有許多優(yōu)勢(shì):

  • 延遲時(shí)間:不需要通過網(wǎng)絡(luò)連接發(fā)送請(qǐng)求并等待響應(yīng)。 這對(duì)處理從攝像頭傳入的連續(xù)幀的視頻應(yīng)用至關(guān)重要。
  • 可用性:應(yīng)用甚至可以在沒有網(wǎng)絡(luò)覆蓋的條件下運(yùn)行。
  • 速度:與單純的通用 CPU 相比,特定于神經(jīng)網(wǎng)絡(luò)處理的新硬件可以提供顯著加快的計(jì)算速度。
  • 隱私:數(shù)據(jù)不會(huì)離開設(shè)備。
  • 費(fèi)用:所有計(jì)算都在設(shè)備上執(zhí)行,不需要服務(wù)器場(chǎng)。 還存在一些開發(fā)者應(yīng)考慮的利弊:
  • 系統(tǒng)利用率:評(píng)估神經(jīng)網(wǎng)絡(luò)涉及許多計(jì)算,這會(huì)增加電池消耗。 如果應(yīng)用需要注意耗電量,應(yīng)當(dāng)考慮監(jiān)視電池運(yùn)行狀況,尤其是針對(duì)長時(shí)間運(yùn)行的計(jì)算進(jìn)行監(jiān)視。
  • 應(yīng)用大小:注意模型的大小。 模型可能會(huì)占用很多兆字節(jié)的空間。 如果在APK中綁定較大的模型會(huì)過度地影響用戶,則您需要考慮在應(yīng)用安裝后下載模型、使用較小的模型或在云中運(yùn)行您的計(jì)算。 NNAPI 未提供在云中運(yùn)行模型的功能。

Neural Networks API 運(yùn)行時(shí)

NNAPI 將通過機(jī)器學(xué)習(xí)庫、框架和工具調(diào)用,這些工具可以讓開發(fā)者脫離設(shè)備訓(xùn)練他們的模型并將其部署在 Android 設(shè)備上。 應(yīng)用一般不會(huì)直接使用 NNAPI,但會(huì)直接使用更高級(jí)的機(jī)器學(xué)習(xí)框架。 這些框架反過來可以使用 NNAPI 在受支持的設(shè)備上執(zhí)行硬件加速的推理運(yùn)算。

根據(jù)應(yīng)用的要求和設(shè)備上的硬件能力,Android 的神經(jīng)網(wǎng)絡(luò)運(yùn)行時(shí)可以在可用的設(shè)備上處理器(包括專用的神經(jīng)網(wǎng)絡(luò)硬件、圖形處理單元 (GPU) 和數(shù)字信號(hào)處理器 (DSP))之間有效地分配計(jì)算工作負(fù)載。

對(duì)于缺少專用的供應(yīng)商驅(qū)動(dòng)程序的設(shè)備,NNAPI 運(yùn)行時(shí)將依賴優(yōu)化的代碼在 CPU 上執(zhí)行請(qǐng)求。

NNAPI 的高級(jí)系統(tǒng)架構(gòu)

NNAPI編程模型

要使用 NNAPI 執(zhí)行計(jì)算,首先需要構(gòu)建一個(gè)可以定義要執(zhí)行的計(jì)算的有向圖。 此計(jì)算圖與您的輸入數(shù)據(jù)(例如,從機(jī)器學(xué)習(xí)框架傳遞過來的權(quán)重和偏差)相結(jié)合,構(gòu)成 NNAPI 運(yùn)行時(shí)評(píng)估的模型。

NNAPI 使用四種主要抽象:

  • 模型:數(shù)學(xué)運(yùn)算和通過訓(xùn)練過程學(xué)習(xí)的常量值的計(jì)算圖。 這些運(yùn)算特定于神經(jīng)網(wǎng)絡(luò), 并且包括二維 (2D) 卷積、邏輯 (sigmoid)) 激活和整流線性 (ReLU) 激活等。 創(chuàng)建模型是一個(gè)同步操作,但是一旦成功創(chuàng)建,就可以在線程和編譯之間重用模型。 在 NNAPI 中,一個(gè)模型表示為一個(gè) ANeuralNetworksModel 實(shí)例。
  • 編譯:表示用于將 NNAPI 模型編譯到更低級(jí)別代碼中的配置。 創(chuàng)建編譯是一個(gè)同步操作,但是一旦成功創(chuàng)建,就可以在線程和執(zhí)行之間重用編譯。 在 NNAPI 中,每個(gè)編譯表示為一個(gè) ANeuralNetworksCompilation 實(shí)例。
  • 內(nèi)存:表示共享內(nèi)存、內(nèi)存映射文件和類似的內(nèi)存緩沖區(qū)。 使用內(nèi)存緩沖區(qū)可以讓 NNAPI 運(yùn)行時(shí)將數(shù)據(jù)更高效地傳輸?shù)津?qū)動(dòng)程序。 一個(gè)應(yīng)用一般會(huì)創(chuàng)建一個(gè)共享內(nèi)存緩沖區(qū),其中包含定義模型所需的每一個(gè)張量。 還可以使用內(nèi)存緩沖區(qū)存儲(chǔ)執(zhí)行實(shí)例的輸入和輸出。 在 NNAPI 中,每個(gè)內(nèi)存緩沖區(qū)表示為一個(gè) ANeuralNetworksMemory 實(shí)例。
  • 執(zhí)行:用于將 NNAPI 模型應(yīng)用到一組輸入并采集結(jié)果的接口。 執(zhí)行是一種異步操作。 多個(gè)線程可以在相同的執(zhí)行上等待。 當(dāng)執(zhí)行完成時(shí),所有的線程都將釋放。 在 NNAPI 中,每一個(gè)執(zhí)行表示為一個(gè) ANeuralNetworksExecution實(shí)例。
基本的編程流

提供訓(xùn)練數(shù)據(jù)訪問權(quán)限

訓(xùn)練權(quán)重和偏差數(shù)據(jù)可能存儲(chǔ)在一個(gè)文件中。 要讓 NNAPI 運(yùn)行時(shí)有效地獲取此數(shù)據(jù),請(qǐng)調(diào)用 ANeuralNetworksMemory_createFromFd() 函數(shù)并傳入已打開數(shù)據(jù)文件的文件描述符,創(chuàng)建一個(gè) ANeuralNetworksMemory 實(shí)例。

也可以在共享內(nèi)存區(qū)域于文件中開始的位置指定內(nèi)存保護(hù)標(biāo)志和偏移。

// 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);

盡管在此示例中我們僅為所有權(quán)重使用了一個(gè) ANeuralNetworksMemory 實(shí)例,但是可以為多個(gè)文件使用一個(gè)以上的 ANeuralNetworksMemory 實(shí)例

模型

模型是 NNAPI 中的基本計(jì)算單位。 每個(gè)模型都由一個(gè)或多個(gè)操作數(shù)運(yùn)算定義。

操作數(shù)

操作數(shù)是定義計(jì)算圖時(shí)使用的數(shù)據(jù)對(duì)象。 其中包括模型的輸入和輸出、包含從一個(gè)運(yùn)算流向另一個(gè)運(yùn)算的數(shù)據(jù)的中間節(jié)點(diǎn),以及傳遞到這些運(yùn)算的常量。

可以向 NNAPI 模型中添加兩種類型的操作數(shù):標(biāo)量張量

標(biāo)量表示一個(gè)數(shù)字。 NNAPI 支持 32 位浮點(diǎn)、32 位整數(shù)和無符號(hào) 32 位整數(shù)格式的標(biāo)量值。

NNAPI 的大多數(shù)運(yùn)算都涉及張量。 張量是 N 維數(shù)組。 NNAPI 支持具有 32 位整數(shù)、32 位浮點(diǎn)和 8 位量化值的張量。

表示一個(gè)具有兩種運(yùn)算的模型:先加法后乘法。 模型獲取輸入張量并生成一個(gè)輸出張量

上面的模型有七個(gè)操作數(shù)。 這些操作數(shù)按照它們添加到模型中的順序索引顯式標(biāo)識(shí)。 添加的第一個(gè)操作數(shù)的索引為 0,第二個(gè)操作數(shù)的索引為 1,依此類推。

添加操作數(shù)的順序不重要。 例如,模型輸出操作數(shù)可以是添加的第一個(gè)操作數(shù)。 重要的部分是在引用操作數(shù)時(shí)使用正確的索引值。

操作數(shù)具有類型。 這些類型在添加到模型中時(shí)指定。 一個(gè)操作數(shù)無法同時(shí)用作模型的輸入和輸出

運(yùn)算

運(yùn)算指定要執(zhí)行的計(jì)算。 每個(gè)運(yùn)算都包含下面這些元素:

  • 運(yùn)算類型(例如,加法、乘法、卷積),
  • 運(yùn)算用于輸入的操作數(shù)索引列表,以及
  • 運(yùn)算用于輸出的操作數(shù)索引列表。

操作數(shù)在這些列表中的順序非常重要;請(qǐng)針對(duì)每個(gè)運(yùn)算查閱 NNAPI API 參考,了解預(yù)期輸入和輸出。

在添加運(yùn)算之前,必須先將運(yùn)算消耗或生成的操作數(shù)添加到模型中。

添加運(yùn)算的順序不重要。 NNAPI 依賴操作數(shù)和運(yùn)算的計(jì)算圖建立的依賴關(guān)系來確定運(yùn)算的執(zhí)行順序。

下表匯總了 NNAPI 支持的運(yùn)算:


運(yùn)算

已知問題:ANEURALNETWORKS_TENSOR_QUANT8_ASYMM 張量傳遞到 ANEURALNETWORKS_PAD運(yùn)算(在 Android 9(API 級(jí)別 28)及更高版本中提供)時(shí),NNAPI 的輸出可能與較高級(jí)別機(jī)器學(xué)習(xí)框架(如 TensorFlow Lite)的輸出不匹配。 應(yīng)只傳遞 ANEURALNETWORKS_TENSOR_FLOAT32直到問題得到解決。

構(gòu)建模型

1 .調(diào)用 ANeuralNetworksModel_create() 函數(shù)來定義一個(gè)空模型。

ANeuralNetworksModel* model = NULL;
ANeuralNetworksModel_create(&model);

2 . 調(diào)用 ANeuralNetworks_addOperand(),將操作數(shù)添加到您的模型中。 它們的數(shù)據(jù)類型使用 ANeuralNetworksOperandType 數(shù)據(jù)結(jié)構(gòu)定義。

// 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 useful for quantized tensors.
tensor3x4Type.zeroPoint = 0;  // These fields are useful 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 6

3 . 對(duì)于具有常量值的操作數(shù),例如應(yīng)用從訓(xùn)練過程獲取的權(quán)重和偏差,請(qǐng)使用 ANeuralNetworksModel_setOperandValue()ANeuralNetworksModel_setOperandValueFromMemory() 函數(shù)。

// In our example, operands 1 and 3 are constant tensors whose value was
// 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));

4 .對(duì)于有向圖中您想要計(jì)算的每個(gè)運(yùn)算,請(qǐng)調(diào)用 ANeuralNetworksModel_addOperation() 函數(shù),將運(yùn)算添加到您的模型中。

應(yīng)用必須以此調(diào)用的參數(shù)形式提供以下各項(xiàng):

  • 運(yùn)算類型
  • 輸入值計(jì)數(shù),
  • 輸入操作數(shù)索引的數(shù)組,
  • 輸出值計(jì)數(shù),以及
  • 輸出操作數(shù)索引的數(shù)組。

請(qǐng)注意,一個(gè)操作數(shù)無法同時(shí)用作同一個(gè)運(yùn)算的輸入和輸出

// 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);

5 .調(diào)用 ANeuralNetworksModel_identifyInputsAndOutputs() 函數(shù),確定模型應(yīng)將哪些操作數(shù)視為其輸入和輸出。 此函數(shù)可以將模型配置為使用上面的第 4 步中指定的輸入和輸出操作數(shù)子集

// 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);

6 . 可選)通過調(diào)用 ANeuralNetworksModel_relaxComputationFloat32toFloat16(),指定是否允許 ANEURALNETWORKS_TENSOR_FLOAT32 使用低至 IEEE 754 16 位浮點(diǎn)格式的范圍或精度計(jì)算。

7 .調(diào)用 ANeuralNetworksModel_finish() 來最終確定模型的定義。 如果沒有錯(cuò)誤,此函數(shù)將返回 ANEURALNETWORKS_NO_ERROR 的結(jié)果代碼。

ANeuralNetworksModel_finish(model);

編譯

編譯步驟確定模型將在哪些處理器上執(zhí)行,并要求對(duì)應(yīng)的驅(qū)動(dòng)程序準(zhǔn)備其執(zhí)行。 這可能包括生成機(jī)器代碼,此代碼特定于模型將在其上面運(yùn)行的處理器。

要編譯模型,請(qǐng)按以下步驟操作:

  1. 調(diào)用 ANeuralNetworksCompilation_create() 函數(shù)來創(chuàng)建一個(gè)新的編譯實(shí)例。
// Compile the model.
ANeuralNetworksCompilation* compilation;
ANeuralNetworksCompilation_create(model, &compilation);

2.可以選擇性地影響運(yùn)行時(shí)如何在電池消耗與執(zhí)行速度之間權(quán)衡。 為此,可以調(diào)用 ANeuralNetworksCompilation_setPreference()

// Ask to optimize for low power consumption.
ANeuralNetworksCompilation_setPreference(compilation, ANEURALNETWORKS_PREFER_LOW_POWER);

可以指定的有效首選項(xiàng)包括:

*   [`ANEURALNETWORKS_PREFER_LOW_POWER`](https://developer.android.com/ndk/reference/group/neural-networks#group___neural_networks_1gga034380829226e2d980b2a7e63c992f18a370c42db64448662ad79116556bcec01): 傾向于以最大程度減小電池消耗的方式執(zhí)行。 此首選項(xiàng)適合將要經(jīng)常執(zhí)行的編譯。
*   [`ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER`](https://developer.android.com/ndk/reference/group/neural-networks#group___neural_networks_1gga034380829226e2d980b2a7e63c992f18af7fff807061a3e9358364a502691d887): 傾向于盡快返回單個(gè)回答,即使這會(huì)導(dǎo)致耗電量增加。
*   [`ANEURALNETWORKS_PREFER_SUSTAINED_SPEED`](https://developer.android.com/ndk/reference/group/neural-networks#group___neural_networks_1gga034380829226e2d980b2a7e63c992f18af727c25f1e2d8dcc693c477aef4ea5f5): 傾向于最大化連續(xù)幀的吞吐量,例如,在處理來自攝像頭的連續(xù)幀時(shí)。
  1. 調(diào)用 ANeuralNetworksCompilation_finish(),最終確定編譯定義。 如果沒有錯(cuò)誤,此函數(shù)將返回 ANEURALNETWORKS_NO_ERROR 的結(jié)果代碼。
ANeuralNetworksCompilation_finish(compilation);

執(zhí)行

執(zhí)行步驟會(huì)將模型應(yīng)用到一組輸入,并將計(jì)算輸出存儲(chǔ)到一個(gè)或多個(gè)用戶緩沖區(qū)或者應(yīng)用分配的內(nèi)存空間中。

要執(zhí)行編譯的模型,請(qǐng)按以下步驟操作:

  1. 調(diào)用 ANeuralNetworksExecution_create() 函數(shù)來創(chuàng)建一個(gè)新的執(zhí)行實(shí)例。
// Run the compiled model against a set of inputs.
ANeuralNetworksExecution* run1 = NULL;
ANeuralNetworksExecution_create(compilation, &run1);

2 指定應(yīng)用為計(jì)算讀取輸入值的位置。 通過分別調(diào)用 ANeuralNetworksExecution_setInput()ANeuralNetworksExecution_setInputFromMemory(),應(yīng)用可以從用戶緩沖區(qū)或分配的內(nèi)存空間讀取輸入值。

// 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));

3.指定應(yīng)用寫入輸出值的位置。 通過分別調(diào)用 ANeuralNetworksExecution_setOutput()ANeuralNetworksExecution_setOutputFromMemory(),應(yīng)用可以將輸出值分別寫入用戶緩沖區(qū)或分配的內(nèi)存空間。

// Set the output.
float32 myOutput[3][4];
ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));

  1. 調(diào)用 ANeuralNetworksExecution_startCompute() 函數(shù),計(jì)劃要開始的執(zhí)行。 如果沒有錯(cuò)誤,此函數(shù)將返回 ANEURALNETWORKS_NO_ERROR 的結(jié)果代碼。
// Starts the work. The work proceeds asynchronously.
ANeuralNetworksEvent* run1_end = NULL;
ANeuralNetworksExecution_startCompute(run1, &run1_end);
  1. 調(diào)用 ANeuralNetworksEvent_wait() 函數(shù)以等待執(zhí)行完成。 如果執(zhí)行成功,此函數(shù)將返回 ANEURALNETWORKS_NO_ERROR 的結(jié)果代碼。 等待可以在不同于開始執(zhí)行的線程上完成。
// 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);

6.或者,也可以使用同一個(gè)編譯實(shí)例來創(chuàng)建一個(gè)新的 ANeuralNetworksExecution 實(shí)例,將一組不同的輸入應(yīng)用到編譯的模型。

// 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);

清理

清理步驟可以處理計(jì)算所用內(nèi)部資源的釋放。

// Cleanup
ANeuralNetworksCompilation_free(compilation);
ANeuralNetworksModel_free(model);
ANeuralNetworksMemory_free(mem1);

操作數(shù)的更多主題

下面一部分介紹了有關(guān)使用操作數(shù)的高級(jí)主題。

量化張量
量化張量是一種表示 N 維浮點(diǎn)值數(shù)組的緊湊型方式。

NNAPI 支持 8 位非對(duì)稱量化張量。 對(duì)于這些張量,每個(gè)單元格的值都通過一個(gè) 8 位整數(shù)表示。 與張量關(guān)聯(lián)的是一個(gè)比例和一個(gè)零點(diǎn)值。 這些用于將 8 位整數(shù)轉(zhuǎn)換成要表示的浮點(diǎn)值。

公式為:

(cellValue - zeroPoint) * scale

其中,zeroPoint 值是一個(gè) 32 位整數(shù),scale 是一個(gè) 32 位浮點(diǎn)值。

與 32 位浮點(diǎn)值的張量相比,8 位量化張量具有兩個(gè)優(yōu)勢(shì):

  • 應(yīng)用將更小,因?yàn)橛?xùn)練的權(quán)重占 32 位張量大小的四分之一。
  • 計(jì)算通常可以更快地執(zhí)行。 這是因?yàn)閮H需要從內(nèi)存提取少量數(shù)據(jù),并且 DSP 等處理器進(jìn)行整數(shù)數(shù)學(xué)運(yùn)算的效率更高。

盡管可以將浮點(diǎn)值模型轉(zhuǎn)換成量化模型,但我們的經(jīng)驗(yàn)表明,直接訓(xùn)練量化模型可以取得更好的結(jié)果。 事實(shí)上,神經(jīng)網(wǎng)絡(luò)會(huì)通過學(xué)習(xí)來補(bǔ)償每個(gè)值增大的粒度。 對(duì)于量化張量,scale 和 zeroPoint 值在訓(xùn)練過程中確定。

在 NNAPI 中,需要將 ANeuralNetworksOperandType 數(shù)據(jù)結(jié)構(gòu)的類型字段設(shè)置為 ANEURALNETWORKS_TENSOR_QUANT8_ASYMM,定義量化張量類型。 還需要在該數(shù)據(jù)結(jié)構(gòu)中指定張量的 scale 和 zeroPoint 值。

可選操作數(shù)
一些運(yùn)算(例如 ANEURALNETWORKS_LSH_PROJECTION)會(huì)采用可選操作數(shù)。 要在模型中指示忽略可選操作數(shù),請(qǐng)調(diào)用 ANeuralNetworksModel_setOperandValue() 函數(shù),為 buffer 傳遞 NULL,為 length 傳遞 0。

如果是否使用操作數(shù)的決定因執(zhí)行而異,應(yīng)通過以下方式指示忽略操作數(shù):使用 ANeuralNetworksExecution_setInput()ANeuralNetworksExecution_setOutput() 函數(shù),同時(shí)為 buffer 傳遞 NULL,為 length 傳遞 0。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,136評(píng)論 1 32
  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 7,872評(píng)論 0 27
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí),會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,429評(píng)論 0 9
  • 所有知識(shí)點(diǎn)已整理成app app下載地址 J2EE 部分: 1.Switch能否用string做參數(shù)? 在 Jav...
    侯蛋蛋_閱讀 2,491評(píng)論 1 4
  • 女兒高考后開始搬家,從來不知道家里有如此多的東西,理解了嫂子告訴我的話,破家值萬貫的道理。 這是最后一車要拉的東西...
    悠然_3c09閱讀 461評(píng)論 1 4