Neural Networks API

Android Neural Networks API (NNAPI) adalah Android C API yang dirancang untuk menjalankan operasi komputasi intensif untuk machine learning di perangkat seluler. NNAPI dirancang untuk menyediakan lapisan dasar fungsionalitas untuk framework machine learning tingkat tinggi (seperti TensorFlow Lite, Caffe2, atau lainnya) yang mem-build dan melatih neural networks. API ini tersedia pada semua perangkat yang menjalankan Android 8.1 (API level 27) atau yang lebih tinggi.

NNAPI mendukung inferensi dengan menerapkan data dari perangkat Android ke model yang sebelumnya telah dilatih dan dirancang oleh developer. Contoh inferensi meliputi pengelompokan gambar, prediksi perilaku pengguna, dan pemilihan respons yang tepat untuk kueri penelusuran.

Inferensi pada perangkat memiliki banyak keuntungan:

  • Latensi: Anda tidak perlu mengirim permintaan melalui sambungan jaringan dan menunggu respons. Ini sangat penting untuk aplikasi video yang memproses bingkai berturutan dari kamera.
  • Ketersediaan: Aplikasi bisa berjalan bahkan saat berada di luar jangkauan jaringan.
  • Kecepatan: Hardware baru yang khusus untuk pemrosesan neural networks menghasilkan komputasi yang jauh lebih cepat daripada CPU biasa.
  • Privasi: Data disimpan di perangkat.
  • Biaya: Tidak diperlukan farm server ketika semua komputasi dilakukan pada perangkat.

Ada juga pilihan yang harus dipertimbangkan developer:

  • Pemanfaatan sistem: Evaluasi neural networks melibatkan banyak komputasi, yang bisa meningkatkan penggunaan daya baterai. Anda harus mempertimbangkan pemantauan kesehatan baterai jika ini menjadi perhatian bagi aplikasi Anda, terutama untuk komputasi yang berjalan lama.
  • Ukuran aplikasi: Perhatikan ukuran model Anda. Model bisa berukuran hingga beberapa megabyte. Jika pemaketan model besar di APK akan berdampak buruk bagi pengguna, Anda mungkin perlu mempertimbangkan untuk mendownload model setelah penginstalan aplikasi, menggunakan model yang lebih kecil, atau menjalankan komputasi di cloud. NNAPI tidak menyediakan fungsionalitas untuk menjalankan model di cloud.

Contoh terkait:

  1. Contoh Android Neural Networks API

Memahami Waktu Proses Neural Networks API

NNAPI dimaksudkan untuk dipanggil oleh library machine learning, framework, dan fitur yang memungkinkan developer melatih model mereka di luar perangkat dan menerapkannya di perangkat Android. Aplikasi biasanya tidak langsung menggunakan NNAPI, tetapi justru secara langsung menggunakan framework machine learning dengan tingkat yang lebih tinggi. Framework ini pada nantinya bisa menggunakan NNAPI untuk melakukan operasi inferensi dengan akselerasi hardware pada perangkat yang didukung.

Berdasarkan persyaratan aplikasi dan kemampuan hardware perangkat, waktu proses Android neural networks bisa secara efisien mendistribusikan beban kerja komputasi ke seluruh prosesor pada perangkat yang ada, termasuk hardware khusus neural network, graphics processing unit (GPU), dan digital signal processor (DSP).

Untuk perangkat yang tidak memiliki driver vendor khusus, waktu proses NNAPI bergantung pada kode yang dioptimalkan untuk menjalankan permintaan pada CPU.

Gambar 1 menunjukkan arsitektur sistem tingkat tinggi untuk NNAPI.

Gambar 1. Arsitektur sistem untuk Android Neural Networks API

Model pemrograman Neural Networks API

Untuk melakukan komputasi menggunakan NNAPI, pertama-tama Anda harus membuat grafik terarah yang akan menentukan komputasi yang dilakukan. Grafik komputasi ini, digabungkan dengan data masukan Anda (misalnya, bobot dan bias yang diturunkan dari framework machine learning), membentuk model untuk evaluasi waktu proses NNAPI.

NNAPI menggunakan empat abstraksi utama:

  • Model: Grafik komputasi dari operasi matematis dan nilai konstanta dipelajari melalui proses pelatihan. Operasi ini khusus untuk neural network. Ini mencakup konvolusi 2-dimensi (2D), aktivasi (sigmoid)) logistik, aktivasi linier diperbaiki (ReLU), dan banyak lagi. Membuat model adalah operasi yang dilakukan bersamaan, tetapi begitu berhasil dibuat, model bisa digunakan kembali untuk thread dan kompilasi. Dalam NNAPI, sebuah model dinyatakan sebagai instance ANeuralNetworksModel.
  • Kompilasi: Menyatakan konfigurasi untuk mengompilasi model NNAPI ke dalam kode dengan tingkat yang lebih rendah. Membuat kompilasi adalah operasi yang dilakukan bersamaan, tetapi begitu berhasil dibuat, model bisa digunakan kembali untuk thread dan kompilasi. Dalam NNAPI, setiap kompilasi dinyatakan sebagai instance ANeuralNetworksCompilation .
  • Memori: Menyatakan memori bersama, file yang dipetakan memori, dan buffer memori serupa. Menggunakan buffer memori memungkinkan waktu proses NNAPI mentransfer data ke driver dengan lebih efisien. Sebuah aplikasi biasanya membuat satu buffer memori bersama yang berisi setiap tensor yang diperlukan untuk menetapkan model. Anda juga bisa menggunakan buffer memori untuk menyimpan masukan dan keluaran untuk instance eksekusi. Dalam NNAPI, setiap buffer memori dinyatakan sebagai instance ANeuralNetworksMemory .
  • Eksekusi: Antarmuka untuk menerapkan model NNAPI ke sekumpulan masukan dan mengumpulkan hasilnya. Eksekusi adalah operasi yang tidak bersamaan. Beberapa thread bisa menunggu eksekusi yang sama. Setelah eksekusi selesai, semua thread akan dirilis. Dalam NNAPI, setiap eksekusi dinyatakan sebagai instance ANeuralNetworksExecution .

Gambar 2 menunjukkan alur pemrograman dasar.

Gambar 2. Alur pemrograman untuk Android Neural Networks API

Bagian selanjutnya akan menjelaskan langkah-langkah untuk menyiapkan model NNAPI guna melakukan komputasi, mengompilasi model, dan menjalankan model yang dikompilasi.

Menyediakan akses ke data latihan

Data bias dan bobot terlatih Anda kemungkinan tersimpan dalam sebuah file. Untuk menyediakan waktu proses NNAPI dengan akses yang efisien ke data ini, buat instance ANeuralNetworksMemory dengan memanggil fungsi ANeuralNetworksMemory_createFromFd() , dan meneruskan file deskriptor dari file data yang dibuka.

Anda juga bisa menetapkan flag proteksi memori dan offset tempat region memori bersama dimulai pada file tersebut.

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

Meskipun dalam contoh ini kita hanya menggunakan satu instance ANeuralNetworksMemory untuk semua bobot, menggunakan lebih dari satu instance ANeuralNetworksMemory untuk beberapa file juga diperbolehkan.

Model

Model adalah unit dasar komputasi dalam NNAPI. Setiap model didefinisikan oleh satu atau beberapa operand dan operasi.

Operand

Operand adalah objek data yang digunakan dalam menetapkan grafik. Ini mencakup masukan dan keluaran model, node perantara yang berisi data yang mengalir dari satu operasi ke operasi lainnya, dan konstanta yang diteruskan ke operasi ini.

Ada dua jenis operand yang bisa ditambahkan ke model NNAPI: skalar dan tensor.

Skalar menyatakan sebuah nomor. NNAPI mendukung nilai skalar dalam titik mengambang 32-bit, integer 32-bit, dan format integer 32-bit tak bertanda tangan.

Sebagian besar operasi dengan NNAPI melibatkan tensor. Tensor adalah array n-dimensional. NNAPI mendukung tensor dengan integer 32-bit, titik mengambang 32-bit, dan nilai quantized 8-bit.

Sebagai contoh, gambar 3 merepresentasikan sebuah model dengan dua operasi: sebuah operasi penjumlahan kemudian diikuti oleh perkalian. Model ini mengambil sebuah tensor masukan dan menghasilkan satu tensor keluaran.

Gambar 3. Contoh operand untuk model NNAPI

Model di atas memiliki tujuh operand. Operand ini diidentifikasi secara implisit oleh indeks urutan ketika mereka ditambahkan ke model. Operand pertama yang ditambahkan memiliki indeks 0, yang kedua memiliki indeks 1, dan seterusnya.

Urutan Anda menambahkan operand tidak menjadi masalah. Misalnya, operand keluaran model bisa menjadi yang pertama ditambahkan. Bagian terpenting adalah menggunakan nilai indeks yang tepat saat merujuk ke operand.

Operand memiliki beberapa tipe. Ini ditetapkan ketika mereka ditambahkan ke model. Operand tidak bisa digunakan sebagai masukan dan keluaran dari sebuah model.

Untuk topik tambahan tentang penggunaan operand, lihat Selengkapnya tentang operand.

Operasi

Sebuah operasi menentukan komputasi yang akan dilakukan. Setiap operasi terdiri dari elemen-elemen ini:

  • tipe operasi (misalnya, penjumlahan, perkalian, convolution),
  • daftar indeks operand yang digunakan operasi untuk masukan, dan
  • daftar indeks operand yang digunakan operasi untuk keluaran.

Urutan di daftar ini penting; lihat referensi NNAPI API untuk setiap operasi masukan dan keluaran yang diharapkan.

Anda harus menambahkan operand yang dipakai atau dihasilkan oleh operasi ke model sebelum menambahkan operasi tersebut.

Urutan Anda menambahkan operasi tidak menjadi masalah. NNAPI bergantung pada dependensi yang ditetapkan oleh grafik operand dan operasi komputasi untuk menentukan urutan operasi yang dijalankan.

Operasi yang didukung oleh NNAPI dirangkum dalam tabel di bawah ini:

Kategori Operasi
Operasi matematis element-wise
Operasi Array
Operasi Gambar
Operasi Lookup
Operasi Normalization
Operasi Convolution
Operasi Pooling
Operasi Aktivasi
Operasi lainnya

Masalah yang dikenal: Ketika meneruskan tensor ANEURALNETWORKS_TENSOR_QUANT8_ASYMM ke operasi ANEURALNETWORKS_PAD , yang tersedia di Android 9 (API level 28) dan yang lebih tinggi, keluaran dari NNAPI mungkin tidak cocok dengan keluaran dari framework machine learning tingkat tinggi, seperti TensorFlow Lite. Anda sebaiknya hanya meneruskan ANEURALNETWORKS_TENSOR_FLOAT32 sampai masalahnya teratasi.

Mem-build model

Untuk mem-build model, ikuti langkah-langkah berikut:

  1. Panggil fungsi ANeuralNetworksModel_create() untuk menetapkan model kosong.

    Pada contoh berikut, kita membuat dua model operasi yang terdapat dalam gambar 3.

    ANeuralNetworksModel* model = NULL;
    ANeuralNetworksModel_create(&model);
    
  2. Tambahkan operand ke model Anda dengan memanggil ANeuralNetworks_addOperand(). Tipe data mereka ditetapkan menggunakan struktur data ANeuralNetworksOperandType .

    // 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. Untuk operand yang memiliki nilai konstanta, seperti bobot dan bias yang diperoleh aplikasi dari proses latihan, gunakan fungsi ANeuralNetworksModel_setOperandValue() dan ANeuralNetworksModel_setOperandValueFromMemory() .

    Dalam contoh berikut, kita menetapkan nilai konstanta dari file data latihan yang kita buat sebagai buffer memori di atas.

    // 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. Untuk setiap operasi dalam grafik terarah yang ingin dihitung, tambahkan operasi ke model Anda dengan memanggil fungsi ANeuralNetworksModel_addOperation() .

    Sebagai parameter untuk panggilan ini, aplikasi Anda harus menyediakan:

    • tipe operasi,
    • jumlah nilai masukan,
    • array indeks untuk operand masukan,
    • jumlah nilai keluaran, dan
    • array indeks untuk operand keluaran.

    Perhatikan bahwa operand tidak bisa digunakan untuk masukan dan keluaran dari operasi yang sama.

    // 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. Identifikasi operand yang diperlakukan model sebagai masukan dan keluarannya, dengan memanggil fungsi ANeuralNetworksModel_identifyInputsAndOutputs() . Fungsi ini memungkinkan Anda mengonfigurasi model agar menggunakan subset dari operand masukan dan keluaran yang Anda tetapkan sebelumnya pada langkah 4.

    // 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. Opsional, tetapkan apakah ANEURALNETWORKS_TENSOR_FLOAT32 diizinkan untuk dihitung dengan kisaran atau presisi serendah format titik mengambang 16-bit IEEE 754 dengan memanggil ANeuralNetworksModel_relaxComputationFloat32toFloat16().

  7. Panggil ANeuralNetworksModel_finish() untuk menyelesaikan definisi model Anda. Jika tidak ada error, fungsi ini akan menampilkan kode hasil ANEURALNETWORKS_NO_ERROR.

    ANeuralNetworksModel_finish(model);
    

Setelah membuat model, Anda bisa mengompilasinya beberapa kali dan menjalankan setiap kompilasi beberapa kali.

Kompilasi

Langkah kompilasi menentukan prosesor yang akan menjalankan model Anda dan meminta driver yang sesuai untuk mempersiapkan eksekusinya. Ini bisa mencakup pembuatan kode mesin khusus untuk prosesor tempat model Anda akan berjalan.

Untuk mengompilasi model, ikuti langkah-langkah berikut:

  1. Panggil fungsi ANeuralNetworksCompilation_create() untuk membuat kompilasi instance baru.

    // Compile the model.
    ANeuralNetworksCompilation* compilation;
    ANeuralNetworksCompilation_create(model, &compilation);
    
  2. Anda bisa secara opsional memengaruhi bagaimana waktu proses memilih antara penggunaan daya baterai dan kecepatan eksekusi. Anda bisa melakukannya dengan memanggil ANeuralNetworksCompilation_setPreference().

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

    Preferensi valid yang bisa Anda tetapkan antara lain:

  3. Menyelesaikan definisi kompilasi dengan memanggil ANeuralNetworksCompilation_finish(). Jika tidak ada error, fungsi ini akan menampilkan kode hasil ANEURALNETWORKS_NO_ERROR.

    ANeuralNetworksCompilation_finish(compilation);
    

Eksekusi

Langkah eksekusi menerapkan model ke satu set masukan, dan menyimpan keluaran komputasi ke satu atau beberapa buffer pengguna atau ruang memori yang dialokasikan aplikasi Anda.

Untuk menjalankan model yang dikompilasi, ikuti langkah-langkah berikut:

  1. Panggil fungsi ANeuralNetworksExecution_create() untuk membuat instance eksekusi baru.

    // Run the compiled model against a set of inputs.
    ANeuralNetworksExecution* run1 = NULL;
    ANeuralNetworksExecution_create(compilation, &run1);
    
  2. Tetapkan di mana aplikasi Anda akan membaca nilai masukan untuk komputasi. Aplikasi Anda bisa membaca nilai masukan dari buffer pengguna atau ruang memori yang dialokasikan, dengan memanggil ANeuralNetworksExecution_setInput() atau ANeuralNetworksExecution_setInputFromMemory() .

    // 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. Tetapkan di mana aplikasi Anda akan menulis nilai keluaran. Aplikasi Anda bisa menulis nilai keluaran ke buffer pengguna atau ruang memori yang dialokasikan, dengan memanggil ANeuralNetworksExecution_setOutput() atau ANeuralNetworksExecution_setOutputFromMemory() .

    // Set the output.
    float32 myOutput[3][4];
    ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
    
  4. Jadwalkan eksekusi yang akan dimulai, dengan memanggil fungsi ANeuralNetworksExecution_startCompute() . Jika tidak ada error, fungsi ini akan menampilkan kode hasil ANEURALNETWORKS_NO_ERROR.

    // Starts the work. The work proceeds asynchronously.
    ANeuralNetworksEvent* run1_end = NULL;
    ANeuralNetworksExecution_startCompute(run1, &run1_end);
    
  5. Panggil fungsi ANeuralNetworksEvent_wait() untuk menunggu eksekusi selesai. Jika eksekusi berhasil, fungsi ini akan menampilkan kode hasil ANEURALNETWORKS_NO_ERROR. Menunggu bisa dilakukan pada thread yang berbeda selain thread yang memulai eksekusi.

    // 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. Opsional, Anda bisa menerapkan serangkaian masukan yang berbeda ke model yang dikompilasi dengan menggunakan kompilasi instance yang sama untuk membuat instance ANeuralNetworksExecution baru.

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

Pembersihan

Langkah pembersihan menangani pengosongan sumber daya internal yang digunakan untuk komputasi Anda.

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

Selengkapnya tentang operand

Bagian berikut membahas topik lanjutan tentang penggunaan operand.

Quantized tensor

Quantized tensor adalah cara ringkas untuk menyatakan array n-dimensional dari nilai titik mengambang.

NNAPI mendukung quantized tensor asimetris 8-bit. Untuk tensor-tensor ini, nilai setiap sel dinyatakan dengan integer 8-bit. Terkait dengan tensor adalah scale dan nilai zero point. Ini digunakan untuk mengonversi integer 8-bit menjadi nilai titik mengambang yang sedang dinyatakan.

Rumusnya adalah:

(cellValue - zeroPoint) * scale

dengan nilai zeroPoint adalah integer 32-bit dan scale-nya adalah nilai titik mengambang 32-bit.

Dibandingkan dengan tensor nilai titik mengambang 32-bit, quantized tensor 8-bit memiliki dua keunggulan:

  • Aplikasi Anda akan lebih kecil, karena bobot terlatih hanya berukuran seperempat ukuran tensor 32-bit.
  • Komputasi bisa dieksekusi lebih cepat. Hal ini disebabkan kecilnya data yang perlu diambil dari memori dan efisiensi prosesor seperti DSP dalam melakukan perhitungan integer.

Meskipun memungkinkan untuk mengubah model titik mengambang menjadi quantized, pengalaman kami menunjukkan bahwa hasil yang lebih baik bisa dicapai dengan melatih model quantized secara langsung. Akibatnya, neural network belajar untuk mengompensasi granularitas yang meningkat dari masing-masing nilai. Untuk setiap quantized tensor, scale dan nilai zeroPoint ditetapkan selama proses latihan.

Dalam NNAPI, Anda menentukan tipe quantized tensor dengan menyetel kolom tipe struktur data ANeuralNetworksOperandType ke ANEURALNETWORKS_TENSOR_QUANT8_ASYMM. Anda juga menentukan scale dan nilai zeroPoint tensor dalam struktur data tersebut.

Operand opsional

Beberapa operasi, seperti ANEURALNETWORKS_LSH_PROJECTION, melakukan operand opsional. Untuk menunjukkan pada model bahwa operand opsional dihilangkan, panggil fungsi ANeuralNetworksModel_setOperandValue() , teruskan NULL untuk buffer dan 0 untuk panjangnya.

Bila keputusan mengenai terdapat atau tidaknya operand berbeda-beda untuk setiap eksekusi, Anda menunjukkan bahwa operand dihilangkan dengan menggunakan fungsi ANeuralNetworksExecution_setInput() atau ANeuralNetworksExecution_setOutput() , berikan NULL untuk buffer dan 0 untuk panjangnya.