Packaging annotated datasets into final training data. Purpose is to have a repeatable process and well-packaged data to remove any data management in the training step.
The packer consists of the following tools:
oellm-package-data- Take source files and apply decontamination, PII-masking, and sampling. Each file in the source directory gets a correspondent file with the data processed. The tool is idempotent, if it fails, then run it again, and it will take of where it left. This is the main tool. The tool contain three sub tools:lint,sample,release(default), andmerge.oellm-collect-metrics- Collect and summarize metrics from a collection directory. This does also happens automatically when runoellm-package-datain modemerge.oellm-propella-structure- Structure Propella data based on source data structure. For each record in the source files, if its ID exists in the Propella data, it is written to the output. This arranges Propella data in the same oßrder as source data.oellm-propella-merge- If Propella data is to big to be in memoryoellm-propella-structurecan run on individual Propella-parquet files. Then use this tool to merge the results from all Propella-parquet files.
All tools read a file metadata.yaml containing metadata about the structure and processing of the data.
Here are steps to run packaging and reduce the number of files. Instructions are intended for Lumi and using latest Singularity container.
- Check out
https://github.com/OpenEuroLLM/training-data-packer - In
tools/slurmthere are scripts to schedule jobs with SLURM. - Lint your metadata to find errors in the declaration:
/scratch/project_465002530/containers/training-data-packer/oellm-package-data.sh --collection-dir /scratch/project_465002530/training/collection/flag/finepdfs-edu-1.0.0 --mode lint - Start pipline with package, merge, and tokenization
./tools/slurm/pipeline.sh /scratch/project_465002530/training/collection/flag/finepdfs-edu-1.0.0 0-10 0-38 0-38Change path to the dataset you want to process. Adjust the array size to the size of data. - Check logs and metrics for each step.
- Move logs from
logsdirectory within the directory where you checked out the code into/scratch/project_465002530/training/collection/flag/log/release
The two first steps are a one time operation or when you update the packager.
A dataset directory managed by the packager contains input sources, annotations, configuration, and generated outputs. The directories relate to each other as follows:
dataset-directory/
├── metadata.yaml # Dataset configuration, schema mappings, and pipeline rules
├── source/ # Original raw data files (e.g., .jsonl.gz), arranged in hierarchical subdirectories
├── open-privacy-filter/ # PII annotation files mirroring source/ paths (documents PII spans to mask). Output from open-privacy-filter
├── nemo-curator/ # Contamination annotation files mirroring source/ paths (documents benchmark-contaminated records to remove). Output from Nemo-Curator
├── propella-4b/ # Optional Propella quality score parquet files used for quality filtering
├── release-raw/ # Output of oellm-package-data, mode release: per-file processed data with PII masked, decontaminated, and sampled
├── release/ # Output of oellm-package-data, mode merge: consolidated and merged files ready for tokenization
└── sample/ # Output of oellm-package-data, mode sample: files sampled on quality using propella-4b and other quality metrics
-
Input & Annotations:
source/: The primary input data. Files are typically organized by domain, language, or partition.open-privacy-filter/,propella-4b&nemo-curator/: Annotation companion directories. Every annotation file mirrors the relative path and filename of the corresponding file insource/.metadata.yaml: Coordinates the pipeline by specifying dataset name, source URLs, annotation file formats, sampling ratios, and shard parameters.
-
Packaging (
oellm-package-data):- Reads
source/, applies masking annotations frompii/and exclusions fromcontamination/, and writes the packaged files torelease-raw/. - Output files in
release-raw/preserve the directory hierarchy ofsource/and include hidden metric sidecars (.*.metrics.json).
- Reads
-
Consolidation (
oellm-package-data -m merge):- Reads files from
release-raw/and merges smaller chunks into target-sized files inrelease/, maintaining path semantics while reducing file handle overhead for tokenization and pretraining. - Compile metrics
- Reads files from
This project uses data produced from several other projects:
- Training data collection containing the metadata for OpenEuroLLM models
- Decontamination
- PII detection for baby cycle and flag cycle
After packaging is happening tokenization must happen before the training. This is done according to the description here.
You must have uv installed, see uv-homepage.
Check out this repo and do:
uv sync --dev
uv run pre-commit installTip is to do an uv sync evry time pulling from the repository.
The software is tested on Linux and MacOS. These are the focus platforms. It is not tested on Windows.
To run the tools in a container instead, see docs/containers.md. To run on LUMI, see docs/lumi.md.
The linter check the validity of metadata.yaml file and the directory structure for annoying errors.
Run the linter pefore packaging data, it will save you time.
The lint mode check:
- metadata.yaml follow schema
- Extensive checks on fields that are correlated to each other.
- Reads some data files, if exists, to verify alignment with
metadata.yaml.
To lint tests/resources/integration/non_partitioned run:
uv run oellm-package-data --collection-dir tests/resources/integration/flat_release -m lintSee docs/linter.md for a quick start and how to read the log messages.
The packager takes source files and applies decontamination, PII-masking, and sampling. The tool is idempotent, if it fails, then run it again, and it will take of where it left.
To package the data in tests/resources/integration/non_partitioned run:
uv run oellm-package-data --collection-dir tests/resources/integration/flat_releaseThe program checks if output files exist, if they exist new data is not regenerated.
oellm-package-data can run in different modes, --mode. This controls what
part of the process will happen. Default is release mode packaging the final
result and output into release-raw. The mode sample do just a sampling according
to the sample section in metadata.yaml, and output into sampledirectory.
The merger reduces the number of files but still keeps semantics in paths, like language or quality.
The merger uses the metadata.yaml in provided collection-directory.
Input directory is the subdirectory release-raw and output to release subdirectory.
Metrics computed during release are aggregated.
The merger run after oellm-package-data.
To run local:
uv run oellm-package-data -m merge --collection-dir ${COLLECTION_DIR} --workers 1This is only relevant if you want to annotate your data with propella annotations. It must run before packer.
The propella-structure tool filters source records based on IDs found in Propella parquet files. It reads
source files from collection_dir/source, looks up each record's ID in the propella directory, and
writes matching records to collection_dir/propella-4b. This is useful for structuring Propella data, according to
source data.
The Propella data is read into memory and requires about 50 times the size of the Propella parquet file. If
your Propella data does not fit into memory. Run ones per parquet file and then merge the result with
oellm-propella-merge. When pointing to an individual parquet file, the output directory will get an extra
level of directory named after the parquet file. This can be removed after oellm-propella-merge.
To run:
uv run oellm-propella-structure --collection-dir ${COLLECTION_DIR} --propella ${PROPELLA_DIR} --part ${part}To run oellm-propella-merge:
uv run oellm-propella-merge --collection-dir ${COLLECTION_DIR} --part ${part}The tokenizer turns JSONL files into Megatron indexed datasets (.bin and .idx) without needing a collection
directory or a metadata.yaml. Every document is wrapped with the tokenizer's <bos> and <eos> tokens.
Arguments:
input- a single file, or a directory where all*.jsonl.zstfiles below it are tokenized-t,--tokenizer- HuggingFace Hub repo id or local path to the tokenizer (required)--text- the field in each document holding the text, defaulttext-o,--output-dir- where to write the output, default the current directory-s,--slurm- only tokenize the files assigned to the current SLURM array task, see below
For a directory, the relative path of each input file is kept under the output directory, and the file extension
is dropped, e.g. input/deu_Latn/000.jsonl.zst becomes output/deu_Latn/000.bin and output/deu_Latn/000.idx.
A metrics file is written next to each dataset. Files that already have output are skipped, so the tool can be
rerun after a failure.
To run:
uv run oellm-tokenize ${INPUT_DIR} --tokenizer ${TOKENIZER} --text text --output-dir ${OUTPUT_DIR}To spread the files over a SLURM job array, add --slurm. Each array task tokenizes every
SLURM_ARRAY_TASK_COUNT-th file, starting at its SLURM_ARRAY_TASK_ID:
sbatch --array=0-9 --wrap "uv run oellm-tokenize ${INPUT_DIR} --tokenizer ${TOKENIZER} --output-dir ${OUTPUT_DIR} --slurm"Before checking in run tests, linting and formating:
uv run pre-commitWhen working with tokenizer it is valuable to ensure compatibility with Megatron-LM. These tests are skipped if megatron not installed. To install megatron do:
uv sync --extra megatron