zeroRL
Reinforcement learning is a demanding field that requires significant time and focus to train agents properly. Existing solutions such as SB3, RLlib, and Tianshou reduce this friction by providing ready to use implementations. However, some experiments and deeper modifications can be difficult to implement and may require understanding their abstractions and navigating a more complex codebase.
zeroRL takes a different approach: a simple, explicit, and modular architecture designed to facilitate experimentation in reinforcement learning.
The framework allows you to:
- Easily implement algorithms that are not included in the framework;
- Easily integrate new environments;
- Run experiments without modifying the training pipeline;
- Replace or modify components of algorithm implementations;
- Maintain full control over the training pipeline.
zeroRL is designed to make reinforcement learning experimentation easier without imposing heavy abstractions.
Installation
Before installing zeroRL, ensure Python 3.11+ is available.
Install zeroRL with uv or pip:
uv pip install zerorl
The package depends on torch, numpy, gymnasium, tqdm, and imageio.
Quick Start
The fastest way to train an agent — one function call:
from zerorl.algorithms.ppo import easy_train_ppo
from zerorl.config import TrainConfig, AlgoConfig
config = TrainConfig(model_name="Pendulum", project_name="my_experiment")
algo_config = AlgoConfig(ent_coef=0.0)
trainer = easy_train_ppo("Pendulum-v1", config, algo_config)
trainer.train(use_tb=True)
trainer.test()
This creates an ActorCriticAgent, vectorized environments, a rollout buffer, and runs PPO — all wired together automatically. Override any component:
# Custom environment (BaseAgent subclass)
trainer = easy_train_ppo("Pendulum-v1", config, algo_config, agent=my_agent)
# Custom environment (BaseEnv subclass)
trainer = easy_train_ppo(my_env, config, algo_config)
# Multiple environments
config.num_envs = 4
trainer = easy_train_ppo("CartPole-v1", config, algo_config)
Advanced Usage
For full control over agent, environment, and the training loop:
import torch
import torch.nn as nn
import numpy as np
from zerorl.helpers.agent import BaseAgent
from zerorl.train import BaseTrain
from zerorl.buffer import Buffer
from zerorl.config import TrainConfig, AlgoConfig
from zerorl.algorithms.ppo import gae_compute, ppo_func
from zerorl.factory import get_env
from zerorl.functions import get_obs_act
# 1. Define your agent
class Agent(BaseAgent):
def __init__(self, obs_dim, act_dim):
super().__init__()
self.actor = nn.Sequential(
nn.Linear(obs_dim, 64), nn.Tanh(),
nn.Linear(64, 64), nn.Tanh(),
nn.Linear(64, act_dim),
)
self.critic = nn.Sequential(
nn.Linear(obs_dim, 64), nn.Tanh(),
nn.Linear(64, 64), nn.Tanh(),
nn.Linear(64, 1),
)
def forward(self, state):
return self.actor(state), self.critic(state)
def build_distribution(self, logits):
return torch.distributions.Categorical(logits=logits)
def get_action(self, state, action=None):
logits, value = self.forward(state)
dist = self.build_distribution(logits)
if action is None:
action = dist.sample()
log_prob, entropy = eval_action(dist, action)
return {"action": action, "log_prob": log_prob, "entropy": entropy, "value": value}
# 2. Set up environment and buffer
config = TrainConfig(project_name="cartpole_example", model_name="agent", timestamp=1_000_000, num_envs=2)
algo_config = AlgoConfig()
env = get_env("CartPole-v1", config.num_envs)
obs_shape, act_shape, obs_n, act_n, _ = get_obs_act(env)
agent = Agent(obs_n, act_.n)
buffer = Buffer(
data={
"state": obs_shape, "action": act_shape,
"reward": (), "done": (), "truncated": (),
"entropy": (), "value": (), "return": (),
"log_prob": (), "advantage": (), "truncated": ()
},
config=config,
)
# 3. Define the update weights function
def update_weights(agent, buffer, scheduler, optimizer, last_output, algo_config):
all_data = buffer.get_all()
gae_compute(all_data["reward"], all_data["value"], last_output["value"],
all_data["done"], buffer, algo_config)
return ppo_func(agent, optimizer, buffer, algo_config, scheduler, device=agent.device)
# 4. Train
trainer = BaseTrain(agent, env, buffer, update_weights, config, algo_config)
trainer.train(use_wandb=True, model_save=True)
Custom Environment
Implement BaseEnv to use your own environment with easy_train_ppo or BaseTrain:
import numpy as np
from gymnasium import spaces
from zerorl.helpers.env import BaseEnv
class GridWorld(BaseEnv):
"""Simple 4x4 grid world — agent starts at (0,0), goal at (3,3)."""
def __init__(self):
super().__init__()
self.observation_space = spaces.Box(
low=0.0, high=3.0, shape=(2,), dtype=np.float32
)
self.action_space = spaces.Discrete(4) # up, down, left, right
self._pos = None
def reset(self, *, seed=None, options=None):
self._pos = np.array([0, 0], dtype=np.float32)
return self._pos.copy(), {}
def step(self, action):
direction = np.array([[0, 1], [0, -1], [-1, 0], [1, 0]])[action]
self._pos = np.clip(self._pos + direction, 0, 3)
terminated = np.array_equal(self._pos, [3, 3])
reward = 1.0 if terminated else -0.01
return self._pos.copy(), reward, terminated, False, {}
def close(self):
pass
Then pass it directly:
from zerorl.algorithms.ppo import easy_train_ppo
from zerorl.config import TrainConfig, AlgoConfig
config = TrainConfig(model_name="gridworld", project_name="gridworld_exp", timestamp=500_000)
algo_config = AlgoConfig()
trainer = easy_train_ppo(GridWorld(), config, algo_config)
trainer.train()
Modular function
All RL algorithm are modular function where you can change some components:
from torch import Tensor
from zerorl.algorithms.ppo import ppo_func, gae_compute
from zerorl.algorithms.helpers.agent import BaseAgent
def custom_ppo_loss(agent: BaseAgent,
params: dict,
buffers: dict,
states: Tensor,
actions: Tensor,
old_log_prob: Tensor,
old_values: Tensor,
advantages: Tensor,
returns: Tensor,
ent_coef: float,
value_coef: float,
clip_eps: float,
clip_vf: float,
) -> dict[str, Tensor]:
#write your own PPO loss
...
def update_weights(agent, buffer, scheduler, optimizer, last_output, algo_config):
all_data = buffer.get_all()
gae_compute(all_data["reward"], all_data["value"], last_output["value"],
all_data["done"], buffer, algo_config)
return ppo_func(agent, optimizer, buffer, algo_config, scheduler, ppo_loss_func = custom_ppo_loss, device=agent.device)
Implemented your own algorithm
import torch
from zerorl import BaseTrain
# 1. Define your pure PyTorch update function
def reinforce_update(agent, buffer, optimizer, algo_config, scheduler=None, last_output=None):
data = buffer.get_all()
rewards = data["reward"].squeeze()
dones = data["done"].squeeze()
returns = []
R = 0.0
for r, d in zip(reversed(rewards.tolist()), reversed(dones.tolist())):
if d: R = 0.0
R = r + algo_config.gamma * R
returns.insert(0, R)
returns = torch.tensor(returns, device=agent.device)
logits, _ = agent(data["state"])
dist = agent.build_distribution(logits)
log_probs = dist.log_prob(data["action"]).sum(dim=-1)
loss = -(log_probs * returns).mean()
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(agent.parameters(), 0.5) # Max grad norm
optimizer.step()
return {"loss": loss}
# 2. Plug it in. BaseTrain handles rollouts.
trainer = BaseTrain(
agent=agent,
env=env,
buffer=buffer,
update_weights=reinforce_update,
config=config,
algo_config=algo_config
)
trainer.train()
What's Included
| Component | Description |
|---|---|
BaseAgent |
Plain nn.Module base class. Agents must define get_action() returning a dict and build_distributions() returning a Distribution from torch. |
BaseEnv |
Abstract Gymnasium environment. Implement reset(), step(), and close(). |
BaseTrain |
Training orchestrator: rollout collection, observation normalization, updates weights, model saving ans profiling. |
Buffer |
Inspired from TorchDict is a dictionary-like data container for tensors that lets you manipulate a collection of tensors. |
AlgoConfig |
Mutable hyperparameters for RL algorithms: lr, gamma, gae_lambda, clip_eps, ent_coef, value_coef, batch_size, epochs, tau. |
TrainConfig |
Training settings with auto-computed model_path, num_update, and device. |
easy_train_ppo |
One-call setup: creates agent, env, buffer, and returns a ready-to-train BaseTrain. |
ActorCriticAgent |
Built-in agent with orthogonal init, supports discrete and continuous action spaces. |
vectorize_env |
Wraps env specs into SyncVectorEnv with SAME_STEP autoreset. |
| Algorithm | Included |
|---|---|
PPO |
✅ |
SAC |
❌ |
DQN |
❌ |
TD3 |
❌ |
DDPM |
❌ |
Configuration
from zerorl.config import AlgoConfig, TrainConfig
algo = AlgoConfig(
lr=3e-4,
gamma=0.99,
gae_lambda=0.95,
clip_eps=0.2,
ent_coef=0.01,
value_coef=0.5,
batch_size=64,
epochs=10,
tau: float = 0.005
)
train = TrainConfig(
model_name="my_agent", # Required, used for save model in specific path
project_name="my_experiment", # Required, used for wandb/tensorboard
model_save_path=".checkpoints", # Default
timestamp=1_000_000, # Total timesteps
rollout_steps=2048, # Steps per rollout
num_envs=1, # Parallel environments
normalize=False, # Normalize observations of environment
profile=False, # Profile steps of training
device=torch.device("cuda"), # Tensor device, check if the device has a GPU
num_update=timestamp // (rollout_steps * num_envs), # Number of weights update
model_path=".checkpoints/my_agent.pt" # Path for saving agent weights
)
License
MIT License - see LICENSE for details.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zerorl-0.1.1.tar.gz.
File metadata
- Download URL: zerorl-0.1.1.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
233780f1c4a307214799bf3f80ae59c741c298ce1dd6c18de0eea33dfb521a3e
|
|
| MD5 |
7e8850a07aa06f4fa8abbeadb23a62a6
|
|
| BLAKE2b-256 |
f465d45170e357570e1d17f1e0e71c445d1fa926883ae3b3cc09ed1397af2fa5
|
File details
Details for the file zerorl-0.1.1-py3-none-any.whl.
File metadata
- Download URL: zerorl-0.1.1-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
31ee343c7aa14924f921b27595df488b733b78cc6c01a8c1a602a43236ac8a7e
|
|
| MD5 |
f55e78651825b06dc8a151a2b7cf6736
|
|
| BLAKE2b-256 |
8d3362a84a71da0995984db3be798b3fcbc386a10566cedbe53e7d7059013650
|