mirror of
https://github.com/vale981/ray
synced 2025-03-10 05:16:49 -04:00
278 lines
10 KiB
Python
278 lines
10 KiB
Python
"""
|
|
Deep Q-Networks (DQN, Rainbow, Parametric DQN)
|
|
==============================================
|
|
|
|
This file defines the distributed Trainer class for the Deep Q-Networks
|
|
algorithm. See `dqn_[tf|torch]_policy.py` for the definition of the policies.
|
|
|
|
Detailed documentation:
|
|
https://docs.ray.io/en/master/rllib-algorithms.html#deep-q-networks-dqn-rainbow-parametric-dqn
|
|
""" # noqa: E501
|
|
|
|
import logging
|
|
from typing import List, Optional, Type
|
|
|
|
from ray.rllib.agents.dqn.dqn_tf_policy import DQNTFPolicy
|
|
from ray.rllib.agents.dqn.dqn_torch_policy import DQNTorchPolicy
|
|
from ray.rllib.agents.dqn.simple_q import (
|
|
SimpleQConfig,
|
|
SimpleQTrainer,
|
|
)
|
|
from ray.rllib.agents.trainer import Trainer
|
|
from ray.rllib.execution.rollout_ops import (
|
|
synchronous_parallel_sample,
|
|
)
|
|
from ray.rllib.execution.train_ops import (
|
|
train_one_step,
|
|
multi_gpu_train_one_step,
|
|
)
|
|
from ray.rllib.policy.policy import Policy
|
|
from ray.rllib.utils.annotations import override
|
|
from ray.rllib.utils.replay_buffers.utils import update_priorities_in_replay_buffer
|
|
from ray.rllib.utils.typing import (
|
|
ResultDict,
|
|
TrainerConfigDict,
|
|
)
|
|
from ray.rllib.utils.metrics import (
|
|
NUM_ENV_STEPS_SAMPLED,
|
|
NUM_AGENT_STEPS_SAMPLED,
|
|
)
|
|
from ray.rllib.utils.deprecation import (
|
|
Deprecated,
|
|
DEPRECATED_VALUE,
|
|
)
|
|
from ray.rllib.utils.annotations import ExperimentalAPI
|
|
from ray.rllib.utils.metrics import SYNCH_WORKER_WEIGHTS_TIMER
|
|
from ray.rllib.execution.common import (
|
|
LAST_TARGET_UPDATE_TS,
|
|
NUM_TARGET_UPDATES,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# fmt: off
|
|
# __sphinx_doc_begin__
|
|
DEFAULT_CONFIG = Trainer.merge_trainer_configs(
|
|
SimpleQConfig().to_dict(),
|
|
{
|
|
# === Model ===
|
|
# Number of atoms for representing the distribution of return. When
|
|
# this is greater than 1, distributional Q-learning is used.
|
|
# the discrete supports are bounded by v_min and v_max
|
|
"num_atoms": 1,
|
|
"v_min": -10.0,
|
|
"v_max": 10.0,
|
|
# Whether to use noisy network
|
|
"noisy": False,
|
|
# control the initial value of noisy nets
|
|
"sigma0": 0.5,
|
|
# Whether to use dueling dqn
|
|
"dueling": True,
|
|
# Dense-layer setup for each the advantage branch and the value branch
|
|
# in a dueling architecture.
|
|
"hiddens": [256],
|
|
# Whether to use double dqn
|
|
"double_q": True,
|
|
# N-step Q learning
|
|
"n_step": 1,
|
|
|
|
# === Replay buffer ===
|
|
# Deprecated, use capacity in replay_buffer_config instead.
|
|
"buffer_size": DEPRECATED_VALUE,
|
|
"replay_buffer_config": {
|
|
# Enable the new ReplayBuffer API.
|
|
"_enable_replay_buffer_api": True,
|
|
"type": "MultiAgentPrioritizedReplayBuffer",
|
|
# Size of the replay buffer. Note that if async_updates is set,
|
|
# then each worker will have a replay buffer of this size.
|
|
"capacity": 50000,
|
|
"prioritized_replay_alpha": 0.6,
|
|
# Beta parameter for sampling from prioritized replay buffer.
|
|
"prioritized_replay_beta": 0.4,
|
|
# Epsilon to add to the TD errors when updating priorities.
|
|
"prioritized_replay_eps": 1e-6,
|
|
# The number of continuous environment steps to replay at once. This may
|
|
# be set to greater than 1 to support recurrent models.
|
|
"replay_sequence_length": 1,
|
|
},
|
|
# Set this to True, if you want the contents of your buffer(s) to be
|
|
# stored in any saved checkpoints as well.
|
|
# Warnings will be created if:
|
|
# - This is True AND restoring from a checkpoint that contains no buffer
|
|
# data.
|
|
# - This is False AND restoring from a checkpoint that does contain
|
|
# buffer data.
|
|
"store_buffer_in_checkpoints": False,
|
|
|
|
|
|
# Callback to run before learning on a multi-agent batch of
|
|
# experiences.
|
|
"before_learn_on_batch": None,
|
|
|
|
# The intensity with which to update the model (vs collecting samples
|
|
# from the env). If None, uses the "natural" value of:
|
|
# `train_batch_size` / (`rollout_fragment_length` x `num_workers` x
|
|
# `num_envs_per_worker`).
|
|
# If provided, will make sure that the ratio between ts inserted into
|
|
# and sampled from the buffer matches the given value.
|
|
# Example:
|
|
# training_intensity=1000.0
|
|
# train_batch_size=250 rollout_fragment_length=1
|
|
# num_workers=1 (or 0) num_envs_per_worker=1
|
|
# -> natural value = 250 / 1 = 250.0
|
|
# -> will make sure that replay+train op will be executed 4x as
|
|
# often as rollout+insert op (4 * 250 = 1000).
|
|
# See: rllib/agents/dqn/dqn.py::calculate_rr_weights for further
|
|
# details.
|
|
"training_intensity": None,
|
|
|
|
# === Parallelism ===
|
|
# Whether to compute priorities on workers.
|
|
"worker_side_prioritization": False,
|
|
},
|
|
_allow_unknown_configs=True,
|
|
)
|
|
# __sphinx_doc_end__
|
|
# fmt: on
|
|
|
|
|
|
def calculate_rr_weights(config: TrainerConfigDict) -> List[float]:
|
|
"""Calculate the round robin weights for the rollout and train steps"""
|
|
if not config["training_intensity"]:
|
|
return [1, 1]
|
|
|
|
# Calculate the "native ratio" as:
|
|
# [train-batch-size] / [size of env-rolled-out sampled data]
|
|
# This is to set freshly rollout-collected data in relation to
|
|
# the data we pull from the replay buffer (which also contains old
|
|
# samples).
|
|
native_ratio = config["train_batch_size"] / (
|
|
config["rollout_fragment_length"]
|
|
* config["num_envs_per_worker"]
|
|
* config["num_workers"]
|
|
)
|
|
|
|
# Training intensity is specified in terms of
|
|
# (steps_replayed / steps_sampled), so adjust for the native ratio.
|
|
weights = [1, config["training_intensity"] / native_ratio]
|
|
return weights
|
|
|
|
|
|
class DQNTrainer(SimpleQTrainer):
|
|
@classmethod
|
|
@override(SimpleQTrainer)
|
|
def get_default_config(cls) -> TrainerConfigDict:
|
|
return DEFAULT_CONFIG
|
|
|
|
@override(SimpleQTrainer)
|
|
def validate_config(self, config: TrainerConfigDict) -> None:
|
|
# Call super's validation method.
|
|
super().validate_config(config)
|
|
|
|
# Update effective batch size to include n-step
|
|
adjusted_rollout_len = max(config["rollout_fragment_length"], config["n_step"])
|
|
config["rollout_fragment_length"] = adjusted_rollout_len
|
|
|
|
@override(SimpleQTrainer)
|
|
def get_default_policy_class(
|
|
self, config: TrainerConfigDict
|
|
) -> Optional[Type[Policy]]:
|
|
if config["framework"] == "torch":
|
|
return DQNTorchPolicy
|
|
else:
|
|
return DQNTFPolicy
|
|
|
|
@ExperimentalAPI
|
|
def training_iteration(self) -> ResultDict:
|
|
"""DQN training iteration function.
|
|
|
|
Each training iteration, we:
|
|
- Sample (MultiAgentBatch) from workers.
|
|
- Store new samples in replay buffer.
|
|
- Sample training batch (MultiAgentBatch) from replay buffer.
|
|
- Learn on training batch.
|
|
- Update remote workers' new policy weights.
|
|
- Update target network every target_network_update_freq steps.
|
|
- Return all collected metrics for the iteration.
|
|
|
|
Returns:
|
|
The results dict from executing the training iteration.
|
|
"""
|
|
train_results = {}
|
|
|
|
# We alternate between storing new samples and sampling and training
|
|
store_weight, sample_and_train_weight = calculate_rr_weights(self.config)
|
|
|
|
for _ in range(store_weight):
|
|
# Sample (MultiAgentBatch) from workers.
|
|
new_sample_batch = synchronous_parallel_sample(
|
|
worker_set=self.workers, concat=True
|
|
)
|
|
|
|
# Update counters
|
|
self._counters[NUM_AGENT_STEPS_SAMPLED] += new_sample_batch.agent_steps()
|
|
self._counters[NUM_ENV_STEPS_SAMPLED] += new_sample_batch.env_steps()
|
|
|
|
# Store new samples in replay buffer.
|
|
self.local_replay_buffer.add_batch(new_sample_batch)
|
|
|
|
global_vars = {
|
|
"timestep": self._counters[NUM_ENV_STEPS_SAMPLED],
|
|
}
|
|
|
|
for _ in range(sample_and_train_weight):
|
|
# Sample training batch (MultiAgentBatch) from replay buffer.
|
|
train_batch = self.local_replay_buffer.sample(
|
|
self.config["train_batch_size"]
|
|
)
|
|
|
|
# Old-style replay buffers return None if learning has not started
|
|
if train_batch is None or len(train_batch) == 0:
|
|
self.workers.local_worker().set_global_vars(global_vars)
|
|
break
|
|
|
|
# Postprocess batch before we learn on it
|
|
post_fn = self.config.get("before_learn_on_batch") or (lambda b, *a: b)
|
|
train_batch = post_fn(train_batch, self.workers, self.config)
|
|
|
|
# Learn on training batch.
|
|
# Use simple optimizer (only for multi-agent or tf-eager; all other
|
|
# cases should use the multi-GPU optimizer, even if only using 1 GPU)
|
|
if self.config.get("simple_optimizer") is True:
|
|
train_results = train_one_step(self, train_batch)
|
|
else:
|
|
train_results = multi_gpu_train_one_step(self, train_batch)
|
|
|
|
# Update replay buffer priorities.
|
|
update_priorities_in_replay_buffer(
|
|
self.local_replay_buffer,
|
|
self.config,
|
|
train_batch,
|
|
train_results,
|
|
)
|
|
|
|
# Update target network every `target_network_update_freq` steps.
|
|
cur_ts = self._counters[NUM_ENV_STEPS_SAMPLED]
|
|
last_update = self._counters[LAST_TARGET_UPDATE_TS]
|
|
if cur_ts - last_update >= self.config["target_network_update_freq"]:
|
|
to_update = self.workers.local_worker().get_policies_to_train()
|
|
self.workers.local_worker().foreach_policy_to_train(
|
|
lambda p, pid: pid in to_update and p.update_target()
|
|
)
|
|
self._counters[NUM_TARGET_UPDATES] += 1
|
|
self._counters[LAST_TARGET_UPDATE_TS] = cur_ts
|
|
|
|
# Update weights and global_vars - after learning on the local worker -
|
|
# on all remote workers.
|
|
with self._timers[SYNCH_WORKER_WEIGHTS_TIMER]:
|
|
self.workers.sync_weights(global_vars=global_vars)
|
|
|
|
# Return all collected metrics for the iteration.
|
|
return train_results
|
|
|
|
|
|
@Deprecated(
|
|
new="Sub-class directly from `DQNTrainer` and override its methods", error=False
|
|
)
|
|
class GenericOffPolicyTrainer(DQNTrainer):
|
|
pass
|