گوچاهای پیاده سازی رایج

این صفحه گوچا اجرای رایج را هنگام پیاده سازی مجموعه داده جدید توضیح می دهد.

Legacy SplitGenerator باید اجتناب شود

API قدیمی tfds.core.SplitGenerator منسوخ شده است.

def _split_generator(...):
  return [
      tfds.core.SplitGenerator(name='train', gen_kwargs={'path': train_path}),
      tfds.core.SplitGenerator(name='test', gen_kwargs={'path': test_path}),
  ]

باید جایگزین شود:

def _split_generator(...):
  return {
      'train': self._generate_examples(path=train_path),
      'test': self._generate_examples(path=test_path),
  }

دلیل : API جدید کم‌تر و واضح‌تر است. API قدیمی در نسخه بعدی حذف خواهد شد.

مجموعه داده های جدید باید در یک پوشه به صورت مستقل باشند

هنگام افزودن یک مجموعه داده در مخزن tensorflow_datasets/ ، لطفاً مطمئن شوید که ساختار مجموعه داده‌ها به‌عنوان پوشه را دنبال می‌کنید (همه جمع‌های چک، داده‌های ساختگی، کد پیاده‌سازی در یک پوشه وجود دارد).

  • مجموعه داده های قدیمی (بد): <category>/<ds_name>.py
  • مجموعه داده های جدید (خوب): <category>/<ds_name>/<ds_name>.py

از TFDS CLI ( tfds new یا gtfds new for googlers) برای تولید الگو استفاده کنید.

دلیل : ساختار قدیمی به مسیرهای مطلق برای جمع‌های چک، داده‌های جعلی نیاز داشت و فایل‌های مجموعه داده را در بسیاری از مکان‌ها توزیع می‌کرد. پیاده سازی مجموعه داده ها در خارج از مخزن TFDS را دشوارتر می کرد. برای ثبات، ساختار جدید باید در همه جا استفاده شود.

لیست های توضیحات باید به صورت علامت گذاری فرمت شوند

str DatasetInfo.description به صورت علامت گذاری فرمت شده است. لیست های Markdown نیاز به یک خط خالی قبل از اولین مورد دارند:

_DESCRIPTION = """
Some text.
                      # << Empty line here !!!
1. Item 1
2. Item 1
3. Item 1
                      # << Empty line here !!!
Some other text.
"""

دلیل : توضیحات با قالب بندی بد، مصنوعات بصری را در اسناد کاتالوگ ما ایجاد می کند. بدون خطوط خالی، متن بالا به صورت زیر نمایش داده می شود:

مقداری متن 1. مورد 1 2. مورد 1 3. مورد 1 برخی از متن های دیگر

نام ClassLabel را فراموش کرده اید

هنگام استفاده از tfds.features.ClassLabel ، سعی کنید برچسب های قابل خواندن توسط انسان str با names= یا names_file= (به جای num_classes=10 ) ارائه دهید.

features = {
    'label': tfds.features.ClassLabel(names=['dog', 'cat', ...]),
}

دلیل : برچسب های قابل خواندن توسط انسان در بسیاری از مکان ها استفاده می شود:

  • اجازه دادن مستقیم به str در _generate_examples : yield {'label': 'dog'}
  • در معرض کاربرانی مانند info.features['label'].names (روش تبدیل .str2int('dog') ,... نیز موجود است)
  • در کاربردهای تجسم tfds.show_examples ، tfds.as_dataframe استفاده می شود

شکل تصویر را فراموش کرده اید

هنگام استفاده از tfds.features.Image ، tfds.features.Video ، اگر تصاویر دارای شکل ثابت هستند، باید به صراحت مشخص شوند:

features = {
    'image': tfds.features.Image(shape=(256, 256, 3)),
}

منطق : استنتاج شکل ثابت را می دهد (به عنوان مثال ds.element_spec['image'].shape )، که برای دسته بندی مورد نیاز است (بچینگ تصاویر با شکل ناشناخته ابتدا نیاز به تغییر اندازه آنها دارد).

نوع خاص تر را به جای tfds.features.Tensor ترجیح دهید

در صورت امکان، انواع خاص تر tfds.features.ClassLabel ، tfds.features.BBoxFeatures ،... را به جای tfds.features.Tensor عمومی ترجیح دهید.

دلیل : ویژگی های خاص علاوه بر اینکه از نظر معنایی درست تر هستند، ابرداده های اضافی را به کاربران ارائه می دهند و توسط ابزارها شناسایی می شوند.

واردات تنبل در فضای جهانی

واردات تنبل را نباید از فضای جهانی خواند. به عنوان مثال موارد زیر اشتباه است:

tfds.lazy_imports.apache_beam # << Error: Import beam in the global scope

def f() -> beam.Map:
  ...

منطق : استفاده از واردات تنبل در حوزه جهانی، ماژول را برای همه کاربران tfds وارد می کند و هدف واردات تنبل را شکست می دهد.

محاسبه پویا تقسیم قطار/تست

اگر مجموعه داده تقسیمات رسمی را ارائه ندهد، TFDS نیز نباید. از موارد زیر باید اجتناب شود:

_TRAIN_TEST_RATIO = 0.7

def _split_generator():
  ids = list(range(num_examples))
  np.random.RandomState(seed).shuffle(ids)

  # Split train/test
  train_ids = ids[_TRAIN_TEST_RATIO * num_examples:]
  test_ids = ids[:_TRAIN_TEST_RATIO * num_examples]
  return {
      'train': self._generate_examples(train_ids),
      'test': self._generate_examples(test_ids),
  }

منطق : TFDS سعی می کند مجموعه داده هایی را به اندازه داده های اصلی ارائه دهد. در عوض باید از sub-split API استفاده شود تا به کاربران اجازه دهد به صورت پویا زیرشاخه های مورد نظر خود را ایجاد کنند:

ds_train, ds_test = tfds.load(..., split=['train[:80%]', 'train[80%:]'])

راهنمای سبک پایتون

ترجیحا از pathlib API استفاده کنید

به جای tf.io.gfile API، ترجیحاً از pathlib API استفاده کنید. همه متدهای dl_manager اشیاء شبیه pathlib سازگار با GCS، S3،... را برمی گرداند.

path = dl_manager.download_and_extract('http://some-website/my_data.zip')

json_path = path / 'data/file.json'

json.loads(json_path.read_text())

منطق : pathlib API یک API فایل شی گرا مدرن است که boilerplate را حذف می کند. استفاده از .read_text() / .read_bytes() همچنین تضمین می کند که فایل ها به درستی بسته شده اند.

اگر روش self استفاده نمی‌کند، باید تابع باشد

اگر متد کلاسی self استفاده نمی‌کند، باید یک تابع ساده (خارج از کلاس تعریف شده) باشد.

منطق : برای خواننده روشن می کند که این تابع دارای عوارض جانبی یا ورودی / خروجی پنهان نیست:

x = f(y)  # Clear inputs/outputs

x = self.f(y)  # Does f depend on additional hidden variables ? Is it stateful ?

واردات تنبل در پایتون

ما با تنبلی ماژول های بزرگی مانند TensorFlow را وارد می کنیم. واردات تنبل، واردات واقعی ماژول را به اولین استفاده از ماژول موکول می کند. بنابراین کاربرانی که به این ماژول بزرگ نیاز ندارند هرگز آن را وارد نمی کنند. ما از etils.epy.lazy_imports استفاده می کنیم.

from tensorflow_datasets.core.utils.lazy_imports_utils import tensorflow as tf
# After this statement, TensorFlow is not imported yet

...

features = tfds.features.Image(dtype=tf.uint8)
# After using it (`tf.uint8`), TensorFlow is now imported

در زیر هود، کلاس LazyModule به‌عنوان یک کارخانه عمل می‌کند، که تنها زمانی که به یک ویژگی دسترسی داشته باشید، ماژول را وارد می‌کند ( __getattr__ ).

همچنین می توانید به راحتی با یک مدیر زمینه از آن استفاده کنید:

from etils import epy

with epy.lazy_imports(error_callback=..., success_callback=...):
  import some_big_module