TF প্রোফাইলারের সাথে tf.data কর্মক্ষমতা বিশ্লেষণ করুন

ওভারভিউ

এই নির্দেশিকাটি TensorFlow প্রোফাইলার এবং tf.data এর সাথে পরিচিতি অনুমান করে। ব্যবহারকারীদের ইনপুট পাইপলাইন পারফরম্যান্স সমস্যাগুলি নির্ণয় এবং সমাধান করতে সহায়তা করার জন্য উদাহরণ সহ ধাপে ধাপে নির্দেশাবলী প্রদান করা এর লক্ষ্য।

শুরু করতে, আপনার TensorFlow কাজের একটি প্রোফাইল সংগ্রহ করুন। CPUs/GPUs এবং Cloud TPUs- এর জন্য কীভাবে তা করা যায় তার নির্দেশাবলী উপলব্ধ।

TensorFlow Trace Viewer

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

বিশ্লেষণ কর্মপ্রবাহ

নীচের কর্মপ্রবাহ অনুসরণ করুন. এটিকে উন্নত করতে আমাদের সাহায্য করার জন্য যদি আপনার মতামত থাকে, তাহলে অনুগ্রহ করে "comp:data" লেবেল দিয়ে একটি গিথুব সমস্যা তৈরি করুন।

1. আপনার tf.data পাইপলাইন কি যথেষ্ট দ্রুত ডেটা উৎপাদন করছে?

ইনপুট পাইপলাইন আপনার টেনসরফ্লো প্রোগ্রামের জন্য বাধা কিনা তা নিশ্চিত করে শুরু করুন।

এটি করতে, ট্রেস ভিউয়ারে IteratorGetNext::DoCompute ops সন্ধান করুন। সাধারণভাবে, আপনি একটি ধাপের শুরুতে এগুলি দেখতে পাবেন। এই স্লাইসগুলি আপনার ইনপুট পাইপলাইনের জন্য যখন অনুরোধ করা হয় তখন উপাদানগুলির একটি ব্যাচ তৈরি করতে যে সময় লাগে তা উপস্থাপন করে। আপনি যদি tf_data_iterator_get_next tf.function পাওয়া উচিত।

মনে রাখবেন যে আপনি যদি একটি বিতরণ কৌশল ব্যবহার করেন তবে আপনি IteratorGetNext::DoCompute এর পরিবর্তে IteratorGetNext::DoCompute IteratorGetNextAsOptional::DoCompute ইভেন্ট দেখতে পাবেন (TF 2.3 অনুযায়ী)।

image

যদি কলগুলি দ্রুত ফিরে আসে (<= 50 us), এর মানে হল আপনার ডেটা যখন অনুরোধ করা হয় তখন উপলব্ধ থাকে৷ ইনপুট পাইপলাইন আপনার বাধা নয়; আরও জেনেরিক কর্মক্ষমতা বিশ্লেষণ টিপসের জন্য প্রোফাইলার গাইড দেখুন।

image

কলগুলি ধীরে ধীরে ফিরে আসলে, tf.data গ্রাহকের অনুরোধের সাথে তাল মিলিয়ে চলতে অক্ষম। পরবর্তী বিভাগে অবিরত.

2. আপনি কি ডেটা প্রিফেচ করছেন?

ইনপুট পাইপলাইন পারফরম্যান্সের জন্য সর্বোত্তম অনুশীলন হল আপনার tf.data পাইপলাইনের শেষে একটি tf.data.Dataset.prefetch রূপান্তর সন্নিবেশ করানো। এই রূপান্তরটি মডেল গণনার পরবর্তী ধাপের সাথে ইনপুট পাইপলাইনের প্রাক-প্রসেসিং গণনাকে ওভারল্যাপ করে এবং আপনার মডেলকে প্রশিক্ষণ দেওয়ার সময় সর্বোত্তম ইনপুট পাইপলাইন কর্মক্ষমতার জন্য প্রয়োজন। আপনি যদি ডেটা প্রিফেচ করছেন, তাহলে আপনার IteratorGetNext::DoCompute Iterator::Prefetch দেখতে হবে।

image

আপনার পাইপলাইনের শেষে একটি prefetch না থাকলে , আপনার একটি যোগ করা উচিত। tf.data কর্মক্ষমতা সুপারিশ সম্পর্কে আরও তথ্যের জন্য, tf.data কর্মক্ষমতা নির্দেশিকা দেখুন।

আপনি যদি ইতিমধ্যেই ডেটা প্রিফেচ করছেন , এবং ইনপুট পাইপলাইন এখনও আপনার বাধা হয়ে দাঁড়িয়েছে, কর্মক্ষমতা আরও বিশ্লেষণ করতে পরবর্তী বিভাগে চালিয়ে যান।

3. আপনি কি উচ্চ CPU ব্যবহারে পৌঁছেছেন?

tf.data উপলব্ধ সম্পদের সর্বোত্তম সম্ভাব্য ব্যবহার করার চেষ্টা করে উচ্চ থ্রুপুট অর্জন করে। সাধারণভাবে, GPU বা TPU-এর মতো অ্যাক্সিলারেটরে আপনার মডেল চালানোর সময়ও, tf.data পাইপলাইনগুলি CPU-তে চালানো হয়। আপনি sar এবং htop- এর মতো টুল দিয়ে আপনার ব্যবহার পরীক্ষা করতে পারেন, অথবা আপনি যদি GCP-তে চালাচ্ছেন তাহলে ক্লাউড মনিটরিং কনসোলে

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

যদি আপনার ব্যবহার সম্পদ সীমার কাছাকাছি চলে আসে, তাহলে কর্মক্ষমতা আরও উন্নত করার জন্য, আপনাকে হয় আপনার ইনপুট পাইপলাইনের দক্ষতা উন্নত করতে হবে (উদাহরণস্বরূপ, অপ্রয়োজনীয় গণনা এড়ানো) বা অফলোড গণনা।

আপনি tf.data এ অপ্রয়োজনীয় গণনা এড়িয়ে আপনার ইনপুট পাইপলাইনের দক্ষতা উন্নত করতে পারেন। এটি করার একটি উপায় হল গণনা-নিবিড় কাজের পরে একটি tf.data.Dataset.cache রূপান্তর সন্নিবেশ করান যদি আপনার ডেটা মেমরিতে ফিট হয়; এটি বর্ধিত মেমরি ব্যবহারের খরচে গণনা হ্রাস করে। অতিরিক্তভাবে, tf.data তে ইন্ট্রা-অপ প্যারালেলিজম নিষ্ক্রিয় করার ফলে দক্ষতা > 10% বৃদ্ধির সম্ভাবনা রয়েছে এবং আপনার ইনপুট পাইপলাইনে নিম্নলিখিত বিকল্পটি সেট করে এটি করা যেতে পারে:

dataset = ...
options = tf.data.Options()
options.experimental_threading.max_intra_op_parallelism = 1
dataset = dataset.with_options(options)

4. বটলনেক বিশ্লেষণ

নিচের অংশটি ট্রেস ভিউয়ারে tf.data ইভেন্টগুলি কীভাবে পড়তে হয় তা বোঝার জন্য কোথায় বাধা রয়েছে এবং সম্ভাব্য প্রশমন কৌশলগুলি নিয়ে চলে।

প্রোফাইলারে tf.data ঘটনা বোঝা

প্রোফাইলারের প্রতিটি tf.data ইভেন্টের নাম রয়েছে Iterator::<Dataset> , যেখানে <Dataset> ডেটাসেট উৎস বা রূপান্তরের নাম। প্রতিটি ইভেন্টের দীর্ঘ নামও রয়েছে Iterator::<Dataset_1>::...::<Dataset_n> , যা আপনি tf.data ইভেন্টে ক্লিক করে দেখতে পারেন। দীর্ঘ নামের ক্ষেত্রে, <Dataset_n> (ছোট) নামের সাথে <Dataset> > মেলে, এবং দীর্ঘ নামের অন্যান্য ডেটাসেটগুলি নিচের দিকের রূপান্তরকে উপস্থাপন করে।

image

উদাহরণস্বরূপ, উপরের স্ক্রিনশটটি নিম্নলিখিত কোড থেকে তৈরি করা হয়েছে:

dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)

এখানে, Iterator::Map ইভেন্টের দীর্ঘ নাম Iterator::BatchV2::FiniteRepeat::Map । মনে রাখবেন যে ডেটাসেটের নামটি পাইথন API থেকে কিছুটা আলাদা হতে পারে (উদাহরণস্বরূপ, পুনরাবৃত্তির পরিবর্তে FiniteRepeat), কিন্তু পার্স করার জন্য যথেষ্ট স্বজ্ঞাত হওয়া উচিত।

সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস রূপান্তর

সিঙ্ক্রোনাস tf.data রূপান্তরের জন্য (যেমন Batch এবং Map ), আপনি একই থ্রেডে আপস্ট্রিম রূপান্তর থেকে ইভেন্ট দেখতে পাবেন। উপরের উদাহরণে, যেহেতু ব্যবহৃত সমস্ত রূপান্তরগুলি সিঙ্ক্রোনাস, সমস্ত ঘটনা একই থ্রেডে উপস্থিত হয়।

অ্যাসিঙ্ক্রোনাস ট্রান্সফর্মেশনের জন্য (যেমন Prefetch , ParallelMap , ParallelInterleave এবং MapAndBatch ) আপস্ট্রিম ট্রান্সফর্মেশন থেকে ইভেন্টগুলি একটি ভিন্ন থ্রেডে থাকবে। এই ধরনের ক্ষেত্রে, "দীর্ঘ নাম" আপনাকে সনাক্ত করতে সাহায্য করতে পারে যে একটি পাইপলাইনে কোন রূপান্তর একটি ইভেন্টের সাথে মিলে যায়।

image

উদাহরণস্বরূপ, উপরের স্ক্রিনশটটি নিম্নলিখিত কোড থেকে তৈরি করা হয়েছে:

dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)
dataset = dataset.prefetch(1)

এখানে, Iterator::Prefetch ইভেন্টগুলি tf_data_iterator_get_next থ্রেডে রয়েছে। যেহেতু Prefetch অ্যাসিঙ্ক্রোনাস, তাই এর ইনপুট ইভেন্টগুলি ( BatchV2 ) একটি ভিন্ন থ্রেডে থাকবে, এবং দীর্ঘ নাম Iterator::Prefetch::BatchV2 অনুসন্ধান করে সনাক্ত করা যেতে পারে। এই ক্ষেত্রে, তারা tf_data_iterator_resource থ্রেডে রয়েছে। এর দীর্ঘ নাম থেকে, আপনি অনুমান করতে পারেন যে BatchV2 হল Prefetch আপস্ট্রিম। উপরন্তু, parent_id ইভেন্টের BatchV2 ইভেন্টের আইডির সাথে Prefetch

বাধা সনাক্তকরণ

সাধারণভাবে, আপনার ইনপুট পাইপলাইনে বাধা শনাক্ত করতে, ইনপুট পাইপলাইনটি সবচেয়ে বাইরের রূপান্তর থেকে উৎস পর্যন্ত হাঁটুন। আপনার পাইপলাইনে চূড়ান্ত রূপান্তর থেকে শুরু করে, আপস্ট্রিম ট্রান্সফরমেশনে ফিরে যান যতক্ষণ না আপনি একটি ধীর রূপান্তর খুঁজে পান বা একটি উৎস ডেটাসেটে পৌঁছান, যেমন TFRecord । উপরের উদাহরণে, আপনি Prefetch থেকে শুরু করবেন, তারপর BatchV2 , FiniteRepeat , Map , এবং অবশেষে Range এ আপস্ট্রিমে হাঁটবেন।

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

মনে রাখবেন যে বেশিরভাগ হোস্ট ইনপুট পাইপলাইনে চূড়ান্ত (বহিরতম) রূপান্তর হল Iterator::Model ইভেন্ট। মডেল ট্রান্সফরমেশন স্বয়ংক্রিয়ভাবে tf.data রানটাইম দ্বারা প্রবর্তিত হয় এবং ইনপুট পাইপলাইন কর্মক্ষমতা যন্ত্র এবং স্বয়ংক্রিয় টিউন করার জন্য ব্যবহৃত হয়।

যদি আপনার কাজ একটি বিতরণ কৌশল ব্যবহার করে, ট্রেস ভিউয়ারে অতিরিক্ত ইভেন্ট থাকবে যা ডিভাইস ইনপুট পাইপলাইনের সাথে সম্পর্কিত। ডিভাইস পাইপলাইনের বাইরেরতম রূপান্তর ( IteratorGetNextOp::DoCompute বা IteratorGetNextAsOptionalOp::DoCompute DoCompute এর অধীনে নেস্টেড) হবে একটি Iterator::Prefetch ইভেন্ট একটি upstream Iterator::Generator ইভেন্ট সহ। আপনি Iterator::Model ইভেন্টগুলি অনুসন্ধান করে সংশ্লিষ্ট হোস্ট পাইপলাইন খুঁজে পেতে পারেন।

উদাহরণ 1

image

উপরের স্ক্রিনশটটি নিম্নলিখিত ইনপুট পাইপলাইন থেকে তৈরি করা হয়েছে:

dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record)
dataset = dataset.batch(32)
dataset = dataset.repeat()

স্ক্রিনশটে, লক্ষ্য করুন যে (1) Iterator::Map ইভেন্টগুলি দীর্ঘ, কিন্তু (2) এর ইনপুট ইভেন্টগুলি ( Iterator::FlatMap ) দ্রুত ফিরে আসে। এটি পরামর্শ দেয় যে অনুক্রমিক মানচিত্রের রূপান্তরটি বাধা।

নোট করুন যে স্ক্রিনশটে, InstantiatedCapturedFunction::Run ইভেন্ট মানচিত্র ফাংশনটি কার্যকর করতে যে সময় নেয় তার সাথে মিলে যায়।

উদাহরণ 2

image

উপরের স্ক্রিনশটটি নিম্নলিখিত ইনপুট পাইপলাইন থেকে তৈরি করা হয়েছে:

dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record, num_parallel_calls=2)
dataset = dataset.batch(32)
dataset = dataset.repeat()

এই উদাহরণটি উপরের অনুরূপ, কিন্তু মানচিত্রের পরিবর্তে সমান্তরালম্যাপ ব্যবহার করে। আমরা এখানে লক্ষ্য করি যে (1) Iterator::ParallelMap ইভেন্টগুলি দীর্ঘ, কিন্তু (2) এর ইনপুট ইভেন্টগুলি Iterator::FlatMap (যেগুলি একটি ভিন্ন থ্রেডে রয়েছে, যেহেতু ParallelMap অ্যাসিঙ্ক্রোনাস) ছোট৷ এটি পরামর্শ দেয় যে সমান্তরালম্যাপ রূপান্তরটি বাধা।

প্রতিবন্ধকতা সম্বোধন

উৎস ডেটাসেট

আপনি যদি কোনও ডেটাসেট উত্সকে বাধা হিসাবে চিহ্নিত করেন, যেমন TFRecord ফাইলগুলি থেকে পড়া, আপনি ডেটা নিষ্কাশনকে সমান্তরাল করে কার্যক্ষমতা উন্নত করতে পারেন। এটি করার জন্য, নিশ্চিত করুন যে আপনার ডেটা একাধিক ফাইল জুড়ে শার্ড করা হয়েছে এবং tf.data.AUTOTUNE এ সেট করা num_parallel_calls প্যারামিটার সহ tf.data.Dataset.interleave ব্যবহার করুন। যদি আপনার প্রোগ্রামের জন্য determinism গুরুত্বপূর্ণ না হয়, তাহলে আপনি tf.data.Dataset.interleavedeterministic=False ফ্ল্যাগ সেট করে TF 2.2 অনুযায়ী কর্মক্ষমতা আরও উন্নত করতে পারেন। উদাহরণস্বরূপ, আপনি যদি TFRecords থেকে পড়ছেন, আপনি নিম্নলিখিতগুলি করতে পারেন:

dataset = tf.data.Dataset.from_tensor_slices(filenames)
dataset = dataset.interleave(tf.data.TFRecordDataset,
  num_parallel_calls=tf.data.AUTOTUNE,
  deterministic=False)

মনে রাখবেন যে একটি ফাইল খোলার ওভারহেড পরিমাপ করার জন্য শার্ড করা ফাইলগুলি যুক্তিসঙ্গতভাবে বড় হওয়া উচিত। সমান্তরাল ডেটা নিষ্কাশন সম্পর্কে আরও বিশদ বিবরণের জন্য, tf.data কর্মক্ষমতা নির্দেশিকাটির এই বিভাগটি দেখুন।

রূপান্তর ডেটাসেট

যদি আপনি একটি মধ্যবর্তী tf.data রূপান্তরকে বাধা হিসেবে চিহ্নিত করেন, তাহলে আপনি রূপান্তরটিকে সমান্তরাল করে বা গণনা ক্যাশে করে তা সমাধান করতে পারেন যদি আপনার ডেটা মেমরিতে ফিট হয় এবং এটি উপযুক্ত হয়। কিছু রূপান্তর যেমন Map সমান্তরাল প্রতিরূপ আছে; tf.data কর্মক্ষমতা নির্দেশিকা দেখায় কিভাবে এগুলোকে সমান্তরাল করা যায়। অন্যান্য রূপান্তর, যেমন Filter , Unbatch এবং Batch সহজাতভাবে অনুক্রমিক; আপনি "বাহ্যিক সমান্তরালতা" প্রবর্তন করে তাদের সমান্তরাল করতে পারেন। উদাহরণ স্বরূপ, ধরুন আপনার ইনপুট পাইপলাইনটি প্রাথমিকভাবে নিচের মত দেখায়, Batch বাধা হিসাবে:

filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
dataset = filenames_to_dataset(filenames)
dataset = dataset.batch(batch_size)

আপনি শার্ডেড ইনপুটগুলির উপর ইনপুট পাইপলাইনের একাধিক অনুলিপি চালিয়ে এবং ফলাফলগুলিকে একত্রিত করে "বাহ্যিক সমান্তরালতা" প্রবর্তন করতে পারেন:

filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)

def make_dataset(shard_index):
  filenames = filenames.shard(NUM_SHARDS, shard_index)
  dataset = filenames_to_dataset(filenames)
  Return dataset.batch(batch_size)

indices = tf.data.Dataset.range(NUM_SHARDS)
dataset = indices.interleave(make_dataset,
                             num_parallel_calls=tf.data.AUTOTUNE)
dataset = dataset.prefetch(tf.data.AUTOTUNE)

অতিরিক্ত সম্পদ