How to make a deep RL experiment
The usual script to run a deep RL experiment does not significantly differ from
the one for a shallow RL experiment.
This tutorial shows how to solve Atari
games in MushroomRL using DQN, and how to solve
MuJoCo tasks using DDPG. This
tutorial will not explain some technicalities that are already described in the
previous tutorials, and will only briefly explain how to run deep RL experiments.
Be sure to read the previous tutorials before starting this one.
Solving Atari with DQN
This script runs the experiment to solve the Atari Breakout game as described in
the DQN paper “Human-level control through deep reinforcement learning”, Mnih V. et
al., 2015). We import AtariNetwork from the networks package and define
a few helper functions:
import torch.optim as optim
import torch.nn.functional as F
from mushroom_rl.algorithms.value import DQN
from mushroom_rl.approximators.parametric import TorchApproximator
from mushroom_rl.approximators.parametric.networks import AtariNetwork
from mushroom_rl.core import Core
from mushroom_rl.environments import Atari
from mushroom_rl.policy import EpsGreedy
from mushroom_rl.rl_utils.parameters import LinearParameter, Parameter
def print_epoch(epoch):
print('################################################################')
print('Epoch: ', epoch)
print('----------------------------------------------------------------')
def get_stats(dataset):
score = dataset.compute_metrics()
print(('min_reward: %f, max_reward: %f, mean_reward: %f,'
' median_reward: %f, games_completed: %d' % score))
return score
We then set some hyperparameters and create the mdp and the policy pi.
Differently from the literature, we use Adam as the optimizer:
optimizer = dict()
optimizer['class'] = optim.Adam
optimizer['params'] = dict(lr=.00025)
# Settings
width = 84
height = 84
history_length = 4
train_frequency = 4
evaluation_frequency = 250000
target_update_frequency = 10000
initial_replay_size = 50000
max_replay_size = 500000
test_samples = 125000
max_steps = 50000000
# MDP
mdp = Atari('ALE/Breakout-v5', width, height)
# Policy
epsilon = LinearParameter(value=1.,
threshold_value=.1,
n=1000000)
epsilon_test = Parameter(value=.05)
epsilon_random = Parameter(value=1)
pi = EpsGreedy(epsilon=epsilon_random, backend='torch')
Then, the approximator:
# Approximator
input_shape = (history_length,) + mdp.info.observation_space.shape
approximator_params = dict(
network=AtariNetwork,
input_shape=input_shape,
output_shape=(mdp.info.action_space.n,),
n_actions=mdp.info.action_space.n,
n_features=AtariNetwork.n_features,
optimizer=optimizer,
loss=F.smooth_l1_loss
)
approximator = TorchApproximator
Finally, the agent and the core:
# Agent
algorithm_params = dict(
batch_size=32,
target_update_frequency=target_update_frequency // train_frequency,
replay_memory=None,
initial_replay_size=initial_replay_size,
max_replay_size=max_replay_size,
history_length=history_length
)
agent = DQN(mdp.info, pi, approximator,
approximator_params=approximator_params,
**algorithm_params)
# Algorithm
core = Core(agent, mdp)
Eventually, the learning loop is performed. As done in literature, learning and evaluation steps are alternated:
# Fill replay memory with random dataset
print_epoch(0)
core.learn(n_steps=initial_replay_size,
n_steps_per_fit=initial_replay_size)
# Evaluate initial policy
pi.set_epsilon(epsilon_test)
dataset = core.evaluate(n_steps=test_samples)
scores = [get_stats(dataset)]
for n_epoch in range(1, max_steps // evaluation_frequency + 1):
print_epoch(n_epoch)
print('- Learning:')
pi.set_epsilon(epsilon)
core.learn(n_steps=evaluation_frequency,
n_steps_per_fit=train_frequency)
print('- Evaluation:')
pi.set_epsilon(epsilon_test)
dataset = core.evaluate(n_steps=test_samples)
scores.append(get_stats(dataset))
Solving MuJoCo with DDPG
This script runs the experiment to solve the Walker-Stand MuJoCo task, as
implemented in MuJoCo. We import
ActorNetwork and CriticNetwork from the networks package.
We create the mdp, the policy, and set some hyperparameters:
import numpy as np
import torch
import torch.optim as optim
import torch.nn.functional as F
from mushroom_rl.algorithms.actor_critic import DDPG
from mushroom_rl.approximators.parametric.networks import ActorNetwork, CriticNetwork
from mushroom_rl.core import Core
from mushroom_rl.environments.dm_control_env import DMControl
from mushroom_rl.policy import OrnsteinUhlenbeckPolicy
# MDP
horizon = 500
gamma = 0.99
mdp = DMControl('walker', 'stand', horizon, gamma)
# Policy
policy_class = OrnsteinUhlenbeckPolicy
policy_params = dict(sigma=torch.ones(1) * .2, theta=.15, dt=1e-2)
# Settings
initial_replay_size = 500
max_replay_size = 5000
batch_size = 200
n_features = 80
tau = .001
Note that the policy is not instantiated in the script, since in DDPG the instantiation is done inside the algorithm constructor.
We create the actor and the critic approximators. The critic takes state and action as two
separate inputs, so its input_shape is a list of the two shapes:
# Approximator
actor_input_shape = mdp.info.observation_space.shape
actor_params = dict(network=ActorNetwork,
n_features=n_features,
input_shape=actor_input_shape,
output_shape=mdp.info.action_space.shape)
actor_optimizer = {'class': optim.Adam,
'params': {'lr': 1e-5}}
critic_input_shape = [actor_input_shape, mdp.info.action_space.shape]
critic_params = dict(network=CriticNetwork,
optimizer={'class': optim.Adam,
'params': {'lr': 1e-3}},
loss=F.mse_loss,
n_features=n_features,
input_shape=critic_input_shape,
output_shape=(1,))
Finally, we create the agent and the core:
# Agent
agent = DDPG(mdp.info, policy_class, policy_params,
actor_params, actor_optimizer, critic_params,
batch_size, initial_replay_size, max_replay_size,
tau)
# Algorithm
core = Core(agent, mdp)
# Fill the replay memory with random samples
core.learn(n_steps=initial_replay_size, n_steps_per_fit=initial_replay_size)
As in DQN, we alternate learning and evaluation steps:
# RUN
n_epochs = 40
n_steps = 1000
n_steps_test = 2000
dataset = core.evaluate(n_steps=n_steps_test, render=False)
J = dataset.discounted_return
print('Epoch: 0')
print('J: ', np.mean(J))
for n in range(n_epochs):
print('Epoch: ', n+1)
core.learn(n_steps=n_steps, n_steps_per_fit=1)
dataset = core.evaluate(n_steps=n_steps_test, render=False)
J = dataset.discounted_return
print('J: ', np.mean(J))