C51 Distributional Reinforcement Learning implementation for 100-bridge fleet maintenance optimization.
Based on: "A Distributional Perspective on Reinforcement Learning" (Bellemare et al., PMLR 2017)
- Categorical distribution over return values (51 atoms)
- Distributional Bellman update with projection step
- Cross-entropy loss instead of MSE loss
- Support range:
[V_min, V_max]=[-100, +100] - Delta_z:
4.0(uniform spacing)
- ✅ Noisy Networks for exploration (no ε-greedy needed)
- ✅ Dueling DQN architecture
- ✅ Double DQN for target calculation
- ✅ Prioritized Experience Replay (PER)
- ✅ N-step Learning (n=3)
- ✅ AsyncVectorEnv for parallel training
- ✅ Mixed Precision Training (AMP)
# Output: Categorical distributions instead of Q-values
q_values, distributions = agent(state)
# q_values: [batch, n_bridges, n_actions] # Expected values
# distributions: [batch, n_bridges, n_actions, 51] # Probability distributionsArchitecture:
- Shared network:
[100] -> [512, 256] - Value stream:
[256] -> [128] -> [51](NoisyLinear) - Advantage stream:
[256] -> [128] -> [30600](NoisyLinear, 100×6×51)
# Project T_z = r + γ * z onto fixed support
# Cross-entropy loss: -sum(m * log(p))
loss = c51_distributional_loss(agent, target_net, s, a, r, s', done)Key Steps:
- Compute target distribution with projection
- Calculate cross-entropy between current and target distributions
- Update priorities using KL divergence
- Loss Function: Cross-entropy loss with distributional projection
- TD Errors: KL divergence for PER priority updates
- Q-Value Estimation: Expected value from distribution
pip install -r requirements.txtpython test_noisy_net.pyExpected output:
C51 DISTRIBUTIONAL DQN VERIFICATION (v0.8)
✓ NoisyLinear test PASSED!
✓ FleetC51 test PASSED!
✓ Training loop test PASSED!
ALL TESTS PASSED! ✓
# Full training (1000 episodes)
python train_markov_fleet.py --episodes 1000 --device cuda
# Quick test (100 episodes)
python train_markov_fleet.py --episodes 100 --device cudaTraining output:
MARKOV FLEET C51 DISTRIBUTIONAL DQN TRAINING (v0.8)
================================================================================
C51 Distribution (Bellemare et al., PMLR 2017):
N_atoms: 51
V_min: -100.0, V_max: 100.0
Delta_z: 4.00
Optimizations:
✓ C51 Distributional RL (categorical distribution)
✓ Cross-entropy loss with projection
✓ Noisy Networks (no ε-greedy needed!)
...
# Training curves
python visualize_markov_v08.py outputs_markov/models/markov_fleet_c51_final_1000ep.pt
# Training curves + C51 distributions
python visualize_markov_v08.py outputs_markov/models/markov_fleet_c51_final_1000ep.pt --plot-distmarkov-dqn-v08-c51/
├── train_markov_fleet.py # Main training script (C51)
│ ├── FleetC51 # C51 Distributional DQN
│ ├── c51_distributional_loss() # Cross-entropy loss
│ └── train_markov_fleet() # Training loop
│
├── test_noisy_net.py # C51 verification tests
├── visualize_markov_v08.py # Training visualization + C51 distributions
├── config.yaml # Hyperparameters (C51 params)
│
├── src/
│ ├── markov_fleet_environment.py # Fleet environment
│ └── fleet_environment_gym.py # Gym interface
│
└── outputs_markov/
├── models/ # Trained models
├── plots/ # Visualizations
└── logs/ # Training logs
network:
n_atoms: 51 # Number of atoms in distribution
v_min: -100.0 # Minimum return value
v_max: 100.0 # Maximum return value
# Support: z_i = v_min + i * delta_z, i = 0, ..., 50
# delta_z = (v_max - v_min) / (n_atoms - 1) = 4.0training:
num_episodes: 1000
learning_rate: 0.0015
batch_size: 64
buffer_capacity: 10000
target_sync_steps: 500
n_steps: 3 # N-step returnsfleet:
n_urban: 20 # Urban bridges (higher importance)
n_rural: 80 # Rural bridges
urban_importance: 1.5 # Reward weight multiplierInstead of learning
Bellman Update:
Projection:
Project
where:
-
$m_i$ : Projected target distribution -
$p_i$ : Current distribution
- Better approximation of return distribution (not just mean)
- Reduced instability from distributional Bellman
- Improved exploration from uncertainty in distribution
- Better credit assignment with full distribution
- Higher final rewards
- More stable training
- Better worst-case performance
- Faster convergence
- Episode Reward: Total fleet health improvement
- Episode Cost: Total maintenance spending (30 years)
- Cross-Entropy Loss: Distributional learning progress
# Training curves
python visualize_markov_v08.py <checkpoint>
# C51 distributions (per-action return distributions)
python visualize_markov_v08.py <checkpoint> --plot-dist
# Detailed distribution analysis
python analyze_c51_distribution.py <checkpoint> --save-dir <output_dir>Distribution Plot: Shows probability distribution over return values for each action at a sample state.
State-Dependent Distributions:

python test_noisy_net.pyTests:
- ✅ NoisyLinear layer functionality
- ✅ FleetC51 forward pass
- ✅ Distribution validity (sum to 1)
- ✅ Q-value calculation from distribution
- ✅ Training loop with C51 loss
1. Distribution doesn't sum to 1
- Issue: Numerical instability in softmax
- Fix: Added
torch.softmax()normalization
2. Projection produces NaN
- Issue: Invalid support range
- Fix: Clamp
t_zto[v_min, v_max]
3. Loss explodes
- Issue: Log of zero probability
- Fix: Add epsilon
log(p + 1e-8)
"A Distributional Perspective on Reinforcement Learning"
- Authors: Bellemare, Dabney, Munos
- Conference: PMLR 2017
- Key Idea: Learn return distribution instead of expected value
- Noisy Networks: Fortunato et al., ICLR 2018
- Dueling DQN: Wang et al., ICML 2016
- Double DQN: van Hasselt et al., AAAI 2016
- Prioritized Replay: Schaul et al., ICLR 2016
python train_markov_fleet.py \
--episodes 1000 \ # Number of episodes
--n-envs 4 \ # Parallel environments
--n-atoms 51 \ # C51 atoms
--v-min -100.0 \ # Min return value
--v-max 100.0 \ # Max return value
--lr 0.0015 \ # Learning rate
--batch-size 64 \ # Batch size
--buffer-size 10000 \ # Replay buffer
--target-sync 500 \ # Target network sync
--device cuda # cuda or cpu- ✅ C51 categorical distribution
- ✅ Distributional Bellman update with projection
- ✅ Cross-entropy loss
- ✅ Distribution visualization
- ✅ Noisy Networks for exploration
- ✅ Removed ε-greedy
- ✅ Dueling DQN + Double DQN
- ✅ Prioritized N-step Replay
- ✅ Standard DQN with ε-greedy
- ✅ Basic fleet environment
For questions or issues with C51 implementation:
- Check
test_noisy_net.pyfor debugging - Review
visualize_markov_v08.py --plot-distfor distribution visualization - Verify
config.yamlC51 parameters
- QR-DQN: Quantile regression for distribution learning
- IQN: Implicit quantile networks
- Rainbow: Combine all DQN improvements
- PPO/A3C: Actor-Critic methods
- Try different
n_atoms(21, 51, 101) - Adjust
v_minandv_maxbased on actual return range - Experiment with learning rate schedules
Happy training with C51 Distributional RL! 🚀





