HF - DatasetBuilder
DatasetBuilder를 활용한 재현 가능한 학습 데이터 정의
1. DatasetBuilder의 필요성
머신러닝 및 딥러닝 학습에서 dataset(데이터셋)은
- 단순한 파일 집합이 아니라,
- 학습 과정 전반을 규정하는 핵심 요소이다.
이미지 파일, CSV, JSON 등의 형태로만 존재하는 데이터는 다음과 같은 한계를 가짐:
- 데이터의 column 구조가 명확히 정의되지 않음
- label 의 의미가 코드 외부에 암묵적으로 존재함
- train, validation, test 분할 기준이 명확하지 않음
- 동일한 데이터셋을 다시 불러왔을 때 재현성을 보장하기 어려움
Hugging Face Datasets 라이브러리는 이러한 문제를 해결하기 위해
- 데이터셋을 코드로 정의하는 방식을 제공하며,
- 그 핵심 구성 요소가 DatasetBuilder 임.
2. DatasetBuilder의 역할
DatasetBuilder는 데이터셋에 대해 다음 사항을 코드 수준에서 명확히 정의:
- 데이터셋이 가지는 column 구조
- 각 컬럼의 자료형 및 의미
- train, validation, test와 같은 데이터 분할 방식
load_dataset()호출 시 생성될 DatasetDict의 형태
즉, DatasetBuilder는
- "이 데이터셋이 무엇이며, 어떤 구조로 해석되어야 하는가" 를
- 명시적으로 기술하는 정의서 역할을 수행.
DatasetBuiler 는 Dataset Definition!!
이로 인해 DatasetBuilder가 포함된 데이터셋은 단순한 데이터 파일 묶음이 아니라, 재현 가능한 학습 resource로 인식됨.
3. DatasetBuilder와 DatasetDict의 관계
load_dataset() 함수가 호출되면 내부적으로 다음 과정이 수행됨:
- DatasetBuilder가 정의된 스크립트를 탐색
- DatasetBuilder 실행
- split 규칙에 따라 각 Dataset 생성
- 생성된 Dataset들을 하나로 묶어 DatasetDict 반환
DatasetBuilder 를 통해 DatasetDict가 생성됨.
- DatasetDict는 결과물 임.
4. GeneratorBasedBuilder의 개념
DatasetBuilder에는 여러 구현 방식이 존재하지만,
가장 기본적이고 널리 사용되는 형태는 GeneratorBasedBuilder이다.
이 방식의 특징은 다음과 같다.
- 데이터를 한 번에 메모리에 적재하지 않음
- 파일을 순차적으로 읽으며 샘플을 생성함
- 이미지, 텍스트, CSV 등 다양한 데이터 형식에 적용 가능함
GeneratorBasedBuilder를 사용할 경우,
다음 세 가지 메서드를 반드시 오버라이딩해야 한다.
| 메서드 | 기능 |
|---|---|
_info() |
데이터셋의 컬럼과 자료형 정의 |
_split_generators() |
train/validation/test 분할 정의 |
_generate_examples() |
실제 데이터 샘플 생성 |
5. 실습: DatasetBuilder를 이용한 DatasetDict 생성
(1) 실습용 디렉토리 구조
다음과 같은 디렉토리 구조를 구성한다.
dataset_builder_practice/
├─ my_dataset.py
└─ data/
├─ train/
│ ├─ cat/
│ └─ dog/
├─ validation/
│ ├─ cat/
│ └─ dog/
└─ test/
├─ cat/
└─ dog/
- 각 클래스 디렉토리에는 임의의 이미지 파일을 배치.
- 중요한 것은 split 디렉토리와 클래스 디렉토리의 계층 구조 임.
(2) DatasetBuilder 코드 작성
my_dataset.py 파일에 다음 코드를 작성.
from pathlib import Path
import datasets
IMAGE_EXTS = {".png", ".jpg", ".jpeg"}
class MyDataset(datasets.GeneratorBasedBuilder):
VERSION = datasets.Version("1.0.0")
def _info(self):
return datasets.DatasetInfo(
features=datasets.Features({
"image": datasets.Image(),
"label": datasets.ClassLabel(names=["cat", "dog"]),
})
)
def _split_generators(self, dl_manager):
root = Path(self.config.data_dir)
data_root = root / "data"
return [
datasets.SplitGenerator(
name=datasets.Split.TRAIN,
gen_kwargs={"split_root": data_root / "train"},
),
datasets.SplitGenerator(
name=datasets.Split.VALIDATION,
gen_kwargs={"split_root": data_root / "validation"},
),
datasets.SplitGenerator(
name=datasets.Split.TEST,
gen_kwargs={"split_root": data_root / "test"},
),
]
def _generate_examples(self, split_root):
class_to_id = {"cat": 0, "dog": 1}
for class_name, label in class_to_id.items():
class_dir = split_root / class_name
for img_path in class_dir.iterdir():
if img_path.suffix.lower() not in IMAGE_EXTS:
continue
key = str(img_path.relative_to(split_root))
yield key, {
"image": str(img_path),
"label": label,
}
이 코드에서
_info()는 데이터셋의 스키마를 정의_split_generators()는 DatasetDict의 분할(split) 구조를 결정,_generate_examples()는 실제 파일을 읽어 샘플을 생성.
(3) DatasetDict 생성 확인
다음 코드를 실행한다.
from datasets import load_dataset
dataset_dict = load_dataset(
path="dataset_builder_practice",
name=None,
)
print(dataset_dict)
출력 결과는 train, validation, test split을 포함하는 DatasetDict 객체임.
(4) DatasetDict 내부 구조 확인
print(dataset_dict.keys())
print(dataset_dict["train"][0])
print(dataset_dict["train"].features["label"].names)
- DatasetDict의 split 구조가 코드에 의해 결정됨
- label의 의미가
ClassLabel로 고정됨 - 데이터셋의 구조와 의미가 코드 수준에서 재현 가능함
6. 정리
지금까지 DatasetBuilder를 사용하여
- 학습 데이터셋을 코드로 정의하고,
- 그 결과로 DatasetDict를 생성하는 과정을 확인함.
이를 통해 다음과 같은 인식을 갖는 것이 중요하다.
데이터셋은 단순한 파일 집합이 아니라, 코드로 정의되는 하나의 소프트웨어 아티팩트이다.