DataTrove

repository·main·Indexed 25 days ago

https://github.com/huggingface/datatrove

A platform-agnostic HuggingFace library for processing, filtering, and deduplicating large-scale text data for LLM training datasets. It supports local execution, Slurm clusters, and Ray. Version 0.9.0 includes capabilities for synthetic data generation and vLLM inference benchmarking, allowing users to measure throughput and optimize configurations such as speculative decoding, quantization, and tensor parallelism.

Tokens
12.4K
Snippets
16
Records
57
Agent score
85%

What's inside datatrove

  1. Overview of vLLM Inference Benchmark tools

    main

    The vLLM Inference Benchmark suite provides tools to measure vLLM inference throughput across various models and configurations using SLURM. It consists of two primary scripts:

    • launch_experiments.py: Orchestrates the benchmark by reading a YAML configuration, expanding parameter sweeps (such as model, Tensor Parallelism (TP), or speculative decoding), and submitting the resulting jobs to SLURM.
    • analyze_results.py: Processes the output by scanning run directories, parsing throughput metrics from server logs, and generating CSV summaries including derived performance metrics.

    The benchmark relies on generate_data.py (located in the parent directory) as the core inference script.

  2. Understand DataTrove Terminology

    main

    To use DataTrove effectively, understand the following hierarchy and concepts:

    • Pipeline: A list of processing steps (readers, filters, writers, etc.).
    • Executor: The environment that runs a pipeline (e.g., Slurm, local machine, Ray).
    • Job: The execution of a pipeline on a specific executor.
    • Task: A unit of parallel execution within a job. A job is split into multiple tasks, where each task typically processes a shard of data.
    • Shard: A group of input files assigned to a single task.
    • File: An individual input file. Note that one file is processed by exactly one task; DataTrove does not automatically split single files into multiple parts. For maximum parallelism, use many medium-sized files rather than one large file.
    • Worker: A compute resource (e.g., a CPU core) that executes a task. If you have 50 cores, you can run a LocalPipelineExecutor with workers=50 to process 50 tasks simultaneously.
  3. Analyze vLLM Inference Benchmark Results

    main

    The vLLM inference benchmark provides performance metrics (tokens per second per GPU) for various models under different configurations. Results are typically obtained through a two-tier optimization sweep on 80GB H100 GPUs.

    Key Optimization Levers

    • Speculative Decoding (spec): Provides significant speedups for small models (e.g., ngram_6 or suffix_32).
    • Batch Size Tuning: Adjusting mns (max number of sequences) and mnbt (max number of tokens) is a consistent way to improve performance across model sizes.
    • Tensor Parallelism (tp): Tuning tp is particularly effective for MoE (Mixture of Experts) models.
    • GPU Utilization (gmu): Tuning the GPU utilization parameter can impact throughput for certain model scales.

    Environment Requirements

    To replicate or run these benchmarks, the following environment is used:

    • vLLM: 0.15.0
    • PyTorch: 2.9.1
    • Transformers: 4.57.6
    • DataTrove: 0.8.0
    • CUDA: 12.8
  4. Install datatrove with inference dependencies

    main

    To use the synthetic data generation features, install datatrove with the inference extra using uv:

    uv sync --extra inference

    Note: You must be logged into your Hugging Face account via hf auth login with a token that has write access, as the generation scripts create dataset repositories and upload data to the Hugging Face Hub.

  5. Create a DataTrove Pipeline

    main

    A pipeline is defined as a list of pipeline blocks. Each block takes a generator of Document objects as input and returns a generator of Document objects.

    Common block types include:

    • Readers: Read data from various formats and yield Document objects.
    • Writers: Save Document objects to disk or cloud.
    • Extractors: Extract text from raw formats (e.g., HTML).
    • Filters: Remove Document objects based on criteria.
    • Stats: Collect statistics on the dataset.
    • Tokens: Tokenize data or count tokens.
    • Dedup: Perform deduplication tasks.
    from datatrove.pipeline.readers import CSVReader
    from datatrove.pipeline.filters import SamplerFilter
    from datatrove.pipeline.writers import JsonlWriter
    
    pipeline = [
        CSVReader(
            data_folder="/my/input/path"
        ),
        SamplerFilter(rate=0.5),
        JsonlWriter(
            output_folder="/my/output/path"
        )
    ]
  6. Configure vLLM benchmark experiments via YAML

    main

    To run a benchmark, create a YAML configuration file. You can define fixed_args that apply to all experiments and an experiments list for specific sweeps. List values in args or fixed_args are automatically expanded into a Cartesian product (parameter sweeps).

    Key Configuration Sections:

    • script: Path to the inference script.
    • continue_on_failure: Boolean to determine if the orchestrator should proceed if a job fails.
    • fixed_args: Arguments applied to every experiment.
    • experiments: A list of experiment objects. Each can have a name and args. Experiment-level args override fixed_args.
    script: "examples/inference/generate_data.py"
    continue_on_failure: true
    
    fixed_args:
      qos: "high"
      time: "1:00:00"
      model-max-context: 2048
      max-tokens: 1024
      input-dataset-name: "simplescaling/s1K-1.1"
      input-dataset-split: "train"
      prompt-column: "question"
      output-dataset-name: "s1K-1.1-benchmark"
      output-dir: "data"
    
    experiments:
      - name: "Qwen3-4B"
        args:
          model-name-or-path: "Qwen/Qwen3-4B-Thinking-2507"
          tp: [1, 2, 4]  # Sweep over TP configurations
          speculative-config: [None]
  7. Generate synthetic data on a Slurm cluster

    main

    For large-scale production, distribute processing across multiple nodes using Slurm. The script manages a three-part job architecture:

    1. inference: A GPU array job that processes data using vLLM and writes Parquet shards to the HF Hub.
    2. monitor: A CPU job that updates the dataset card with a live progress bar and ETA.
    3. datacard: A CPU job that runs after successful inference to generate a final dataset card with token statistics.

    Use --tasks to control the Slurm array size and --workers to specify concurrent jobs.

    python examples/inference/generate_data.py \
        --input-dataset-name simplescaling/s1K-1.1 \
        --input-dataset-split train \
        --prompt-column question \
        --model-name-or-path Qwen/Qwen3-4B-Thinking-2507 \
        --output-dataset-name s1K-1.1-dataforge \
        --output-dir data \
        --workers 10 \
        --tasks 20 \
        --examples-per-chunk 50
  8. Configure multi-node parallelism with LocalPipelineExecutor

    main

    To distribute tasks across multiple machines using LocalPipelineExecutor, use local_tasks and local_rank_offset.

    Important: The tasks value (total tasks across all machines) must be identical on every machine to ensure input file distribution does not overlap.

    • tasks: Total tasks to be executed across all machines.
    • local_tasks: Number of tasks to be executed on this specific machine.
    • local_rank_offset: The rank of the first task to be executed on this machine (e.g., if machine 1 ran 250 tasks and machine 2 ran 150, machine 3's offset is 400).
  9. Generate synthetic data with InferenceRunner

    main

    To use synthetic data generation features, install inference extras using:

    uv sync --extra inference

    The InferenceRunner supports vLLM, SGLang, OpenAI-compatible HTTPS endpoints, and a local dummy server. It uses asynchronous batching to maintain high GPU utilization.

    Custom Rollouts

    A rollout is an async callable that receives a Document, a generate(payload) callback, and shared_context kwargs. You can orchestrate multiple sequential or parallel calls within a single rollout.

    • Set rollouts_per_document to run the same rollout multiple times per sample; results are stored in document.metadata["rollout_results"].

    Recoverable Generation

    • Checkpointing: Set checkpoints_local_dir and records_per_chunk to write documents to local chunk files. Failed tasks resume from the last finished chunk. Use ${chunk_index} in the output filename template.
    • Deduplication: When checkpointing is enabled, a sqlite-backed RequestCache deduplicates rollouts via payload hashes (requires xxhash and aiosqlite).
    • Error Handling: Set skip_bad_requests=True on InferenceRunner to ignore BadRequestError (e.g., context overflows) and continue processing.
    from datatrove.data import Document
    from datatrove.executor.local import LocalPipelineExecutor
    from datatrove.pipeline.inference.run_inference import InferenceConfig, InferenceRunner
    from datatrove.pipeline.writers import JsonlWriter
    
    async def simple_rollout(doc: Document, generate):
        payload = {"messages": [{"role": "user", "content": [{"type": "text", "text": doc.text}]}], "max_tokens": 2048}
        return await generate(payload)
    
    documents = [Document(text="What's the weather in Tokyo?", id=str(i)) for i in range(1005)]
    config = InferenceConfig(server_type="vllm", model_name_or_path="google/gemma-3-27b-it", rollouts_per_document=1, max_concurrent_generations=500)
    
    LocalPipelineExecutor(
        pipeline=[
            documents,
            InferenceRunner(
                rollout_fn=simple_rollout,
                config=config,
                skip_bad_requests=True,
                records_per_chunk=500,
                checkpoints_local_dir="/fsx/.../translate-checkpoints",
                output_writer=JsonlWriter("s3://.../final_output_data", output_filename="${rank}_chunk_${chunk_index}.jsonl"),
            ),
        ],
        logging_dir="/fsx/.../inference_logs",
        tasks=1,
    ).run()
  10. Use the s3 and local binaries for MinHash step 3

    main

    There are two versions of the tool available depending on your data location:

    1. s3: Reads and writes data directly to S3.
    2. local: Reads and writes data from/to the local filesystem.

    Both binaries accept the following command-line arguments:

    • --input-folder: The path to the input buckets.
    • --output-folder: The path where the results will be saved.
    • --total-files: The total number of files (tasks) from the previous step (e.g., if step 2 was run with 700 tasks, set this to 700).
    • --downloads: The number of concurrent downloads.
  11. Execute pipelines with different executors

    main

    Pipelines in DataTrove are platform-agnostic. You can run the same pipeline definition across different environments by using the appropriate PipelineExecutor. To execute a pipeline, call the .run() method on the executor instance.

    Common Executor Options

    All executors support these options:

    • pipeline: A list of the pipeline steps to be executed.
    • logging_dir: A directory where logs, statistics, and completion markers are saved. Do not reuse folders for different jobs to avoid overwriting data.
    • skip_completed (bool, default True): If True, DataTrove checks the ${logging_dir}/completions folder and skips tasks that have already finished. Set to False to disable this.
    • randomize_start_duration (int, default 0): Maximum seconds to delay the start of each task to prevent system overload from simultaneous starts.
    from datatrove.executor import LocalPipelineExecutor
    
    executor = LocalPipelineExecutor(
        pipeline=[...],
        logging_dir="logs/",
        tasks=10,
        workers=5
    )
    executor.run()