대규모 데이터셋 처리하기
대규모 데이터셋 처리하기
모든 Job에는 하드웨어 flavor가 정한 고정된 양의 로컬 디스크가 제공돼요(this Ephemeral Storage 컬럼, hf jobs hardware로도 표시). 전체 데이터셋을 그 디스크에 맞출 필요는 없어요 — 데이터셋과 Storage Buckets는 Hub에서 직접 읽을 수 있어서(스트리밍, 쿼리, 마운트), 단일 Job이 디스크보다 훨씬 큰 데이터를 처리할 수 있답니다.
출처: 문서
본문
모든 Job에는 하드웨어 flavor가 정한 고정된 양의 로컬 디스크가 제공돼요(the Ephemeral Storage 컬럼, hf jobs hardware로도 표시). 전체 데이터셋을 그 디스크에 맞출 필요는 없어요: 데이터셋과 Storage Buckets는 Hub에서 직접 읽을 수 있어요 — 스트리밍, 쿼리, 마운트 — 그래서 단일 Job이 디스크보다 훨씬 큰 데이터를 처리할 수 있죠. 이 페이지는 옵션들과 각각을 언제 사용해야 하는지를 다뤄요.
어떤 접근 방식?
- 디스크에 맞는다면 → 일반
load_dataset(...)가 그대로 동작해요(또는 더 큰 flavor 선택 — 최대 1 TB 임시 디스크). - 행을 반복하며 처리·학습한다면 → 스트리밍해요.
- 필터링·컬럼 정리 스캔 → Polars나 DuckDB로
hf://에서 직접 쿼리해요. - 로컬 파일 경로를 기대하는 도구 → 저장소를 마운트하고 지연 로딩해요.
- 결과 영속화 → Storage Bucket에 써서 Job 이후에도 남게 해요.
Dataset 스트리밍
스트리밍은 코드가 소비할 때 예시를 Hub에서 읽어요 — 다운로드가 없고, 로컬 복사본도 없고, 멀티-TB 데이터셋까지 확장돼요. 최근 릴리스는 이를 최대 100배 더 효율적으로 만들어, 여러 워커에 걸쳐 학습할 때 로컬 SSD와 맞먹는 성능에 도달했어요:
from datasets import load_dataset
ds = load_dataset("HuggingFaceFW/fineweb-edu", "sample-10BT", split="train", streaming=True)
for example in ds.take(1000):
... # streams in as you iterate, nothing hits disk
스트리밍된 데이터셋은 지연 .filter(), .map(), .shuffle(buffer_size=...), .batch()를 지원하는 IterableDataset이며, 디스크보다 큰 데이터를 학습하도록 PyTorch DataLoader나 Trainer에 바로 전달할 수 있어요. 전체 API는 Stream 가이드, 단일 노드·분산 설정의 종단간 학습 워크스루는 예시 & 튜토리얼을 참고해요.
스트리밍은 Spark를 사용하는 분산 설정에서도 동작해요:
import pyspark_huggingface
df = spark.read.format("huggingface").option("config", "sample-10BT").load("HuggingFaceFW/fineweb-edu")
결과 Spark 데이터프레임은 분산돼 있어요: 각 워커가 자신의 파일 하위 집합에서 스트리밍해요. 행·컬럼을 읽고, 쓰고, 효율적으로 필터링하는 예시는 Spark 문서를 참고해요.
hf://에서 직접 읽기와 필터링
많은 데이터 라이브러리가 hf:// 경로로 Hub 데이터셋을 직접 읽어요 — Polars, DuckDB, pandas 모두 Hub Parquet을 네이티브로 스캔하고, 필터와 컬럼 선택을 스캔 안으로 밀어 넣어요. 그래서 단일 Job이 메모리나 디스크에 맞는 것보다 훨씬 많은 데이터를 처리할 수 있어요. 이 쿼리는 기본 CPU flavor에서 약 28GB의 Parquet을 4분 정도에 요약해요:
import polars as pl
agg = (
pl.scan_parquet("hf://datasets/HuggingFaceFW/fineweb-edu/sample/10BT/*.parquet")
.filter(pl.col("int_score") >= 4)
.group_by("int_score")
.agg(pl.len().alias("docs"), pl.col("token_count").sum().alias("tokens"))
.sort("int_score")
.collect()
)
print(agg)
이런 스캔은 메모리 바운드가 아니라 네트워크 바운드이고, 엔진마다 원격 읽기를 병렬화하는 정도가 달라서 시간은 라이브러리별로 달라져요. 실질적인 결론 두 가지: 긴 스캔에는 기본 30분보다 긴 --timeout을 설정하고, 병렬로 실행되는 여러 Job에 작업을 나눠 더 빠르게요. 라이브러리별 예시는 Polars, DuckDB, pandas, 그리고 hf://로 버킷을 읽는 법(HfFileSystem 거침)은 Python 데이터 도구를 참고해요.
Dataset, model, 또는 bucket 마운트
-v / --volume으로 저장소나 버킷을 로컬 경로로 Job에 마운트해요; 코드가 읽을 때 파일이 네트워크를 통해 지연 로딩되므로, 로컬 파일을 읽는 어떤 도구든 바로 동작해요:
hf jobs uv run --flavor cpu-upgrade \
-v hf://datasets/HuggingFaceFW/fineweb-edu:/mnt/data \
process.py
# /// script
# dependencies = ["polars"]
# ///
# process.py — the mounted repo is just a directory of files
from pathlib import Path
import polars as pl
for path in Path("/mnt/data/sample/10BT").glob("*.parquet"):
shard = pl.read_parquet(path)
... # process one shard at a time, write results out
마운트는 파일을 통째로 소비할 때 — 모델 가중치, 오디오·이미지 파일, 아카이브 — 또는 도구가 파일 경로만 받을 때 자연스러운 선택이에요. 대규모 다중 파일 Parquet 스캔의 경우, hf://에서 직접 쿼리하는 게 마운트로 스캔하는 것보다 보통 몇 배 더 빨라요.
데이터셋과 모델은 읽기 전용으로 마운트되고, 버킷은 읽기-쓰기라서 결과 저장에 좋은 곳이에요. 전체 -v 문법은 Configuration, 세부사항은 버킷 접근 패턴을 참고해요.
[!TIP] 마운트를 통해 읽은 파일은 Job의 임시 디스크에 캐시되므로, 지연 로딩(한 번에 한 파일)하면 사용량을 작게 유지할 수 있어요.
hf jobs uv run으로 로컬 스크립트를 실행하면 스크립트 디렉토리가/data에 마운트되니, 데이터는 다른 곳(예:/mnt/data)에 마운트해요.
결과 저장
임시 디스크는 Job 이후에 남지 않으므로, 보관하고 싶은 것은 무엇이든 읽기-쓰기로 마운트된 Storage Bucket에 쓰거나 Hub에 데이터셋으로 push해요. DuckDB는 hf://로 원본을 필터링하고 일치하는 항목을 마운트된 버킷에 한 번의 아웃오브코어 쿼리로 곧바로 쓸 수 있어서, 결과가 메모리에 맞아야 할 필요가 없어요:
hf jobs uv run --flavor cpu-upgrade --timeout 1h \
-v hf://buckets/username/my-output:/mnt/out \
filter.py
# /// script
# dependencies = ["duckdb"]
# ///
# filter.py — scan ~28 GB of Parquet, keep only the matching rows
import duckdb
duckdb.sql(
"""
COPY (
SELECT text, url, token_count
FROM 'hf://datasets/HuggingFaceFW/fineweb-edu/sample/10BT/*.parquet'
WHERE int_score >= 4 AND token_count >= 4000
) TO '/mnt/out/result.parquet' (FORMAT parquet)
"""
)
버킷 마운트 경로 아래에 쓴 파일은 Job이 끝난 뒤에도 유지돼요. 처리된 데이터셋을 게시하려면 Dataset.push_to_hub를 사용해요.
작업 예시: 다운로드 없이 Common Crawl 쿼리하기
Common Crawl은 아카이브를 버킷 commoncrawl/commoncrawl에 미러링해요 — 수백 TB. 하나의 WET(평문) shard를 hf://에서 바로 스트리밍하고, 파싱하고, DuckDB로 쿼리해요; gzip이 순차적으로 읽히고 일찍 중지되므로 몇 MB만 이동해요:
# /// script
# requires-python = ">=3.11"
# dependencies = ["huggingface_hub>=1.9", "fastwarc>=0.15", "duckdb>=1.0"]
# ///
import duckdb
from fastwarc.warc import ArchiveIterator, WarcRecordType
from huggingface_hub import hffs
WET = (
"buckets/commoncrawl/commoncrawl/crawl-data/CC-MAIN-2026-17/"
"segments/1775805908305.14/wet/"
"CC-MAIN-20260410081153-20260410111153-00000.warc.wet.gz"
)
rows = []
with hffs.open(WET, "rb") as f:
for rec in ArchiveIterator(f, record_types=WarcRecordType.conversion):
lang = (rec.headers.get("WARC-Identified-Content-Language", "") or "und").split(",")[0]
rows.append((rec.headers.get("WARC-Target-URI", ""), lang, len(rec.reader.read())))
if len(rows) >= 5000:
break
con = duckdb.connect()
con.execute("CREATE TABLE wet(url VARCHAR, lang VARCHAR, n_chars BIGINT)")
con.executemany("INSERT INTO wet VALUES (?,?,?)", rows)
con.sql("SELECT lang, count(*) AS docs FROM wet GROUP BY lang ORDER BY docs DESC LIMIT 10").show()
hf jobs uv run cc_wet.py로 실행해요 — 기본 CPU flavor에서 약 1분 만에 완료되고 다음을 출력해요:
┌─────────┬───────┐
│ lang │ docs │
│ varchar │ int64 │
├─────────┼───────┤
│ eng │ 1974 │
│ zho │ 586 │
│ rus │ 434 │
│ jpn │ 244 │
│ … │ … │
└─────────┴───────┘
함께 보기
- Stream · Streaming datasets: 100× more efficient
- Pricing & hardware — flavor별 임시 디스크 · Configuration — 볼륨
- Storage Buckets · 접근 패턴 · 통합
더 알아보기 (Learn more)
- 데이터셋은 스트리밍,
hf://쿼리, 마운트 세 가지로 디스크보다 큰 데이터를 처리할 수 있어요. - 결과는 임시 디스크가 아니라 Storage Bucket에 써야 Job 이후에도 유지돼요.