ملخص
يفترض هذا الدليل الإلمام بملف تعريف TensorFlow و tf.data
. ويهدف إلى توفير إرشادات خطوة بخطوة مع أمثلة لمساعدة المستخدمين على تشخيص وإصلاح مشكلات أداء خط أنابيب الإدخال.
للبدء، قم بتجميع ملف تعريف لوظيفة TensorFlow الخاصة بك. تتوفر إرشادات حول كيفية القيام بذلك لوحدات المعالجة المركزية/وحدات معالجة الرسومات ووحدات TPU السحابية .
يركز سير عمل التحليل المفصل أدناه على أداة عارض التتبع في ملف التعريف. تعرض هذه الأداة مخططًا زمنيًا يوضح مدة العمليات التي ينفذها برنامج TensorFlow الخاص بك وتسمح لك بتحديد العمليات التي تستغرق وقتًا أطول للتنفيذ. لمزيد من المعلومات حول عارض التتبع، راجع هذا القسم من دليل TF Profiler. بشكل عام، ستظهر أحداث tf.data
على المخطط الزمني لوحدة المعالجة المركزية المضيفة.
سير عمل التحليل
يرجى اتباع سير العمل أدناه. إذا كانت لديك تعليقات لمساعدتنا في تحسينها، فيرجى إنشاء مشكلة في github تحمل التصنيف "comp:data".
1. هل يقوم خط أنابيب tf.data
الخاص بك بإنتاج البيانات بالسرعة الكافية؟
ابدأ بالتأكد مما إذا كان خط أنابيب الإدخال هو عنق الزجاجة لبرنامج TensorFlow الخاص بك.
للقيام بذلك، ابحث عن عمليات IteratorGetNext::DoCompute
في عارض التتبع. بشكل عام، تتوقع رؤية هذه العناصر في بداية الخطوة. تمثل هذه الشرائح الوقت الذي يستغرقه مسار الإدخال الخاص بك لإنتاج مجموعة من العناصر عند طلبها. إذا كنت تستخدم keras أو تتكرر على مجموعة البيانات الخاصة بك في tf.function
، فيجب العثور عليها في سلاسل الرسائل tf_data_iterator_get_next
.
لاحظ أنه إذا كنت تستخدم إستراتيجية توزيع ، فقد ترى أحداث IteratorGetNextAsOptional::DoCompute
بدلاً من IteratorGetNext::DoCompute
(اعتبارًا من TF 2.3).
إذا عادت المكالمات بسرعة (<= 50 لنا)، فهذا يعني أن بياناتك متاحة عند طلبها. خط أنابيب الإدخال ليس عنق الزجاجة الخاص بك؛ راجع دليل ملف التعريف للحصول على نصائح أكثر عمومية لتحليل الأداء.
إذا عادت المكالمات ببطء، فلن يتمكن tf.data
من مواكبة طلبات المستهلك. انتقل إلى القسم التالي.
2. هل تقوم بجلب البيانات مسبقًا؟
أفضل الممارسات لأداء مسار الإدخال هي إدراج تحويل tf.data.Dataset.prefetch
في نهاية مسار tf.data
الخاص بك. يتداخل هذا التحويل مع حساب المعالجة المسبقة لخط أنابيب الإدخال مع الخطوة التالية لحساب النموذج وهو مطلوب لتحقيق الأداء الأمثل لخط أنابيب الإدخال عند تدريب النموذج الخاص بك. إذا كنت تقوم بالجلب المسبق للبيانات، فيجب أن تشاهد شريحة Iterator::Prefetch
على نفس مؤشر الترابط مثل IteratorGetNext::DoCompute
.
إذا لم يكن لديك prefetch
في نهاية المسار ، فيجب عليك إضافة واحد. لمزيد من المعلومات حول توصيات أداء tf.data
، راجع دليل أداء tf.data .
إذا كنت تقوم بالفعل بالجلب المسبق للبيانات ، ولا يزال مسار الإدخال يمثل عنق الزجاجة لديك، فانتقل إلى القسم التالي لمزيد من تحليل الأداء.
3. هل وصلت إلى معدل استخدام عالٍ لوحدة المعالجة المركزية؟
يحقق tf.data
إنتاجية عالية من خلال محاولة تحقيق أفضل استخدام ممكن للموارد المتاحة. بشكل عام، حتى عند تشغيل النموذج الخاص بك على مسرع مثل GPU أو TPU، يتم تشغيل خطوط أنابيب tf.data
على وحدة المعالجة المركزية. يمكنك التحقق من استخدامك باستخدام أدوات مثل sar و htop ، أو في وحدة التحكم في المراقبة السحابية إذا كنت تستخدم Google Cloud Platform.
إذا كان استخدامك منخفضًا، فهذا يشير إلى أن خط الإدخال الخاص بك قد لا يستفيد بشكل كامل من وحدة المعالجة المركزية المضيفة. يجب عليك الرجوع إلى دليل أداء 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>
من الاسم (القصير)، وتمثل مجموعات البيانات الأخرى في الاسم الطويل تحويلات المصب.
على سبيل المثال، تم إنشاء لقطة الشاشة أعلاه من الكود التالي:
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
. لاحظ أن اسم مجموعة البيانات قد يختلف قليلاً عن واجهة برمجة تطبيقات python (على سبيل المثال، FiniteRepeat بدلاً من Repeat)، ولكن يجب أن يكون بديهيًا بدرجة كافية للتحليل.
التحولات المتزامنة وغير المتزامنة
بالنسبة لتحويلات tf.data
المتزامنة (مثل Batch
و Map
)، سترى الأحداث من التحويلات الأولية في نفس الموضوع. في المثال أعلاه، نظرًا لأن جميع التحويلات المستخدمة متزامنة، فإن جميع الأحداث تظهر في نفس الموضوع.
بالنسبة للتحويلات غير المتزامنة (مثل Prefetch
و ParallelMap
و ParallelInterleave
و MapAndBatch
) ستكون الأحداث من التحويلات الأولية على خيط مختلف. في مثل هذه الحالات، يمكن أن يساعدك "الاسم الطويل" في تحديد التحويل في المسار الذي يتوافق معه الحدث.
على سبيل المثال، تم إنشاء لقطة الشاشة أعلاه من الكود التالي:
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
) سيكون حدث Iterator::Prefetch
مع حدث Iterator::Generator
الرئيسي. يمكنك العثور على مسار المضيف المقابل من خلال البحث عن أحداث Iterator::Model
.
مثال 1
يتم إنشاء لقطة الشاشة أعلاه من مسار الإدخال التالي:
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
يتم إنشاء لقطة الشاشة أعلاه من مسار الإدخال التالي:
dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record, num_parallel_calls=2)
dataset = dataset.batch(32)
dataset = dataset.repeat()
هذا المثال مشابه لما ورد أعلاه، ولكنه يستخدم ParallelMap بدلاً من Map. نلاحظ هنا أن (1) أحداث Iterator::ParallelMap
طويلة، لكن (2) أحداث الإدخال الخاصة بها Iterator::FlatMap
(الموجودة في سلسلة رسائل مختلفة، نظرًا لأن ParallelMap غير متزامنة) قصيرة. يشير هذا إلى أن تحويل ParallelMap هو عنق الزجاجة.
معالجة عنق الزجاجة
مجموعات البيانات المصدر
إذا قمت بتحديد مصدر مجموعة بيانات باعتباره عنق الزجاجة، مثل القراءة من ملفات TFRecord، فيمكنك تحسين الأداء من خلال موازنة استخراج البيانات. للقيام بذلك، تأكد من تقسيم بياناتك عبر ملفات متعددة واستخدم tf.data.Dataset.interleave
مع تعيين المعلمة num_parallel_calls
على tf.data.AUTOTUNE
. إذا لم تكن الحتمية مهمة لبرنامجك، فيمكنك تحسين الأداء بشكل أكبر عن طريق تعيين العلامة deterministic=False
على tf.data.Dataset.interleave
اعتبارًا من TF 2.2. على سبيل المثال، إذا كنت تقرأ من TRecords، فيمكنك القيام بما يلي:
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)
موارد إضافية
- دليل أداء tf.data حول كيفية كتابة خطوط أنابيب إدخال
tf.data
- داخل فيديو TensorFlow: أفضل ممارسات
tf.data
- دليل التعريف
- البرنامج التعليمي لملف التعريف مع colab