일반적인 구현 문제

이 페이지에서는 새 데이터세트를 구현할 때 일반적인 구현 방법을 설명합니다.

레거시 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/ 저장소 내에 데이터세트를 추가할 때 데이터세트 폴더 구조(모든 체크섬, 더미 데이터, 구현 코드가 폴더에 자체적으로 포함됨)를 따르도록 하세요.

  • 이전 데이터세트(불량): <category>/<ds_name>.py
  • 새 데이터 세트(양호): <category>/<ds_name>/<ds_name>.py

TFDS CLI ( tfds new 또는 Google 직원의 경우 gtfds new )를 사용하여 템플릿을 생성하세요.

이유 : 이전 구조에는 체크섬, 가짜 데이터에 대한 절대 경로가 필요했으며 데이터 세트 파일을 여러 위치에 배포했습니다. 이로 인해 TFDS 저장소 외부에 데이터세트를 구현하는 것이 더 어려워졌습니다. 일관성을 위해 이제 새로운 구조를 모든 곳에서 사용해야 합니다.

설명 목록은 마크다운 형식으로 지정되어야 합니다.

DatasetInfo.description str 마크다운 형식으로 지정됩니다. 마크다운 목록의 첫 번째 항목 앞에는 빈 줄이 필요합니다.

_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 사용할 때, 사람이 읽을 수 있는 레이블 strnames= 또는 names_file= ( num_classes=10 대신)을 제공하도록 노력하세요.

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

근거 : 사람이 읽을 수 있는 라벨은 여러 곳에서 사용됩니다.

  • _generate_examples 에서 직접 str 생성하도록 허용: 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.Tensor 대신 더 구체적인 유형인 tfds.features.ClassLabel , tfds.features.BBoxFeatures ,...를 선호하세요.

근거 : 의미상 더 정확한 것 외에도 특정 기능은 사용자에게 추가 메타데이터를 제공하고 도구에 의해 감지됩니다.

전역 공간에서의 지연 가져오기

지연 가져오기는 전역 공간에서 호출하면 안 됩니다. 예를 들어 다음은 잘못되었습니다.

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%:]'])

파이썬 스타일 가이드

pathlib API 사용을 선호합니다.

tf.io.gfile API 대신 pathlib API를 사용하는 것이 좋습니다. 모든 dl_manager 메소드는 GCS, S3 등과 호환되는 pathlib 유사 객체를 반환합니다.

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 ?

Python의 지연 가져오기

우리는 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