Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Algorithms

The algorithm examples show complete PPO and A2C configurations through r2l. They are useful when you want a runnable training loop before customizing lower-level hooks or samplers.

PPO

The PPO example trains on Pendulum-v1, writes training artifacts, reloads the best policy through InferenceRunner, and runs rendered inference episodes.

Run it from the workspace root:

cargo run -p r2l-examples --example ppo
use r2l::{Error, GymEnv, InferenceRunner, PPOBuilder, TrainingArtifactsConfig, TrainingLimit};

fn main() -> Result<(), Error> {
    const ENV_NAME: &str = "Pendulum-v1";

    // Path where the training artifacts are going to be stored. Training artifacts could include:
    // - Parameters of the model that was trained + the weights as a safetensor and the optional obs normalizer serialized
    // - Measurements on how the trained model perfoms after each training round
    // - Measurements on how long parts of the trainig run took
    const ARTIFACT_DIR: &str = "runs/pendulum";
    let artifacts_config = TrainingArtifactsConfig::new(ARTIFACT_DIR);

    // An environmnet builder how environments are to be constructed. Environment construction can
    // be elaborate (especially when working with external dependencies), so r2l opts to not pass
    // the environment directly (the environment would have to be Send for multi sampling), but
    // instead accepts anything that implements the `EnvBuilder` trait. Simplest example is just a
    // function/closure that returns the Env.
    let env_builder = || GymEnv::new(ENV_NAME, None);

    // The algorightm is constructed through a PPOBuilder. For A2C, the A2Cbuilder would be
    // equivalent. Builders expose a lot of common parameter setters. To check all the options, you
    // check https://docs.rs/r2l/latest/r2l/type.PPOBuilder.html.
    let mut ppo = PPOBuilder::new(env_builder, 10)?
        .with_training_artifacts(artifacts_config)
        .with_policy_hidden_layers(vec![64, 64])
        .with_lambda(0.95)
        .with_gamma(0.9)
        .with_learning_rate(0.001)
        .with_training_limit(TrainingLimit::rollouts(30))
        .build()?;

    // This kicks off and finishes training.
    ppo.train()?;

    // Once training in done, the training artifacts as serialized. You can reuse the trained model
    // by constructing an InferenceRunner. InferenceRunner can single step or run episodes on the
    // environment it recieves.
    let env = GymEnv::new(ENV_NAME, Some("human".to_owned()))?;
    let mut inference = InferenceRunner::load_from_env(ARTIFACT_DIR, env)?;
    for _ in 0..10 {
        inference.run_episode()?;
    }

    Ok(())
}

The important pieces are with_training_artifacts, which writes actor.safetensors, inference.yaml, and metrics files, and InferenceRunner::load_from_env, which rebuilds the inference runner from those saved files.

A2C

The A2C example selects the Candle backend, configures rollout collection, and uses a reporter channel to observe training statistics.

Run it from the workspace root:

cargo run -p r2l-examples --example a2c
use std::{
    sync::mpsc::{self, Receiver, Sender},
    thread,
};

use candle_core::Device;
use r2l::{A2CBuilder, A2CRolloutStats, SamplerExecutionMode, TrainingLimit};

fn main() -> anyhow::Result<()> {
    let (update_tx, update_rx): (Sender<A2CRolloutStats>, Receiver<A2CRolloutStats>) =
        mpsc::channel();

    let a2c_builder = A2CBuilder::gym("Pendulum-v1", 10)?
        .with_candle(Device::Cpu)
        .with_seed(0)
        .with_entropy_coefficient(0.2)
        .with_gradient_clipping(Some(0.5))
        .with_rollout_steps(2048)
        .with_execution_mode(SamplerExecutionMode::SingleThreaded)
        .with_training_limit(TrainingLimit::rollouts(300))
        .with_rollout_reporter(Some(update_tx));
    let mut a2c = a2c_builder.build()?;
    let t = thread::spawn(move || {
        while let Ok(stats) = update_rx.recv() {
            println!("avg reward: {}", stats.average_reward);
        }
    });
    a2c.train()?;
    drop(a2c);
    t.join()
        .map_err(|_| anyhow::anyhow!("A2C reporter thread panicked"))?;
    Ok(())
}

Both PPO and A2C builders expose the same broad setup concepts: choose an environment builder, select a backend, set rollout bounds, configure a learning schedule, then call build() and train().