หน้านี้อธิบายการใช้งาน 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', ...]),
}
เหตุผล : มีการใช้ฉลากที่มนุษย์อ่านได้ในหลายที่:
- อนุญาตให้ให้ผลตอบแทน
str
โดยตรงใน_generate_examples
:yield {'label': 'dog'}
- เปิดเผยในผู้ใช้เช่น
info.features['label'].names
(วิธีการแปลง.str2int('dog')
,... ยังมีอยู่) - ใช้ใน การสร้างภาพข้อมูล utils
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
ทั่วไป
เหตุผล : นอกจากจะมีความถูกต้องทางความหมายมากขึ้นแล้ว คุณลักษณะเฉพาะยังให้ข้อมูลเมตาเพิ่มเติมแก่ผู้ใช้และเครื่องมือตรวจพบอีกด้วย
การนำเข้าที่ขี้เกียจในพื้นที่โลก
ไม่ควรเรียกการนำเข้าแบบ 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