Discrete-diffusion (dLLM-style) infilling on top of RWKV-v7. Each logical block of
block_size tokens is laid out three times in a row inside a sample:
[b1_masked] [b2_masked == b1] [b3_clean]
Loss is computed only on b2's originally-masked positions. Because b1 sits in front
of b2 in the RNN time order, b2's hidden state has already absorbed every unmasked
token in b1, giving block-internal pseudo-bidirectional access. b3 (clean) refreshes
the RNN state with ground truth so the next logical block trains in parallel without
compounding errors.
uv sync # training deps (torch cu128, deepspeed, pytorch-lightning, ...)
uv sync --group data # additionally for data prep (datasets, ftfy, tokenizers, ...)
GPU node prerequisites for actually running training:
nvcc on PATH and a matching gcc.train/cuda/ are JIT-compiled at first model import.Two stages: HF β JSONL formatted with the RWKV-v7 G1x chat template, then JSONL β binidx.
# smoke test with 100 conversations
bash train/data_prep/build_tulu3_jsonl.sh --limit 100
# end-to-end (writes to train/data/ by default)
bash train/data_prep/build_tulu3_jsonl.sh
bash train/data_prep/build_glm_reasoning_jsonl.sh
bash train/data_prep/build_claude_reasoning_jsonl.sh
# Concat these three datasets
cat train/data/* > train/data/combined.jsonl
# Convert to binidx (multi-threaded, CPU-only)
bash train/data_prep/merge_and_binidx.sh data/combined.jsonl data/combined_binidx/combined
Output: train/data/concatenated.{bin,idx}. The _text_document suffix is
appended automatically by the json2binidx tool β train with
--data_file train/data/concatenated (no extension).
magic_primeMyDataset.__init__ enforces three constraints (see train/src/dataset.py):
is_prime(magic_prime)magic_prime % 3 == 20.9 < magic_prime / slot_count <= 1In diffusion mode, slot_count = number of valid docs after length filtering
(docs longer than raw_len = (ctx_len // (3*block_size)) * block_size are skipped at
runtime β the JSONL/binidx are NOT mutated). In flat-stream mode it's data_size // ctx_len.
The helper handles both:
# Diffusion mode β also reads the .idx for per-doc lengths and prints recommended
# --magic_prime / --my_exit_tokens / CLI snippet ready to paste into the launcher.
uv run python train/data_prep/find_magic_prime.py --diffusion \
--bin <DATA_DIR>/tulu3_text_document.bin \
--ctx_len 3072 --block_size 32
# Flat-stream (non-diffusion) pretraining
uv run python train/data_prep/find_magic_prime.py \
--bin <DATA_DIR>/tulu3_text_document.bin --ctx_len 4096
Re-run any time ctx_len, block_size, --diff_max_doc_tokens, or the dataset
itself changes β slot_count shifts and the prime needs to track it.
The reference launcher is train/demo-training-run-diffusion.sh.
Open it, plug in MAGIC_PRIME and EXIT_TOKENS from step 3 (the script refuses to
run with the placeholder zeros), then:
bash train/demo-training-run-diffusion.sh
Diffusion-mode CLI flags added on top of the upstream train.py:
| Flag | Default | Meaning |
|---|---|---|
--diffusion_mode | 0 | Set to 1 to switch from standard LM to triplet-diffusion training. |
--diff_block_size | 32 | Tokens per logical block. |
--diff_min_mask_ratio | 0.0 | Lower bound for the per-sample mask ratio r ~ Uniform(min, max). |
--diff_max_mask_ratio | 1.0 | Upper bound for r. |
--diff_pad_id | 65534 | Token used to pad the tail to ctx_len (EOS). Must differ from MASK. |
Constraints worth knowing:
ctx_len >= 3 * diff_block_size (asserted at startup). Pick ctx_len divisible by
3 * diff_block_size to avoid wasted tail padding (e.g. 3072 = 3 * 32 * 32).--vocab_size 65536 matches BlinkDL's published RWKV-v7 world ckpts. Diffusion
mode reuses id vocab_size - 1 = 65535 as MASK (one of the unused dummy slots
past the tokenizer's real vocabulary); vocab is not extended, so any standard
RWKV-v7 world ckpt loads as-is via --load_model.ignore_index; diffusion mode
falls back to F.cross_entropy(..., ignore_index=-100) (somewhat slower / more
VRAM, but correct). Toggle is automatic.Please refer to the inference README for details on running the model in inference mode and serving README for details on serving the model.
Please also refer to the evaluation README for details on evaluating the model's performance.
8 commits
Python
58.0%
Cuda
27.2%
C++
8.6%
Shell
5.9%
Discrete-diffusion (dLLM-style) infilling on top of RWKV-v7. Each logical block of
block_size tokens is laid out three times in a row inside a sample:
[b1_masked] [b2_masked == b1] [b3_clean]
Loss is computed only on b2's originally-masked positions. Because b1 sits in front
of b2 in the RNN time order, b2's hidden state has already absorbed every unmasked
token in b1, giving block-internal pseudo-bidirectional access. b3 (clean) refreshes
the RNN state with ground truth so the next logical block trains in parallel without
compounding errors.
uv sync # training deps (torch cu128, deepspeed, pytorch-lightning, ...)
uv sync --group data # additionally for data prep (datasets, ftfy, tokenizers, ...)
GPU node prerequisites for actually running training:
nvcc on PATH and a matching gcc.train/cuda/ are JIT-compiled at first model import.Two stages: HF β JSONL formatted with the RWKV-v7 G1x chat template, then JSONL β binidx.
# smoke test with 100 conversations
bash train/data_prep/build_tulu3_jsonl.sh --limit 100
# end-to-end (writes to train/data/ by default)
bash train/data_prep/build_tulu3_jsonl.sh
bash train/data_prep/build_glm_reasoning_jsonl.sh
bash train/data_prep/build_claude_reasoning_jsonl.sh
# Concat these three datasets
cat train/data/* > train/data/combined.jsonl
# Convert to binidx (multi-threaded, CPU-only)
bash train/data_prep/merge_and_binidx.sh data/combined.jsonl data/combined_binidx/combined
Output: train/data/concatenated.{bin,idx}. The _text_document suffix is
appended automatically by the json2binidx tool β train with
--data_file train/data/concatenated (no extension).
magic_primeMyDataset.__init__ enforces three constraints (see train/src/dataset.py):
is_prime(magic_prime)magic_prime % 3 == 20.9 < magic_prime / slot_count <= 1In diffusion mode, slot_count = number of valid docs after length filtering
(docs longer than raw_len = (ctx_len // (3*block_size)) * block_size are skipped at
runtime β the JSONL/binidx are NOT mutated). In flat-stream mode it's data_size // ctx_len.
The helper handles both:
# Diffusion mode β also reads the .idx for per-doc lengths and prints recommended
# --magic_prime / --my_exit_tokens / CLI snippet ready to paste into the launcher.
uv run python train/data_prep/find_magic_prime.py --diffusion \
--bin <DATA_DIR>/tulu3_text_document.bin \
--ctx_len 3072 --block_size 32
# Flat-stream (non-diffusion) pretraining
uv run python train/data_prep/find_magic_prime.py \
--bin <DATA_DIR>/tulu3_text_document.bin --ctx_len 4096
Re-run any time ctx_len, block_size, --diff_max_doc_tokens, or the dataset
itself changes β slot_count shifts and the prime needs to track it.
The reference launcher is train/demo-training-run-diffusion.sh.
Open it, plug in MAGIC_PRIME and EXIT_TOKENS from step 3 (the script refuses to
run with the placeholder zeros), then:
bash train/demo-training-run-diffusion.sh
Diffusion-mode CLI flags added on top of the upstream train.py:
| Flag | Default | Meaning |
|---|---|---|
--diffusion_mode | 0 | Set to 1 to switch from standard LM to triplet-diffusion training. |
--diff_block_size | 32 | Tokens per logical block. |
--diff_min_mask_ratio | 0.0 | Lower bound for the per-sample mask ratio r ~ Uniform(min, max). |
--diff_max_mask_ratio | 1.0 | Upper bound for r. |
--diff_pad_id | 65534 | Token used to pad the tail to ctx_len (EOS). Must differ from MASK. |
Constraints worth knowing:
ctx_len >= 3 * diff_block_size (asserted at startup). Pick ctx_len divisible by
3 * diff_block_size to avoid wasted tail padding (e.g. 3072 = 3 * 32 * 32).--vocab_size 65536 matches BlinkDL's published RWKV-v7 world ckpts. Diffusion
mode reuses id vocab_size - 1 = 65535 as MASK (one of the unused dummy slots
past the tokenizer's real vocabulary); vocab is not extended, so any standard
RWKV-v7 world ckpt loads as-is via --load_model.ignore_index; diffusion mode
falls back to F.cross_entropy(..., ignore_index=-100) (somewhat slower / more
VRAM, but correct). Toggle is automatic.Please refer to the inference README for details on running the model in inference mode and serving README for details on serving the model.
Please also refer to the evaluation README for details on evaluating the model's performance.
8 commits
Python
58.0%
Cuda
27.2%
C++
8.6%
Shell
5.9%