TensorFlow Lite di GPU

TensorFlow Lite mendukung beberapa akselerator perangkat keras. Dokumen ini menjelaskan cara menggunakan backend GPU menggunakan API delegasi TensorFlow Lite di Android (memerlukan OpenCL atau OpenGL ES 3.1 dan lebih tinggi) dan iOS (memerlukan iOS 8 atau lebih baru).

Manfaat akselerasi GPU

Kecepatan

GPU dirancang untuk memiliki throughput tinggi untuk beban kerja yang dapat diparalelkan secara masif. Dengan demikian, mereka sangat cocok untuk jaring saraf dalam, yang terdiri dari sejumlah besar operator, masing-masing bekerja pada beberapa tensor input yang dapat dengan mudah dibagi menjadi beban kerja yang lebih kecil dan dilakukan secara paralel. Paralelisme ini biasanya menghasilkan latensi yang lebih rendah. Dalam skenario terbaik, inferensi pada GPU dapat berjalan cukup cepat sehingga cocok untuk aplikasi waktu nyata yang sebelumnya tidak mungkin dilakukan.

Ketepatan

GPU melakukan komputasinya dengan angka floating point 16-bit atau 32-bit dan (tidak seperti CPU) tidak memerlukan kuantisasi untuk kinerja yang optimal. Jika penurunan akurasi membuat kuantisasi tidak dapat dipertahankan untuk model Anda, menjalankan jaringan saraf Anda pada GPU dapat menghilangkan masalah ini.

Efisiensi energi

Manfaat lain yang datang dengan inferensi GPU adalah efisiensi dayanya. GPU melakukan komputasi dengan cara yang sangat efisien dan optimal, mengonsumsi lebih sedikit daya dan menghasilkan lebih sedikit panas daripada tugas yang sama yang dijalankan pada CPU.

Operasi yang didukung

TensorFlow Lite pada GPU mendukung operasi berikut dalam presisi float 16-bit dan 32-bit:

  • ADD
  • AVERAGE_POOL_2D
  • CONCATENATION
  • CONV_2D
  • DEPTHWISE_CONV_2D v1-2
  • EXP
  • FULLY_CONNECTED
  • LOGISTIC
  • LSTM v2 (Basic LSTM only)
  • MAX_POOL_2D
  • MAXIMUM
  • MINIMUM
  • MUL
  • PAD
  • PRELU
  • RELU
  • RELU6
  • RESHAPE
  • RESIZE_BILINEAR v1-3
  • SOFTMAX
  • STRIDED_SLICE
  • SUB
  • TRANSPOSE_CONV

Secara default, semua operasi hanya didukung pada versi 1. Mengaktifkan dukungan kuantisasi eksperimental memungkinkan versi yang sesuai; misalnya, TAMBAHKAN v2.

Penggunaan dasar

Ada dua cara untuk memanggil akselerasi model di Android bergantung pada apakah Anda menggunakan Android Studio ML Model Binding atau TensorFlow Lite Interpreter.

Android melalui Penerjemah TensorFlow Lite

Tambahkan paket tensorflow-lite-gpu di samping paket tensorflow-lite yang ada di blok dependencies yang ada.

dependencies {
    ...
    implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    implementation 'org.tensorflow:tensorflow-lite-gpu:2.3.0'
}

Kemudian jalankan TensorFlow Lite di GPU dengan TfLiteDelegate . Di Java, Anda dapat menentukan GpuDelegate melalui Interpreter.Options .

Kotlin

    import org.tensorflow.lite.Interpreter
    import org.tensorflow.lite.gpu.CompatibilityList
    import org.tensorflow.lite.gpu.GpuDelegate

    val compatList = CompatibilityList()

    val options = Interpreter.Options().apply{
        if(compatList.isDelegateSupportedOnThisDevice){
            // if the device has a supported GPU, add the GPU delegate
            val delegateOptions = compatList.bestOptionsForThisDevice
            this.addDelegate(GpuDelegate(delegateOptions))
        } else {
            // if the GPU is not supported, run on 4 threads
            this.setNumThreads(4)
        }
    }

    val interpreter = Interpreter(model, options)

    // Run inference
    writeToInput(input)
    interpreter.run(input, output)
    readFromOutput(output)
      

Jawa

    import org.tensorflow.lite.Interpreter;
    import org.tensorflow.lite.gpu.CompatibilityList;
    import org.tensorflow.lite.gpu.GpuDelegate;

    // Initialize interpreter with GPU delegate
    Interpreter.Options options = new Interpreter.Options();
    CompatibilityList compatList = CompatibilityList();

    if(compatList.isDelegateSupportedOnThisDevice()){
        // if the device has a supported GPU, add the GPU delegate
        GpuDelegate.Options delegateOptions = compatList.getBestOptionsForThisDevice();
        GpuDelegate gpuDelegate = new GpuDelegate(delegateOptions);
        options.addDelegate(gpuDelegate);
    } else {
        // if the GPU is not supported, run on 4 threads
        options.setNumThreads(4);
    }

    Interpreter interpreter = new Interpreter(model, options);

    // Run inference
    writeToInput(input);
    interpreter.run(input, output);
    readFromOutput(output);
      

Android (C/C++)

Untuk penggunaan C/C++ GPU TensorFlow Lite di Android, delegasi GPU dapat dibuat dengan TfLiteGpuDelegateV2Create() dan dihancurkan dengan TfLiteGpuDelegateV2Delete() .

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// NEW: Prepare GPU delegate.
auto* delegate = TfLiteGpuDelegateV2Create(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// NEW: Clean up.
TfLiteGpuDelegateV2Delete(delegate);

Lihatlah TfLiteGpuDelegateOptionsV2 untuk membuat instance delegasi dengan opsi khusus. Anda dapat menginisialisasi opsi default dengan TfLiteGpuDelegateOptionsV2Default() dan kemudian memodifikasinya seperlunya.

GPU TFLite untuk Android C/C++ menggunakan sistem build Bazel . Delegasi dapat dibangun, misalnya, menggunakan perintah berikut:

bazel build -c opt --config android_arm64 tensorflow/lite/delegates/gpu:delegate                           # for static library
bazel build -c opt --config android_arm64 tensorflow/lite/delegates/gpu:libtensorflowlite_gpu_delegate.so  # for dynamic library

iOS (C++)

Untuk menggunakan TensorFlow Lite di GPU, dapatkan delegasi GPU melalui TFLGpuDelegateCreate() lalu teruskan ke Interpreter::ModifyGraphWithDelegate() (alih-alih memanggil Interpreter::AllocateTensors() ).

// Set up interpreter.
auto model = FlatBufferModel::BuildFromFile(model_path);
if (!model) return false;
tflite::ops::builtin::BuiltinOpResolver op_resolver;
std::unique_ptr<Interpreter> interpreter;
InterpreterBuilder(*model, op_resolver)(&interpreter);

// NEW: Prepare GPU delegate.

auto* delegate = TFLGpuDelegateCreate(/*default options=*/nullptr);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

// Run inference.
WriteToInputTensor(interpreter->typed_input_tensor<float>(0));
if (interpreter->Invoke() != kTfLiteOk) return false;
ReadFromOutputTensor(interpreter->typed_output_tensor<float>(0));

// Clean up.
TFLGpuDelegateDelete(delegate);

Penggunaan lanjutan

Opsi Delegasi untuk iOS

Konstruktor untuk delegasi GPU menerima struct opsi. ( API Swift, API Objective-C, API C )

Melewati nullptr (C API) atau tidak sama sekali (Objective-C dan Swift API) ke penginisialisasi menetapkan opsi default (yang dijelaskan dalam contoh Penggunaan Dasar di atas).

Cepat

    // THIS:
    var options = MetalDelegate.Options()
    options.isPrecisionLossAllowed = false
    options.waitType = .passive
    options.isQuantizationEnabled = true
    let delegate = MetalDelegate(options: options)

    // IS THE SAME AS THIS:
    let delegate = MetalDelegate()
      

Objective-C

    // THIS:
    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.precisionLossAllowed = false;
    options.waitType = TFLMetalDelegateThreadWaitTypePassive;
    options.quantizationEnabled = true;

    TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] initWithOptions:options];

    // IS THE SAME AS THIS:
    TFLMetalDelegate* delegate = [[TFLMetalDelegate alloc] init];
      

C

    // THIS:
    const TFLGpuDelegateOptions options = {
      .allow_precision_loss = false,
      .wait_type = TFLGpuDelegateWaitType::TFLGpuDelegateWaitTypePassive,
      .enable_quantization = true,
    };

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);

    // IS THE SAME AS THIS:
    TfLiteDelegate* delegate = TFLGpuDelegateCreate(nullptr);
      

Meskipun nyaman untuk menggunakan nullptr atau konstruktor default, kami menyarankan Anda mengatur opsi secara eksplisit, untuk menghindari perilaku yang tidak diharapkan jika nilai default diubah di masa mendatang.

Menjalankan model terkuantisasi pada GPU

Bagian ini menjelaskan bagaimana delegasi GPU mempercepat model terkuantisasi 8-bit. Ini mencakup semua rasa kuantisasi, termasuk:

Untuk mengoptimalkan performa, gunakan model yang memiliki tensor input & output floating-point.

Bagaimana cara kerjanya?

Karena backend GPU hanya mendukung eksekusi titik-mengambang, kami menjalankan model terkuantisasi dengan memberinya 'tampilan titik-mengambang' dari model aslinya. Pada tingkat tinggi, ini memerlukan langkah-langkah berikut:

  • Tensor konstan (seperti bobot/bias) didekuantisasi sekali ke dalam memori GPU. Ini terjadi ketika delegasi diterapkan ke TFLite Interpreter.

  • Input dan output ke program GPU, jika dikuantisasi 8-bit, didekuantisasi dan dikuantisasi (masing-masing) untuk setiap inferensi. Ini dilakukan pada CPU menggunakan kernel TFLite yang dioptimalkan.

  • Program GPU dimodifikasi untuk meniru perilaku terkuantisasi dengan memasukkan simulator kuantisasi di antara operasi. Ini diperlukan untuk model di mana operasi mengharapkan aktivasi mengikuti batas yang dipelajari selama kuantisasi.

Fitur ini dapat diaktifkan menggunakan opsi delegasi sebagai berikut:

Android

Android API mendukung model terkuantisasi secara default. Untuk menonaktifkan, lakukan hal berikut:

C++ API

TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default();
options.experimental_flags = TFLITE_GPU_EXPERIMENTAL_FLAGS_NONE;

auto* delegate = TfLiteGpuDelegateV2Create(options);
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

API Jawa

GpuDelegate delegate = new GpuDelegate(new GpuDelegate.Options().setQuantizedModelsAllowed(false));

Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);

iOS

API iOS mendukung model terkuantisasi secara default. Untuk menonaktifkan, lakukan hal berikut:

Cepat

    var options = MetalDelegate.Options()
    options.isQuantizationEnabled = false
    let delegate = MetalDelegate(options: options)
      

Objective-C

    TFLMetalDelegateOptions* options = [[TFLMetalDelegateOptions alloc] init];
    options.quantizationEnabled = false;
      

C

    TFLGpuDelegateOptions options = TFLGpuDelegateOptionsDefault();
    options.enable_quantization = false;

    TfLiteDelegate* delegate = TFLGpuDelegateCreate(options);
      

Buffer Input/Output (hanya iOS, C++ API)

Untuk melakukan komputasi pada GPU, data harus tersedia untuk GPU. Ini sering membutuhkan melakukan salinan memori. Sebaiknya jangan melewati batas memori CPU/GPU jika memungkinkan, karena hal ini dapat memakan banyak waktu. Biasanya, penyeberangan seperti itu tidak dapat dihindari, tetapi dalam beberapa kasus khusus, satu atau yang lain dapat dihilangkan.

Jika input jaringan adalah gambar yang sudah dimuat dalam memori GPU (misalnya, tekstur GPU yang berisi umpan kamera), ia dapat tetap berada di memori GPU tanpa pernah memasuki memori CPU. Demikian pula, jika keluaran jaringan dalam bentuk gambar yang dapat dirender (misalnya, transfer gaya gambar ), maka dapat langsung ditampilkan di layar.

Untuk mencapai kinerja terbaik, TensorFlow Lite memungkinkan pengguna untuk langsung membaca dan menulis ke buffer perangkat keras TensorFlow dan mengabaikan salinan memori yang dapat dihindari.

Dengan asumsi input gambar dalam memori GPU, itu harus terlebih dahulu dikonversi ke objek MTLBuffer untuk Metal. Anda dapat mengaitkan TfLiteTensor ke MTLBuffer yang disiapkan pengguna dengan MTLBuffer TFLGpuDelegateBindMetalBufferToTensor() . Perhatikan bahwa TFLGpuDelegateBindMetalBufferToTensor() harus dipanggil setelah Interpreter::ModifyGraphWithDelegate() . Selain itu, output inferensi, secara default, disalin dari memori GPU ke memori CPU. Perilaku ini dapat dimatikan dengan memanggil Interpreter::SetAllowBufferHandleOutput(true) selama inisialisasi.

#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"

// ...

// Prepare GPU delegate.
auto* delegate = TFLGpuDelegateCreate(nullptr);

if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;

interpreter->SetAllowBufferHandleOutput(true);  // disable default gpu->cpu copy
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->inputs()[0], user_provided_input_buffer)) {
  return false;
}
if (!TFLGpuDelegateBindMetalBufferToTensor(
        delegate, interpreter->outputs()[0], user_provided_output_buffer)) {
  return false;
}

// Run inference.
if (interpreter->Invoke() != kTfLiteOk) return false;

Serialisasi Delegasi GPU

Menggunakan serialisasi kode kernel GPU dan data model dari inisialisasi sebelumnya dapat mengurangi latensi inisialisasi delegasi GPU hingga 90%. Peningkatan ini dicapai dengan menukar ruang disk untuk penghematan waktu. Anda dapat mengaktifkan fitur ini dengan beberapa opsi konfigurasi, seperti yang ditunjukkan pada contoh kode berikut:

C++

    TfLiteGpuDelegateOptionsV2 options = TfLiteGpuDelegateOptionsV2Default();
    options.experimental_flags |= TFLITE_GPU_EXPERIMENTAL_FLAGS_ENABLE_SERIALIZATION;
    options.serialization_dir = kTmpDir;
    options.model_token = kModelToken;

    auto* delegate = TfLiteGpuDelegateV2Create(options);
    if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) return false;
      

Jawa

    GpuDelegate delegate = new GpuDelegate(
      new GpuDelegate.Options().setSerializationParams(
        /* serializationDir= */ serializationDir,
        /* modelToken= */ modelToken));

    Interpreter.Options options = (new Interpreter.Options()).addDelegate(delegate);
      

Saat menggunakan fitur serialisasi, pastikan kode Anda mematuhi aturan penerapan berikut:

  • Simpan data serialisasi di direktori yang tidak dapat diakses oleh aplikasi lain. Pada perangkat Android, gunakan getCodeCacheDir() yang menunjuk ke lokasi yang bersifat pribadi untuk aplikasi saat ini.
  • Token model harus unik untuk perangkat untuk model tertentu. Anda dapat menghitung token model dengan membuat sidik jari dari data model (misalnya menggunakan farmhash::Fingerprint64 ).

Tips dan Trik

  • Beberapa operasi yang sepele pada CPU mungkin memakan biaya tinggi pada GPU. Satu kelas operasi tersebut mencakup berbagai bentuk operasi pembentukan ulang (termasuk BATCH_TO_SPACE , SPACE_TO_BATCH , SPACE_TO_DEPTH , dan operasi serupa). Jika operasi ini tidak diperlukan (misalnya, operasi tersebut dimasukkan untuk membantu alasan arsitek jaringan tentang sistem tetapi tidak mempengaruhi output), ada baiknya menghapusnya untuk kinerja.

  • Pada GPU, data tensor dipotong menjadi 4 saluran. Jadi, komputasi pada tensor bentuk [B, H, W, 5] akan bekerja hampir sama pada tensor bentuk [B, H, W, 8] , tetapi secara signifikan lebih buruk daripada [B, H, W, 4] .

    • Misalnya, jika perangkat keras kamera mendukung bingkai gambar dalam RGBA, mengumpankan input 4 saluran tersebut secara signifikan lebih cepat, karena salinan memori (dari RGB 3 saluran ke RGBX 4 saluran) dapat dihindari.
  • Untuk kinerja terbaik, jangan ragu untuk melatih kembali pengklasifikasi Anda dengan arsitektur jaringan yang dioptimalkan untuk seluler. Itu adalah bagian penting dari pengoptimalan untuk inferensi pada perangkat.