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 bagi framework machine learning tingkat tinggi (seperti TensorFlow Lite, Caffe2, atau lainnya) yang membangun dan melatih jaringan neural. API ini tersedia di 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 telah dilatih sebelumnya dan ditetapkan oleh developer. Contoh inferensi meliputi mengklasifikasi gambar, memprediksi perilaku pengguna, dan memilih respons yang tepat untuk sebuah 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 frame berurutan dari kamera.
  • Ketersediaan: Aplikasi bisa berjalan bahkan saat berada di luar jangkauan jaringan.
  • Kecepatan: Hardware baru yang khusus untuk pemrosesan jaringan neural 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 beberapa konsekuensi yang harus dipertimbangkan developer:

  • Pemanfaatan sistem: Evaluasi jaringan neural melibatkan banyak komputasi, yang bisa meningkatkan penggunaan daya baterai. Sebaiknya Anda mempertimbangkan pemantauan kesehatan baterai jika hal 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 dalam 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. Sampel Android Neural Networks API

Memahami Waktu Proses Neural Networks API

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

Berdasarkan persyaratan aplikasi dan kemampuan hardware perangkat, waktu proses jaringan neural Android bisa secara efisien mendistribusikan beban kerja komputasi ke seluruh prosesor pada perangkat yang ada, termasuk hardware jaringan neural khusus, unit pemrosesan grafis (GPU), dan pemroses sinyal digital (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 menjalankan komputasi menggunakan NNAPI, pertama-tama Anda harus membuat grafik terarah yang akan menentukan komputasi yang dilakukan. Grafik komputasi ini, digabungkan dengan data input 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 yang dipelajari melalui proses pelatihan. Operasi ini khusus untuk jaringan neural. Operasi ini mencakup konvolusi 2 dimensi (2D), aktivasi logistik (sigmoid), aktivasi linear terkoreksi (ReLU), dan banyak lagi. Pembuatan model adalah operasi sinkron (dilakukan bersamaan), tetapi begitu berhasil dibuat, model bisa digunakan kembali pada berbagai thread dan kompilasi. Pada NNAPI, model dinyatakan sebagai instance ANeuralNetworksModel.
  • Kompilasi: Menunjukkan konfigurasi untuk mengompilasi model NNAPI ke dalam kode dengan level lebih rendah. Pembuatan kompilasi adalah operasi sinkron (dilakukan bersamaan), tetapi begitu berhasil dibuat, model bisa digunakan kembali pada berbagai thread dan kompilasi. Pada NNAPI, setiap kompilasi dinyatakan sebagai instance ANeuralNetworksCompilation.
  • Memori: Menunjukkan memori bersama, file yang dipetakan memori, dan buffer memori serupa. Penggunaan 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 dapat menggunakan buffer memori untuk menyimpan input dan output untuk instance eksekusi. Pada NNAPI, setiap buffer memori dinyatakan sebagai instance ANeuralNetworksMemory.
  • Eksekusi: Antarmuka untuk menerapkan model NNAPI ke sekumpulan input dan mengumpulkan hasilnya. Eksekusi adalah operasi asinkron (tidak bersamaan). Beberapa thread bisa menunggu eksekusi yang sama. Setelah eksekusi selesai, semua thread akan dirilis. Pada 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(), lalu meneruskan deskriptor file dari file data yang terbuka.

Anda juga dapat menetapkan tanda proteksi memori dan offset di mana 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 kami hanya menggunakan satu instance ANeuralNetworksMemory untuk semua bobot, Anda dapat menggunakan lebih dari satu instance ANeuralNetworksMemory untuk beberapa file.

Model

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

Operand

Operand adalah objek data yang digunakan dalam menetapkan grafik. Operand mencakup input dan output model, node perantara yang memuat data yang mengalir dari satu operasi ke operasi lainnya, dan konstanta yang diteruskan ke operasi tersebut.

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

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

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

Misalnya, gambar 3 menunjukkan model dengan dua operasi: penambahan diikuti dengan perkalian. Model ini mengambil sebuah tensor input dan menghasilkan satu tensor output.

Gambar 3. Contoh operand untuk model NNAPI

Model di atas memiliki tujuh operand. Operand tersebut diidentifikasi secara implisit oleh indeks dengan urutan seperti ketika 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 output model bisa menjadi yang pertama ditambahkan. Bagian terpenting adalah menggunakan nilai indeks yang tepat saat merujuk ke sebuah operand.

Operand memiliki beberapa jenis. Jenis ini ditetapkan saat ditambahkan ke model. Sebuah operand tidak dapat digunakan sebagai input sekaligus output untuk 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:

  • jenis operasi (misalnya, penambahan, perkalian, konvolusi),
  • daftar indeks operand yang digunakan operasi untuk input, dan
  • daftar indeks operand yang digunakan operasi untuk output.

Urutan dalam daftar ini penting; lihat Referensi NNAPI API untuk setiap operasi untuk input dan output yang diharapkan.

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

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

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

Kategori Operasi
Operasi matematis seluruh elemen
Operasi array
Operasi gambar
Operasi pencarian
Operasi normalisasi
Operasi konvolusi
Operasi penggabungan
Operasi aktivasi
Operasi lainnya

Masalah umum: Saat meneruskan tensor ANEURALNETWORKS_TENSOR_QUANT8_ASYMM ke operasi ANEURALNETWORKS_PAD, yang tersedia pada Android 9 (API level 28) dan yang lebih tinggi, output dari NNAPI mungkin tidak cocok dengan output dari framework machine learning level lebih tinggi, seperti TensorFlow Lite. Sebaiknya Anda hanya meneruskan ANEURALNETWORKS_TENSOR_FLOAT32 setelah masalah teratasi.

Membuat model

Untuk membuat model, ikuti langkah-langkah berikut:

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

    Dalam contoh berikut, kami membuat model dua operasi yang ditemukan pada gambar 3.

    ANeuralNetworksModel* model = NULL;
        ANeuralNetworksModel_create(&model);
        
  2. Tambahkan operand ke model Anda dengan memanggil ANeuralNetworks_addOperand(). Jenis datanya 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 konstan, seperti bobot dan bias yang diperoleh aplikasi dari proses pelatihan, gunakan fungsi ANeuralNetworksModel_setOperandValue() dan ANeuralNetworksModel_setOperandValueFromMemory().

    Dalam contoh berikut, kami menetapkan nilai konstan dari file data pelatihan yang buffer memorinya dibuat 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 Anda hitung, tambahkan operasi ke model dengan memanggil fungsi ANeuralNetworksModel_addOperation().

    Sebagai parameter untuk panggilan ini, aplikasi Anda harus menyediakan:

    • jenis operasi,
    • jumlah nilai input,
    • array indeks untuk operand input,
    • jumlah nilai output, dan
    • array indeks untuk operand output.

    Perhatikan bahwa sebuah operand tidak dapat digunakan sebagai input sekaligus output untuk 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 harus diperlakukan oleh model sebagai input dan outputnya, dengan memanggil fungsi ANeuralNetworksModel_identifyInputsAndOutputs(). Fungsi ini memungkinkan Anda mengonfigurasi model untuk menggunakan subset operand input dan output 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. Jika ingin, tentukan apakah ANEURALNETWORKS_TENSOR_FLOAT32 boleh dihitung dengan rentang atau presisi yang sama rendahnya dengan format bilangan titik mengambang 16-bit IEEE 754 dengan memanggil ANeuralNetworksModel_relaxComputationFloat32toFloat16().

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

    ANeuralNetworksModel_finish(model);
        

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

Kompilasi

Langkah kompilasi menentukan prosesor mana yang akan digunakan untuk menjalankan model dan meminta driver terkait untuk mempersiapkan eksekusinya. Langkah ini bisa mencakup pembuatan kode mesin khusus untuk prosesor tempat model akan dijalankan.

Untuk mengompilasi model, ikuti langkah-langkah berikut:

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

    // Compile the model.
        ANeuralNetworksCompilation* compilation;
        ANeuralNetworksCompilation_create(model, &compilation);
        
  2. Jika ingin, Anda dapat memengaruhi trade-off waktu proses antara penggunaan daya baterai dan kecepatan eksekusi. Anda dapat 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. Selesaikan penetapan 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 input, dan menyimpan output komputasi ke satu atau beberapa buffer pengguna atau ruang memori yang dialokasikan aplikasi Anda.

Untuk menjalankan model yang telah 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 membaca nilai input untuk komputasi. Aplikasi Anda dapat membaca nilai input dari buffer pengguna atau dari ruang memori yang dialokasikan, dengan memanggil, secara berurutan, 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 menulis nilai output. Aplikasi Anda dapat menulis nilai output ke buffer pengguna atau ke ruang memori yang dialokasikan, dengan memanggil, secara berurutan, 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 dapat dilakukan pada thread yang berbeda dengan 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. Jika ingin, Anda dapat menerapkan set input berbeda ke model yang telah dikompilasi dengan menggunakan instance kompilasi 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 resource 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.

Tensor terkuantisasi

Tensor terkuantisasi adalah cara ringkas untuk menyatakan array n-dimensional dari nilai bilangan titik mengambang.

NNAPI mendukung tensor terkuantisasi asimetris 8-bit. Untuk tensor ini, nilai setiap sel dinyatakan dengan bilangan bulat 8-bit. Tensor terkait dengan skala dan nilai zeroPoint yang digunakan untuk mengonversi bilangan bulat 8-bit menjadi nilai bilangan titik mengambang yang dinyatakan.

Rumusnya adalah:

(cellValue - zeroPoint) * scale
    

di mana nilai zeroPoint adalah bilangan bulat 32-bit dan skalanya adalah nilai bilangan titik mengambang 32-bit.

Dibanding tensor nilai bilangan titik mengambang 32-bit, tensor terkuantisasi 8-bit memiliki dua keunggulan:

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

Meskipun memungkinkan untuk mengubah model bilangan titik mengambang menjadi model terkuantisasi, pengalaman kami menunjukkan bahwa melatih model terkuantisasi secara langsung akan memberikan hasil lebih baik. Akibatnya, jaringan neural belajar untuk mengompensasi peningkatan granularitas dari masing-masing nilai. Untuk setiap tensor terkuantisasi, skala dan nilai zeroPoint ditetapkan selama proses latihan.

Dalam NNAPI, Anda menentukan jenis tensor terkuantisasi dengan menetapkan kolom jenis struktur data ANeuralNetworksOperandType ke ANEURALNETWORKS_TENSOR_QUANT8_ASYMM. Anda juga menentukan skala dan nilai zeroPoint tensor dalam struktur data tersebut.

Operand opsional

Beberapa operasi, seperti ANEURALNETWORKS_LSH_PROJECTION, menggunakan operand opsional. Untuk menunjukkan dalam model bahwa operand opsional dihilangkan, panggil fungsi ANeuralNetworksModel_setOperandValue(), yang berarti meneruskan NULL untuk buffer dan 0 untuk panjang.

Jika keputusan terkait ada tidaknya operand bervariasi untuk setiap eksekusi, untuk menunjukkan bahwa operand dihilangkan, panggil fungsi ANeuralNetworksExecution_setInput() atau ANeuralNetworksExecution_setOutput(), yang berarti meneruskan NULL untuk buffer dan 0 untuk panjangnya.