What Is DoReMi and How to Use It in Nanotron
DoReMi is a training algorithm in the huggingface/nanotron framework that dynamically optimizes domain mixing weights by monitoring per-domain losses from a reference model, automatically focusing training on the hardest data domains.
DoReMi (Domain Reweighting with Minimized Loss) eliminates the need for manual data mixture tuning when training large language models on heterogeneous corpora. Unlike static sampling strategies, this implementation—located under examples/doremi/—adjusts sampling probabilities on-the-fly based on real-time difficulty estimates, ensuring optimal allocation of model capacity across domains.
How DoReMi Optimizes Domain Weights
The algorithm treats per-domain loss as a proxy for domain difficulty. During training, DoReMi maintains a reference model trained with uniform sampling to establish baseline loss expectations. The DoReMiTrainer then compares current training losses against this reference, increasing sampling weights for domains with higher relative loss (indicating greater difficulty) while applying smoothing to prevent erratic updates.
This reweighting mechanism relies on distributed communication across data-parallel groups via dist.all_reduce with the AVG operation to aggregate loss statistics. The smoothing_param and step_size hyperparameters control the convergence speed of the weight vector, preventing over-correction while allowing the distribution to adapt to the model's evolving capabilities.
Core Components of the DoReMi Implementation
The Nanotron implementation consists of four coordinated components that handle configuration, state management, and distributed training orchestration.
DoReMiContext
The DoReMiContext class, defined in [examples/doremi/doremi/doremi_context.py](https://github.com/huggingface/nanotron/blob/main/examples/doremi/doremi/doremi_context.py#L12-L38), serves as the central state container. It stores the list of domain names, the current weight vector (defaulting to uniform 1/num_domains), and the complete history of weight updates. Key methods include add_weight_with_history for logging updates and get_domain_name for domain resolution.
DoReMiConfig
Configuration validation and parsing are handled by DoReMiConfig in [examples/doremi/doremi/config.py](https://github.com/huggingface/nanotron/blob/main/examples/doremi/doremi/config.py#L9-L38). This dataclass ingests user-provided domain lists, optional initial weights, smoothing parameters, step sizes, and the critical ref_model_resume_checkpoint_path pointing to the reference model checkpoint. It validates that domain names and reference checkpoints are present before training begins.
DoReMiTrainer
The proxy training loop is implemented in DoReMiTrainer, a subclass of DistributedTrainer located at [examples/doremi/doremi/trainer.py](https://github.com/huggingface/nanotron/blob/main/examples/doremi/doremi/trainer.py#L36-L77). This trainer loads the reference model, synchronizes weight tensors across ranks, logs per-domain losses, and saves weight history after each step. It optionally streams metrics to Weights & Biases for monitoring. The core update logic invokes self.doremi_context.add_weight_with_history after aggregating losses from all data-parallel workers.
ReferenceTrainer
To generate the required baseline, ReferenceTrainer ([examples/doremi/doremi/trainer.py](https://github.com/huggingface/nanotron/blob/main/examples/doremi/doremi/trainer.py#L200-L207)) executes the initial training phase using fixed uniform or user-specified sampling distributions. This creates the checkpoint that DoReMiTrainer loads for loss comparison.
DistributedSamplerForDoReMi
The data pipeline integrates a specialized sampler in [examples/doremi/doremi/dataloader.py](https://github.com/huggingface/nanotron/blob/main/examples/doremi/doremi/dataloader.py#L131-L166) that respects the current domain_weights when constructing batches. During the proxy run, sampling remains uniform for logging purposes, while the weights are recorded for subsequent analysis and application.
Step-by-Step Workflow for DoReMi Training
Implementing DoReMi requires a two-stage training process followed by weight extraction.
-
Prepare the Dataset: Tokenize your corpus ensuring each sample contains a
domain_idscolumn mapping to a domain index. Organize files according to the folder layout specified in [examples/doremi/README.md](https://github.com/huggingface/nanotron/blob/main/examples/doremi/README.md). -
Configure DoReMi: Create a YAML configuration listing domain names, smoothing parameters, step size, and the reference checkpoint path.
-
Train the Reference Model: Execute
train_reference.pywith uniform sampling to generate the baseline checkpoint. -
Run Proxy Training: Launch
train_doremi.pyto perform the dynamic reweighting phase. The trainer outputsdoremi_domain_weights_<step>.ptfiles containing the weight history. -
Derive Final Weights: Load the saved history files, average the stored
domain_weightstensors across steps, and use these optimized values for training your full-scale model.
Configuration and Code Examples
Minimal DoReMi Configuration
The proxy training requires a configuration file specifying domains and the reference checkpoint:
# examples/doremi/configs/config_280m_llama_proxy.yaml
doremi:
domain_names: "domain_a,domain_b,domain_c"
smoothing_param: 0.001
step_size: 1.0
ref_model_resume_checkpoint_path: "checkpoints/reference-280m"
Executing the Two-Stage Training
Train the reference model first, then run the DoReMi proxy:
# Stage 1: Train reference model with uniform sampling
CUDA_DEVICE_MAX_CONNECTIONS=1 torchrun --nproc_per_node=4 \
examples/doremi/train_reference.py --config-file examples/doremi/configs/config_280m_llama.yaml
# Stage 2: Train proxy model with dynamic reweighting
CUDA_DEVICE_MAX_CONNECTIONS=1 torchrun --nproc_per_node=4 \
examples/doremi/train_doremi.py --config-file examples/doremi/configs/config_280m_llama_proxy.yaml
Processing Saved Domain Weights
Extract and average the optimized weights from the checkpoint history:
import torch
# Load history checkpoint written by DoReMiTrainer
history_path = "checkpoints/doremi/proxy-280m-llama/doremi_domain_weights_100000.pt"
history = torch.load(history_path) # List of dicts with "step" and "weight" keys
# Compute average weight vector across training steps
total = sum(entry["weight"] for entry in history)
avg_weights = total / len(history) # Tensor of shape (num_domains,)
print("Optimized domain weights:", avg_weights)
Applying Optimized Weights to Large-Scale Training
Use the averaged weights in your production configuration:
# examples/doremi/configs/config_2.8b_llama_with_tuned_weights.yaml
doremi:
domain_names: "domain_a,domain_b,domain_c"
domain_weights: "0.42,0.35,0.23" # Derived from avg_weights.tolist()
smoothing_param: 0.001
step_size: 1.0
ref_model_resume_checkpoint_path: "checkpoints/reference-2.8b"
Summary
- DoReMi dynamically adjusts domain sampling probabilities based on per-domain loss differentials against a reference model.
- The implementation requires two distinct training phases: reference training (uniform sampling) and proxy training (dynamic reweighting via
DoReMiTrainer). - Core classes include
DoReMiContextfor state management,DoReMiConfigfor validation, and specialized trainers orchestrating the pipeline inexamples/doremi/. - Optimized weights are derived by averaging the saved weight history from proxy training checkpoints.
- The technique integrates with Nanotron's distributed training infrastructure, utilizing
dist.all_reducefor cross-rank loss aggregation.
Frequently Asked Questions
What is the purpose of the reference model in DoReMi?
The reference model provides a baseline loss expectation for each domain under uniform sampling. By comparing the proxy model's current per-domain loss against this reference, DoReMi identifies which domains are relatively harder or easier for the model to learn, enabling data-driven reweighting rather than relying on manual heuristics.
How does DoReMi determine which domains are "harder"?
Domains exhibiting higher loss relative to the reference model are considered harder and receive increased sampling weights. The algorithm aggregates these loss differentials across all data-parallel ranks using dist.all_reduce, then applies a smoothed update rule controlled by the step_size and smoothing_param configuration parameters to gradually shift the sampling distribution.
Can I use DoReMi with existing tokenized datasets?
Yes, provided your dataset includes a domain_ids column that maps each sample to a specific domain index. The DistributedSamplerForDoReMi in examples/doremi/doremi/dataloader.py uses these indices to respect the current weight vector when constructing training batches. Refer to the folder layout requirements in examples/doremi/README.md for specific formatting guidelines.
Where are the optimized domain weights stored during training?
The DoReMiTrainer saves weight history to checkpoint files named doremi_domain_weights_<step>.pt at regular intervals. These files contain serialized dictionaries with step numbers and weight tensors, which you can load using torch.load() to compute average weights for subsequent training runs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →