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배로 변환되고 → 셔플되고 → 배치가 생성.