নিউরাল নেটওয়ার্ক API

অ্যান্ড্রয়েড নিউরাল নেটওয়ার্কস এপিআই (NNAPI) হলো একটি অ্যান্ড্রয়েড সি এপিআই, যা অ্যান্ড্রয়েড ডিভাইসে মেশিন লার্নিংয়ের জন্য গণনা-নিবিড় অপারেশন চালানোর উদ্দেশ্যে ডিজাইন করা হয়েছে। NNAPI-কে উচ্চ-স্তরের মেশিন লার্নিং ফ্রেমওয়ার্ক, যেমন TensorFlow Lite এবং Caffe2, যেগুলো নিউরাল নেটওয়ার্ক তৈরি ও প্রশিক্ষণ দেয়, সেগুলোর জন্য কার্যকারিতার একটি ভিত্তি স্তর প্রদান করার উদ্দেশ্যে ডিজাইন করা হয়েছে। এই এপিআইটি অ্যান্ড্রয়েড ৮.১ (এপিআই লেভেল ২৭) বা তার উচ্চতর সংস্করণে চালিত সমস্ত অ্যান্ড্রয়েড ডিভাইসে উপলব্ধ ছিল, কিন্তু অ্যান্ড্রয়েড ১৫-এ এটিকে অপ্রচলিত (deprecated) ঘোষণা করা হয়।

NNAPI পূর্বে প্রশিক্ষিত ও ডেভেলপার-সংজ্ঞায়িত মডেলগুলিতে অ্যান্ড্রয়েড ডিভাইসের ডেটা প্রয়োগ করে ইনফারেন্সিং সমর্থন করে। ইনফারেন্সিং-এর উদাহরণগুলির মধ্যে রয়েছে ছবির শ্রেণিবিন্যাস, ব্যবহারকারীর আচরণের পূর্বাভাস দেওয়া এবং একটি সার্চ কোয়েরির জন্য উপযুক্ত প্রতিক্রিয়া নির্বাচন করা।

ডিভাইসে ইনফারেন্সিংয়ের অনেক সুবিধা রয়েছে:

  • লেটেন্সি : আপনাকে নেটওয়ার্ক সংযোগের মাধ্যমে কোনো অনুরোধ পাঠিয়ে প্রতিক্রিয়ার জন্য অপেক্ষা করতে হয় না। উদাহরণস্বরূপ, ক্যামেরা থেকে আসা ধারাবাহিক ফ্রেম প্রসেস করে এমন ভিডিও অ্যাপ্লিকেশনগুলির জন্য এটি অত্যন্ত গুরুত্বপূর্ণ হতে পারে।
  • প্রাপ্যতা : অ্যাপ্লিকেশনটি নেটওয়ার্ক কভারেজের বাইরে থাকলেও চলে।
  • গতি : নিউরাল নেটওয়ার্ক প্রসেসিংয়ের জন্য বিশেষভাবে তৈরি নতুন হার্ডওয়্যার, শুধুমাত্র একটি সাধারণ সিপিইউ-এর তুলনায় উল্লেখযোগ্যভাবে দ্রুততর গণনা প্রদান করে।
  • গোপনীয়তা : ডেটা অ্যান্ড্রয়েড ডিভাইস থেকে বাইরে যায় না।
  • খরচ : সমস্ত গণনা অ্যান্ড্রয়েড ডিভাইসে করা হলে কোনো সার্ভার ফার্মের প্রয়োজন হয় না।

এছাড়াও কিছু সুবিধা-অসুবিধা রয়েছে যা একজন ডেভেলপারের মনে রাখা উচিত:

  • সিস্টেম ইউটিলাইজেশন : নিউরাল নেটওয়ার্ক মূল্যায়নে প্রচুর গণনা জড়িত থাকে, যা ব্যাটারির শক্তি ব্যবহার বাড়িয়ে দিতে পারে। যদি এটি আপনার অ্যাপের জন্য উদ্বেগের কারণ হয়, বিশেষ করে দীর্ঘ সময় ধরে চলা গণনার ক্ষেত্রে, আপনার ব্যাটারির স্বাস্থ্য পর্যবেক্ষণ করার কথা বিবেচনা করা উচিত।
  • অ্যাপ্লিকেশনের আকার : আপনার মডেলগুলোর আকারের দিকে মনোযোগ দিন। মডেলগুলো একাধিক মেগাবাইট জায়গা নিতে পারে। যদি আপনার APK-তে বড় মডেল যুক্ত করা আপনার ব্যবহারকারীদের উপর অযাচিত প্রভাব ফেলে, তাহলে আপনি অ্যাপ ইনস্টলেশনের পরে মডেলগুলো ডাউনলোড করা, ছোট মডেল ব্যবহার করা, অথবা ক্লাউডে আপনার গণনাগুলো চালানোর কথা বিবেচনা করতে পারেন। NNAPI ক্লাউডে মডেল চালানোর জন্য কোনো কার্যকারিতা প্রদান করে না।

NNAPI কীভাবে ব্যবহার করতে হয় তার একটি উদাহরণ দেখতে অ্যান্ড্রয়েড নিউরাল নেটওয়ার্কস এপিআই স্যাম্পলটি দেখুন।

নিউরাল নেটওয়ার্ক এপিআই রানটাইম বুঝুন

NNAPI মূলত মেশিন লার্নিং লাইব্রেরি, ফ্রেমওয়ার্ক এবং টুলগুলোর ব্যবহারের জন্য তৈরি, যা ডেভেলপারদের ডিভাইসের বাইরে তাদের মডেল প্রশিক্ষণ দিতে এবং অ্যান্ড্রয়েড ডিভাইসে সেগুলো স্থাপন করতে সাহায্য করে। অ্যাপগুলো সাধারণত সরাসরি NNAPI ব্যবহার করে না, বরং উচ্চ-স্তরের মেশিন লার্নিং ফ্রেমওয়ার্ক ব্যবহার করে। এই ফ্রেমওয়ার্কগুলো আবার সমর্থিত ডিভাইসগুলোতে হার্ডওয়্যার-ত্বরিত ইনফারেন্স অপারেশন সম্পাদনের জন্য NNAPI ব্যবহার করতে পারে।

একটি অ্যাপের প্রয়োজনীয়তা এবং অ্যান্ড্রয়েড ডিভাইসের হার্ডওয়্যার সক্ষমতার ওপর ভিত্তি করে, অ্যান্ড্রয়েডের নিউরাল নেটওয়ার্ক রানটাইম ডিভাইসে উপলব্ধ প্রসেসরগুলোর মধ্যে, যেমন—বিশেষ নিউরাল নেটওয়ার্ক হার্ডওয়্যার, গ্রাফিক্স প্রসেসিং ইউনিট (GPU), এবং ডিজিটাল সিগন্যাল প্রসেসর (DSP), কম্পিউটেশনের কাজের চাপ দক্ষতার সাথে বন্টন করতে পারে।

যেসব অ্যান্ড্রয়েড ডিভাইসে বিশেষায়িত ভেন্ডর ড্রাইভার নেই, সেগুলোতে NNAPI রানটাইম সিপিইউ-তে অনুরোধগুলো সম্পাদন করে।

চিত্র ১-এ NNAPI-এর উচ্চ-স্তরের সিস্টেম আর্কিটেকচার দেখানো হয়েছে।

চিত্র ১. অ্যান্ড্রয়েড নিউরাল নেটওয়ার্কস এপিআই-এর সিস্টেম আর্কিটেকচার

নিউরাল নেটওয়ার্ক এপিআই প্রোগ্রামিং মডেল

NNAPI ব্যবহার করে গণনা সম্পাদন করতে, আপনাকে প্রথমে একটি ডিরেক্টেড গ্রাফ তৈরি করতে হবে যা সম্পাদনযোগ্য গণনাগুলোকে সংজ্ঞায়িত করে। এই গণনা গ্রাফটি, আপনার ইনপুট ডেটার (উদাহরণস্বরূপ, একটি মেশিন লার্নিং ফ্রেমওয়ার্ক থেকে প্রাপ্ত ওয়েট এবং বায়াস) সাথে মিলিত হয়ে, NNAPI রানটাইম মূল্যায়নের জন্য মডেলটি গঠন করে।

NNAPI চারটি প্রধান অ্যাবস্ট্রাকশন ব্যবহার করে:

  • মডেল : একটি প্রশিক্ষণ প্রক্রিয়ার মাধ্যমে শেখা গাণিতিক অপারেশন এবং ধ্রুবক মানগুলির একটি গণনা গ্রাফ। এই অপারেশনগুলি নিউরাল নেটওয়ার্কের জন্য নির্দিষ্ট। এর মধ্যে রয়েছে দ্বি-মাত্রিক (2D) কনভোলিউশন , লজিস্টিক ( সিগময়েড ) অ্যাক্টিভেশন, রেকটিফায়েড লিনিয়ার (ReLU) অ্যাক্টিভেশন এবং আরও অনেক কিছু। একটি মডেল তৈরি করা একটি সিনক্রোনাস অপারেশন। একবার সফলভাবে তৈরি হয়ে গেলে, এটি বিভিন্ন থ্রেড এবং কম্পাইলেশনে পুনরায় ব্যবহার করা যায়। NNAPI-তে, একটি মডেলকে ANeuralNetworksModel ইনস্ট্যান্স হিসাবে উপস্থাপন করা হয়।
  • কম্পাইলেশন : একটি NNAPI মডেলকে নিম্ন-স্তরের কোডে কম্পাইল করার জন্য একটি কনফিগারেশনকে বোঝায়। একটি কম্পাইলেশন তৈরি করা একটি সিনক্রোনাস অপারেশন। একবার সফলভাবে তৈরি হয়ে গেলে, এটি বিভিন্ন থ্রেড এবং এক্সিকিউশনে পুনরায় ব্যবহার করা যায়। NNAPI-তে, প্রতিটি কম্পাইলেশন একটি ANeuralNetworksCompilation ইনস্ট্যান্স হিসাবে উপস্থাপিত হয়।
  • মেমরি : শেয়ার্ড মেমরি, মেমরি ম্যাপড ফাইল এবং অনুরূপ মেমরি বাফারকে বোঝায়। মেমরি বাফার ব্যবহার করে NNAPI রানটাইম আরও দক্ষতার সাথে ড্রাইভারগুলিতে ডেটা স্থানান্তর করতে পারে। একটি অ্যাপ সাধারণত একটি শেয়ার্ড মেমরি বাফার তৈরি করে, যাতে একটি মডেল সংজ্ঞায়িত করার জন্য প্রয়োজনীয় প্রতিটি টেনসর থাকে। আপনি একটি এক্সিকিউশন ইনস্ট্যান্সের ইনপুট এবং আউটপুট সংরক্ষণ করতেও মেমরি বাফার ব্যবহার করতে পারেন। NNAPI-তে, প্রতিটি মেমরি বাফারকে একটি ANeuralNetworksMemory ইনস্ট্যান্স হিসাবে উপস্থাপন করা হয়।
  • এক্সিকিউশন : এক সেট ইনপুটের উপর একটি NNAPI মডেল প্রয়োগ করতে এবং ফলাফল সংগ্রহ করার জন্য ব্যবহৃত ইন্টারফেস। এক্সিকিউশন সিনক্রোনাসলি বা অ্যাসিনক্রোনাসলি সম্পন্ন করা যেতে পারে।

    অ্যাসিঙ্ক্রোনাস এক্সিকিউশনের ক্ষেত্রে, একাধিক থ্রেড একই এক্সিকিউশনের জন্য অপেক্ষা করতে পারে। এই এক্সিকিউশনটি সম্পন্ন হলে, সমস্ত থ্রেডকে মুক্ত করে দেওয়া হয়।

    NNAPI-তে, প্রতিটি এক্সিকিউশন একটি ANeuralNetworksExecution ইনস্ট্যান্স হিসেবে উপস্থাপিত হয়।

চিত্র ২-এ মৌলিক প্রোগ্রামিং প্রবাহ দেখানো হয়েছে।

চিত্র ২. অ্যান্ড্রয়েড নিউরাল নেটওয়ার্কস এপিআই-এর প্রোগ্রামিং প্রবাহ

এই বিভাগের বাকি অংশে আপনার NNAPI মডেলকে গণনা সম্পাদনের জন্য প্রস্তুত করা, মডেলটি কম্পাইল করা এবং কম্পাইল করা মডেলটি কার্যকর করার ধাপগুলো বর্ণনা করা হয়েছে।

প্রশিক্ষণ ডেটাতে প্রবেশাধিকার প্রদান করুন

আপনার প্রশিক্ষিত ওয়েটস এবং বায়াস ডেটা সম্ভবত একটি ফাইলে সংরক্ষিত থাকে। NNAPI রানটাইমকে এই ডেটাতে কার্যকর অ্যাক্সেস দেওয়ার জন্য, খোলা ডেটা ফাইলের ফাইল ডেসক্রিপ্টরটি পাস করে ANeuralNetworksMemory_createFromFd() ফাংশনটি কল করে একটি ANeuralNetworksMemory ইনস্ট্যান্স তৈরি করুন। এছাড়াও আপনাকে মেমরি প্রোটেকশন ফ্ল্যাগ এবং ফাইলে শেয়ার্ড মেমরি রিজিয়নটি যেখান থেকে শুরু হয়, সেই অফসেটটি নির্দিষ্ট করতে হবে।

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

যদিও এই উদাহরণে আমরা আমাদের সমস্ত ওয়েটের জন্য শুধুমাত্র একটি ANeuralNetworksMemory ইনস্ট্যান্স ব্যবহার করেছি, একাধিক ফাইলের জন্য একাধিক ANeuralNetworksMemory ইনস্ট্যান্স ব্যবহার করা সম্ভব।

নেটিভ হার্ডওয়্যার বাফার ব্যবহার করুন

আপনি মডেল ইনপুট, আউটপুট এবং ধ্রুবক অপারেন্ড মানের জন্য নেটিভ হার্ডওয়্যার বাফার ব্যবহার করতে পারেন। কিছু ক্ষেত্রে, ড্রাইভারের ডেটা কপি করার প্রয়োজন ছাড়াই একটি NNAPI অ্যাক্সেলারেটর AHardwareBuffer অবজেক্ট অ্যাক্সেস করতে পারে। AHardwareBuffer বিভিন্ন কনফিগারেশন রয়েছে, এবং প্রতিটি NNAPI অ্যাক্সেলারেটর এই সমস্ত কনফিগারেশন সমর্থন নাও করতে পারে। এই সীমাবদ্ধতার কারণে, ANeuralNetworksMemory_createFromAHardwareBuffer রেফারেন্স ডকুমেন্টেশনে তালিকাভুক্ত সীমাবদ্ধতাগুলো দেখুন এবং অ্যাক্সেলারেটর নির্দিষ্ট করার জন্য ডিভাইস অ্যাসাইনমেন্ট ব্যবহার করে টার্গেট ডিভাইসগুলোতে আগে থেকেই পরীক্ষা করে নিশ্চিত করুন যে AHardwareBuffer ব্যবহারকারী কম্পাইলেশন এবং এক্সিকিউশনগুলো প্রত্যাশিতভাবে কাজ করছে।

NNAPI রানটাইমকে একটি AHardwareBuffer অবজেক্ট অ্যাক্সেস করার অনুমতি দিতে, নিম্নলিখিত কোড নমুনায় দেখানো অনুযায়ী ANeuralNetworksMemory_createFromAHardwareBuffer ফাংশনটি কল করে এবং AHardwareBuffer অবজেক্টটি পাস করে একটি ANeuralNetworksMemory ইনস্ট্যান্স তৈরি করুন:

// 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-এর আর AHardwareBuffer অবজেক্টটি অ্যাক্সেস করার প্রয়োজন হয় না, তখন সংশ্লিষ্ট ANeuralNetworksMemory ইনস্ট্যান্সটিকে মুক্ত করুন:

ANeuralNetworksMemory_free(mem2);

দ্রষ্টব্য:

  • আপনি AHardwareBuffer শুধুমাত্র সম্পূর্ণ বাফারের জন্য ব্যবহার করতে পারবেন; এটি ARect প্যারামিটারের সাথে ব্যবহার করা যাবে না।
  • NNAPI রানটাইম বাফার ফ্লাশ করবে না। এক্সিকিউশন শিডিউল করার আগে আপনাকে নিশ্চিত করতে হবে যে ইনপুট এবং আউটপুট বাফারগুলো অ্যাক্সেসযোগ্য আছে।
  • সিঙ্ক ফেন্স ফাইল ডেসক্রিপ্টরের জন্য কোনো সমর্থন নেই।
  • বিক্রেতা-নির্দিষ্ট ফরম্যাট এবং ব্যবহারের বিটসহ কোনো AHardwareBuffer ক্ষেত্রে, ক্যাশ ফ্লাশ করার দায়িত্ব ক্লায়েন্ট নাকি ড্রাইভারের, তা নির্ধারণ করা বিক্রেতার বাস্তবায়নের উপর নির্ভর করে।

মডেল

NNAPI-তে মডেল হলো গণনার মৌলিক একক। প্রতিটি মডেল এক বা একাধিক অপারেন্ড ও অপারেশন দ্বারা সংজ্ঞায়িত হয়।

অপারেন্ড

অপারেন্ড হলো গ্রাফ নির্ধারণে ব্যবহৃত ডেটা অবজেক্ট। এগুলোর মধ্যে অন্তর্ভুক্ত রয়েছে মডেলের ইনপুট ও আউটপুট, এক অপারেশন থেকে অন্য অপারেশনে প্রবাহিত ডেটা ধারণকারী মধ্যবর্তী নোড এবং এই অপারেশনগুলোতে প্রেরিত ধ্রুবকসমূহ।

NNAPI মডেলে দুই ধরনের অপারেন্ড যোগ করা যায়: স্কেলার এবং টেনসর

স্কেলার একটি একক মানকে বোঝায়। NNAPI বুলিয়ান, ১৬-বিট ফ্লোটিং পয়েন্ট, ৩২-বিট ফ্লোটিং পয়েন্ট, ৩২-বিট ইন্টিজার এবং আনসাইনড ৩২-বিট ইন্টিজার ফরম্যাটে স্কেলার মান সমর্থন করে।

NNAPI-এর বেশিরভাগ অপারেশনে টেনসর জড়িত থাকে। টেনসর হলো n-মাত্রিক অ্যারে। NNAPI ১৬-বিট ফ্লোটিং পয়েন্ট, ৩২-বিট ফ্লোটিং পয়েন্ট, ৮-বিট কোয়ান্টাইজড , ৩২-বিট ইন্টিজার এবং ৮-বিট বুলিয়ান মানের টেনসর সমর্থন করে।

উদাহরণস্বরূপ, চিত্র ৩-এ দুটি অপারেশনসহ একটি মডেল দেখানো হয়েছে: একটি যোগ এবং তারপরে একটি গুণ। মডেলটি একটি ইনপুট টেনসর গ্রহণ করে এবং একটি আউটপুট টেনসর তৈরি করে।

চিত্র ৩. একটি NNAPI মডেলের অপারেন্ডের উদাহরণ

উপরের মডেলটিতে সাতটি অপারেন্ড রয়েছে। এই অপারেন্ডগুলোকে মডেলে যুক্ত করার ক্রম অনুসারে সূচক দ্বারা পরোক্ষভাবে চিহ্নিত করা হয়। প্রথম যুক্ত হওয়া অপারেন্ডটির সূচক হলো ০, দ্বিতীয়টির সূচক ১, এবং এভাবেই চলতে থাকে। অপারেন্ড ১, ২, ৩ এবং ৫ হলো ধ্রুবক অপারেন্ড।

আপনি কোন ক্রমে অপারেন্ডগুলো যোগ করছেন তা গুরুত্বপূর্ণ নয়। উদাহরণস্বরূপ, মডেল আউটপুট অপারেন্ডটি প্রথমে যোগ করা যেতে পারে। গুরুত্বপূর্ণ বিষয়টি হলো, কোনো অপারেন্ডকে উল্লেখ করার সময় সঠিক ইনডেক্স মান ব্যবহার করা।

অপারেন্ডগুলোর টাইপ থাকে। মডেলে যুক্ত করার সময় এগুলো নির্দিষ্ট করা হয়।

একটি অপারেন্ডকে কোনো মডেলের ইনপুট এবং আউটপুট উভয় হিসেবে ব্যবহার করা যায় না।

প্রতিটি অপারেন্ডকে অবশ্যই একটি মডেল ইনপুট, একটি ধ্রুবক, অথবা ঠিক একটি অপারেশনের আউটপুট অপারেন্ড হতে হবে।

অপারেন্ড ব্যবহারের বিষয়ে অতিরিক্ত তথ্যের জন্য, ‘অপারেন্ড সম্পর্কে আরও’ দেখুন।

অপারেশন

একটি অপারেশন সম্পাদিতব্য গণনাসমূহকে নির্দিষ্ট করে। প্রতিটি অপারেশন নিম্নলিখিত উপাদানগুলো নিয়ে গঠিত:

  • একটি অপারেশনের ধরণ (উদাহরণস্বরূপ, যোগ, গুণ, কনভোলিউশন),
  • অপারেশনটি ইনপুট হিসেবে যে অপারেন্ডগুলো ব্যবহার করে, সেগুলোর সূচকের একটি তালিকা, এবং
  • অপারেশনটি আউটপুটের জন্য যে অপারেন্ডগুলো ব্যবহার করে, সেগুলোর সূচকের একটি তালিকা।

এই তালিকাগুলোর ক্রম গুরুত্বপূর্ণ; প্রতিটি অপারেশন টাইপের প্রত্যাশিত ইনপুট ও আউটপুট জানতে NNAPI API রেফারেন্স দেখুন।

অপারেশনটি যোগ করার আগে, অপারেশনটি যে অপারেন্ডগুলো গ্রহণ বা উৎপাদন করে, সেগুলো অবশ্যই মডেলে যোগ করতে হবে।

আপনি কোন ক্রমে অপারেশনগুলো যোগ করছেন তা গুরুত্বপূর্ণ নয়। অপারেশনগুলো কোন ক্রমে সম্পাদিত হবে তা নির্ধারণ করতে NNAPI, অপারেন্ড এবং অপারেশনগুলোর কম্পিউটেশন গ্রাফ দ্বারা প্রতিষ্ঠিত নির্ভরশীলতার উপর নির্ভর করে।

NNAPI যে অপারেশনগুলো সমর্থন করে, সেগুলো নিচের সারণিতে সংক্ষেপে তুলে ধরা হলো:

বিভাগ অপারেশন
উপাদান-ভিত্তিক গাণিতিক ক্রিয়াকলাপ
টেনসর ম্যানিপুলেশন
ইমেজ অপারেশন
অনুসন্ধান কার্যক্রম
স্বাভাবিকীকরণ কার্যক্রম
কনভোলিউশন অপারেশন
পুলিং অপারেশন
সক্রিয়করণ কার্যক্রম
অন্যান্য কার্যক্রম

এপিআই লেভেল ২৮-এ একটি জ্ঞাত সমস্যা: যখন অ্যান্ড্রয়েড ৯ (এপিআই লেভেল ২৮) এবং তার পরবর্তী সংস্করণগুলিতে উপলব্ধ ANEURALNETWORKS_PAD অপারেশনে ANEURALNETWORKS_TENSOR_QUANT8_ASYMM টেনসর পাস করা হয়, তখন NNAPI থেকে প্রাপ্ত আউটপুট TensorFlow Lite-এর মতো উচ্চ-স্তরের মেশিন লার্নিং ফ্রেমওয়ার্কগুলির আউটপুটের সাথে নাও মিলতে পারে। এর পরিবর্তে আপনার শুধুমাত্র ANEURALNETWORKS_TENSOR_FLOAT32 পাস করা উচিত। অ্যান্ড্রয়েড ১০ (এপিআই লেভেল ২৯) এবং তার পরবর্তী সংস্করণগুলিতে এই সমস্যাটির সমাধান করা হয়েছে।

মডেল তৈরি করুন

নিম্নলিখিত উদাহরণে, আমরা চিত্র ৩- এ প্রদর্শিত দুই-অপারেশন মডেলটি তৈরি করব।

মডেলটি তৈরি করতে, এই ধাপগুলো অনুসরণ করুন:

  1. একটি খালি মডেল সংজ্ঞায়িত করতে ANeuralNetworksModel_create() ফাংশনটি কল করুন।

    ANeuralNetworksModel* model = NULL;
    ANeuralNetworksModel_create(&model);
  2. ANeuralNetworks_addOperand() কল করে আপনার মডেলে অপারেন্ডগুলো যোগ করুন। এদের ডেটা টাইপ 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 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. যেসব অপারেন্ডের মান ধ্রুবক, যেমন ওয়েট এবং বায়াস যা আপনার অ্যাপ একটি ট্রেনিং প্রক্রিয়া থেকে পায়, সেগুলোর জন্য ANeuralNetworksModel_setOperandValue() এবং ANeuralNetworksModel_setOperandValueFromMemory() ফাংশনগুলো ব্যবহার করুন।

    নিম্নলিখিত উদাহরণে, আমরা "প্রশিক্ষণ ডেটাতে অ্যাক্সেস প্রদান করুন" অংশে তৈরি করা মেমরি বাফারের সাথে সঙ্গতিপূর্ণ প্রশিক্ষণ ডেটা ফাইল থেকে ধ্রুবক মান সেট করি।

    // 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. ডিরেক্টেড গ্রাফের প্রতিটি অপারেশনের জন্য যা আপনি গণনা করতে চান, ANeuralNetworksModel_addOperation() ফাংশনটি কল করে অপারেশনটি আপনার মডেলে যোগ করুন।

    এই কলের প্যারামিটার হিসেবে আপনার অ্যাপকে অবশ্যই নিম্নলিখিতগুলি প্রদান করতে হবে:

    • অপারেশনের ধরণ
    • ইনপুট মানগুলির সংখ্যা
    • ইনপুট অপারেন্ডগুলির সূচকের অ্যারে
    • আউটপুট মানগুলির সংখ্যা
    • আউটপুট অপারেন্ডগুলির সূচকগুলির অ্যারে

    মনে রাখবেন যে, একই অপারেশনের ইনপুট এবং আউটপুট উভয় কাজের জন্য একটি অপারেন্ড ব্যবহার করা যায় না।

    // 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() ফাংশনটি কল করে শনাক্ত করুন যে মডেলটি কোন অপারেন্ডগুলোকে তার ইনপুট এবং আউটপুট হিসেবে বিবেচনা করবে।

    // 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. ঐচ্ছিকভাবে, ANeuralNetworksModel_relaxComputationFloat32toFloat16() কল করে নির্দিষ্ট করুন যে ANEURALNETWORKS_TENSOR_FLOAT32 IEEE 754 16-বিট ফ্লোটিং-পয়েন্ট ফরম্যাটের মতো নিম্ন রেঞ্জ বা প্রিসিশনে গণনা করার অনুমতি দেওয়া হবে কিনা।

  7. আপনার মডেলের সংজ্ঞা চূড়ান্ত করতে ANeuralNetworksModel_finish() কল করুন। যদি কোনো ত্রুটি না থাকে, তাহলে এই ফাংশনটি ANEURALNETWORKS_NO_ERROR ফলাফল কোডটি রিটার্ন করে।

    ANeuralNetworksModel_finish(model);

একবার একটি মডেল তৈরি করলে, আপনি সেটিকে যতবার খুশি কম্পাইল করতে পারেন এবং প্রতিটি কম্পাইলেশনও যতবার খুশি চালাতে পারেন।

প্রবাহ নিয়ন্ত্রণ করুন

একটি NNAPI মডেলে কন্ট্রোল ফ্লো অন্তর্ভুক্ত করতে, নিম্নলিখিতগুলি করুন:

  1. সংশ্লিষ্ট এক্সিকিউশন সাবগ্রাফগুলি (একটি IF স্টেটমেন্টের জন্য then এবং else সাবগ্রাফ, একটি WHILE লুপের জন্য condition এবং body সাবগ্রাফ) স্বতন্ত্র ANeuralNetworksModel* মডেল হিসাবে তৈরি করুন:

    ANeuralNetworksModel* thenModel = makeThenModel();
    ANeuralNetworksModel* elseModel = makeElseModel();
  2. কন্ট্রোল ফ্লো ধারণকারী মডেলের মধ্যে সেই মডেলগুলোকে রেফারেন্স করে এমন অপারেন্ড তৈরি করুন:

    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. নিয়ন্ত্রণ প্রবাহ অপারেশন যোগ করুন:

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

সংকলন

কম্পাইলেশন ধাপটি নির্ধারণ করে যে আপনার মডেলটি কোন কোন প্রসেসরে নির্বাহ করা হবে এবং এর নির্বাহের জন্য সংশ্লিষ্ট ড্রাইভারগুলোকে প্রস্তুত হতে বলে। এর মধ্যে আপনার মডেলটি যে প্রসেসরগুলোতে চলবে, সেগুলোর জন্য নির্দিষ্ট মেশিন কোড তৈরি করাও অন্তর্ভুক্ত থাকতে পারে।

একটি মডেল সংকলন করতে, এই ধাপগুলো অনুসরণ করুন:

  1. একটি নতুন কম্পাইলেশন ইনস্ট্যান্স তৈরি করতে ANeuralNetworksCompilation_create() ফাংশনটি কল করুন।

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

    ঐচ্ছিকভাবে, আপনি ডিভাইস অ্যাসাইনমেন্ট ব্যবহার করে কোন ডিভাইসগুলিতে প্রোগ্রামটি চালানো হবে তা স্পষ্টভাবে বেছে নিতে পারেন।

  2. আপনি চাইলে রানটাইম কীভাবে ব্যাটারি শক্তি ব্যবহার এবং নির্বাহের গতির মধ্যে ভারসাম্য রক্ষা করবে, তা প্রভাবিত করতে পারেন। ANeuralNetworksCompilation_setPreference() কল করার মাধ্যমে আপনি এটি করতে পারেন।

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

    আপনি যে পছন্দগুলি নির্দিষ্ট করতে পারেন তার মধ্যে রয়েছে:

    • ANEURALNETWORKS_PREFER_LOW_POWER : এমনভাবে নির্বাহ করাকে অগ্রাধিকার দেওয়া হয় যাতে ব্যাটারির ব্যবহার সর্বনিম্ন হয়। যেসব কম্পাইলেশন প্রায়শই চালানো হয়, সেগুলোর জন্য এটি কাম্য।
    • ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER : যত দ্রুত সম্ভব একটিমাত্র উত্তর ফেরত দেওয়াকে অগ্রাধিকার দেওয়া হয়, এমনকি যদি এর ফলে বিদ্যুৎ খরচ বেশিও হয়। এটিই ডিফল্ট।
    • ANEURALNETWORKS_PREFER_SUSTAINED_SPEED : পরপর ফ্রেমগুলোর থ্রুপুট সর্বাধিক করাকে অগ্রাধিকার দেওয়া, যেমন ক্যামেরা থেকে আসা পরপর ফ্রেমগুলো প্রসেস করার সময়।
  3. আপনি চাইলে ANeuralNetworksCompilation_setCaching কল করে কম্পাইলেশন ক্যাশিং সেট আপ করতে পারেন।

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

    cacheDir এর জন্য getCodeCacheDir() ব্যবহার করুন। নির্দিষ্ট করা token অ্যাপ্লিকেশনের প্রতিটি মডেলের জন্য অনন্য হতে হবে।

  4. ANeuralNetworksCompilation_finish() কল করে কম্পাইলেশন ডেফিনিশনটি চূড়ান্ত করুন। যদি কোনো ত্রুটি না থাকে, তাহলে এই ফাংশনটি ANEURALNETWORKS_NO_ERROR রেজাল্ট কোডটি রিটার্ন করে।

    ANeuralNetworksCompilation_finish(compilation);

ডিভাইস আবিষ্কার এবং বরাদ্দ

অ্যান্ড্রয়েড ১০ (এপিআই লেভেল ২৯) এবং এর চেয়ে উন্নত সংস্করণে চালিত অ্যান্ড্রয়েড ডিভাইসগুলোতে, NNAPI এমন কিছু ফাংশন সরবরাহ করে যা মেশিন লার্নিং ফ্রেমওয়ার্ক লাইব্রেরি এবং অ্যাপগুলোকে উপলব্ধ ডিভাইসগুলো সম্পর্কে তথ্য পেতে এবং এক্সিকিউশনের জন্য কোন ডিভাইস ব্যবহার করা হবে তা নির্দিষ্ট করতে সাহায্য করে। উপলব্ধ ডিভাইসগুলো সম্পর্কে তথ্য সরবরাহ করার ফলে অ্যাপগুলো কোনো ডিভাইসে থাকা ড্রাইভারের সঠিক সংস্করণটি পেতে পারে, যা পরিচিত অসঙ্গতিগুলো এড়াতে সাহায্য করে। একটি মডেলের বিভিন্ন অংশ কোন ডিভাইসে এক্সিকিউট করা হবে তা নির্দিষ্ট করার ক্ষমতা অ্যাপগুলোকে দেওয়ার মাধ্যমে, সেগুলোকে যে অ্যান্ড্রয়েড ডিভাইসে ডেপ্লয় করা হয়েছে তার জন্য অপ্টিমাইজ করা সম্ভব হয়।

ডিভাইস আবিষ্কার

উপলব্ধ ডিভাইসের সংখ্যা জানতে ANeuralNetworks_getDeviceCount ব্যবহার করুন। প্রতিটি ডিভাইসের জন্য, সেই ডিভাইসের একটি রেফারেন্সে একটি ANeuralNetworksDevice ইনস্ট্যান্স সেট করতে ANeuralNetworks_getDevice ব্যবহার করুন।

একবার আপনার কাছে ডিভাইস রেফারেন্স থাকলে, আপনি নিম্নলিখিত ফাংশনগুলি ব্যবহার করে সেই ডিভাইস সম্পর্কে অতিরিক্ত তথ্য জানতে পারবেন:

ডিভাইস অ্যাসাইনমেন্ট

একটি মডেলের কোন অপারেশনগুলো নির্দিষ্ট ডিভাইসে চালানো যাবে তা জানতে ANeuralNetworksModel_getSupportedOperationsForDevices ব্যবহার করুন।

এক্সিকিউশনের জন্য কোন অ্যাক্সিলারেটরগুলো ব্যবহার করা হবে তা নিয়ন্ত্রণ করতে, ANeuralNetworksCompilation_create ANeuralNetworksCompilation_createForDevices করুন। ফলস্বরূপ প্রাপ্ত ANeuralNetworksCompilation অবজেক্টটি স্বাভাবিকভাবে ব্যবহার করুন। যদি প্রদত্ত মডেলে এমন কোনো অপারেশন থাকে যা নির্বাচিত ডিভাইসগুলো দ্বারা সমর্থিত নয়, তাহলে ফাংশনটি একটি এরর রিটার্ন করবে।

একাধিক ডিভাইস নির্দিষ্ট করা থাকলে, ডিভাইসগুলোর মধ্যে কাজ বন্টনের দায়িত্ব রানটাইমের ওপর বর্তায়।

অন্যান্য ডিভাইসের মতোই, NNAPI CPU ইমপ্লিমেন্টেশনটি nnapi-reference নামের এবং ANEURALNETWORKS_DEVICE_TYPE_CPU টাইপের একটি ANeuralNetworksDevice দ্বারা উপস্থাপিত হয়। ANeuralNetworksCompilation_createForDevices কল করার সময়, মডেল কম্পাইলেশন এবং এক্সিকিউশনের ব্যর্থতার পরিস্থিতিগুলো সামাল দিতে CPU ইমপ্লিমেন্টেশনটি ব্যবহৃত হয় না।

একটি মডেলকে এমন সাব-মডেলে বিভক্ত করা একটি অ্যাপ্লিকেশনের দায়িত্ব, যা নির্দিষ্ট ডিভাইসগুলিতে চলতে পারে। যে অ্যাপ্লিকেশনগুলির ম্যানুয়াল বিভাজন করার প্রয়োজন নেই, তাদের মডেলের গতি বাড়ানোর জন্য সমস্ত উপলব্ধ ডিভাইস (সিপিইউ সহ) ব্যবহার করতে সহজতর ANeuralNetworksCompilation_create কল করা উচিত। যদি ANeuralNetworksCompilation_createForDevices ব্যবহার করে আপনার নির্দিষ্ট করা ডিভাইসগুলি দ্বারা মডেলটি সম্পূর্ণরূপে সমর্থিত না হয়, তাহলে ANEURALNETWORKS_BAD_DATA রিটার্ন করা হয়।

মডেল বিভাজন

যখন মডেলটির জন্য একাধিক ডিভাইস উপলব্ধ থাকে, তখন NNAPI রানটাইম ডিভাইসগুলোর মধ্যে কাজটি বন্টন করে দেয়। উদাহরণস্বরূপ, যদি ANeuralNetworksCompilation_createForDevices এ একাধিক ডিভাইস সরবরাহ করা হয়, তবে কাজটি বরাদ্দ করার সময় নির্দিষ্ট করা সবগুলোই বিবেচনা করা হবে। উল্লেখ্য যে, যদি সিপিইউ ডিভাইসটি তালিকায় না থাকে, তবে সিপিইউ এক্সিকিউশন নিষ্ক্রিয় হয়ে যাবে। ANeuralNetworksCompilation_create ব্যবহার করার সময় সিপিইউ সহ সমস্ত উপলব্ধ ডিভাইস বিবেচনায় নেওয়া হবে।

মডেলের প্রতিটি অপারেশনের জন্য, উপলব্ধ ডিভাইসগুলির তালিকা থেকে অপারেশনটি সমর্থনকারী ডিভাইসটি নির্বাচন করে এবং ক্লায়েন্ট দ্বারা নির্দিষ্ট করা এক্সিকিউশন পছন্দের উপর নির্ভর করে সেরা পারফরম্যান্স, অর্থাৎ দ্রুততম এক্সিকিউশন সময় বা সর্বনিম্ন বিদ্যুৎ খরচ ঘোষণা করে এই বন্টনটি করা হয়। এই পার্টিশনিং অ্যালগরিদমটি বিভিন্ন প্রসেসরের মধ্যে IO-এর কারণে সৃষ্ট সম্ভাব্য অদক্ষতা বিবেচনা করে না, তাই একাধিক প্রসেসর নির্দিষ্ট করার সময় ( ANeuralNetworksCompilation_createForDevices ব্যবহার করে স্পষ্টভাবে অথবা ANeuralNetworksCompilation_create ব্যবহার করে পরোক্ষভাবে) ফলস্বরূপ অ্যাপ্লিকেশনটির প্রোফাইলিং করা গুরুত্বপূর্ণ।

NNAPI দ্বারা আপনার মডেলটি কীভাবে বিভক্ত করা হয়েছে তা বুঝতে, অ্যান্ড্রয়েড লগে একটি বার্তা (INFO লেভেলে ExecutionPlan ট্যাগ সহ) পরীক্ষা করুন:

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

op-name হলো গ্রাফে অপারেশনটির বর্ণনামূলক নাম এবং device-index হলো ডিভাইসগুলোর তালিকায় সম্ভাব্য ডিভাইসটির সূচক। এই তালিকাটিই হলো ANeuralNetworksCompilation_createForDevices এ প্রদত্ত ইনপুট অথবা, যদি ANeuralNetworksCompilation_createForDevices ব্যবহার করা হয়, তবে এটি হলো ANeuralNetworks_getDeviceCount এবং ANeuralNetworks_getDevice ব্যবহার করে সমস্ত ডিভাইসের উপর পুনরাবৃত্তি করার পর ফেরত আসা ডিভাইসগুলোর তালিকা।

বার্তাটি (INFO লেভেলে এবং ExecutionPlan ট্যাগ সহ):

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

এই বার্তাটি নির্দেশ করে যে device-name ডিভাইসটিতে সম্পূর্ণ গ্রাফটি ত্বরান্বিত করা হয়েছে।

মৃত্যুদণ্ড

এক্সিকিউশন ধাপে মডেলটিকে এক সেট ইনপুটের উপর প্রয়োগ করা হয় এবং গণনার আউটপুটগুলো আপনার অ্যাপ দ্বারা বরাদ্দকৃত এক বা একাধিক ইউজার বাফার বা মেমরি স্পেসে সংরক্ষণ করা হয়।

একটি কম্পাইল করা মডেল কার্যকর করতে, এই ধাপগুলো অনুসরণ করুন:

  1. একটি নতুন এক্সিকিউশন ইনস্ট্যান্স তৈরি করতে ANeuralNetworksExecution_create() ফাংশনটি কল করুন।

    // Run the compiled model against a set of inputs
    ANeuralNetworksExecution* run1 = NULL;
    ANeuralNetworksExecution_create(compilation, &run1);
  2. আপনার অ্যাপ গণনার জন্য ইনপুট মানগুলি কোথা থেকে পড়বে তা নির্দিষ্ট করুন। আপনার অ্যাপ যথাক্রমে ANeuralNetworksExecution_setInput() বা 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. আপনার অ্যাপ আউটপুট মানগুলো কোথায় লিখবে তা নির্দিষ্ট করুন। আপনার অ্যাপ যথাক্রমে ANeuralNetworksExecution_setOutput() বা ANeuralNetworksExecution_setOutputFromMemory() কল করার মাধ্যমে আউটপুট মানগুলো একটি ইউজার বাফার বা বরাদ্দকৃত মেমরি স্পেসে লিখতে পারে।

    // Set the output
    float32 myOutput[3][4];
    ANeuralNetworksExecution_setOutput(run1, 0, NULL, myOutput, sizeof(myOutput));
  4. ANeuralNetworksExecution_startCompute() ফাংশনটি কল করে এক্সিকিউশন শুরু করার সময়সূচী নির্ধারণ করুন। যদি কোনো ত্রুটি না থাকে, তাহলে এই ফাংশনটি ANEURALNETWORKS_NO_ERROR ফলাফল কোডটি রিটার্ন করে।

    // Starts the work. The work proceeds asynchronously
    ANeuralNetworksEvent* run1_end = NULL;
    ANeuralNetworksExecution_startCompute(run1, &run1_end);
  5. এক্সিকিউশন সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে ANeuralNetworksEvent_wait() ফাংশনটি কল করুন। এক্সিকিউশন সফল হলে, এই ফাংশনটি ANEURALNETWORKS_NO_ERROR রেজাল্ট কোড রিটার্ন করে। যে থ্রেড থেকে এক্সিকিউশন শুরু হয়েছে, তার থেকে ভিন্ন কোনো থ্রেডেও অপেক্ষা করার কাজটি করা যেতে পারে।

    // 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. ঐচ্ছিকভাবে, আপনি একই কম্পাইলেশন ইনস্ট্যান্স ব্যবহার করে একটি নতুন ANeuralNetworksExecution ইনস্ট্যান্স তৈরি করার মাধ্যমে কম্পাইল করা মডেলে ভিন্ন ইনপুট সেট প্রয়োগ করতে পারেন।

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

সিঙ্ক্রোনাস এক্সিকিউশন

অ্যাসিঙ্ক্রোনাস এক্সিকিউশন থ্রেড তৈরি এবং সিঙ্ক্রোনাইজ করতে সময় ব্যয় করে। উপরন্তু, এর ল্যাটেন্সি ব্যাপকভাবে পরিবর্তনশীল হতে পারে; একটি থ্রেডকে অবহিত বা জাগিয়ে তোলার সময় থেকে শুরু করে অবশেষে একটি সিপিইউ কোরের সাথে যুক্ত হওয়ার সময় পর্যন্ত দীর্ঘতম বিলম্ব ৫০০ মাইক্রোসেকেন্ড পর্যন্ত পৌঁছাতে পারে।

লেটেন্সি উন্নত করার জন্য, আপনি এর পরিবর্তে একটি অ্যাপ্লিকেশনকে রানটাইমে একটি সিনক্রোনাস ইনফারেন্স কল করার নির্দেশ দিতে পারেন। সেই কলটি ইনফারেন্স শুরু হওয়ার সাথে সাথে রিটার্ন না করে, শুধুমাত্র ইনফারেন্সটি সম্পন্ন হওয়ার পরেই রিটার্ন করবে। রানটাইমে একটি অ্যাসিঙ্ক্রোনাস ইনফারেন্স কলের জন্য ANeuralNetworksExecution_startCompute কল করার পরিবর্তে, অ্যাপ্লিকেশনটি রানটাইমে একটি সিনক্রোনাস কল করার জন্য ANeuralNetworksExecution_compute কল করে। ANeuralNetworksExecution_compute এর একটি কলে কোনো ANeuralNetworksEvent লাগে না এবং এটি ANeuralNetworksEvent_wait এর কলের সাথে যুক্ত থাকে না।

আকস্মিক মৃত্যুদণ্ড

অ্যান্ড্রয়েড ১০ (এপিআই লেভেল ২৯) এবং তার চেয়ে উন্নত সংস্করণে চালিত অ্যান্ড্রয়েড ডিভাইসগুলোতে, এনএনএপিআই (NNAPI) ANeuralNetworksBurst অবজেক্টের মাধ্যমে বার্স্ট এক্সিকিউশন সমর্থন করে। বার্স্ট এক্সিকিউশন হলো একই কম্পাইলেশনের একাধিক এক্সিকিউশনের একটি ক্রম যা দ্রুত গতিতে ঘটে, যেমন ক্যামেরা ক্যাপচারের ফ্রেম বা পরপর অডিও স্যাম্পলের উপর পরিচালিত এক্সিকিউশন। ANeuralNetworksBurst অবজেক্ট ব্যবহার করলে এক্সিকিউশন আরও দ্রুত হতে পারে, কারণ এটি অ্যাক্সিলারেটরকে নির্দেশ করে যে এক্সিকিউশনগুলোর মাঝে রিসোর্স পুনরায় ব্যবহার করা যেতে পারে এবং বার্স্টের সময়কাল জুড়ে অ্যাক্সিলারেটরগুলোর একটি উচ্চ-পারফরম্যান্স অবস্থায় থাকা উচিত।

ANeuralNetworksBurst সাধারণ এক্সিকিউশন পাথে শুধুমাত্র একটি ছোট পরিবর্তন আনে। আপনি ANeuralNetworksBurst_create ব্যবহার করে একটি বার্স্ট অবজেক্ট তৈরি করেন, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

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

বার্স্ট এক্সিকিউশনগুলো সিনক্রোনাস হয়। তবে, প্রতিটি ইনফারেন্স সম্পাদনের জন্য ANeuralNetworksExecution_compute ব্যবহার করার পরিবর্তে, আপনি ANeuralNetworksExecution_burstCompute ফাংশনটি কল করার সময় বিভিন্ন ANeuralNetworksExecution অবজেক্টগুলোকে একই ANeuralNetworksBurst এর সাথে যুক্ত করেন।

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

যখন ANeuralNetworksBurst অবজেক্টটির আর প্রয়োজন হবে না, তখন ANeuralNetworksBurst_free ব্যবহার করে এটিকে মুক্ত করুন।

// Cleanup
ANeuralNetworksBurst_free(burst);

অ্যাসিঙ্ক্রোনাস কমান্ড কিউ এবং ফেন্সড এক্সিকিউশন

অ্যান্ড্রয়েড ১১ এবং এর পরবর্তী সংস্করণগুলিতে, NNAPI ANeuralNetworksExecution_startComputeWithDependencies() মেথডের মাধ্যমে অ্যাসিঙ্ক্রোনাস এক্সিকিউশন শিডিউল করার একটি অতিরিক্ত উপায় সমর্থন করে। যখন আপনি এই মেথডটি ব্যবহার করেন, তখন ইভ্যালুয়েশন শুরু করার আগে এক্সিকিউশনটি এর উপর নির্ভরশীল সমস্ত ইভেন্টের সিগন্যাল পাওয়ার জন্য অপেক্ষা করে। এক্সিকিউশন সম্পন্ন হয়ে গেলে এবং আউটপুটগুলো ব্যবহারের জন্য প্রস্তুত হলে, রিটার্ন করা ইভেন্টটির সিগন্যাল দেওয়া হয়।

কোন ডিভাইসগুলো এক্সিকিউশন পরিচালনা করছে তার উপর নির্ভর করে, ইভেন্টটি একটি সিঙ্ক ফেন্স দ্বারা সমর্থিত হতে পারে। ইভেন্টের জন্য অপেক্ষা করতে এবং এক্সিকিউশনের জন্য ব্যবহৃত রিসোর্সগুলো পুনরুদ্ধার করতে আপনাকে অবশ্যই ANeuralNetworksEvent_wait() কল করতে হবে। আপনি ANeuralNetworksEvent_createFromSyncFenceFd() ব্যবহার করে একটি ইভেন্ট অবজেক্টে সিঙ্ক ফেন্স ইম্পোর্ট করতে পারেন, এবং ANeuralNetworksEvent_getSyncFenceFd() ব্যবহার করে একটি ইভেন্ট অবজেক্ট থেকে সিঙ্ক ফেন্স এক্সপোর্ট করতে পারেন।

গতিশীলভাবে আকার দেওয়া আউটপুট

যেসব মডেলে আউটপুটের আকার ইনপুট ডেটার উপর নির্ভর করে—অর্থাৎ, মডেল নির্বাহের সময় আকার নির্ধারণ করা যায় না—সেগুলোকে সমর্থন করার জন্য ANeuralNetworksExecution_getOutputOperandRank এবং ANeuralNetworksExecution_getOutputOperandDimensions ব্যবহার করুন।

নিম্নলিখিত কোড নমুনাটি দেখায় কিভাবে এটি করতে হয়:

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

পরিষ্কার-পরিচ্ছন্নতা

ক্লিনআপ ধাপটি আপনার গণনার জন্য ব্যবহৃত অভ্যন্তরীণ রিসোর্সসমূহকে মুক্ত করার কাজটি করে।

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

ত্রুটি ব্যবস্থাপনা এবং সিপিইউ ফলব্যাক

পার্টিশনিংয়ের সময় কোনো ত্রুটি হলে, কোনো ড্রাইভার যদি মডেলের কোনো অংশ কম্পাইল করতে ব্যর্থ হয়, অথবা কম্পাইল করা মডেলের কোনো অংশ এক্সিকিউট করতে ব্যর্থ হয়, তাহলে NNAPI এক বা একাধিক অপারেশনের জন্য তার নিজস্ব সিপিইউ ইমপ্লিমেন্টেশনে ফিরে যেতে পারে।

যদি NNAPI ক্লায়েন্টে অপারেশনটির অপ্টিমাইজড সংস্করণ থাকে (যেমন, TFLite), তাহলে সিপিইউ ফলব্যাক নিষ্ক্রিয় করে ক্লায়েন্টের অপ্টিমাইজড অপারেশন ইমপ্লিমেন্টেশন দিয়ে ব্যর্থতাগুলো সামলানো সুবিধাজনক হতে পারে।

অ্যান্ড্রয়েড ১০-এ, যদি ANeuralNetworksCompilation_createForDevices ব্যবহার করে কম্পাইলেশন করা হয়, তাহলে সিপিইউ ফলব্যাক নিষ্ক্রিয় হয়ে যাবে।

অ্যান্ড্রয়েড পি-তে, ড্রাইভারে এক্সিকিউশন ব্যর্থ হলে NNAPI এক্সিকিউশন সিপিইউ-তে ফিরে যায়। অ্যান্ড্রয়েড ১০-এর ক্ষেত্রেও এটি সত্য, যখন ANeuralNetworksCompilation_create এর পরিবর্তে ANeuralNetworksCompilation_createForDevices ব্যবহার করা হয়।

প্রথমবার এক্সিকিউশনটি শুধুমাত্র সেই একটি পার্টিশনের জন্য ফিরে যায়, এবং তাতেও ব্যর্থ হলে, এটি সিপিইউ-তে সম্পূর্ণ মডেলটি পুনরায় চেষ্টা করে।

পার্টিশনিং বা কম্পাইলেশন ব্যর্থ হলে, সম্পূর্ণ মডেলটি সিপিইউ-তে চালানোর চেষ্টা করা হবে।

এমন কিছু ক্ষেত্র রয়েছে যেখানে কিছু অপারেশন সিপিইউ-তে সমর্থিত নয়, এবং এই ধরনের পরিস্থিতিতে ফলব্যাক করার পরিবর্তে কম্পাইলেশন বা এক্সিকিউশন ব্যর্থ হয়ে যায়।

সিপিইউ ফলব্যাক নিষ্ক্রিয় করার পরেও, মডেলে এমন কিছু অপারেশন থাকতে পারে যা সিপিইউ-তে নির্ধারিত থাকে। যদি সিপিইউটি ANeuralNetworksCompilation_createForDevices এ সরবরাহ করা প্রসেসরের তালিকায় থাকে, এবং সেটিই হয় একমাত্র প্রসেসর যা ওই অপারেশনগুলো সমর্থন করে অথবা সেই প্রসেসর যা ওই অপারেশনগুলোর জন্য সেরা পারফরম্যান্সের দাবি করে, তবে সেটিকে প্রাথমিক (নন-ফলব্যাক) এক্সিকিউটর হিসেবে বেছে নেওয়া হবে।

সিপিইউ এক্সিকিউশন যাতে না হয়, তা নিশ্চিত করতে ডিভাইসের তালিকা থেকে nnapi-reference বাদ দিয়ে ANeuralNetworksCompilation_createForDevices ব্যবহার করুন। অ্যান্ড্রয়েড পি থেকে শুরু করে, debug.nn.partition প্রপার্টির মান 2 সেট করার মাধ্যমে DEBUG বিল্ডে এক্সিকিউশনের সময় ফলব্যাক নিষ্ক্রিয় করা সম্ভব।

মেমরি ডোমেইন

অ্যান্ড্রয়েড ১১ এবং এর পরবর্তী সংস্করণগুলিতে, NNAPI মেমরি ডোমেইন সমর্থন করে যা অপেক মেমরিগুলির জন্য অ্যালোকেটর ইন্টারফেস প্রদান করে। এটি অ্যাপ্লিকেশনগুলিকে এক এক্সিকিউশন থেকে অন্য এক্সিকিউশনে ডিভাইস-নেটিভ মেমরি পাস করার সুযোগ দেয়, ফলে একই ড্রাইভারে পরপর এক্সিকিউশন চালানোর সময় NNAPI অপ্রয়োজনে ডেটা কপি বা ট্রান্সফর্ম করে না।

মেমরি ডোমেইন ফিচারটি এমন টেনসরগুলোর জন্য তৈরি করা হয়েছে যেগুলো মূলত ড্রাইভারের অভ্যন্তরীণ এবং ক্লায়েন্ট সাইডে যেগুলোর ঘন ঘন অ্যাক্সেসের প্রয়োজন হয় না। এই ধরনের টেনসরের উদাহরণ হলো সিকোয়েন্স মডেলের স্টেট টেনসরগুলো। যেসব টেনসরের ক্লায়েন্ট সাইডে ঘন ঘন সিপিইউ অ্যাক্সেসের প্রয়োজন হয়, সেগুলোর জন্য পরিবর্তে শেয়ার্ড মেমরি পুল ব্যবহার করুন।

একটি অস্বচ্ছ মেমরি বরাদ্দ করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. নতুন মেমরি ডেসক্রিপ্টর তৈরি করতে ANeuralNetworksMemoryDesc_create() ফাংশনটি কল করুন:

    // Create a memory descriptor
    ANeuralNetworksMemoryDesc* desc;
    ANeuralNetworksMemoryDesc_create(&desc);
  2. ANeuralNetworksMemoryDesc_addInputRole() এবং ANeuralNetworksMemoryDesc_addOutputRole() কল করে সমস্ত উদ্দিষ্ট ইনপুট এবং আউটপুট ভূমিকা নির্দিষ্ট করুন।

    // 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. ঐচ্ছিকভাবে, ANeuralNetworksMemoryDesc_setDimensions() কল করে মেমরির মাত্রা নির্দিষ্ট করুন।

    // Specify the memory dimensions
    uint32_t dims[] = {3, 4};
    ANeuralNetworksMemoryDesc_setDimensions(desc, 2, dims);
  4. ANeuralNetworksMemoryDesc_finish() কল করে ডেসক্রিপ্টর সংজ্ঞাটি চূড়ান্ত করুন।

    ANeuralNetworksMemoryDesc_finish(desc);
  5. ANeuralNetworksMemory_createFromDesc() ফাংশনে ডেসক্রিপ্টরটি পাস করে আপনার প্রয়োজন অনুযায়ী মেমরি বরাদ্দ করুন।

    // Allocate two opaque memories with the descriptor
    ANeuralNetworksMemory* opaqueMem;
    ANeuralNetworksMemory_createFromDesc(desc, &opaqueMem);
  6. যখন আর প্রয়োজন হবে না, তখন মেমরি ডেসক্রিপ্টরটি মুক্ত করুন।

    ANeuralNetworksMemoryDesc_free(desc);

ক্লায়েন্ট শুধুমাত্র ANeuralNetworksMemoryDesc অবজেক্টে নির্দিষ্ট করা ভূমিকা অনুসারে ANeuralNetworksExecution_setInputFromMemory() বা ANeuralNetworksExecution_setOutputFromMemory() এর সাথে তৈরি করা ANeuralNetworksMemory অবজেক্টটি ব্যবহার করতে পারে। offset এবং length আর্গুমেন্ট দুটি অবশ্যই 0-তে সেট করতে হবে, যা নির্দেশ করে যে সম্পূর্ণ মেমরি ব্যবহৃত হচ্ছে। ক্লায়েন্ট ANeuralNetworksMemory_copy() ব্যবহার করে মেমরির বিষয়বস্তু স্পষ্টভাবে সেট বা নিষ্কাশনও করতে পারে।

আপনি অনির্দিষ্ট ডাইমেনশন বা র‍্যাঙ্কের রোল সহ অপাক মেমোরি তৈরি করতে পারেন। সেক্ষেত্রে, যদি এটি অন্তর্নিহিত ড্রাইভার দ্বারা সমর্থিত না হয়, তবে মেমোরি তৈরি করার প্রক্রিয়াটি ANEURALNETWORKS_OP_FAILED স্ট্যাটাস সহ ব্যর্থ হতে পারে। ক্লায়েন্টকে Ashmem বা BLOB-মোড AHardwareBuffer দ্বারা সমর্থিত একটি যথেষ্ট বড় বাফার বরাদ্দ করে ফলব্যাক লজিক প্রয়োগ করার জন্য উৎসাহিত করা হচ্ছে।

যখন NNAPI-এর আর অস্বচ্ছ মেমরি অবজেক্টটি অ্যাক্সেস করার প্রয়োজন হয় না, তখন সংশ্লিষ্ট ANeuralNetworksMemory ইনস্ট্যান্সটিকে মুক্ত করুন:

ANeuralNetworksMemory_free(opaqueMem);

কর্মক্ষমতা পরিমাপ করুন

আপনি এক্সিকিউশন টাইম পরিমাপ করে অথবা প্রোফাইলিংয়ের মাধ্যমে আপনার অ্যাপের পারফরম্যান্স মূল্যায়ন করতে পারেন।

কার্যকর করার সময়

যখন আপনি রানটাইমের মাধ্যমে মোট এক্সিকিউশন টাইম নির্ধারণ করতে চান, তখন আপনি সিনক্রোনাস এক্সিকিউশন এপিআই ব্যবহার করে কলটির সময় পরিমাপ করতে পারেন। যখন আপনি সফটওয়্যার স্ট্যাকের নিম্ন স্তরের মাধ্যমে মোট এক্সিকিউশন টাইম নির্ধারণ করতে চান, তখন আপনি ANeuralNetworksExecution_setMeasureTiming এবং ANeuralNetworksExecution_getDuration ব্যবহার করে নিম্নলিখিত তথ্যগুলো পেতে পারেন:

  • অ্যাক্সিলারেটরে নির্বাহের সময় (ড্রাইভারের সময় নয়, যা হোস্ট প্রসেসরে চলে)।
  • ড্রাইভারের মধ্যে কার্য সম্পাদনের সময়, যার মধ্যে অ্যাক্সিলারেটরে ব্যয়িত সময়ও অন্তর্ভুক্ত।

ড্রাইভারের কার্য সম্পাদনের সময়ে রানটাইমের নিজস্ব ওভারহেড এবং রানটাইমের সাথে ড্রাইভারের যোগাযোগের জন্য প্রয়োজনীয় আইপিসি (IPC)-এর মতো বিষয়গুলো অন্তর্ভুক্ত থাকে না।

এই API-গুলো, কনটেক্সট সুইচিংয়ের কারণে বাধাগ্রস্ত হতে পারে এমন ইনফারেন্স সম্পাদনে কোনো ড্রাইভার বা অ্যাক্সিলারেটরের ব্যয় করা সময়ের পরিবর্তে, কাজ জমা দেওয়া এবং কাজ সম্পন্ন হওয়ার ইভেন্টগুলোর মধ্যবর্তী সময়কাল পরিমাপ করে।

উদাহরণস্বরূপ, যদি ইনফারেন্স ১ শুরু হয়, তারপর ড্রাইভার ইনফারেন্স ২ সম্পাদন করার জন্য কাজ থামিয়ে দেয়, এবং এরপর আবার কাজ শুরু করে ইনফারেন্স ১ সম্পন্ন করে, তাহলে ইনফারেন্স ১-এর এক্সিকিউশন টাইমের মধ্যে সেই সময়টিও অন্তর্ভুক্ত থাকবে যখন ইনফারেন্স ২ সম্পাদন করার জন্য কাজ থামানো হয়েছিল।

এই টাইমিং তথ্য কোনো অ্যাপ্লিকেশনের প্রোডাকশন ডেপ্লয়মেন্টের ক্ষেত্রে অফলাইন ব্যবহারের জন্য টেলিমেট্রি সংগ্রহ করতে সহায়ক হতে পারে। আপনি উন্নততর পারফরম্যান্সের জন্য অ্যাপটি পরিবর্তন করতে এই টাইমিং ডেটা ব্যবহার করতে পারেন।

এই কার্যকারিতাটি ব্যবহার করার সময় নিম্নলিখিত বিষয়গুলি মনে রাখবেন:

  • টাইমিং তথ্য সংগ্রহের ফলে পারফরম্যান্সের ওপর নেতিবাচক প্রভাব পড়তে পারে।
  • শুধুমাত্র একটি ড্রাইভারই NNAPI রানটাইম এবং IPC-তে ব্যয়িত সময় বাদে, নিজের মধ্যে বা অ্যাক্সিলারেটরে ব্যয়িত সময় গণনা করতে সক্ষম।
  • আপনি এই API-গুলি শুধুমাত্র এমন একটি ANeuralNetworksExecution সাথে ব্যবহার করতে পারবেন, যা ANeuralNetworksCompilation_createForDevices কমান্ড দিয়ে numDevices = 1 ব্যবহার করে তৈরি করা হয়েছে।
  • No driver is required to be able to report timing information.

Profile your application with Android Systrace

Starting with Android 10, NNAPI automatically generates systrace events that you can use to profile your application.

The NNAPI Source comes with a parse_systrace utility to process the systrace events generated by your application and generate a table view showing the time spent in the different phases of the model lifecycle (Instantiation, Preparation, Compilation Execution and Termination) and different layers of the applications. The layers in which your application is split are:

  • Application : the main application code
  • Runtime : NNAPI Runtime
  • IPC : The inter process communication between NNAPI Runtime and the Driver code
  • Driver : the accelerator driver process.

Generate the profiling analysys data

Assuming you checked out the AOSP source tree at $ANDROID_BUILD_TOP, and using the TFLite image classification example as target application, you can generate the NNAPI profiling data with the following steps:

  1. Start the Android systrace with the following command:
$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

The -o trace.html parameter indicates that the traces will be written in the trace.html . When profiling own application you will need to replace org.tensorflow.lite.examples.classification with the process name specified in your app manifest.

This will keep one of your shell console busy, don't run the command in background since it is interactively waiting for an enter to terminate.

  1. After the systrace collector is started, start your app and run your benchmark test.

In our case you can start the Image Classification app from Android Studio or directly from your test phone UI if the app has already been installed. To generate some NNAPI data you need to configure the app to use NNAPI by selecting NNAPI as target device in the app configuration dialog.

  1. When the test completes, terminate the systrace by pressing enter on the console terminal active since step 1.

  2. Run the systrace_parser utility generate cumulative statistics:

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

The parser accepts the following parameters: - --total-times : shows the total time spent in a layer including the time spent waiting for execution on a call to an underlying layer - --print-detail : prints all the events that have been collected from systrace - --per-execution : prints only the execution and its subphases (as per-execution times) instead of stats for all phases - --json : produces the output in JSON format

An example of the output is shown below:

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

The parser might fail if the collected events do not represent a complete application trace. In particular it might fail if systrace events generated to mark the end of a section are present in the trace without an associated section start event. This usually happens if some events from a previous profiling session are being generated when you start the systrace collector. In this case you would have to run your profiling again.

Add statistics for your application code to systrace_parser output

The parse_systrace application is based on the built-in Android systrace functionality. You can add traces for specific operations in your app using the systrace API ( for Java , for native applications ) with custom event names.

To associate your custom events with phases of the Application lifecycle, prepend your event name with one of the following strings:

  • [NN_LA_PI] : Application level event for Initialization
  • [NN_LA_PP] : Application level event for Preparation
  • [NN_LA_PC] : Application level event for Compilation
  • [NN_LA_PE] : Application level event for Execution

Here is an example of how you can alter the TFLite image classification example code by adding a runInferenceModel section for the Execution phase and the Application layer containing another other sections preprocessBitmap that won't be considered in NNAPI traces. The runInferenceModel section will be part of the systrace events processed by the nnapi systrace parser:

কোটলিন

/** 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
}

জাভা

/** 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;
}

পরিষেবার মান

In Android 11 and higher, NNAPI enables better quality of service (QoS) by allowing an application to indicate the relative priorities of its models, the maximum amount of time expected to prepare a given model, and the maximum amount of time expected to complete a given computation. Android 11 also introduces additional NNAPI result codes that enable applications to understand failures such as missed execution deadlines.

Set the priority of a workload

To set the priority of an NNAPI workload, call ANeuralNetworksCompilation_setPriority() prior to calling ANeuralNetworksCompilation_finish() .

Set deadlines

Applications can set deadlines for both model compilation and inference.

More about operands

The following section covers advanced topics about using operands.

Quantized tensors

A quantized tensor is a compact way to represent an n-dimensional array of floating point values.

NNAPI supports 8-bit asymmetric quantized tensors. For these tensors, the value of each cell is represented by an 8-bit integer. Associated with the tensor is a scale and a zero point value. These are used to convert the 8-bit integers into the floating point values that are being represented.

The formula is:

(cellValue - zeroPoint) * scale

where the zeroPoint value is a 32-bit integer and the scale a 32-bit floating point value.

Compared to tensors of 32-bit floating point values, 8-bit quantized tensors have two advantages:

  • Your application is smaller, as the trained weights take a quarter of the size of 32-bit tensors.
  • Computations can often be executed faster. This is due to the smaller amount of data that needs to be fetched from memory and the efficiency of processors such as DSPs in doing integer math.

While it is possible to convert a floating point model to a quantized one, our experience has shown that better results are achieved by training a quantized model directly. In effect, the neural network learns to compensate for the increased granularity of each value. For each quantized tensor, the scale and zeroPoint values are determined during the training process.

In NNAPI, you define quantized tensor types by setting the type field of the ANeuralNetworksOperandType data structure to ANEURALNETWORKS_TENSOR_QUANT8_ASYMM . You also specify the scale and zeroPoint value of the tensor in that data structure.

In addition to 8-bit asymmetric quantized tensors, NNAPI supports the following:

Optional operands

A few operations, like ANEURALNETWORKS_LSH_PROJECTION , take optional operands. To indicate in the model that the optional operand is omitted, call the ANeuralNetworksModel_setOperandValue() function, passing NULL for the buffer and 0 for the length.

If the decision on whether the operand is present or not varies for each execution, you indicate that the operand is omitted by using the ANeuralNetworksExecution_setInput() or ANeuralNetworksExecution_setOutput() functions, passing NULL for the buffer and 0 for the length.

Tensors of unknown rank

Android 9 (API level 28) introduced model operands of unknown dimensions but known rank (the number of dimensions). Android 10 (API level 29) introduced tensors of unknown rank, as shown in ANeuralNetworksOperandType .

NNAPI benchmark

The NNAPI benchmark is available on AOSP in platform/test/mlts/benchmark (benchmark app) and platform/test/mlts/models (models and datasets).

The benchmark evaluates latency and accuracy and compares drivers to the same work done using Tensorflow Lite running on the CPU, for the same models and datasets.

To use the benchmark, do the following:

  1. Connect a target Android device to your computer, open a terminal window, and make sure the device is reachable through adb.

  2. If more than one Android device is connected, export the target device ANDROID_SERIAL environment variable.

  3. Navigate to the Android top-level source directory.

  4. Run the following commands:

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

    At the end of a benchmark run, its results will be presented as an HTML page passed to xdg-open .

NNAPI logs

NNAPI generates useful diagnostic information in the system logs. To analyze the logs, use the logcat utility.

Enable verbose NNAPI logging for specific phases or components by setting the property debug.nn.vlog (using adb shell ) to the following list of values, separated by space, colon, or comma:

  • model : Model building
  • compilation : Generation of the model execution plan and compilation
  • execution : Model execution
  • cpuexe : Execution of operations using the NNAPI CPU implementation
  • manager : NNAPI extensions, available interfaces and capabilities related info
  • all or 1 : All the elements above

For example, to enable full verbose logging use the command adb shell setprop debug.nn.vlog all . To disable verbose logging, use the command adb shell setprop debug.nn.vlog '""' .

Once enabled, verbose logging generates log entries at INFO level with a tag set to the phase or component name.

Beside the debug.nn.vlog controlled messages, NNAPI API components provide other log entries at various levels, each one using a specific log tag.

To get a list of components, search the source tree using the following expression:

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

This expression currently returns the following tags:

  • BurstBuilder
  • Callbacks
  • CompilationBuilder
  • CpuExecutor
  • ExecutionBuilder
  • ExecutionBurstController
  • ExecutionBurstServer
  • ExecutionPlan
  • FibonacciDriver
  • GraphDump
  • IndexedShapeWrapper
  • IonWatcher
  • ব্যবস্থাপক
  • স্মৃতি
  • MemoryUtils
  • MetaModel
  • ModelArgumentInfo
  • ModelBuilder
  • NeuralNetworks
  • OperationResolver
  • অপারেশন
  • OperationsUtils
  • PackageInfo
  • TokenHasher
  • TypeManager
  • Utils
  • ValidateHal
  • VersionedInterfaces

To control the level of log messages shown by logcat , use the environment variable ANDROID_LOG_TAGS .

To show the full set of NNAPI log messages and disable any others, set ANDROID_LOG_TAGS to the following:

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.

You can set ANDROID_LOG_TAGS using the following command:

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

Note that this is just a filter that applies to logcat . You still need to set the property debug.nn.vlog to all to generate verbose log info.