Vấn đề triển khai phổ biến

Trang này mô tả các vấn đề triển khai phổ biến khi triển khai một tập dữ liệu mới.

Nên tránh SplitGenerator kế thừa

API tfds.core.SplitGenerator cũ không được dùng nữa.

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

Nên thay thế bằng:

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

Cơ sở lý luận : API mới ít dài dòng hơn và rõ ràng hơn. API cũ sẽ bị xóa trong phiên bản sau.

Bộ dữ liệu mới phải được chứa trong một thư mục

Khi thêm tập dữ liệu bên trong kho lưu trữ tensorflow_datasets/ , vui lòng đảm bảo tuân theo cấu trúc tập dữ liệu dưới dạng thư mục (tất cả tổng kiểm tra, dữ liệu giả, mã triển khai độc lập trong một thư mục).

  • Bộ dữ liệu cũ (xấu): <category>/<ds_name>.py
  • Bộ dữ liệu mới (tốt): <category>/<ds_name>/<ds_name>.py

Sử dụng TFDS CLI ( tfds new hoặc gtfds new cho nhân viên Google) để tạo mẫu.

Cơ sở lý luận : Cấu trúc cũ yêu cầu đường dẫn tuyệt đối cho tổng kiểm tra, dữ liệu giả mạo và đang phân phối các tệp dữ liệu ở nhiều nơi. Việc triển khai các bộ dữ liệu bên ngoài kho lưu trữ TFDS ngày càng trở nên khó khăn hơn. Để nhất quán, cấu trúc mới nên được sử dụng ở mọi nơi ngay bây giờ.

Danh sách mô tả phải được định dạng dưới dạng đánh dấu

str DatasetInfo.description được định dạng dưới dạng đánh dấu. Danh sách đánh dấu yêu cầu một dòng trống trước mục đầu tiên:

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

Cơ sở lý luận : Mô tả có định dạng sai sẽ tạo ra các tạo phẩm trực quan trong tài liệu danh mục của chúng tôi. Nếu không có dòng trống, văn bản trên sẽ được hiển thị dưới dạng:

Một số tiếp theo. 1. Mục 1 2. Mục 1 3. Mục 1 Một số văn bản khác

Quên tên ClassLabel

Khi sử dụng tfds.features.ClassLabel , hãy cố gắng cung cấp các nhãn str mà con người có thể đọc được với names= names_file= (thay vì num_classes=10 ).

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

Cơ sở lý luận : Nhãn mà con người có thể đọc được được sử dụng ở nhiều nơi:

  • Cho phép sinh ra str trực tiếp trong _generate_examples : yield {'label': 'dog'}
  • Được hiển thị cho người dùng như info.features['label'].names (phương thức chuyển đổi .str2int('dog') ,... cũng có sẵn)
  • Được sử dụng trong các tiện ích trực quan hóa tfds.show_examples , tfds.as_dataframe

Quên hình dạng hình ảnh

Khi sử dụng tfds.features.Image , tfds.features.Video , nếu hình ảnh có hình dạng tĩnh, chúng phải được chỉ định rõ ràng:

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

Cơ sở lý luận : Nó cho phép suy luận hình dạng tĩnh (ví dụ ds.element_spec['image'].shape ), cần thiết để tạo khối (việc ghép các hình ảnh có hình dạng không xác định sẽ yêu cầu thay đổi kích thước chúng trước tiên).

Thích loại cụ thể hơn thay vì tfds.features.Tensor

Khi có thể, hãy ưu tiên các loại cụ thể hơn tfds.features.ClassLabel , tfds.features.BBoxFeatures ,... thay vì các loại chung chung tfds.features.Tensor .

Cơ sở lý luận : Ngoài việc chính xác hơn về mặt ngữ nghĩa, các tính năng cụ thể còn cung cấp siêu dữ liệu bổ sung cho người dùng và được các công cụ phát hiện.

Nhập khẩu lười biếng trong không gian toàn cầu

Nhập khẩu lười biếng không nên được gọi từ không gian toàn cầu. Ví dụ như sau đây là sai:

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

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

Lý do : Việc sử dụng tính năng nhập lười biếng trong phạm vi toàn cầu sẽ nhập mô-đun cho tất cả người dùng tfds, làm mất đi mục đích của tính năng nhập lười biếng.

Tính toán động các phần tách đào tạo/kiểm tra

Nếu tập dữ liệu không cung cấp sự phân chia chính thức thì TFDS cũng không nên. Những điều sau đây nên tránh:

_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),
  }

Cơ sở lý luận : TFDS cố gắng cung cấp các tập dữ liệu gần giống với dữ liệu gốc. Thay vào đó, nên sử dụng API phân chia phụ để cho phép người dùng tự động tạo các phân chia phụ mà họ muốn:

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

Hướng dẫn phong cách Python

Thích sử dụng API pathlib

Thay vì API tf.io.gfile , tốt hơn nên sử dụng API pathlib . Tất cả các phương thức dl_manager đều trả về các đối tượng giống pathlib tương thích với 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())

Lý do : API pathlib là API tệp hướng đối tượng hiện đại giúp loại bỏ bản soạn sẵn. Việc sử dụng .read_text() / .read_bytes() cũng đảm bảo các tệp được đóng chính xác.

Nếu phương thức không được sử dụng self thì nó phải là một hàm

Nếu một phương thức lớp không sử dụng self thì đó phải là một hàm đơn giản (được định nghĩa bên ngoài lớp).

Cơ sở lý luận : Nó giúp người đọc thấy rõ rằng hàm này không có tác dụng phụ cũng như đầu vào/đầu ra ẩn:

x = f(y)  # Clear inputs/outputs

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

Nhập khẩu lười biếng trong Python

Chúng tôi lười biếng nhập các mô-đun lớn như TensorFlow. Nhập lười biếng trì hoãn việc nhập mô-đun thực tế vào lần sử dụng đầu tiên của mô-đun. Vì vậy, những người dùng không cần mô-đun lớn này sẽ không bao giờ nhập nó. Chúng tôi sử dụng 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

Dưới lớp vỏ bọc, lớp LazyModule hoạt động như một nhà máy, nó sẽ chỉ thực sự nhập mô-đun khi một thuộc tính được truy cập ( __getattr__ ).

Bạn cũng có thể sử dụng nó một cách thuận tiện với trình quản lý bối cảnh:

from etils import epy

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