Single-file Deterministic RL Training with vLLM & TorchTitan
October 24, 2025 · View on GitHub
This is a project demonstrating simple, readable RL. It achieves bitwise-deterministic reinforcement learning for language models using vLLM for generation and TorchTitan for training.
The single file is here.
Overview
This project demonstrates:
- vLLM rollouts with TorchTitan training converting and updating weights between them
- Bitwise-deterministic training using TorchTitan with vLLM-compatible kernels
- Exact matching between generation and training forward passes (0.0000000 difference)
- Simple RL training with GRPO-style policy gradients
Quick Start
1. Install Dependencies
Install vLLM nightly:
pip install https://vllm-wheels.s3.us-west-2.amazonaws.com/nightly/vllm-1.0.0.dev-cp38-abi3-manylinux1_x86_64.whl
Install TorchTitan from the custom fork:
git clone -b shim https://github.com/bwasti/torchtitan.git
cd torchtitan
pip install -e .
cd ..
Install other dependencies:
pip install torch safetensors huggingface_hub transformers tensorboard
2. Run the training script!
VLLM_BATCH_INVARIANT=1 VLLM_FLASH_ATTN_VERSION=3 python simple_rl.py
3. Monitor Training with TensorBoard
# In a separate terminal
tensorboard --logdir=./runs
# Then open: http://localhost:6006
Training Output
During training, you'll see determinism checks at each step:
================================================================================
Forward Pass Determinism Check (Training vs Generation)
================================================================================
Token 1: ID=12095 [✓]
vLLM gen (bf16): -0.6266400814 [hex: bf20]
Titan train (bf16): -0.6266400814 [hex: bf20]
Δ (fp32 math): 0.000000000000000
Token 2: ID=25445 [✓]
vLLM gen (bf16): -1.2345678901 [hex: bf9d]
Titan train (bf16): -1.2345678901 [hex: bf9d]
Δ (fp32 math): 0.000000000000000
Token 3: ID=11234 [✓]
vLLM gen (bf16): -0.9876543210 [hex: bf7d]
Titan train (bf16): -0.9876543210 [hex: bf7d]
Δ (fp32 math): 0.000000000000000
Bitwise Summary over 20 tokens:
Bitwise identical (bf16): True
Different tokens: 0 / 20
✓✓✓ EXACT BITWISE MATCH!
================================================================================
Step 1/100:
Loss: 2.3456
Reward Mean: 0.15
PG Loss: 1.234
KL Div: 0.012
Entropy: 4.567
Interpretation:
- [✓]: Token logprobs match bitwise between generation and training
- [hex: ...]: Raw bfloat16 bit representation for verification
- Bitwise identical: Uses native bf16 comparison (no fp32 conversion)
- ✓✓✓ EXACT BITWISE MATCH: Training uses exact same forward pass as generation
TensorBoard Metrics
The training logs the following metrics to TensorBoard:
Loss Metrics
loss: Total training loss (policy gradient + KL + entropy)pg_loss: Policy gradient losskl_div: KL divergence between current and reference policyentropy: Policy entropy (higher = more exploration)
Reward Metrics
reward_mean: Average reward across samplesreward_std: Reward standard deviation
Policy Metrics
ratio_mean: Mean importance sampling ratio (exp(logprob_new - logprob_old))ratio_clipped_frac: Fraction of ratios clipped by PPO (high = policy changing too fast)
Weight Deltas
weight_delta/{layer}/magnitude: L2 norm of weight changes per layerweight_delta/{layer}/relative_change: Relative weight change (normalized by weight magnitude)
Determinism Verification
- Per-step determinism checks show exact bitwise match between generation and training
How It Works
1. Rollouts with vLLM
# Generate samples using vLLM (fast!)
vllm_engine = VLLMRolloutEngine(model_path)
completions, logprobs = vllm_engine.generate(prompts)
2. Training with TorchTitan
# Train using TorchTitan (exact same forward pass!)
model = Qwen3VLLMCompatModel(model_args) # Uses vLLM's kernels
loss = compute_policy_gradient_loss(model, completions, advantages)
loss.backward()
optimizer.step()
Technical Details
Bitwise Determinism Achieved By:
-
vLLM's exact kernels:
SiluAndMul: Custom CUDA kernel (torch.ops._C.silu_and_mul)RMSNorm: Custom Triton kernelmatmul_persistent: Deterministic matrix multiplication- Flash Attention with
num_splits=1
-
Batch-invariant mode:
VLLM_BATCH_INVARIANT=1: Enables deterministic vLLM operationsbatch_invariant_backward.py: Adds gradient support
-
Merged projections:
- vLLM-compat uses
gate_up_proj = [w1; w3](merged) - More numerically stable than separate w1/w3
- Exactly matches vLLM's architecture
- vLLM-compat uses
Weight Format
Standard TorchTitan:
layers.0.feed_forward.w1.weight [hidden_dim, dim]
layers.0.feed_forward.w2.weight [dim, hidden_dim]
layers.0.feed_forward.w3.weight [hidden_dim, dim]
vLLM-Compat:
layers.0.feed_forward.gate_up_proj.weight [hidden_dim * 2, dim] # [w1; w3] merged
layers.0.feed_forward.down_proj.weight [dim, hidden_dim]
Model Support
Currently tested with:
- Qwen3-1.7B ✅
Should work with other Qwen3 models with the same architecture.
License
BSD 3-Clause License (same as TorchTitan and vLLM)