HuggingFace Datasets Tutorial

July 4, 2026 ยท View on GitHub

MetadataValue
LevelIntermediate
Runtime~30 min
PrerequisitesHuggingFace Quick Reference, Pipeline Tutorial
FormatPython + Jupyter

Overview

This tutorial provides a detailed guide to using HuggingFace Datasets with Datarax. You'll learn to work with different data modalities, configure advanced options like field filtering and shuffle buffers, and build production-ready training pipelines.

What You'll Learn

  1. Load different dataset types (images, text, audio) from HuggingFace Hub
  2. Configure field filtering with include_keys and exclude_keys
  3. Set up shuffling with proper buffer configuration for streaming datasets
  4. Build complete training pipelines with preprocessing and augmentation
  5. Handle streaming vs downloaded modes effectively
  6. Use named RNG streams for reproducible data loading

Coming from PyTorch?

If you're familiar with PyTorch's dataset ecosystem, here's how Datarax + HuggingFace compares:

PyTorchDatarax
datasets.load_dataset('mnist', split='train')HFEagerSource(HFEagerConfig(name='mnist', split='train'))
DataLoader(shuffle=True, num_workers=4)HFEagerSource with shuffle=True, seed=N
datasets.set_format('torch')Automatic JAX array conversion
Manual field selection in __getitem__include_keys / exclude_keys in config
IterableDataset for streamingfrom_hf(name, split, streaming=True) / HFStreamingSource

Key difference: Datarax uses JAX arrays and provides declarative configuration instead of imperative code.

Coming from TensorFlow?

TensorFlow tf.dataDatarax
tfds.load('mnist', split='train')HFEagerSource(HFEagerConfig(name='mnist', split='train'))
dataset.shuffle(buffer_size=1000)shuffle=True in config (O(1) index shuffle, no buffer)
dataset.take(1000)split='train[:1000]' syntax
dataset.skip(1000)split='train[1000:]' syntax
dataset.map(fn).filter(pred)Chain operators by passing them in the stages=[...] list

Files

Quick Start

# Run the Python script
python examples/integration/huggingface/02_hf_tutorial.py

# Or launch the Jupyter notebook
jupyter lab examples/integration/huggingface/02_hf_tutorial.ipynb

Part 1: Understanding HFEagerSource Configuration

HFEagerConfig provides extensive options for loading HuggingFace datasets.

Note: You can also use the factory function from_hf(name, split, ...) which auto-selects between eager and streaming modes.

Key Configuration Parameters

ParameterDescriptionDefaultExample
nameDataset identifier on HF HubRequired"mnist", "stanfordnlp/imdb"
splitWhich split to useRequired"train", "test[:1000]"
shuffleEnable shufflingFalseTrue for training
include_keysOnly include these fieldsNone{"image", "label"}
exclude_keysExclude these fieldsNone{"metadata", "id"}
seedInteger seed for shuffling420, 123

Auto-derived internals: stochastic and stream_name are not user-set knobs. When shuffle=True, the source uses the RNG stream named "shuffle" (with stochastic=True); otherwise stream_name defaults to None. Eager shuffling is an O(1) Feistel index shuffle, so there is no shuffle buffer in eager mode.

Basic Configuration Example

import jax
import jax.numpy as jnp
from flax import nnx

from datarax.pipeline import Pipeline
from datarax.operators import ElementOperator, ElementOperatorConfig
from datarax.sources import HFEagerConfig, HFEagerSource

# Basic configuration for MNIST
basic_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:1000]",  # Load first 1000 samples
)

basic_source = HFEagerSource(basic_config, rngs=nnx.Rngs(0))
print(f"Basic MNIST source: {len(basic_source)} samples")

Terminal Output:

Basic MNIST source: 1000 samples

Part 2: Field Filtering

Use include_keys or exclude_keys to control which fields are loaded and returned.

Benefits of Field Filtering

  • Reduces memory usage by excluding unnecessary fields
  • Simplifies downstream processing
  • Faster iteration when working with large metadata fields
  • Cleaner batch dictionaries

Include Keys Example

# Include only specific fields
filtered_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:500]",
    include_keys={"image", "label"},  # Only return these fields
)

filtered_source = HFEagerSource(filtered_config, rngs=nnx.Rngs(1))

# Check what fields are available
pipeline = Pipeline(source=filtered_source, stages=[], batch_size=1, rngs=nnx.Rngs(0))
batch = next(iter(pipeline))

print("Filtered fields:")
for key in batch.keys():
    print(f"  - {key}: shape={batch[key].shape}")

Terminal Output:

Filtered fields:
  - image: shape=(1, 28, 28, 1)
  - label: shape=(1,)

Exclude Keys Example

# Exclude metadata fields
exclude_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:500]",
    exclude_keys={"id"},  # Exclude ID field
)

exclude_source = HFEagerSource(exclude_config, rngs=nnx.Rngs(2))

Terminal Output:

Excluded 'id' field from dataset
Remaining fields: image, label

Part 3: Shuffling Configuration

Shuffling is essential for training ML models. In eager mode, HFEagerSource shuffles with an O(1) Feistel index shuffle - there is no shuffle buffer, so you only need shuffle=True and an integer seed.

Shuffle Modes

ModeWhen to UseConfiguration
No shuffleTesting, evaluationshuffle=False
Index shuffleEager (downloaded) datasetsshuffle=True, seed=N
Buffer shuffleStreaming datasets (large)HFStreamingConfig(shuffle=True, shuffle_buffer_size=N)

Eager Shuffle Example

# Configure shuffling with a reproducible seed
shuffle_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:2000]",
    shuffle=True,
    seed=42,  # Integer seed for Grain's index_shuffle
)

# Create source with explicit RNG for reproducibility
shuffle_source = HFEagerSource(
    shuffle_config,
    rngs=nnx.Rngs(42),
)

print("Shuffle configuration:")
print(f"  Seed: {shuffle_config.seed}")

Terminal Output:

Shuffle configuration:
  Seed: 42

Part 4: Streaming vs Downloaded Mode

Downloaded / Eager Mode (HFEagerSource)

  • Full dataset downloaded and cached locally
  • Random access to any sample
  • Faster iteration after initial download
  • Requires disk space
# Downloaded (eager) mode
downloaded_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:1000]",
)
downloaded_source = HFEagerSource(downloaded_config, rngs=nnx.Rngs(0))

print(f"Downloaded mode length: {len(downloaded_source)}")

Terminal Output:

Downloaded mode length: 1000

Streaming Mode (HFStreamingSource)

  • Data loaded on-the-fly from HuggingFace servers
  • No disk storage required
  • Ideal for large datasets (ImageNet, Common Crawl)
  • Cannot seek to specific indices
  • Dataset length may not be available
  • Shuffling uses a buffer (shuffle_buffer_size) rather than an index shuffle
from datarax.sources import HFStreamingConfig, HFStreamingSource

# Streaming mode with buffer-based shuffle
streaming_config = HFStreamingConfig(
    name="ylecun/mnist",
    split="train",
    streaming=True,
    shuffle=True,
    shuffle_buffer_size=1000,
)
streaming_source = HFStreamingSource(streaming_config, rngs=nnx.Rngs(0))

try:
    print(f"Streaming mode length: {len(streaming_source)}")
except (NotImplementedError, TypeError):
    print("Streaming mode length: N/A (not available in streaming)")

Terminal Output:

Streaming mode length: N/A (not available in streaming)

Tip: The from_hf(name, split, ...) factory auto-selects HFEagerSource for datasets under ~1GB and HFStreamingSource for larger ones. Pass streaming=True to force streaming regardless of size.

Mode Comparison Table

AspectStreamingDownloaded
Disk usageMinimalFull dataset
First iterationImmediateAfter download
Subsequent iterationsNetwork dependentFast (local)
Random accessNoYes
Length availableUsually noYes
Best for>10GB datasets<1GB datasets

Part 5: Building Complete Training Pipeline

Combine HFEagerSource with operators for a production-ready pipeline.

Define Preprocessing Operators

def normalize_image(element, key=None):
    """Normalize image to [0, 1] and ensure proper shape."""
    image = element.data.get("image")
    if image is not None and hasattr(image, "dtype"):
        # Normalize to [0, 1]
        normalized = image.astype(jnp.float32) / 255.0
        # Add channel dimension if needed (for grayscale)
        if normalized.ndim == 2:
            normalized = normalized[..., None]
        return element.update_data({"image": normalized})
    return element

def random_flip(element, key):
    """Randomly flip image horizontally."""
    flip_key, _ = jax.random.split(key)
    should_flip = jax.random.bernoulli(flip_key, 0.5)

    image = element.data.get("image")
    if image is not None:
        flipped = jax.lax.cond(
            should_flip,
            lambda x: jnp.flip(x, axis=1),  # Flip width axis
            lambda x: x,
            image,
        )
        return element.update_data({"image": flipped})
    return element

# Create operators
normalizer = ElementOperator(
    ElementOperatorConfig(stochastic=False),
    fn=normalize_image,
    rngs=nnx.Rngs(0),
)

flipper = ElementOperator(
    ElementOperatorConfig(stochastic=True, stream_name="flip"),
    fn=random_flip,
    rngs=nnx.Rngs(flip=42),
)

Terminal Output:

Created operators: normalizer (deterministic), flipper (stochastic)

Build Complete Pipeline

from datarax.operators.composite_operator import (
    CompositeOperatorConfig,
    CompositeOperatorModule,
    CompositionStrategy,
)

# Create composite augmentation
augmentation = CompositeOperatorModule(
    CompositeOperatorConfig(
        strategy=CompositionStrategy.SEQUENTIAL,
        operators=[normalizer, flipper],
        stochastic=True,
        stream_name="augment",
    ),
    rngs=nnx.Rngs(augment=999),
)

# Build the complete pipeline
train_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:5000]",
    shuffle=True,
    include_keys={"image", "label"},
    seed=42,
)

train_source = HFEagerSource(train_config, rngs=nnx.Rngs(0))

# Chain: Source -> Augmentation -> Output
training_pipeline = Pipeline(source=train_source, stages=[augmentation], batch_size=64, rngs=nnx.Rngs(0))

print("Training pipeline:")
print("  HFEagerSource(mnist) -> Normalize -> RandomFlip -> Output")
print("  Batch size: 64")

Terminal Output:

Training pipeline:
  HFEagerSource(mnist) -> Normalize -> RandomFlip -> Output
  Batch size: 64

Process Training Data

print("\nProcessing training batches:")
stats = {"batches": 0, "samples": 0}

for i, batch in enumerate(training_pipeline):
    if i >= 5:  # Process 5 batches for demo
        break

    image_batch = batch["image"]
    label_batch = batch["label"]

    stats["batches"] += 1
    stats["samples"] += image_batch.shape[0]

    if i == 0:  # Print details for first batch
        print(f"Batch {i}:")
        print(f"  Image: shape={image_batch.shape}, dtype={image_batch.dtype}")
        img_min, img_max = float(image_batch.min()), float(image_batch.max())
        print(f"  Image range: [{img_min:.3f}, {img_max:.3f}]")
        print(f"  Label: shape={label_batch.shape}")

print(f"\nProcessed {stats['batches']} batches, {stats['samples']} samples")

Terminal Output:

Processing training batches:
Batch 0:
  Image: shape=(64, 28, 28, 1), dtype=float32
  Image range: [0.000, 1.000]
  Label: shape=(64,)

Processed 5 batches, 320 samples

Part 6: Working with Different Datasets

HuggingFace Hub hosts thousands of datasets across different modalities.

Common Dataset Examples

DatasetTypeSplit SyntaxUse Case
mnistImagesplit="train"Computer vision basics
cifar10Imagesplit="train"Image classification
imagenet-1kImagesplit="train"Large-scale vision
stanfordnlp/imdbTextsplit="train"Sentiment analysis
squadQAsplit="train"Question answering
librispeech_asrAudiosplit="train.clean.100"Speech recognition

Split Syntax Examples

print("Split syntax examples:")
print("  'train' - Full training set")
print("  'train[:1000]' - First 1000 samples")
print("  'train[1000:2000]' - Samples 1000-2000")
print("  'train[:10%]' - First 10% of data")
print("  'train[10%:20%]' - Second 10% of data")
print("  'train+test' - Combined splits")

Terminal Output:

Split syntax examples:
  'train' - Full training set
  'train[:1000]' - First 1000 samples
  'train[1000:2000]' - Samples 1000-2000
  'train[:10%]' - First 10% of data
  'train[10%:20%]' - Second 10% of data
  'train+test' - Combined splits

Dataset Discovery

# List available datasets programmatically
from huggingface_hub import list_datasets

datasets_list = list(list_datasets(limit=100))
print(f"Datasets fetched: {len(datasets_list)}")
print(f"Example datasets: {datasets_list[:5]}")

# Get dataset info
from datasets import load_dataset_builder

builder = load_dataset_builder("mnist")
print(f"\nMNIST info:")
print(f"  Description: {builder.info.description[:100]}...")
print(f"  Features: {builder.info.features}")

Terminal Output:

Datasets fetched: 100
Example datasets: ['mnist', 'cifar10', 'imdb', 'squad', 'glue']

MNIST info:
  Description: The MNIST database of handwritten digits...
  Features: {'image': Image(shape=(28, 28, 1), dtype=uint8), 'label': ClassLabel(num_classes=10)}

Architecture Diagram

flowchart TB
    subgraph HF["HuggingFace Hub"]
        Hub[Dataset Repository<br/>85,000+ datasets]
    end

    subgraph Config["Configuration"]
        Cfg[HFEagerConfig<br/>name, split, shuffle<br/>include/exclude keys]
    end

    subgraph Source["HFEagerSource"]
        Stream{Streaming?}
        Download[Download & Cache]
        StreamLoad[Stream from Hub]
    end

    subgraph Pipeline["Datarax Pipeline"]
        Ops[Operators<br/>Normalize, Augment, etc.]
        Batch[Batching]
    end

    subgraph Output["Output"]
        JAX[JAX Arrays<br/>Ready for training]
    end

    Hub --> Cfg
    Cfg --> Stream
    Stream -->|No| Download --> Ops
    Stream -->|Yes| StreamLoad --> Ops
    Ops --> Batch --> JAX

    style HF fill:#e1f5ff
    style Config fill:#fff4e1
    style Source fill:#ffe1e1
    style Pipeline fill:#f0ffe1
    style Output fill:#e1ffe1

Results Summary

Configuration Best Practices

FeatureRecommendationRationale
Large datasetsHFStreamingSource (or from_hf(..., streaming=True))Avoid memory/disk issues
Trainingshuffle=True with a fixed seedEssential for SGD convergence
Streaming shuffleshuffle_buffer_size on HFStreamingConfigBetter shuffle quality when streaming
Field filteringUse include_keysReduce memory overhead
ReproducibilityFixed integer seedDeterministic index shuffle
Developmentsplit="train[:1000]"Fast iteration

Performance Characteristics

OperationStreamingDownloaded
First batch latency~2-5s~0.1s
ThroughputNetwork limitedDisk limited
Memory overheadMinimalFull dataset
ReproducibilityBuffer-basedPerfect

Common Patterns

# Pattern 1: Development (small subset, fast iteration)
dev_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train[:100]",
    shuffle=False,
)

# Pattern 2: Training (full data, shuffled)
train_config = HFEagerConfig(
    name="ylecun/mnist",
    split="train",
    shuffle=True,
    seed=42,
)

# Pattern 3: Large dataset streaming (forces HFStreamingSource)
large_source = from_hf(
    "imagenet-1k",
    "train",
    streaming=True,
    shuffle=True,
    rngs=nnx.Rngs(0),
)

# Pattern 4: Evaluation (deterministic, no shuffle)
eval_config = HFEagerConfig(
    name="ylecun/mnist",
    split="test",
    shuffle=False,
)

Next Steps