gotchas การใช้งานทั่วไป

หน้านี้อธิบายการใช้งาน gotcha ทั่วไปเมื่อใช้งานชุดข้อมูลใหม่

ควรหลีกเลี่ยง Legacy SplitGenerator

tfds.core.SplitGenerator API เก่าเลิกใช้งานแล้ว

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/ repository โปรดตรวจสอบให้แน่ใจว่าได้ทำตามโครงสร้างชุดข้อมูลเป็นโฟลเดอร์ (เช็คซัมทั้งหมด ข้อมูลจำลอง โค้ดการใช้งานมีอยู่ในโฟลเดอร์)

  • ชุดข้อมูลเก่า (ไม่ถูกต้อง): <category>/<ds_name>.py
  • ชุดข้อมูลใหม่ (ดี): <category>/<ds_name>/<ds_name>.py

ใช้ TFDS CLI ( tfds new หรือ gtfds new สำหรับ googler) เพื่อสร้างเทมเพลต

เหตุผล : โครงสร้างเก่าจำเป็นต้องมีเส้นทางที่แน่นอนสำหรับเช็คซัม ข้อมูลปลอม และมีการเผยแพร่ไฟล์ชุดข้อมูลในหลายที่ ทำให้การนำชุดข้อมูลไปใช้นอกพื้นที่เก็บข้อมูล TFDS เป็นเรื่องยากขึ้น เพื่อความสม่ำเสมอ ควรใช้โครงสร้างใหม่ทุกที่ในขณะนี้

รายการคำอธิบายควรอยู่ในรูปแบบมาร์กดาวน์

DatasetInfo.description str ถูกจัดรูปแบบเป็น 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', ...]),
}

เหตุผล : มีการใช้ฉลากที่มนุษย์อ่านได้ในหลายที่:

ลืมรูปร่างของภาพ

เมื่อใช้ 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 ทั่วไป

เหตุผล : นอกจากจะมีความถูกต้องทางความหมายมากขึ้นแล้ว คุณลักษณะเฉพาะยังให้ข้อมูลเมตาเพิ่มเติมแก่ผู้ใช้และเครื่องมือตรวจพบอีกด้วย

การนำเข้าที่ขี้เกียจในพื้นที่โลก

ไม่ควรเรียกการนำเข้าแบบ Lazy จากพื้นที่ทั่วโลก ตัวอย่างต่อไปนี้เป็นสิ่งที่ผิด:

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 พยายามจัดเตรียมชุดข้อมูลที่ใกล้เคียงกับข้อมูลต้นฉบับ ควรใช้ API แยกย่อย แทนเพื่อให้ผู้ใช้สร้างการแยกย่อยที่ต้องการแบบไดนามิก:

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

คู่มือสไตล์ Python

ต้องการใช้ 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 ไฟล์เชิงวัตถุสมัยใหม่ซึ่งจะลบสำเร็จรูป การใช้ .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 ?

การนำเข้า Lazy ใน Python

เรานำเข้าโมดูลขนาดใหญ่อย่าง TensorFlow อย่างเกียจคร้าน การนำเข้าแบบ Lazy จะเลื่อนการนำเข้าโมดูลจริงไปเป็นการใช้งานครั้งแรกของโมดูล ดังนั้นผู้ใช้ที่ไม่ต้องการโมดูลขนาดใหญ่นี้จะไม่มีวันนำเข้ามัน เราใช้ 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