Search
💽

Parquet / WebDataset / TorchData

Category
BlogPost
Venue
Backbone
Text
PPT

Q. Row 기반 포맷(JSONL)과 Columnar 기반 포맷(Parquet)의 차이?

Row 기반(JSONL/CSV): 각 (row)가 독립적으로 직렬화되어 저장됨. (OLTP)
→ online transaction processing
→ 특정 필드만 추출은 가능하지만, 파일을 순차적으로 읽으며 모든 row를 파싱해야 원하는 컬럼에 도달 가능.
Columnar 기반(Parquet): row group 단위로 묶은 뒤 컬럼별(chunk)로 저장됨. (OLAP)
→ online analytical processing
→ 특정 컬럼만 I/O 할 수 있어 대규모 데이터셋에서 효율적.

Q. Parquet 파일 구조.

Row Group: 실제 데이터가 저장되는 기본 단위. 여러 row를 묶은 그룹.
Column Chunk: Row Group 안에서 각 컬럼을 따로 저장한 부분. (컬럼 지향 저장)
Page: Column Chunk 내부를 더 세분화한 저장 단위. (dictionary, data page 등)
Footer/Metadata: 전체 파일의 schema, row group 오프셋 등을 기록 → 필요한 부분만 랜덤 액세스 가능.

Q. Parquet의 인코딩 및 압축 최적화 방식은?

Encoding: RLE (Run-Length Encoding), Dictionary Encoding 등 컬럼 특성에 맞는 방식 사용.
Compression: Snappy, ZSTD 등을 컬럼 단위로 적용 → 데이터 패턴이 유사한 컬럼에 효과적.
효과: 숫자·범주형 데이터에서는 압축 효율이 높음. 반면 자연어 텍스트 데이터는 압축 이득이 상대적으로 작음.

Q. 왜 Columnar Storage는 I/O 절감이 가능한 구조인가?

Row 포맷은 필요한 컬럼만 뽑더라도 전체 row를 순회하며 파싱해야 한다.
Columnar 포맷은 디스크에서 필요한 컬럼 chunk만 읽으면 되므로, I/O 자체가 줄어듦.

Q. Parquet은 왜 "hybrid" 포맷이라고 하나요?

Parquet은 row를 Row Group 단위로 묶지만, 그 내부는 **Column Chunk(컬럼 지향)**으로 저장된다.
즉, row-oriented + column-oriented 특성을 모두 가진 hybrid 구조.

Q. JSONL → Parquet 변환 시 메모리 사용량이 큰 이유는 무엇인가요?

Hugging Face datasets는 내부적으로 Apache Arrow 메모리 포맷을 사용.
변환 과정에서 JSONL 전체를 Arrow의 columnar in-memory 구조로 변환 → 이 단계에서 메모리 급증.
(전체 JSONL을 columnar in-memory 구조로 변환하는 과정에서 일시적으로 두 포맷이 모두 메모리에 존재)
이후 Arrow → Parquet으로 직렬화하면서 압축·인코딩까지 수행하므로 CPU·메모리 모두 많이 사용됨.

Q. Parquet 파일은 왜 여러 개로 쪼개어 저장되는가?

대규모 파일을 단일 .parquet으로 저장하면 랜덤 액세스·병렬 처리가 어려움.
Row Group을 여러 파일로 나누면 Spark, Dask, Ray 등 분산 시스템에서 병렬 I/O 및 처리가 가능해짐.
따라서 Hugging Face 같은 라이브러리도 자동으로 여러 개의 parquet shard로 저장.

Q. 읽기와 편집 측면에서 어떤 포맷이 더 적합한가?

읽기(분석·학습): Parquet → 특정 컬럼만 읽을 수 있어 빠름, 분산 환경에서 유리.
편집(append, 수정): JSONL → 단순 row append가 쉬움, 텍스트 기반이므로 직관적.

Q. WebDataset?

WebDataset은 초대용량 데이터셋을 효율적으로 저장하고 스트리밍 학습하기 위해 설계된 PyTorch용 라이브러리입니다.
기본 아이디어는 데이터를 .tar 아카이브에 묶고, 각 샘플을 (prefix.key) 방식으로 저장한 뒤, 여러 개의 shard로 분할해 분산 환경에서 병렬로 읽을 수 있도록 하는 것입니다.
(tar: 아카이브(archive) 포맷으로 여러 파일을 하나의 파일로 패키징 → I/O 효율 극대화)
개별 파일: - 파일 시스템 메타데이터 조회 × 10,000- inode 테이블 접근 오버헤드 TAR 파일: - 메타데이터 조회 1- 순차 읽기로 SSD 내부 최적화 활용
Python
복사

Q. WebDataset은 어떤 구조로 데이터를 저장?

각 샘플은 하나의 prefix를 기준으로 여러 파일이 묶여 있습니다.
예:
000001.jpg 000001.txt 000002.jpg 000002.txt
Plain Text
복사
여기서 000001이 prefix, .jpg/.txt는 샘플의 서로 다른 modality(이미지/텍스트).
여러 샘플을 하나의 .tar 파일에 묶고, shard 단위로 분할
예: shard-00000.tar, shard-00001.tar
이렇게 하면 분산 학습 시 각 worker가 서로 다른 shard를 읽어 충돌 없이 병렬 학습이 가능

Q. WebDataset의 장점?

1.
스트리밍 가능
tar 파일은 순차 접근에 최적화 → S3/GCS 같은 obj 스토리지에서도 그대로 읽기 가능
전체 다운로드 없이 필요한 shard만 읽어 학습 가능
2.
분산 학습 효율
여러 shard를 서로 다른 worker가 읽기 때문에 데이터 충돌 없이 병렬 처리 가능
3.
샘플 단위 유연성
이미지, 텍스트, 오디오 등 멀티모달 데이터를 하나의 prefix로 묶을 수 있어 다루기 편리

Q. WebDataset vs. 일반 포맷(JSONL, Parquet 등)의 차이는?

JSONL: 편집, 사람이 보기 좋은 row-oriented 포맷. 하지만 대규모 스트리밍/분산 학습에는 비효율적
Parquet: 분석/쿼리에 최적화된 columnar 포맷. 특정 컬럼만 읽을 때 빠르지만, 멀티모달 샘플 단위 저장엔 부적합
WebDataset: 대규모 학습 데이터셋을 샘플 단위로 tar + shard에 담아 학습 스트리밍과 분산 환경에 최적화

Q. TorchData란?

A. TorchData는 PyTorch에서 데이터 로딩 파이프라인을 구성하는 기본 단위(DataPipes)를 제공하는 라이브러리.
파일 나열/열기/파싱/변환/셔플/배치 같은 단계를 작은 DataPipe들로 만들고, 이들을 체인으로 연결해 하나의 파이프라인으로 사용.
만들어진 DataPipe는 그대로 DataLoader에 넣어 학습에 사용할 수 있음.

Q. DataPipe란?

A. DataPipe는 “데이터를 읽고 → 가공해서 → 다음 단계로 흘려보내는” 연산 블록(빌딩 블록).
TorchData는 이 블록들을 클래스 생성자로 감싸거나, 더 권장되는 함수형(Functional) 형태로 기존 DataPipe에 체인 형태로 연결해 파이프라인을 만듭니다. (예: list_files().open_files().parse_csv().shuffle().batch()) 이렇게 여러 IterDataPipe를 연달아 연결해 연속 작업을 수행하는 것이 표준 사용법.

Q. MapDataPipe IterableDataPipe의 차이는?

A.
Map-style DataPipe(MapDataPipe): __getitem__, __len__을 구현하는 맵 스타일로, (정수일 필요 없는) 인덱스/키→샘플 매핑을 표현.
문서에서 PyTorch의 기본 Dataset과 거의 동등한 개념으로 설명됩니다. 즉, dp[idx]처럼 랜덤 접근과 전체 길이를 전제.
Iterable-style DataPipe(IterDataPipe): __iter__를 구현하는 이터러블 스타일로, 랜덤 읽기가 비싸거나 불가능한 경우에 적합한 샘플 스트림을 표현합니다. 전체 길이가 없을 수 있고, 순차적으로 흘려보내며 처리한다는 점이 특징.
⇒ PyTorch에서 Map-style Dataset은 __getitem____len__을 구현해서 인덱스 기반 접근이 가능한 정적 데이터셋을 의미. 즉, dataset[idx]처럼 랜덤 액세스가 가능. 반대로 IterableDataset은 __iter__만 구현되어 있어서 순차적으로 샘플을 흘려보내는 스트리밍 구조. 로그 데이터나 tar shard 같이 길이나 인덱스를 알 수 없는 데이터에 적합. 따라서 랜덤 접근이 필요한 경우에는 MapDataset을, 순차 스트리밍이 적합한 경우에는 IterableDataset을 사용

Q. TorchData에서 ‘Composable Pipeline’이란 무엇이고, 어떻게 만드나요?

A. 작은 단위의 DataPipe들을 함수형 체인으로 연결해, 소스→열기→파싱/디코딩→변환→셔플→배치처럼 단계별로 이어지는 파이프라인을 만드는 개념.
예컨대 FileOpener는 경로를 입력받아 “(경로, 파일 스트림)” 튜플을 yield합니다(파일 열기는 이터레이션 시점에 수행). 이어서 CSV 파서나 tar 로더 같은 DataPipe를 붙여 행 단위 파싱 또는 tar 스트림 열기를 수행하고, 필요하면 버퍼 기반 셔플배치를 붙입니다. 이렇게 만든 DataPipe를 그대로 DataLoader에 전달해 사용.

Q. Sharding이란 무엇이며, TorchData에선 어떻게 동작하나?

A. Sharding은 멀티 워커/분산 환경에서 데이터 스트림을 워커 수만큼 나눠, 중복 없이 서로 다른 요소를 소비하게 만드는 기능.
TorchData의 ShardingFilter(functional: sharding_filter)를 적용하고 apply_sharding이 호출되면, 각 DataPipe 인스턴스(워커)가 원본 스트림의 n개 중 1개 요소씩 맡도록 자동 분할됩니다(n=인스턴스 수). 즉, 워커 간 중복 로딩 없이 균등하게 데이터가 샤딩.

Q. TorchData의 ‘streaming’과 ‘lazy evaluation’은 구체적으로 무엇을 의미하나?

A.
Streaming
TorchData의 스트리밍은 데이터를 메모리에 한 번에 로드하지 않고, 필요할 때마다 순차적으로 샘플 단위로 읽어오는 방식. 그래서 TB 단위 데이터나 무한히 이어지는 스트림 데이터도 효율적으로 처리할 수 있음
⇒ 100TB 이미지-텍스트 데이터셋이 1만 개의 tar shard로 나눠져 있을 때, 스트리밍 방식으로 IterableDataPipe를 통해 하나의 shard를 순서대로 열고, 내부의 샘플을 yield. 메모리에 올리는 건 한 번에 하나의 샘플뿐이라서, 전체 데이터를 한꺼번에 로드 X
Lazy Evaluation
TorchData의 지연 실행은 데이터 변환을 정의만 해두고, 실제 데이터는 이터레이션이 시작될 때 처리하는 방식. 즉, 파이프라인 단계(map, shuffle, batch 등)는 연결만 되고, for batch in datapipe:처럼 순회할 때 실제 I/O와 변환이 일어나는 것.
dp = IterableWrapper(range(10)) dp = dp.map(lambda x: x * 2).shuffle().batch(2)
Python
복사
해당 시점에는 아무 데이터도 실제로 계산되지 않습니다. (불필요한 연산 방지)
for batch in dp:를 실행할 때 비로소 → 원본 range에서 값이 읽히고 → 2배로 변환되고 → 셔플되고 → 배치가 생성.