Neural Networks API

Android Neural Networks API (NNAPI), Android cihazlarda makine öğrenimi için yoğun işlem gücü gerektiren işlemleri çalıştırmak üzere tasarlanmış bir Android C API'sidir. NNAPI, sinir ağları oluşturan ve eğiten TensorFlow Lite ve Caffe2 gibi üst düzey makine öğrenimi çerçeveleri için temel bir işlev katmanı sağlamak üzere tasarlanmıştır. API, Android 8.1 (API düzeyi 27) veya sonraki sürümleri çalıştıran tüm Android cihazlarda kullanılabilir ancak Android 15'te desteği sonlandırılmıştır.

NNAPI, Android cihazlardan alınan verileri önceden eğitilmiş, geliştirici tarafından tanımlanmış modellere uygulayarak çıkarım yapmayı destekler. Çıkarım örnekleri arasında görüntüleri sınıflandırma, kullanıcı davranışını tahmin etme ve bir arama sorgusuna uygun yanıtları seçme yer alır.

Cihaz üzerinde çıkarım yapmanın birçok avantajı vardır:

  • Gecikme: Ağ bağlantısı üzerinden istek göndermeniz ve yanıt beklemeniz gerekmez. Örneğin, kameradan gelen ardışık kareleri işleyen video uygulamaları için bu durum kritik olabilir.
  • Kullanılabilirlik: Uygulama, ağ kapsama alanı dışında bile çalışır.
  • Hız: Nöral ağ işlemeye özel yeni donanım, tek başına genel amaçlı bir CPU'dan çok daha hızlı hesaplama sağlar.
  • Gizlilik: Veriler Android cihazdan dışarı çıkmaz.
  • Maliyet: Tüm hesaplamalar Android cihazda yapıldığından sunucu çiftliğine gerek yoktur.

Geliştiricilerin göz önünde bulundurması gereken bazı dezavantajlar da vardır:

  • Sistem kullanımı: Nöral ağların değerlendirilmesi, çok fazla hesaplama gerektirdiğinden pil gücü kullanımını artırabilir. Uygulamanız için bu durum bir sorun teşkil ediyorsa, özellikle de uzun süren hesaplamalar için pil sağlığını izlemeniz önerilir.
  • Uygulama boyutu: Modellerinizin boyutuna dikkat edin. Modeller birkaç megabayt yer kaplayabilir. Büyük modelleri APK'nıza eklemek kullanıcılarınızı olumsuz etkileyecekse modelleri uygulama yüklendikten sonra indirmeyi, daha küçük modeller kullanmayı veya hesaplamalarınızı bulutta çalıştırmayı düşünebilirsiniz. NNAPI, modelleri bulutta çalıştırma işlevi sunmaz.

NNAPI'nin nasıl kullanılacağına dair bir örnek için Android Neural Networks API örneğine bakın.

Neural Networks API çalışma zamanını anlama

NNAPI, geliştiricilerin modellerini cihaz dışında eğitip Android cihazlara dağıtmasına olanak tanıyan makine öğrenimi kitaplıkları, çerçeveleri ve araçları tarafından çağrılmak üzere tasarlanmıştır. Uygulamalar genellikle NNAPI'yi doğrudan kullanmaz, bunun yerine daha üst düzey makine öğrenimi çerçevelerini kullanır. Bu çerçeveler, desteklenen cihazlarda donanım hızlandırmalı çıkarım işlemleri gerçekleştirmek için NNAPI'yi kullanabilir.

Android'in nöral ağ çalışma zamanı, bir uygulamanın gereksinimlerine ve Android cihazın donanım özelliklerine bağlı olarak, hesaplama iş yükünü özel nöral ağ donanımı, grafik işleme birimleri (GPU'lar) ve dijital sinyal işleyiciler (DSP'ler) dahil olmak üzere mevcut cihaz üzerinde işlemcilere verimli bir şekilde dağıtabilir.

Özel bir tedarikçi sürücüsü olmayan Android cihazlarda NNAPI çalışma zamanı, istekleri CPU'da yürütür.

Şekil 1'de NNAPI'nin üst düzey sistem mimarisi gösterilmektedir.

Şekil 1. Android Neural Networks API için sistem mimarisi

Neural Networks API programlama modeli

NNAPI kullanarak hesaplama yapmak için öncelikle yapılacak hesaplamaları tanımlayan yönlendirilmiş bir grafik oluşturmanız gerekir. Bu hesaplama grafiği, giriş verilerinizle (örneğin, bir makine öğrenimi çerçevesinden aktarılan ağırlıklar ve sapmalar) birleştirildiğinde NNAPI çalışma zamanı değerlendirmesi için modeli oluşturur.

NNAPI dört ana soyutlama kullanır:

  • Model: Matematiksel işlemlerin hesaplama grafiği ve eğitim süreciyle öğrenilen sabit değerler. Bu işlemler, nöral ağlara özeldir. Bunlar arasında 2 boyutlu (2D) konvolüsyon, lojistik (sigmoid) etkinleştirme, doğrultulmuş doğrusal (ReLU) etkinleştirme ve daha fazlası yer alır. Model oluşturma, eşzamanlı bir işlemdir. Başarıyla oluşturulduktan sonra mesaj dizilerinde ve derlemelerde yeniden kullanılabilir. NNAPI'de bir model, ANeuralNetworksModel örneği olarak temsil edilir.
  • Derleme: NNAPI modelini daha düşük seviyeli koda derlemek için bir yapılandırmayı temsil eder. Derleme oluşturma işlemi eşzamanlı bir işlemdir. Başarıyla oluşturulduktan sonra mesaj dizileri ve yürütmelerde yeniden kullanılabilir. NNAPI'de her derleme bir ANeuralNetworksCompilation örneği olarak temsil edilir.
  • Bellek: Paylaşılan belleği, bellekle eşlenen dosyaları ve benzer bellek arabelleklerini temsil eder. Bellek arabelleği kullanmak, NNAPI çalışma zamanının verileri sürücülere daha verimli bir şekilde aktarmasını sağlar. Uygulamalar genellikle bir model tanımlamak için gereken her tensörü içeren bir paylaşılan bellek arabelleği oluşturur. Yürütme örneğinin giriş ve çıkışlarını depolamak için bellek arabelleklerini de kullanabilirsiniz. NNAPI'de her bellek arabelleği bir ANeuralNetworksMemory örneği olarak temsil edilir.
  • Yürütme: NNAPI modelini bir dizi girişe uygulamak ve sonuçları toplamak için kullanılan arayüz. Yürütme, eşzamanlı veya eşzamansız olarak gerçekleştirilebilir.

    Eşzamansız yürütme için birden fazla iş parçacığı aynı yürütmeyi bekleyebilir. Bu yürütme tamamlandığında tüm iş parçacıkları serbest bırakılır.

    NNAPI'de her yürütme bir ANeuralNetworksExecution örneği olarak temsil edilir.

Şekil 2'de temel programlama akışı gösterilmektedir.

Şekil 2. Android Neural Networks API için programlama akışı

Bu bölümün geri kalanında, hesaplama yapmak, modeli derlemek ve derlenen modeli yürütmek için NNAPI modelinizi ayarlama adımları açıklanmaktadır.

Eğitim verilerine erişim sağlama

Eğitilen ağırlıklar ve sapma verileriniz muhtemelen bir dosyada depolanır. NNAPI çalışma zamanının bu verilere verimli bir şekilde erişmesini sağlamak için ANeuralNetworksMemory_createFromFd() işlevini çağırıp açılan veri dosyasının dosya tanımlayıcısını ileterek bir ANeuralNetworksMemory örneği oluşturun. Ayrıca bellek koruma işaretlerini ve paylaşılan bellek bölgesinin dosyada başladığı ofseti de belirtirsiniz.

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

Bu örnekte tüm ağırlıklarımız için yalnızca bir ANeuralNetworksMemory örneği kullansak da birden fazla dosya için birden fazla ANeuralNetworksMemory örneği kullanmak mümkündür.

Yerel donanım arabelleklerini kullanma

Model girişleri, çıkışları ve sabit işlenen değerleri için yerel donanım arabelleklerini kullanabilirsiniz. Bazı durumlarda, NNAPI hızlandırıcı, sürücünün verileri kopyalamasına gerek kalmadan AHardwareBuffer nesnelerine erişebilir. AHardwareBuffer, birçok farklı yapılandırmaya sahiptir ve her NNAPI hızlandırıcı bu yapılandırmaların tümünü desteklemeyebilir. Bu sınırlama nedeniyle, ANeuralNetworksMemory_createFromAHardwareBuffer referans belgelerinde listelenen kısıtlamalara bakın ve AHardwareBuffer kullanan derlemelerin ve yürütmelerin beklendiği gibi davrandığından emin olmak için hedef cihazlarda önceden test yapın. Hızlandırıcıyı belirtmek için cihaz atama özelliğini kullanın.

NNAPI çalışma zamanının bir AHardwareBuffer nesnesine erişmesine izin vermek için aşağıdaki kod örneğinde gösterildiği gibi ANeuralNetworksMemory_createFromAHardwareBuffer işlevini çağırıp AHardwareBuffer nesnesini ileterek bir ANeuralNetworksMemory örneği oluşturun:

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

NNAPI'nin AHardwareBuffer nesnesine erişmesi gerekmediğinde ilgili ANeuralNetworksMemory örneğini serbest bırakın:

ANeuralNetworksMemory_free(mem2);

Not:

  • AHardwareBuffer yalnızca tüm arabellek için kullanılabilir. ARect parametresiyle kullanılamaz.
  • NNAPI çalışma zamanı, arabelleği temizlemez. Yürütmeyi planlamadan önce giriş ve çıkış arabelleklerinin erişilebilir olduğundan emin olmanız gerekir.
  • Senkronizasyon engeli dosya tanımlayıcıları desteklenmez.
  • Tedarikçiye özel biçimlere ve kullanım bitlerine sahip bir AHardwareBuffer için önbelleği temizlemekten istemcinin mi yoksa sürücünün mü sorumlu olduğunu belirlemek tedarikçi uygulamasına bağlıdır.

Model

Model, NNAPI'deki temel hesaplama birimidir. Her model, bir veya daha fazla işlenen ve işlemle tanımlanır.

İşlenenler

İşlenenler, grafiği tanımlamak için kullanılan veri nesneleridir. Bunlar arasında modelin giriş ve çıkışları, bir işlemden diğerine akan verileri içeren ara düğümler ve bu işlemlere iletilen sabitler yer alır.

NNAPI modellerine eklenebilecek iki tür işlenen vardır: skalerler ve tensörler.

Skaler, tek bir değeri temsil eder. NNAPI; boole, 16 bit kayan nokta, 32 bit kayan nokta, 32 bit tam sayı ve işaretsiz 32 bit tam sayı biçimlerindeki skaler değerleri destekler.

NNAPI'deki çoğu işlemde tensörler kullanılır. Tensörler n boyutlu dizilerdir. NNAPI; 16 bit kayan nokta, 32 bit kayan nokta, 8 bit nicelenmiş, 16 bit nicelenmiş, 32 bit tam sayı ve 8 bit boole değerlerine sahip tensörleri destekler.

Örneğin, Şekil 3'te iki işlem içeren bir model gösterilmektedir: toplama ve ardından çarpma. Model, bir giriş tensörü alır ve bir çıkış tensörü üretir.

Şekil 3. NNAPI modeli için işlenen örneği

Yukarıdaki modelde yedi işlenen vardır. Bu işlenenler, modele eklendikleri sıranın diziniyle dolaylı olarak tanımlanır. Eklenen ilk işlenenin dizini 0, ikinci işlenenin dizini 1 şeklinde devam eder. 1, 2, 3 ve 5 numaralı işlenenler sabit işlenenlerdir.

İşlenenleri ekleme sıranız önemli değildir. Örneğin, model çıktısı işleneni, eklenen ilk işlenen olabilir. Önemli olan, bir işlenene atıfta bulunurken doğru dizin değerini kullanmaktır.

İşlenenlerin türleri vardır. Bunlar, modele eklendiklerinde belirtilir.

Bir işlenen, bir modelin hem girişi hem de çıkışı olarak kullanılamaz.

Her işlenen, bir model girişi, bir sabit veya tam olarak bir işlemin çıkış işleneni olmalıdır.

İşlenenleri kullanma hakkında daha fazla bilgi için İşlenenler hakkında daha fazla bilgi başlıklı makaleyi inceleyin.

İşlemler

İşlem, gerçekleştirilecek hesaplamaları belirtir. Her işlem şu öğelerden oluşur:

  • İşlem türü (ör. toplama, çarpma, evrişim)
  • İşlemin giriş için kullandığı işlenenlerin dizinlerinin listesi ve
  • İşlemin çıkış için kullandığı işlenenlerin dizinlerinin listesi.

Bu listelerdeki sıra önemlidir. Her işlem türünün beklenen giriş ve çıkışları için NNAPI API referansına bakın.

İşlemi eklemeden önce, işlemin kullandığı veya ürettiği işlenenleri modele eklemeniz gerekir.

İşlemleri hangi sırayla eklediğiniz önemli değildir. NNAPI, işlemlerin yürütülme sırasını belirlemek için işlenenlerin ve işlemlerin hesaplama grafiği tarafından oluşturulan bağımlılıklardan yararlanır.

NNAPI'nin desteklediği işlemler aşağıdaki tabloda özetlenmiştir:

Kategori İşlemler
Öğe bazlı matematiksel işlemler
Tensör manipülasyonu
Resim işlemleri
Arama işlemleri
Normalleştirme işlemleri
Evrişim işlemleri
Havuz işlemleri
Etkinleştirme işlemleri
Diğer işlemler

API düzeyi 28'de bilinen sorun: Android 9 (API düzeyi 28) ve sonraki sürümlerde kullanılabilen ANEURALNETWORKS_PAD işlemine ANEURALNETWORKS_TENSOR_QUANT8_ASYMM tensörleri iletilirken NNAPI'den gelen çıkış, TensorFlow Lite gibi daha üst düzey makine öğrenimi çerçevelerinden gelen çıkışla eşleşmeyebilir. Bunun yerine yalnızca ANEURALNETWORKS_TENSOR_FLOAT32 değerini iletmeniz gerekir. Sorun, Android 10 (API düzeyi 29) ve sonraki sürümlerde çözülmüştür.

Modeller oluşturun

Aşağıdaki örnekte, Şekil 3'te bulunan iki işlemli modeli oluşturuyoruz.

Modeli oluşturmak için aşağıdaki adımları uygulayın:

  1. Boş bir model tanımlamak için ANeuralNetworksModel_create() işlevini çağırın.

    ANeuralNetworksModel* model = NULL;
    ANeuralNetworksModel_create(&model);
  2. ANeuralNetworks_addOperand() yöntemini çağırarak işlenenleri modelinize ekleyin. Veri türleri, ANeuralNetworksOperandType veri yapısı kullanılarak tanımlanır.

    // 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 6
  3. Uygulamanızın bir eğitim sürecinden elde ettiği ağırlıklar ve sapmalar gibi sabit değerlere sahip işlenenler için ANeuralNetworksModel_setOperandValue() ve ANeuralNetworksModel_setOperandValueFromMemory() işlevlerini kullanın.

    Aşağıdaki örnekte, Eğitim verilerine erişim sağlama bölümünde oluşturduğumuz bellek arabelleğine karşılık gelen eğitim verisi dosyasından sabit değerler ayarlıyoruz.

    // 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));
  4. Yönlendirilmiş grafikte hesaplamak istediğiniz her işlem için ANeuralNetworksModel_addOperation() işlevini çağırarak işlemi modelinize ekleyin.

    Uygulamanız bu çağrıya parametre olarak şunları sağlamalıdır:

    • İşlem türü
    • giriş değerlerinin sayısı
    • Giriş işlenenleri için dizinlerin dizisi
    • Çıkış değerlerinin sayısı
    • Çıkış işlenenleri için dizinlerin dizisi

    Bir işlenenin aynı işlemin hem girişi hem de çıkışı için kullanılamayacağını unutmayı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. ANeuralNetworksModel_identifyInputsAndOutputs() işlevini çağırarak modelin hangi işlenenleri giriş ve çıkış olarak ele alması gerektiğini belirleyin.

    // 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. İsteğe bağlı olarak, ANeuralNetworksModel_relaxComputationFloat32toFloat16() işlevini çağırarak ANEURALNETWORKS_TENSOR_FLOAT32 işlevinin IEEE 754 16 bit kayan nokta biçimi kadar düşük bir aralık veya hassasiyetle hesaplanmasına izin verilip verilmeyeceğini belirtin.

  7. Modelinizin tanımını tamamlamak için ANeuralNetworksModel_finish() işlevini çağırın. Hata yoksa bu işlev ANEURALNETWORKS_NO_ERROR sonuç kodunu döndürür.

    ANeuralNetworksModel_finish(model);

Oluşturduğunuz bir modeli istediğiniz sayıda derleyebilir ve her derlemeyi istediğiniz sayıda çalıştırabilirsiniz.

Kontrol akışı

Kontrol akışını bir NNAPI modeline dahil etmek için aşağıdakileri yapın:

  1. İlgili yürütme alt grafiklerini (IF ifadesi için then ve else alt grafikleri, WHILE döngüsü için condition ve body alt grafikleri) bağımsız ANeuralNetworksModel* modelleri olarak oluşturun:

    ANeuralNetworksModel* thenModel = makeThenModel();
    ANeuralNetworksModel* elseModel = makeElseModel();
  2. Kontrol akışını içeren modelde bu modellere referans veren işlenenler oluşturun:

    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);
  3. Kontrol akışı işlemini ekleyin:

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

Derleme

Derleme adımı, modelinizin hangi işlemcilerde yürütüleceğini belirler ve ilgili sürücülerden yürütme için hazırlanmalarını ister. Bu, modelinizin çalışacağı işlemcilere özel makine dili oluşturulmasını içerebilir.

Bir modeli derlemek için aşağıdaki adımları uygulayın:

  1. Yeni bir derleme örneği oluşturmak için ANeuralNetworksCompilation_create() işlevini çağırın.

    // Compile the model
    ANeuralNetworksCompilation* compilation;
    ANeuralNetworksCompilation_create(model, &compilation);

    İsteğe bağlı olarak, hangi cihazlarda yürütüleceğini açıkça seçmek için cihaz atamasını kullanabilirsiniz.

  2. İsteğe bağlı olarak, çalışma zamanının pil gücü kullanımı ile yürütme hızı arasında nasıl bir denge kuracağını etkileyebilirsiniz. Bunu yapmak için ANeuralNetworksCompilation_setPreference() işlevini çağırabilirsiniz.

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

    Belirtebileceğiniz tercihler şunlardır:

  3. İsteğe bağlı olarak ANeuralNetworksCompilation_setCaching işlevini çağırarak derleme önbelleğe almayı ayarlayabilirsiniz.

    // Set up compilation caching
    ANeuralNetworksCompilation_setCaching(compilation, cacheDir, token);

    cacheDir için getCodeCacheDir() kullanın. Belirtilen token, uygulamadaki her model için benzersiz olmalıdır.

  4. ANeuralNetworksCompilation_finish() yöntemini çağırarak derleme tanımını tamamlayın. Hata yoksa bu işlev ANEURALNETWORKS_NO_ERROR sonuç kodunu döndürür.

    ANeuralNetworksCompilation_finish(compilation);

Cihaz keşfi ve ataması

Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran Android cihazlarda NNAPI, makine öğrenimi çerçeve kitaplıklarının ve uygulamalarının mevcut cihazlar hakkında bilgi edinmesine ve yürütme için kullanılacak cihazları belirtmesine olanak tanıyan işlevler sağlar. Mevcut cihazlar hakkında bilgi sağlamak, uygulamaların bilinen uyumsuzlukları önlemek için bir cihazda bulunan sürücülerin tam sürümünü almasına olanak tanır. Uygulamalara, bir modelin farklı bölümlerini hangi cihazların çalıştıracağını belirleme olanağı vererek uygulamalar, dağıtıldıkları Android cihaz için optimize edilebilir.

Cihaz bulma

Kullanılabilir cihaz sayısını öğrenmek için ANeuralNetworks_getDeviceCount kullanın. Her cihaz için ANeuralNetworks_getDevice kullanarak bir ANeuralNetworksDevice örneğini ilgili cihazın referansına ayarlayın.

Cihaz referansını aldıktan sonra aşağıdaki işlevleri kullanarak cihaz hakkında daha fazla bilgi edinebilirsiniz:

Cihaz ataması

Bir modelin hangi işlemlerinin belirli cihazlarda çalıştırılabileceğini öğrenmek için ANeuralNetworksModel_getSupportedOperationsForDevices kullanın.

Yürütme için hangi hızlandırıcıların kullanılacağını kontrol etmek istiyorsanız ANeuralNetworksCompilation_create yerine ANeuralNetworksCompilation_createForDevices yöntemini çağırın. Elde edilen ANeuralNetworksCompilation nesnesini normal şekilde kullanın. Sağlanan model, seçilen cihazlar tarafından desteklenmeyen işlemler içeriyorsa işlev bir hata döndürür.

Birden fazla cihaz belirtilirse çalışma zamanı, işi cihazlar arasında dağıtmaktan sorumludur.

Diğer cihazlara benzer şekilde, NNAPI CPU uygulaması, nnapi-reference adlı ve ANEURALNETWORKS_DEVICE_TYPE_CPU türünde bir ANeuralNetworksDevice ile temsil edilir. ANeuralNetworksCompilation_createForDevices çağrılırken model derleme ve yürütme ile ilgili hata durumlarını işlemek için CPU uygulaması kullanılmaz.

Bir modeli, belirtilen cihazlarda çalışabilecek alt modellere bölmek uygulamanın sorumluluğundadır. Manuel bölümleme yapması gerekmeyen uygulamalar, modeli hızlandırmak için mevcut tüm cihazları (CPU dahil) kullanmak üzere daha basit olan ANeuralNetworksCompilation_create yöntemini çağırmaya devam etmelidir. Model, ANeuralNetworksCompilation_createForDevices kullanarak belirttiğiniz cihazlar tarafından tam olarak desteklenemiyorsa ANEURALNETWORKS_BAD_DATA döndürülür.

Model bölümleme

Model için birden fazla cihaz mevcut olduğunda NNAPI çalışma zamanı, işi cihazlar arasında dağıtır. Örneğin, ANeuralNetworksCompilation_createForDevices için birden fazla cihaz sağlandıysa iş tahsis edilirken belirtilen tüm cihazlar dikkate alınır. CPU cihazı listede yoksa CPU yürütme devre dışı bırakılır. ANeuralNetworksCompilation_create kullanılırken CPU dahil olmak üzere mevcut tüm cihazlar dikkate alınır.

Dağıtım, modeldeki her işlem için mevcut cihazlar listesinden işlemi destekleyen ve en iyi performansı (yani istemci tarafından belirtilen yürütme tercihine bağlı olarak en hızlı yürütme süresini veya en düşük güç tüketimini) bildiren cihaz seçilerek yapılır. Bu bölümleme algoritması, farklı işlemciler arasındaki G/Ç'nin neden olabileceği verimsizlikleri hesaba katmaz. Bu nedenle, birden fazla işlemci belirtirken (ANeuralNetworksCompilation_createForDevices kullanarak açıkça veya ANeuralNetworksCompilation_create kullanarak dolaylı olarak) sonuçta ortaya çıkan uygulamanın profilini çıkarmak önemlidir.

Modelinizin NNAPI tarafından nasıl bölümlendirildiğini anlamak için Android günlüklerinde bir mesaj (ExecutionPlan etiketli INFO düzeyinde) olup olmadığını kontrol edin:

ModelBuilder::findBestDeviceForEachOperation(op-name): device-index

op-name, grafikteki işlemin açıklayıcı adıdır ve device-index, cihaz listesindeki aday cihazın dizinidir. Bu liste, ANeuralNetworksCompilation_createForDevices için sağlanan giriştir veya ANeuralNetworksCompilation_createForDevices kullanılıyorsa ANeuralNetworks_getDeviceCount ve ANeuralNetworks_getDevice kullanılarak tüm cihazlar üzerinde iterasyon yapıldığında döndürülen cihazların listesidir.

Mesaj (ExecutionPlan etiketli INFO düzeyinde):

ModelBuilder::partitionTheWork: only one best device: device-name

Bu mesaj, grafiğin tamamının cihazda hızlandırıldığını gösterir device-name.

Yürütme

Yürütme adımı, modeli bir dizi girişe uygular ve hesaplama çıkışlarını uygulamanızın tahsis ettiği bir veya daha fazla kullanıcı arabelleğinde ya da bellek alanında depolar.

Derlenmiş bir modeli yürütmek için aşağıdaki adımları uygulayın:

  1. Yeni bir yürütme örneği oluşturmak için ANeuralNetworksExecution_create() işlevini çağırın.

    // Run the compiled model against a set of inputs
    ANeuralNetworksExecution* run1 = NULL;
    ANeuralNetworksExecution_create(compilation, &run1);
  2. Uygulamanızın hesaplama için giriş değerlerini nereden okuduğunu belirtin. Uygulamanız, sırasıyla ANeuralNetworksExecution_setInput() veya ANeuralNetworksExecution_setInputFromMemory() yöntemini çağırarak kullanıcı arabelleğinden ya da ayrılmış bellek alanından giriş değerlerini okuyabilir.

    // 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. Uygulamanızın çıkış değerlerini nereye yazacağını belirtin. Uygulamanız, sırasıyla ANeuralNetworksExecution_setOutput() veya ANeuralNetworksExecution_setOutputFromMemory() yöntemini çağırarak çıkış değerlerini bir kullanıcı arabelleğine ya da ayrılmış bir bellek alanına yazabilir.

    // Set the output
    float32 myOutput[3][4];
    ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
  4. ANeuralNetworksExecution_startCompute() işlevini çağırarak yürütmenin başlamasını planlayın. Hata yoksa bu işlev ANEURALNETWORKS_NO_ERROR sonuç kodunu döndürür.

    // Starts the work. The work proceeds asynchronously
    ANeuralNetworksEvent* run1_end = NULL;
    ANeuralNetworksExecution_startCompute(run1, &run1_end);
  5. Yürütmenin tamamlanmasını beklemek için ANeuralNetworksEvent_wait() işlevini çağırın. Yürütme başarılı olursa bu işlev ANEURALNETWORKS_NO_ERROR sonuç kodunu döndürür. Bekleme işlemi, yürütmeyi başlatan iş parçacığından farklı bir iş parçacığında yapılabilir.

    // 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. İsterseniz yeni bir ANeuralNetworksExecution örneği oluşturmak için aynı derleme örneğini kullanarak derlenen modele farklı bir giriş grubu uygulayabilirsiniz.

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

Eşzamanlı yürütme

Eşzamansız yürütme, iş parçacıklarını oluşturmak ve senkronize etmek için zaman harcar. Ayrıca gecikme süresi de büyük ölçüde değişkenlik gösterebilir. Bir iş parçacığının bildirim aldığı veya uyandırıldığı zaman ile nihayetinde bir CPU çekirdeğine bağlandığı zaman arasındaki en uzun gecikme süresi 500 mikrosaniyeye kadar çıkabilir.

Gecikmeyi azaltmak için bir uygulamayı çalışma zamanına eşzamanlı çıkarım çağrısı yapacak şekilde yönlendirebilirsiniz. Bu çağrı, çıkarım başlatıldıktan sonra değil, yalnızca çıkarım tamamlandıktan sonra döndürülür. Uygulama, çalışma zamanına eşzamansız çıkarım çağrısı yapmak için ANeuralNetworksExecution_startCompute yöntemini çağırmak yerine çalışma zamanına eşzamanlı çağrı yapmak için ANeuralNetworksExecution_compute yöntemini çağırır. ANeuralNetworksExecution_compute çağrısı ANeuralNetworksEvent almaz ve ANeuralNetworksEvent_wait çağrısıyla eşlenmez.

Kısa süreli yürütmeler

Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran Android cihazlarda NNAPI, ANeuralNetworksBurst nesnesi aracılığıyla seri yürütmeleri destekler. Seri yürütmeler, aynı derlemenin hızlı bir şekilde art arda gerçekleşen bir dizi yürütmesidir. Örneğin, bir kamera çekiminin kareleri veya art arda gelen ses örnekleri üzerinde çalışan yürütmeler bu türdendir. ANeuralNetworksBurst nesnelerini kullanmak, yürütme işlemlerinin daha hızlı tamamlanmasını sağlayabilir. Bunun nedeni, hızlandırıcılara kaynakların yürütme işlemleri arasında yeniden kullanılabileceğini ve hızlandırıcıların, patlama süresi boyunca yüksek performans durumunda kalması gerektiğini belirtmeleridir.

ANeuralNetworksBurst, normal yürütme yolunda yalnızca küçük bir değişiklik yapar. Aşağıdaki kod snippet'inde gösterildiği gibi, ANeuralNetworksBurst_create kullanarak bir burst nesnesi oluşturursunuz:

// Create burst object to be reused across a sequence of executions
ANeuralNetworksBurst* burst = NULL;
ANeuralNetworksBurst_create(compilation, &burst);

Anlık yürütmeler eşzamanlıdır. Ancak her çıkarımı gerçekleştirmek için ANeuralNetworksExecution_compute kullanmak yerine, çeşitli ANeuralNetworksExecution nesnelerini ANeuralNetworksExecution_burstCompute işlevine yapılan çağrılarda aynı ANeuralNetworksBurst ile eşlersiniz.

// 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
// ...

Artık ihtiyaç duyulmadığında ANeuralNetworksBurst_free ile ANeuralNetworksBurst nesnesini serbest bırakın.

// Cleanup
ANeuralNetworksBurst_free(burst);

Eşzamansız komut sıraları ve sınırlı yürütme

Android 11 ve sonraki sürümlerde NNAPI, ANeuralNetworksExecution_startComputeWithDependencies() yöntemiyle eşzamansız yürütmeyi planlamanın ek bir yolunu destekler. Bu yöntemi kullandığınızda yürütme, değerlendirmeye başlamadan önce tüm bağımlı etkinliklerin sinyal vermesini bekler. Yürütme tamamlandığında ve çıkışlar kullanılmaya hazır olduğunda döndürülen etkinlik sinyali verilir.

Yürütme işlemini hangi cihazların gerçekleştirdiğine bağlı olarak etkinlik, bir senkronizasyon engeli ile desteklenebilir. Etkinliği beklemek ve yürütme tarafından kullanılan kaynakları geri almak için ANeuralNetworksEvent_wait() yöntemini çağırmanız gerekir. ANeuralNetworksEvent_createFromSyncFenceFd() kullanarak senkronizasyon sınırlarını bir etkinlik nesnesine aktarabilir ve ANeuralNetworksEvent_getSyncFenceFd() kullanarak senkronizasyon sınırlarını bir etkinlik nesnesinden dışa aktarabilirsiniz.

Dinamik olarak boyutlandırılmış çıkışlar

Çıkış boyutunun giriş verilerine bağlı olduğu (yani boyutun model yürütme sırasında belirlenemediği) modelleri desteklemek için ANeuralNetworksExecution_getOutputOperandRank ve ANeuralNetworksExecution_getOutputOperandDimensions kullanın.

Aşağıdaki kod örneğinde bunun nasıl yapılacağı gösterilmektedir:

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

Temizleme

Temizleme adımı, hesaplamanız için kullanılan dahili kaynakların serbest bırakılmasını sağlar.

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

Hata yönetimi ve CPU yedekleme

Bölümleme sırasında bir hata oluşursa, bir sürücü bir modelin (parçasını) derleyemezse veya bir sürücü derlenmiş bir modeli (parçasını) yürütemezse NNAPI, bir veya daha fazla işlemin kendi CPU uygulamasına geri dönebilir.

NNAPI istemcisi, işlemin optimize edilmiş sürümlerini içeriyorsa (ör. TFLite) CPU yedeklemesini devre dışı bırakmak ve hataları istemcinin optimize edilmiş işlem uygulamasıyla işlemek avantajlı olabilir.

Android 10'da, derleme ANeuralNetworksCompilation_createForDevices kullanılarak gerçekleştirilirse CPU yedekleme devre dışı bırakılır.

Android P'de, sürücüde yürütme başarısız olursa NNAPI yürütme işlemi CPU'ya geri döner. Bu durum, Android 10'da ANeuralNetworksCompilation_createForDevices yerine ANeuralNetworksCompilation_create kullanıldığında da geçerlidir.

İlk yürütme, söz konusu tek bölüm için yedekleme yapar ve bu da başarısız olursa tüm modeli CPU'da yeniden dener.

Bölümleme veya derleme başarısız olursa modelin tamamı CPU'da denenir.

Bazı işlemlerin CPU'da desteklenmediği durumlar vardır. Bu gibi durumlarda, derleme veya yürütme işlemi başarısız olur ve geri dönüş yapılmaz.

CPU yedekleme devre dışı bırakıldıktan sonra bile modelde CPU'da planlanan işlemler olabilir. CPU, ANeuralNetworksCompilation_createForDevices için sağlanan işlemciler listesindeyse ve bu işlemleri destekleyen tek işlemciyse veya bu işlemler için en iyi performansı sunan işlemciyse birincil (yedek olmayan) yürütücü olarak seçilir.

CPU yürütmesi olmadığından emin olmak için ANeuralNetworksCompilation_createForDevices kullanın ve nnapi-reference cihazını cihaz listesinden hariç tutun. Android P'den itibaren, debug.nn.partition özelliğini 2 olarak ayarlayarak hata ayıklama derlemelerinde yürütme sırasında yedeklemeyi devre dışı bırakmak mümkündür.

Bellek alanları

Android 11 ve sonraki sürümlerde NNAPI, opak bellekler için ayırıcı arayüzleri sağlayan bellek alanlarını destekler. Bu sayede uygulamalar, yürütmeler arasında cihaza özgü bellekleri aktarabilir. Böylece NNAPI, aynı sürücüde ardışık yürütmeler gerçekleştirirken verileri gereksiz yere kopyalamaz veya dönüştürmez.

Bellek alanı özelliği, çoğunlukla sürücüye ait olan ve istemci tarafına sık erişim gerektirmeyen tensörler için tasarlanmıştır. Sıra modellerindeki durum tensörleri bu tür tensörlere örnek olarak verilebilir. İstemci tarafında sık CPU erişimi gerektiren tensörler için paylaşılan bellek havuzlarını kullanın.

Opak bellek ayırmak için aşağıdaki adımları uygulayın:

  1. Yeni bir bellek tanımlayıcı oluşturmak için ANeuralNetworksMemoryDesc_create() işlevini çağırın:

    // Create a memory descriptor
    ANeuralNetworksMemoryDesc* desc;
    ANeuralNetworksMemoryDesc_create(&desc);
  2. ANeuralNetworksMemoryDesc_addInputRole() ve ANeuralNetworksMemoryDesc_addOutputRole() yöntemlerini çağırarak tüm giriş ve çıkış rollerini belirtin.

    // 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);
  3. İsteğe bağlı olarak, ANeuralNetworksMemoryDesc_setDimensions() yöntemini çağırarak bellek boyutlarını belirtebilirsiniz.

    // Specify the memory dimensions
    uint32_t dims[] = {3, 4};
    ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
  4. ANeuralNetworksMemoryDesc_finish() yöntemini çağırarak tanımlayıcı tanımını tamamlayın.

    ANeuralNetworksMemoryDesc_finish(desc);
  5. Tanımlayıcıyı ANeuralNetworksMemory_createFromDesc() işlevine ileterek ihtiyacınız olduğu kadar bellek ayırın.

    // Allocate two opaque memories with the descriptor
    ANeuralNetworksMemory* opaqueMem;
    ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
  6. Artık ihtiyacınız olmadığında bellek tanımlayıcıyı serbest bırakın.

    ANeuralNetworksMemoryDesc_free(desc);

Müşteri, oluşturulan ANeuralNetworksMemory nesnesini yalnızca ANeuralNetworksMemoryDesc nesnesinde belirtilen rollere göre ANeuralNetworksExecution_setInputFromMemory() veya ANeuralNetworksExecution_setOutputFromMemory() ile kullanabilir. Ofset ve uzunluk bağımsız değişkenleri 0 olarak ayarlanmalıdır. Bu, tüm belleğin kullanıldığını gösterir. İstemci, ANeuralNetworksMemory_copy() kullanarak belleğin içeriğini açıkça ayarlayabilir veya çıkarabilir.

Boyutları veya sıralaması belirtilmemiş rollerle opak anılar oluşturabilirsiniz. Bu durumda, temel sürücü tarafından desteklenmiyorsa bellek oluşturma işlemi ANEURALNETWORKS_OP_FAILED durumuyla başarısız olabilir. İstemcinin, Ashmem veya BLOB modu AHardwareBuffer tarafından desteklenen yeterince büyük bir arabellek ayırarak yedek mantığı uygulaması önerilir.

NNAPI'nin opak bellek nesnesine erişmesi gerekmediğinde ilgili ANeuralNetworksMemory örneğini serbest bırakın:

ANeuralNetworksMemory_free(opaqueMem);

Performans ölçme

Uygulamanızın performansını, yürütme süresini ölçerek veya profil oluşturarak değerlendirebilirsiniz.

Yürütme süresi

Çalışma zamanı üzerinden toplam yürütme süresini belirlemek istediğinizde eşzamanlı yürütme API'sini kullanabilir ve çağrının ne kadar sürdüğünü ölçebilirsiniz. Toplam yürütme süresini yazılım yığınının daha alt bir seviyesinden belirlemek istediğinizde ANeuralNetworksExecution_setMeasureTiming ve ANeuralNetworksExecution_getDuration kullanarak şunları elde edebilirsiniz:

  • yürütme süresini ifade eder (ana işlemcide çalışan sürücüde değil).
  • Sürücüdeki yürütme süresi (hızlandırıcıda geçen süre dahil)

Sürücüdeki yürütme süresi, çalışma zamanının kendisi ve çalışma zamanının sürücüyle iletişim kurması için gereken IPC gibi ek yükleri içermez.

Bu API'ler, bir sürücünün veya hızlandırıcının çıkarım yapmaya ayırdığı süre yerine, gönderilen iş ile tamamlanan iş etkinlikleri arasındaki süreyi ölçer. Bu süre, bağlam değiştirme nedeniyle kesintiye uğrayabilir.

Örneğin, 1. çıkarım başlar, ardından sürücü 2. çıkarımı gerçekleştirmek için çalışmayı durdurur, daha sonra 1. çıkarımı devam ettirip tamamlar. Bu durumda, 1. çıkarımın yürütme süresi, 2. çıkarımı gerçekleştirmek için çalışmanın durdurulduğu süreyi de içerir.

Bu zamanlama bilgileri, çevrimdışı kullanım için telemetri verileri toplamak amacıyla bir uygulamanın üretim dağıtımında yararlı olabilir. Zamanlama verilerini kullanarak uygulamayı daha yüksek performans için değiştirebilirsiniz.

Bu işlevi kullanırken aşağıdakileri göz önünde bulundurun:

  • Zamanlama bilgilerinin toplanması performans maliyetine neden olabilir.
  • NNAPI çalışma zamanında ve IPC'de harcanan süre hariç olmak üzere, yalnızca bir sürücü kendisinde veya hızlandırıcıda harcanan süreyi hesaplayabilir.
  • Bu API'leri yalnızca ANeuralNetworksCompilation_createForDevices ile numDevices = 1 kullanılarak oluşturulmuş bir ANeuralNetworksExecution ile kullanabilirsiniz.
  • Zamanlama bilgilerini raporlamak için sürücü gerekmez.

Android Systrace ile uygulamanızın profilini oluşturma

Android 10'dan itibaren NNAPI, uygulamanızın profilini oluşturmak için kullanabileceğiniz systrace etkinliklerini otomatik olarak oluşturur.

NNAPI kaynağı, uygulamanız tarafından oluşturulan systrace etkinliklerini işlemek ve model yaşam döngüsünün farklı aşamalarında (örneklendirme, hazırlık, derleme yürütme ve sonlandırma) ve uygulamaların farklı katmanlarında harcanan süreyi gösteren bir tablo görünümü oluşturmak için parse_systrace yardımcı programıyla birlikte gelir. Uygulamanızın bölündüğü katmanlar şunlardır:

  • Application: ana uygulama kodu
  • Runtime: NNAPI Çalışma Zamanı
  • IPC: NNAPI Çalışma Zamanı ile Sürücü arasındaki işlemler arası iletişim kodu
  • Driver: Hızlandırıcı sürücü işlemi.

Profil oluşturma analizi verilerini oluşturma

AOSP kaynak ağacını $ANDROID_BUILD_TOP konumunda kontrol ettiğinizi ve hedef uygulama olarak TFLite görüntü sınıflandırma örneğini kullandığınızı varsayarsak aşağıdaki adımları uygulayarak NNAPI profil oluşturma verilerini oluşturabilirsiniz:

  1. Aşağıdaki komutla Android systrace'i başlatın:
$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

-o trace.html parametresi, izlerin trace.html dosyasına yazılacağını belirtir. Kendi uygulamanızın profilini oluştururken org.tensorflow.lite.examples.classification ifadesini, uygulama manifestinizde belirtilen işlem adıyla değiştirmeniz gerekir.

Bu işlem, kabuk konsollarınızdan birini meşgul eder. Etkileşimli olarak bir enter işleminin sonlandırılmasını beklediğinden komutu arka planda çalıştırmayın.

  1. Systrace toplayıcı başlatıldıktan sonra uygulamanızı başlatın ve karşılaştırma testinizi çalıştırın.

Bizim örneğimizde, Image Classification uygulamasını Android Studio'dan veya uygulama zaten yüklüyse doğrudan test telefonunuzun kullanıcı arayüzünden başlatabilirsiniz. NNAPI verileri oluşturmak için uygulama yapılandırma iletişim kutusunda hedef cihaz olarak NNAPI'yi seçerek uygulamayı NNAPI'yi kullanacak şekilde yapılandırmanız gerekir.

  1. Test tamamlandığında, 1. adımdan beri etkin olan konsol terminalinde enter tuşuna basarak systrace'i sonlandırın.

  2. Kümülatif istatistikler oluşturmak için systrace_parser yardımcı programını çalıştırın:

$ANDROID_BUILD_TOP/frameworks/ml/nn/tools/systrace_parser/parse_systrace.py --total-times trace.html

Ayrıştırıcı aşağıdaki parametreleri kabul eder: - --total-times: Alt katmana yapılan bir çağrıda yürütme için bekleme süresi de dahil olmak üzere bir katmanda harcanan toplam süreyi gösterir. - --print-detail: systrace'ten toplanan tüm etkinlikleri yazdırır. - --per-execution: Tüm aşamaların istatistikleri yerine yalnızca yürütmeyi ve alt aşamalarını (yürütme süreleri olarak) yazdırır. - --json: Çıkışı JSON biçiminde oluşturur.

Çıkış örneği aşağıda gösterilmiştir:

===========================================================================================================================================
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

Toplanan etkinlikler tam bir uygulama izini temsil etmiyorsa ayrıştırıcı başarısız olabilir. Özellikle bir bölümün sonunu işaretlemek için oluşturulan systrace etkinlikleri, ilişkili bir bölüm başlangıç etkinliği olmadan izde mevcutsa başarısız olabilir. Bu durum genellikle systrace toplayıcıyı başlattığınızda önceki bir profil oluşturma oturumundan bazı etkinlikler oluşturuluyorsa meydana gelir. Bu durumda profil oluşturma işlemini tekrar çalıştırmanız gerekir.

Uygulama kodunuzla ilgili istatistikleri systrace_parser çıkışına ekleme

parse_systrace uygulaması, yerleşik Android systrace işlevini temel alır. Özel etkinlik adlarıyla systrace API'yi (Java için, yerel uygulamalar için) kullanarak uygulamanızdaki belirli işlemler için izler ekleyebilirsiniz.

Özel etkinliklerinizi uygulama yaşam döngüsünün aşamalarıyla ilişkilendirmek için etkinlik adınızın başına aşağıdaki dizelerden birini ekleyin:

  • [NN_LA_PI]: Başlatma için uygulama düzeyinde etkinlik
  • [NN_LA_PP]: Hazırlık için uygulama düzeyinde etkinlik
  • [NN_LA_PC]: Derleme için uygulama düzeyinde etkinlik
  • [NN_LA_PE]: Yürütme için uygulama düzeyinde etkinlik

Aşağıda, Execution aşaması için bir runInferenceModel bölümü ve NNAPI izlerinde dikkate alınmayacak diğer bölümleri (preprocessBitmap) içeren Application katmanını ekleyerek TFLite görüntü sınıflandırma örnek kodunu nasıl değiştirebileceğinize dair bir örnek verilmiştir. runInferenceModel bölümü, nnapi systrace ayrıştırıcısı tarafından işlenen systrace etkinliklerinin bir parçası olur:

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 List recognizeImage(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;
}

Hizmet kalitesi

Android 11 ve sonraki sürümlerde NNAPI, bir uygulamanın modellerinin göreceli önceliklerini, belirli bir modeli hazırlamak için beklenen maksimum süreyi ve belirli bir hesaplamayı tamamlamak için beklenen maksimum süreyi belirtmesine olanak tanıyarak daha iyi hizmet kalitesi (HK) sağlar. Android 11, uygulamaların yürütme son tarihlerinin kaçırılması gibi hataları anlamasını sağlayan ek NNAPI sonuç kodları da sunar.

İş yükünün önceliğini ayarlama

Bir NNAPI iş yükünün önceliğini ayarlamak için ANeuralNetworksCompilation_finish() işlevini çağırmadan önce ANeuralNetworksCompilation_setPriority() işlevini çağırın.

Teslim tarihleri belirleme

Uygulamalar, hem model derleme hem de çıkarım için son tarihler belirleyebilir.

İşlenenler hakkında daha fazla bilgi

Aşağıdaki bölümde, işlenenlerin kullanımıyla ilgili ileri düzey konular ele alınmaktadır.

Kuantize edilmiş tensörler

Kuantize edilmiş tensör, kayan nokta değerlerinden oluşan n boyutlu bir diziyi temsil etmenin kompakt bir yoludur.

NNAPI, 8 bit asimetrik kuantize tensörleri destekler. Bu tensörlerde her hücrenin değeri 8 bitlik bir tam sayı ile temsil edilir. Tensörle ilişkili bir ölçek ve sıfır noktası değeri vardır. Bunlar, 8 bitlik tam sayıları temsil edilen kayan nokta değerlerine dönüştürmek için kullanılır.

Formül şöyledir:

(cellValue - zeroPoint) * scale

Burada zeroPoint değeri 32 bitlik bir tam sayı, ölçek ise 32 bitlik bir kayan nokta değeridir.

32 bit kayan nokta değerlerine sahip tensörlerle karşılaştırıldığında, 8 bitlik nicelenmiş tensörlerin iki avantajı vardır:

  • Eğitilen ağırlıklar 32 bit tensörlerin boyutunun dörtte birini kapladığı için uygulamanız daha küçüktür.
  • Hesaplamalar genellikle daha hızlı yürütülebilir. Bunun nedeni, bellekten alınması gereken veri miktarının daha az olması ve DSP'ler gibi işlemcilerin tam sayı matematiği yapma konusundaki verimliliğidir.

Kayan nokta modelini nicelenmiş bir modele dönüştürmek mümkün olsa da deneyimlerimiz, nicelenmiş bir modeli doğrudan eğiterek daha iyi sonuçlar elde edildiğini göstermiştir. Aslında nöral ağ, her bir değerin artan ayrıntı düzeyini telafi etmeyi öğrenir. Her bir nicelenmiş tensör için ölçek ve zeroPoint değerleri, eğitim sürecinde belirlenir.

NNAPI'de, ANeuralNetworksOperandType veri yapısının tür alanını ANEURALNETWORKS_TENSOR_QUANT8_ASYMM olarak ayarlayarak nicelenmiş tensör türlerini tanımlarsınız. Ayrıca bu veri yapısında tensörün ölçeğini ve zeroPoint değerini de belirtirsiniz.

NNAPI, 8 bit asimetrik kuantize tensörlere ek olarak aşağıdakileri destekler:

İsteğe bağlı işlenenler

ANEURALNETWORKS_LSH_PROJECTION gibi birkaç işlem, isteğe bağlı işlenenler alır. Modelde isteğe bağlı işlenenin atlandığını belirtmek için ANeuralNetworksModel_setOperandValue() işlevini çağırın. Arabellek için NULL, uzunluk için 0 değerini iletin.

İşlenenin mevcut olup olmadığına dair karar her yürütme için değişiyorsa işlenenin atlandığını belirtmek için ANeuralNetworksExecution_setInput() veya ANeuralNetworksExecution_setOutput() işlevlerini kullanın. Arabellek için NULL, uzunluk için 0 değerini iletin.

Bilinmeyen sıralı tensörler

Android 9 (API düzeyi 28), bilinmeyen boyutlara ancak bilinen sıralamaya (boyut sayısı) sahip model işlenenlerini kullanıma sundu. Android 10 (API düzeyi 29), ANeuralNetworksOperandType bölümünde gösterildiği gibi bilinmeyen dereceli tensörleri kullanıma sundu.

NNAPI karşılaştırması

NNAPI karşılaştırması, AOSP'de platform/test/mlts/benchmark (karşılaştırma uygulaması) ve platform/test/mlts/models (modeller ve veri kümeleri) içinde kullanılabilir.

Karşılaştırma, gecikme ve doğruluğu değerlendirir ve sürücüleri, aynı modeller ve veri kümeleri için CPU üzerinde çalışan TensorFlow Lite kullanılarak yapılan aynı çalışmayla karşılaştırır.

Karşılaştırmayı kullanmak için aşağıdakileri yapın:

  1. Hedef Android cihazı bilgisayarınıza bağlayın, bir terminal penceresi açın ve cihaza adb üzerinden erişilebildiğinden emin olun.

  2. Birden fazla Android cihaz bağlıysa hedef cihaz ortam değişkenini dışa aktarın. ANDROID_SERIAL

  3. Android üst düzey kaynak dizinine gidin.

  4. Aşağıdaki komutları çalıştırın:

    lunch aosp_arm-userdebug # Or aosp_arm64-userdebug if available
    ./test/mlts/benchmark/build_and_run_benchmark.sh
    

    Karşılaştırma çalıştırmasının sonunda, sonuçlar xdg-open'e iletilen bir HTML sayfası olarak sunulur.

NNAPI günlükleri

NNAPI, sistem günlüklerinde faydalı teşhis bilgileri oluşturur. Günlükleri analiz etmek için logcat yardımcı programını kullanın.

debug.nn.vlog özelliğini (adb shell kullanarak) aşağıdaki değer listesine ayarlayarak belirli aşamalar veya bileşenler için ayrıntılı NNAPI günlük kaydını etkinleştirin. Değerleri boşluk, iki nokta üst üste veya virgülle ayırın:

  • model: Model oluşturma
  • compilation: Model yürütme planının oluşturulması ve derlenmesi
  • execution: Model yürütme
  • cpuexe: NNAPI CPU uygulaması kullanılarak işlemlerin yürütülmesi
  • manager: NNAPI uzantıları, kullanılabilir arayüzler ve özelliklerle ilgili bilgiler
  • all veya 1: Yukarıdaki tüm öğeler

Örneğin, tam ayrıntılı günlük kaydını etkinleştirmek için adb shell setprop debug.nn.vlog all komutunu kullanın. Ayrıntılı günlük kaydını devre dışı bırakmak için şu komutu kullanın: adb shell setprop debug.nn.vlog '""'.

Ayrıntılı günlük kaydı etkinleştirildiğinde, aşama veya bileşen adına ayarlanmış bir etiketle BİLGİ düzeyinde günlük girişleri oluşturulur.

NNAPI API bileşenleri, debug.nn.vlog kontrollü mesajların yanı sıra her biri belirli bir günlük etiketi kullanan çeşitli düzeylerde başka günlük girişleri de sağlar.

Bileşenlerin listesini almak için aşağıdaki ifadeyi kullanarak kaynak ağacında arama yapın:

grep -R 'define LOG_TAG' | awk -F '"' '{print $2}' | sort -u | egrep -v "Sample|FileTag|test"

Bu ifade şu anda aşağıdaki etiketleri döndürüyor:

  • BurstBuilder
  • Geri çağırma işlevleri
  • CompilationBuilder
  • CpuExecutor
  • ExecutionBuilder
  • ExecutionBurstController
  • ExecutionBurstServer
  • ExecutionPlan
  • FibonacciDriver
  • GraphDump
  • IndexedShapeWrapper
  • IonWatcher
  • Yönetici
  • Bellek
  • MemoryUtils
  • MetaModel
  • ModelArgumentInfo
  • ModelBuilder
  • NeuralNetworks
  • OperationResolver
  • İşlemler
  • OperationsUtils
  • PackageInfo
  • TokenHasher
  • TypeManager
  • Utils
  • ValidateHal
  • VersionedInterfaces

logcat tarafından gösterilen günlük mesajlarının düzeyini kontrol etmek için ANDROID_LOG_TAGS ortam değişkenini kullanın.

NNAPI günlük mesajlarının tamamını göstermek ve diğerlerini devre dışı bırakmak için ANDROID_LOG_TAGS değerini aşağıdaki gibi ayarlayın:

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.

Aşağıdaki komutu kullanarak ANDROID_LOG_TAGS değerini ayarlayabilirsiniz:

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')

Bunun yalnızca logcat için geçerli bir filtre olduğunu unutmayın. Ayrıntılı günlük bilgileri oluşturmak için debug.nn.vlog özelliğini all olarak ayarlamanız gerekir.