Reinforcement Learning Best Practices
Overview
This skill provides comprehensive guidance for implementing reinforcement learning in Python using the modern ecosystem (Gymnasium >= 1.0, Stable-Baselines3 >= 2.x). Gymnasium has replaced OpenAI Gym as the standard environment interface. Stable-Baselines3 (SB3) is recommended for prototyping, RLlib for production/distributed training, and CleanRL for research.
When to Use
- Building RL agents for discrete or continuous control tasks
- Creating custom simulation environments
- Tuning hyperparameters for RL algorithms
- Debugging training issues (reward curves, policy collapse, numerical instability)
- Deploying trained policies to production
Library Selection
| Library |
Best For |
Ease |
Flexibility |
Production |
| Stable-Baselines3 |
Prototyping, learning |
High |
Medium |
Good |
| RLlib |
Production, distributed |
Medium |
High |
Excellent |
| CleanRL |
Research, understanding |
High |
Low |
Poor |
| TorchRL |
Custom implementations |
Low |
Highest |
Good |
Algorithm Decision Tree
Start
|
v
Action space type?
|
+-- Discrete --> Sample efficiency critical?
| |
| +-- Yes --> DQN (or Double/Dueling DQN)
| +-- No --> Stability critical?
| |
| +-- Yes --> PPO
| +-- No --> A2C (faster iterations)
|
+-- Continuous --> Sample efficiency critical?
|
+-- Yes --> SAC (auto entropy) or TD3
+-- No --> PPO (more stable, less efficient)
Quick Selection Table:
| Scenario |
Recommended |
Why |
| Discrete actions, getting started |
PPO |
Stable, good defaults |
| Continuous control |
SAC or TD3 |
Sample efficient, handles continuous well |
| Sample efficiency critical |
SAC, DQN |
Off-policy, reuses experience |
| Stability critical |
PPO |
Trust region, consistent |
| High-dimensional obs (images) |
PPO + CNN |
Handles visual input well |
| Fast iteration needed |
A2C |
Simpler, faster per update |
Quick Start with Stable-Baselines3
Basic Training
from stable_baselines3 import PPO
from stable_baselines3.common.env_util import make_vec_env
# Create vectorized environment (4 parallel envs)
env = make_vec_env("CartPole-v1", n_envs=4)
# Initialize and train
model = PPO("MlpPolicy", env, verbose=1)
model.learn(total_timesteps=100_000)
# Save and load
model.save("ppo_cartpole")
loaded_model = PPO.load("ppo_cartpole")
# Evaluate
obs = env.reset()
for _ in range(1000):
action, _ = loaded_model.predict(obs, deterministic=True)
obs, reward, done, info = env.step(action)
Custom Environment Template
import gymnasium as gym
from gymnasium import spaces
import numpy as np
class CustomEnv(gym.Env):
metadata = {"render_modes": ["human", "rgb_array"]}
def __init__(self, render_mode=None):
super().__init__()
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf, shape=(4,), dtype=np.float32
)
self.action_space = spaces.Discrete(2)
self.render_mode = render_mode
def reset(self, seed=None, options=None):
super().reset(seed=seed)
self.state = self.np_random.uniform(low=-0.05, high=0.05, size=(4,))
return self.state.astype(np.float32), {}
def step(self, action):
# Implement environment dynamics here
observation = self.state.astype(np.float32)
reward = 1.0
terminated = False # Episode ended due to task completion/failure
truncated = False # Episode ended due to time limit
info = {}
return observation, reward, terminated, truncated, info
def render(self):
pass
Hyperparameter Tuning with Optuna
import optuna
from stable_baselines3 import PPO
from stable_baselines3.common.evaluation import evaluate_policy
def objective(trial):
learning_rate = trial.suggest_float("learning_rate", 1e-5, 1e-3, log=True)
n_steps = trial.suggest_categorical("n_steps", [256, 512, 1024, 2048])
gamma = trial.suggest_float("gamma", 0.9, 0.9999)
model = PPO(
"MlpPolicy", "CartPole-v1",
learning_rate=learning_rate,
n_steps=n_steps,
gamma=gamma,
verbose=0
)
model.learn(total_timesteps=50_000)
mean_reward, _ = evaluate_policy(model, model.get_env(), n_eval_episodes=10)
return mean_reward
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=50)
print(f"Best params: {study.best_params}")
Core Workflow
- Define the environment - Use Gymnasium API
- Validate the environment - Run
python scripts/validate_env.py <env> (API compliance, spaces, determinism, NaN guards)
- Select algorithm - Based on action space and requirements
- Pre-training sanity check - Run
python scripts/pretrain_check.py <env> (random-policy baseline, distribution stats, actionable warnings)
- Start simple - Default hyperparameters, short training
- Monitor training - TensorBoard, check reward curves
- Debug issues - Use the debugging playbook
- Tune hyperparameters - Optuna for systematic search
- Evaluate properly - Run
python scripts/eval_report.py <env> --model <model.zip> (separate eval env, per-episode success tracking, Markdown report)
- Deploy - Export to ONNX/TorchScript
Bundled Scripts (run these instead of re-deriving snippets)
The validation and evaluation patterns from the reference files are packaged as
runnable, self-contained CLIs in scripts/. Run them directly rather than
re-implementing the inline snippets:
| Script |
When to run |
Usage |
scripts/validate_env.py |
After defining or changing an environment, before any training |
python scripts/validate_env.py CartPole-v1 or python scripts/validate_env.py my_pkg.envs:CustomEnv |
scripts/pretrain_check.py |
After validation passes, before launching training |
python scripts/pretrain_check.py CartPole-v1 --episodes 20 |
scripts/eval_report.py |
After training, to produce a Markdown evaluation report |
python scripts/eval_report.py CartPole-v1 --model ppo_cartpole.zip --algo ppo --episodes 100 --output eval_report.md |
All three accept either a registered Gymnasium id (CartPole-v1) or a
module.path:EnvClass spec for unregistered custom environments. validate_env.py
exits nonzero on any failed check, so it works as a CI gate. eval_report.py
without --model evaluates a random policy - useful as a baseline and for
testing the report pipeline.
Reference Files
- algorithms.md - Deep dive on DQN, PPO, SAC, A2C, TD3
- environments.md - Gymnasium setup, custom envs, wrappers
- training.md - Hyperparameters, reward engineering, normalization
- debugging.md - Failure modes, diagnostics, sanity checks
- evaluation.md - Metrics, logging, reproducibility
- deployment.md - ONNX export, inference optimization, safety
Essential Dependencies
pip install gymnasium stable-baselines3 tensorboard optuna
# For Atari environments
pip install gymnasium[atari] gymnasium[accept-rom-license]
# For MuJoCo
pip install gymnasium[mujoco]
Common Pitfalls to Avoid
- Not normalizing observations - Use
VecNormalize wrapper
- Wrong action space handling - Check discrete vs continuous
- Ignoring seed management - Set seeds for reproducibility
- Training and eval on same env - Use separate eval environment
- Not monitoring entropy - Low entropy = policy collapse
- Sparse rewards without shaping - Add intermediate rewards
- Too large/small learning rate - Start with 3e-4 for most algorithms
1---2name: reinforcement-learning3description: Reinforcement Learning best practices for Python using modern libraries (Stable-Baselines3, RLlib, Gymnasium). Use when: - Implementing RL algorithms (PPO, SAC, DQN, TD3, A2C) - Creating custom Gymnasium environments - Training, debugging, or evaluating RL agents - Setting up hyperparameter tuning for RL - Deploying RL models to production4---56# Reinforcement Learning Best Practices78## Overview910This skill provides comprehensive guidance for implementing reinforcement learning in Python using the modern ecosystem (Gymnasium >= 1.0, Stable-Baselines3 >= 2.x). Gymnasium has replaced OpenAI Gym as the standard environment interface. Stable-Baselines3 (SB3) is recommended for prototyping, RLlib for production/distributed training, and CleanRL for research.1112## When to Use1314- Building RL agents for discrete or continuous control tasks15- Creating custom simulation environments16- Tuning hyperparameters for RL algorithms17- Debugging training issues (reward curves, policy collapse, numerical instability)18- Deploying trained policies to production1920## Library Selection2122| Library | Best For | Ease | Flexibility | Production |23|---------|----------|------|-------------|------------|24| Stable-Baselines3 | Prototyping, learning | High | Medium | Good |25| RLlib | Production, distributed | Medium | High | Excellent |26| CleanRL | Research, understanding | High | Low | Poor |27| TorchRL | Custom implementations | Low | Highest | Good |2829## Algorithm Decision Tree3031```32Start33 |34 v35Action space type?36 |37 +-- Discrete --> Sample efficiency critical?38 | |39 | +-- Yes --> DQN (or Double/Dueling DQN)40 | +-- No --> Stability critical?41 | |42 | +-- Yes --> PPO43 | +-- No --> A2C (faster iterations)44 |45 +-- Continuous --> Sample efficiency critical?46 |47 +-- Yes --> SAC (auto entropy) or TD348 +-- No --> PPO (more stable, less efficient)49```5051**Quick Selection Table:**5253| Scenario | Recommended | Why |54|----------|-------------|-----|55| Discrete actions, getting started | PPO | Stable, good defaults |56| Continuous control | SAC or TD3 | Sample efficient, handles continuous well |57| Sample efficiency critical | SAC, DQN | Off-policy, reuses experience |58| Stability critical | PPO | Trust region, consistent |59| High-dimensional obs (images) | PPO + CNN | Handles visual input well |60| Fast iteration needed | A2C | Simpler, faster per update |6162## Quick Start with Stable-Baselines36364### Basic Training6566```python67from stable_baselines3 import PPO68from stable_baselines3.common.env_util import make_vec_env6970# Create vectorized environment (4 parallel envs)71env = make_vec_env("CartPole-v1", n_envs=4)7273# Initialize and train74model = PPO("MlpPolicy", env, verbose=1)75model.learn(total_timesteps=100_000)7677# Save and load78model.save("ppo_cartpole")79loaded_model = PPO.load("ppo_cartpole")8081# Evaluate82obs = env.reset()83for _ in range(1000):84 action, _ = loaded_model.predict(obs, deterministic=True)85 obs, reward, done, info = env.step(action)86```8788### Custom Environment Template8990```python91import gymnasium as gym92from gymnasium import spaces93import numpy as np9495class CustomEnv(gym.Env):96 metadata = {"render_modes": ["human", "rgb_array"]}9798 def __init__(self, render_mode=None):99 super().__init__()100 self.observation_space = spaces.Box(101 low=-np.inf, high=np.inf, shape=(4,), dtype=np.float32102 )103 self.action_space = spaces.Discrete(2)104 self.render_mode = render_mode105106 def reset(self, seed=None, options=None):107 super().reset(seed=seed)108 self.state = self.np_random.uniform(low=-0.05, high=0.05, size=(4,))109 return self.state.astype(np.float32), {}110111 def step(self, action):112 # Implement environment dynamics here113 observation = self.state.astype(np.float32)114 reward = 1.0115 terminated = False # Episode ended due to task completion/failure116 truncated = False # Episode ended due to time limit117 info = {}118 return observation, reward, terminated, truncated, info119120 def render(self):121 pass122```123124### Hyperparameter Tuning with Optuna125126```python127import optuna128from stable_baselines3 import PPO129from stable_baselines3.common.evaluation import evaluate_policy130131def objective(trial):132 learning_rate = trial.suggest_float("learning_rate", 1e-5, 1e-3, log=True)133 n_steps = trial.suggest_categorical("n_steps", [256, 512, 1024, 2048])134 gamma = trial.suggest_float("gamma", 0.9, 0.9999)135136 model = PPO(137 "MlpPolicy", "CartPole-v1",138 learning_rate=learning_rate,139 n_steps=n_steps,140 gamma=gamma,141 verbose=0142 )143 model.learn(total_timesteps=50_000)144145 mean_reward, _ = evaluate_policy(model, model.get_env(), n_eval_episodes=10)146 return mean_reward147148study = optuna.create_study(direction="maximize")149study.optimize(objective, n_trials=50)150print(f"Best params: {study.best_params}")151```152153## Core Workflow1541551. **Define the environment** - Use Gymnasium API1562. **Validate the environment** - Run `python scripts/validate_env.py <env>` (API compliance, spaces, determinism, NaN guards)1573. **Select algorithm** - Based on action space and requirements1584. **Pre-training sanity check** - Run `python scripts/pretrain_check.py <env>` (random-policy baseline, distribution stats, actionable warnings)1595. **Start simple** - Default hyperparameters, short training1606. **Monitor training** - TensorBoard, check reward curves1617. **Debug issues** - Use the debugging playbook1628. **Tune hyperparameters** - Optuna for systematic search1639. **Evaluate properly** - Run `python scripts/eval_report.py <env> --model <model.zip>` (separate eval env, per-episode success tracking, Markdown report)16410. **Deploy** - Export to ONNX/TorchScript165166## Bundled Scripts (run these instead of re-deriving snippets)167168The validation and evaluation patterns from the reference files are packaged as169runnable, self-contained CLIs in `scripts/`. Run them directly rather than170re-implementing the inline snippets:171172| Script | When to run | Usage |173|--------|-------------|-------|174| `scripts/validate_env.py` | After defining or changing an environment, before any training | `python scripts/validate_env.py CartPole-v1` or `python scripts/validate_env.py my_pkg.envs:CustomEnv` |175| `scripts/pretrain_check.py` | After validation passes, before launching training | `python scripts/pretrain_check.py CartPole-v1 --episodes 20` |176| `scripts/eval_report.py` | After training, to produce a Markdown evaluation report | `python scripts/eval_report.py CartPole-v1 --model ppo_cartpole.zip --algo ppo --episodes 100 --output eval_report.md` |177178All three accept either a registered Gymnasium id (`CartPole-v1`) or a179`module.path:EnvClass` spec for unregistered custom environments. `validate_env.py`180exits nonzero on any failed check, so it works as a CI gate. `eval_report.py`181without `--model` evaluates a random policy - useful as a baseline and for182testing the report pipeline.183184## Reference Files185186- [algorithms.md](references/algorithms.md) - Deep dive on DQN, PPO, SAC, A2C, TD3187- [environments.md](references/environments.md) - Gymnasium setup, custom envs, wrappers188- [training.md](references/training.md) - Hyperparameters, reward engineering, normalization189- [debugging.md](references/debugging.md) - Failure modes, diagnostics, sanity checks190- [evaluation.md](references/evaluation.md) - Metrics, logging, reproducibility191- [deployment.md](references/deployment.md) - ONNX export, inference optimization, safety192193## Essential Dependencies194195```bash196pip install gymnasium stable-baselines3 tensorboard optuna197# For Atari environments198pip install gymnasium[atari] gymnasium[accept-rom-license]199# For MuJoCo200pip install gymnasium[mujoco]201```202203## Common Pitfalls to Avoid2042051. **Not normalizing observations** - Use `VecNormalize` wrapper2062. **Wrong action space handling** - Check discrete vs continuous2073. **Ignoring seed management** - Set seeds for reproducibility2084. **Training and eval on same env** - Use separate eval environment2095. **Not monitoring entropy** - Low entropy = policy collapse2106. **Sparse rewards without shaping** - Add intermediate rewards2117. **Too large/small learning rate** - Start with 3e-4 for most algorithms