This repository supports pre-training language models on the C4 dataset with M+Adam, an additive-multiplicative optimizer designed for persistent low-precision training.
M+Adam combines:
- an additive Adam-style branch for local refinement, sign changes, and zero revival; and
- a multiplicative exponent-space branch for scale-aware updates that remain effective when low-precision spacing becomes coarse.
The repository supports:
- BF16 master weights with BF16 compute;
- FP8 master-weight experiments with BF16 compute;
- FP8 master-weight experiments with Transformer Engine FP8 GEMMs;
- distributed training with
torchrun; - FlashAttention 2 and Liger Kernel;
- Weights & Biases logging;
- LLaMA configurations from 60M to larger model sizes.
.
├── main.py
│ └── Main entry point for BF16 master-weight training
│
├── train_fp8_true_master.py
│ └── Entry point for persistent FP8 master-weight experiments
│
├── fp8_block_ops.py
│ └── Blockwise FP8 quantization and dequantization utilities
│
├── optimizer_torch/
│ ├── __init__.py
│ └── m_adam.py
│ └── M+Adam optimizer implementation
│
├── llama_configs/
│ ├── llama_60m.json
│ ├── llama_130m.json
│ ├── llama_350m.json
│ └── ...
│
├── download_c4.py
│ └── C4 download/preparation utility
│
├── scripts/
│ └── m+Adam.sh
│ └── Example BF16 M+Adam launcher
│
├── requirements.txt
└── README.md
| Training mode | Main file | Supporting files |
|---|---|---|
| BF16 weights + BF16 compute | main.py |
optimizer_torch/m_adam.py |
| FP8 master weights + BF16 compute | train_fp8_true_master.py |
optimizer_torch/m_adam.py, fp8_block_ops.py |
| FP8 master weights + FP8 compute | train_fp8_true_master.py |
optimizer_torch/m_adam.py, fp8_block_ops.py, Transformer Engine |
| Example BF16 launcher | scripts/m+Adam.sh |
Calls main.py |
Install the base dependencies:
pip install -r requirements.txtThe project has been used with a Python 3.10, CUDA 12.x, and PyTorch 2.5.x environment. Exact package versions may need to be adjusted for the CUDA toolkit and GPU available on your system.
Optional acceleration packages:
flash-attnfor FlashAttention 2;liger-kernelfor Liger kernels;transformer-enginefor hardware FP8 GEMMs.
The BF16 experiments do not require Transformer Engine. Hardware FP8 compute requires a Hopper-class GPU such as H100 or H200.
The training programs accept local Hugging Face datasets saved with datasets.save_to_disk.
Use the repository utility as the starting point:
python download_c4.py --helpAfter preparing the train and validation datasets, define their paths:
export TRAIN_DATA=/path/to/c4_train
export VAL_DATA=/path/to/c4_validationBoth training entry points accept:
--local_path_train
--local_path_val
The current implementation uses the following command-line mapping:
| M+Adam branch | Paper notation | Command-line argument |
|---|---|---|
| Additive Adam branch | eta_a |
--lr |
| Multiplicative/exponent branch | eta_m |
--m_adam_lr_e |
For the 60M, 1x Chinchilla BF16 configuration:
eta_a = 8e-5
eta_m = 0.004
the correct command-line arguments are:
--lr 8e-5 \
--m_adam_lr_e 0.004Do not swap these two values.
Use main.py for BF16 weights with BF16 compute.
The following example trains LLaMA-60M for a 1x Chinchilla budget with two GPUs and a global batch size of 512:
torchrun --standalone --nproc_per_node=2 main.py \
--model_config llama_configs/llama_60m.json \
--local_path_train "${TRAIN_DATA}" \
--local_path_val "${VAL_DATA}" \
--batch_size 256 \
--total_batch_size 512 \
--max_length 256 \
--num_training_steps 10000 \
--scheduler_effective_training_steps 0 \
--optimizer m_adam \
--dtype bfloat16 \
--lr 8e-5 \
--m_adam_lr_e 0.004 \
--m_adam_beta1 0.87 \
--m_adam_beta2 0.98 \
--m_adam_eps 1e-20 \
--m_adam_weight_decay_m 0.1 \
--m_adam_weight_decay_e 0.01 \
--max_grad_norm 1.0 \
--warmup_steps 2000 \
--scheduler linear \
--min_lr_ratio 0.0 \
--m_adam_norm_type none \
--m_adam_sched_e logcosine \
--m_adam_total_steps_e 10000 \
--m_adam_warmup_steps_e 2000 \
--m_adam_min_lr_ratio_e 0.0 \
--m_adam_logcosine_alpha_e 3.0 \
--m_adam_tie_e_to_m false \
--eval_every 10000 \
--num_eval_tokens 10000000 \
--save_every 10000 \
--save_dir checkpoints/m_adam_bf16_60m_1x \
--workers 1 \
--seed 0 \
--use_flash_attention \
--use_liger_kernel \
--wandb_project "M+Adam" \
--wandb_entity "YOUR_WANDB_ENTITY" \
--wandb_tag "60M-mAdam-1xChinchilla-BF16" \
--name "m_adam_bf16_60m_1x"Remove the following flags when the corresponding packages are unavailable:
--use_flash_attention
--use_liger_kernelThe convenience launcher in scripts/m+Adam.sh can also be used as a starting point for BF16 experiments.
Use train_fp8_true_master.py for FP8 master-weight experiments.
The FP8 path uses fp8_block_ops.py to block-quantize selected LLaMA linear weights. Attention and MLP projection weights are included, while embedding, normalization, and language-model-head weights remain outside the FP8 storage path.
After every optimizer update, the selected weights are quantized blockwise and dequantized to the compute dtype for the next forward and backward pass.
This mode keeps model computation in BF16 while persistently quantizing selected weights to FP8 after each update.
torchrun --standalone --nproc_per_node=2 train_fp8_true_master.py \
--model_config llama_configs/llama_60m.json \
--tokenizer hf-internal-testing/llama-tokenizer \
--local_path_train "${TRAIN_DATA}" \
--local_path_val "${VAL_DATA}" \
--batch_size 256 \
--total_batch_size 512 \
--max_length 256 \
--num_training_steps 10000 \
--scheduler_effective_training_steps 0 \
--optimizer m_adam \
--dtype bfloat16 \
--lr 8e-5 \
--m_adam_lr_e 0.004 \
--m_adam_beta1 0.87 \
--m_adam_beta2 0.98 \
--m_adam_eps 1e-20 \
--m_adam_weight_decay_m 0.1 \
--m_adam_weight_decay_e 0.01 \
--max_grad_norm 1.0 \
--warmup_steps 2000 \
--min_lr_ratio 0.0 \
--m_adam_norm_type none \
--m_adam_sched_e logcosine \
--m_adam_total_steps_e 10000 \
--m_adam_warmup_steps_e 2000 \
--m_adam_min_lr_ratio_e 0.0 \
--m_adam_logcosine_alpha_e 3.0 \
--m_adam_tie_e_to_m false \
--fp8_true_master \
--fp8_format e5m2 \
--fp8_block_size 256 \
--fp8_pow2_scale \
--fp8_adam_state_dtype bfloat16 \
--eval_every 10000 \
--num_eval_tokens 10000000 \
--check_nonfinite_every 1 \
--plot_every 200 \
--workers 1 \
--seed 0 \
--use_flash_attention \
--use_liger_kernel \
--out_dir checkpoints/m_adam_fp8master_60m_1x \
--run_name m_adam_fp8master_60m_1x \
--wandb_project "M+Adam" \
--wandb_entity "YOUR_WANDB_ENTITY" \
--wandb_tag "60M-mAdam-1xChinchilla-FP8Master-BF16Compute" \
--wandb_name "m_adam_fp8master_60m_1x"Available formats:
e4m3
e5m2
Example:
--fp8_format e5m2The default experiment configuration uses:
--fp8_block_size 256The optimizer-side shadow parameter and M+Adam states can use BF16 or FP32:
--fp8_adam_state_dtype bfloat16or:
--fp8_adam_state_dtype float32Use BF16 for lower state memory and FP32 when additional numerical stability is preferred.
To use FP8 master weights together with Transformer Engine FP8 GEMMs, add the following flags to the previous command:
--te_fp8_gemm \
--te_recipe hybrid_delayed \
--te_amax_history 16 \
--te_margin 0A complete command is:
torchrun --standalone --nproc_per_node=2 train_fp8_true_master.py \
--model_config llama_configs/llama_60m.json \
--tokenizer hf-internal-testing/llama-tokenizer \
--local_path_train "${TRAIN_DATA}" \
--local_path_val "${VAL_DATA}" \
--batch_size 256 \
--total_batch_size 512 \
--max_length 256 \
--num_training_steps 10000 \
--scheduler_effective_training_steps 0 \
--optimizer m_adam \
--dtype bfloat16 \
--lr 8e-5 \
--m_adam_lr_e 0.004 \
--m_adam_beta1 0.87 \
--m_adam_beta2 0.98 \
--m_adam_eps 1e-20 \
--m_adam_weight_decay_m 0.1 \
--m_adam_weight_decay_e 0.01 \
--max_grad_norm 1.0 \
--warmup_steps 2000 \
--min_lr_ratio 0.0 \
--m_adam_norm_type none \
--m_adam_sched_e logcosine \
--m_adam_total_steps_e 10000 \
--m_adam_warmup_steps_e 2000 \
--m_adam_min_lr_ratio_e 0.0 \
--m_adam_logcosine_alpha_e 3.0 \
--m_adam_tie_e_to_m false \
--fp8_true_master \
--fp8_format e5m2 \
--fp8_block_size 256 \
--fp8_pow2_scale \
--fp8_adam_state_dtype bfloat16 \
--te_fp8_gemm \
--te_recipe hybrid_delayed \
--te_amax_history 16 \
--te_margin 0 \
--eval_every 10000 \
--num_eval_tokens 10000000 \
--check_nonfinite_every 1 \
--plot_every 200 \
--workers 1 \
--seed 0 \
--use_flash_attention \
--use_liger_kernel \
--out_dir checkpoints/m_adam_fp8master_fp8compute_60m_1x \
--run_name m_adam_fp8master_fp8compute_60m_1x \
--wandb_project "M+Adam" \
--wandb_entity "YOUR_WANDB_ENTITY" \
--wandb_tag "60M-mAdam-1xChinchilla-FP8Master-FP8Compute" \
--wandb_name "m_adam_fp8master_fp8compute_60m_1x"Hardware FP8 compute requires Transformer Engine and a Hopper-class GPU. The training script checks the GPU compute capability before enabling this mode.
Evaluation remains in BF16 by default. To evaluate under Transformer Engine FP8 autocast as well, add:
--te_fp8_evalSelect another model configuration through --model_config:
--model_config llama_configs/llama_130m.jsonor:
--model_config llama_configs/llama_350m.jsonWhen changing model size, token budget, precision regime, or global batch size, the learning rates, warmup, epsilon, and weight decay may need to be re-tuned.
The 60M configuration above is intended as a reproducible starting point, not a universal setting for every scale.
The effective global batch size is:
batch_size x number_of_GPUs x gradient_accumulation
For example:
batch_size = 256
number_of_GPUs = 2
total_batch_size = 512
gives:
gradient_accumulation = 1
Run on four GPUs by changing:
--nproc_per_node=4and choose a compatible --total_batch_size.
The BF16 training path supports:
- model checkpoints through
--save_dir; - checkpoint frequency through
--save_every; - validation through
--eval_every; - W&B logging through
--wandb_project,--wandb_entity, and--name.
The FP8 master-weight path stores:
- argument configuration;
- training and evaluation JSONL metrics;
- loss plots;
- W&B metrics;
- run-specific outputs under
--out_dir.
--lris the additive-branch learning rate.--m_adam_lr_eis the multiplicative/exponent-branch learning rate.- FP8 master-weight storage and FP8 matrix multiplication are separate options.
--fp8_true_masterenables persistent FP8 weight quantization.--te_fp8_gemmenables Transformer Engine FP8 compute.--te_fp8_evalis optional; evaluation is BF16 by default.- Remove FlashAttention or Liger flags when those packages are not installed or unsupported by the target GPU.
This repository builds on components and ideas from: