Hub documentation

Traiter de grands jeux de données

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Traiter de grands jeux de données

Chaque Job dispose d’un espace disque local fixe, défini par son hardware flavor (la colonne Ephemeral Storage, également affichée par hf jobs hardware). Il n’est pas nécessaire de charger un jeu de données entier sur ce disque pour y travailler : les datasets et les Storage Buckets peuvent être lus directement depuis le Hub — en streaming, par requête ou par montage — de sorte qu’un seul Job peut traiter des données bien plus volumineuses que son disque. Cette page présente les options disponibles et le moment où il convient d’utiliser chacune d’entre elles.

Quelle approche ?

  • Tient sur le disque → un simple load_dataset(...) fonctionne tel quel (ou choisissez simplement une version plus grande — jusqu'à 1 To de disque éphémère).
  • Parcourir les lignes, traiter ou entraîner → stream it.
  • Scans filtrés ou avec colonnes élaguées → interrogez-le directement via hf:// avec Polars ou DuckDB.
  • Les outils qui s’attendent à des chemins de fichiers locaux → mount le dépôt et le lire de manière paresseuse.
  • Conserver les résultats → les écrire dans un Storage Bucket pour qu’ils survivent au Job.

Diffuser le jeu de données

La lecture en flux lit les exemples depuis le Hub au fur et à mesure que votre code les consomme — sans téléchargement, sans copie locale, et elle s’adapte aux jeux de données de plusieurs To. Les dernières versions l’ont rendue jusqu’à 100 fois plus efficace, atteignant des performances comparables à celles des SSD locaux lors de l’entraînement sur de nombreux workers :

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

Un jeu de données en flux est un IterableDataset prenant en charge le .filter(), le .map(), le .shuffle(buffer_size=...) et le .batch() paresseux, et peut être transmis directement à un DataLoader PyTorch ou à un Trainer pour entraîner sur des données plus volumineuses que le disque. Consultez le Guide des flux pour l’API complète, et Exemples et tutoriels pour des parcours d’entraînement de bout en bout sur des configurations mono-nœud ou distribuées.

Le streaming fonctionne également dans les configurations distribuées, en utilisant Spark :

import pyspark_huggingface

df = spark.read.format("huggingface").option("config", "sample-10BT").load("HuggingFaceFW/fineweb-edu")

Le dataframe Spark résultant est distribué : chaque worker lit en flux depuis son propre sous-ensemble de fichiers. Consultez la documentation Spark pour des exemples de lecture, d'écriture et de filtrage efficace des lignes et des colonnes.

Lire et filtrer sur hf://

De nombreuses bibliothèques de données lisent directement les jeux de données du Hub via des chemins hf:// — Polars, DuckDB et pandas analysent nativement les fichiers Parquet du Hub, en déléguant les filtres et la sélection de colonnes à l'analyse, ce qui permet à un seul Job de traiter bien plus de données que ce qui tient en mémoire ou sur disque. Cette requête résume environ 28 Go de Parquet en quatre minutes environ sur la saveur CPU par défaut :

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)

Les scans de ce type sont limités par le réseau plutôt que par la mémoire, et les moteurs diffèrent quant à la manière dont ils parallélisent les lectures distantes, ce qui fait varier les temps d’exécution selon la bibliothèque. Deux conséquences pratiques : définir --timeout au-dessus de la valeur par défaut de 30 minutes pour les scans longs, et répartir le travail sur plusieurs Jobs exécutés en parallèle pour aller plus vite. Consultez Polars, DuckDB et pandas pour des exemples par bibliothèque, et Python data tools pour la lecture de buckets via hf:// (ce qui passe par HfFileSystem).

Monter un jeu de données, un modèle ou un bucket

Montez un dépôt ou un bucket dans le Job en tant que chemin local avec -v / --volume ; les fichiers sont récupérés paresseusement sur le réseau au fur et à mesure que votre code les lit, de sorte que tout outil qui lit des fichiers locaux fonctionne sans modification :

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

Le montage est la solution naturelle lorsque les fichiers sont consommés dans leur intégralité — poids de modèle, fichiers audio ou image, archives — ou lorsqu’un outil n’accepte que des chemins de fichiers. Pour les scans Parquet multi-fichiers de grande taille, interroger directement via hf:// est généralement plusieurs fois plus rapide que de scanner via un montage.

Les jeux de données et les modèles sont montés en lecture seule ; les buckets sont en lecture-écriture, ce qui en fait un bon endroit pour enregistrer les résultats. Consultez Configuration pour la syntaxe complète de -v et les motifs d'accès aux buckets pour plus de détails.

Les fichiers lus via un montage sont mis en cache sur le disque éphémère du Job, de sorte que la lecture paresseuse (un fichier à la fois) maintient l’empreinte disque faible. Lors de l’exécution d’un script local avec hf jobs uv run, le répertoire du script est monté sur /data, il faut donc monter vos données ailleurs (par ex. /mnt/data).

Enregistrer les résultats

Le disque éphémère ne survit pas à la fin du Job, il est donc conseillé d’écrire tout ce que vous souhaitez conserver dans un Storage Bucket monté en lecture-écriture, ou de le pousser sur le Hub sous forme de jeu de données. DuckDB peut filtrer la source via hf:// et écrire les correspondances directement dans un bucket monté lors d’une seule requête hors mémoire, de sorte que le résultat n’a jamais besoin de tenir en mémoire :

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)
    """
)

Les fichiers écrits sous le chemin de montage du bucket persistent après la fin du Job. Pour publier un jeu de données traité à la place, utilisez Dataset.push_to_hub.

Exemple concret : interroger Common Crawl sans le télécharger

Common Crawl réplique son archive vers le bucket commoncrawl/commoncrawl — des centaines de To. Streamer un shard WET (texte brut) directement depuis hf://, le parser et l’interroger avec DuckDB ; seuls quelques Mo transitent car le gzip est lu séquentiellement et arrêté tôt :

# /// 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()

Lancez-le avec hf jobs uv run cc_wet.py — il se termine en environ une minute sur le profil CPU par défaut et affiche :

┌─────────┬───────┐
│  lang   │ docs  │
│ varchar │ int64 │
├─────────┼───────┤
│ eng     │  1974 │
│ zho     │   586 │
│ rus     │   434 │
│ jpn     │   244 │
│ …       │    …  │
└─────────┴───────┘

Voir aussi

Update on GitHub