2020-03-17 20:22:37 +08:00
|
|
|
import torch
|
2020-03-18 21:45:41 +08:00
|
|
|
from torch import nn
|
2020-03-17 20:22:37 +08:00
|
|
|
import torch.nn.functional as F
|
|
|
|
|
|
|
|
from tianshou.data import Batch
|
|
|
|
from tianshou.policy import PGPolicy
|
|
|
|
|
|
|
|
|
|
|
|
class A2CPolicy(PGPolicy):
|
2020-04-06 19:36:59 +08:00
|
|
|
"""Implementation of Synchronous Advantage Actor-Critic. arXiv:1602.01783
|
|
|
|
|
|
|
|
:param torch.nn.Module actor: the actor network following the rules in
|
|
|
|
:class:`~tianshou.policy.BasePolicy`. (s -> logits)
|
|
|
|
:param torch.nn.Module critic: the critic network. (s -> V(s))
|
|
|
|
:param torch.optim.Optimizer optim: the optimizer for actor and critic
|
|
|
|
network.
|
|
|
|
:param torch.distributions.Distribution dist_fn: for computing the action,
|
|
|
|
defaults to ``torch.distributions.Categorical``.
|
|
|
|
:param float discount_factor: in [0, 1], defaults to 0.99.
|
|
|
|
:param float vf_coef: weight for value loss, defaults to 0.5.
|
|
|
|
:param float ent_coef: weight for entropy loss, defaults to 0.01.
|
|
|
|
:param float max_grad_norm: clipping gradients in back propagation,
|
|
|
|
defaults to ``None``.
|
|
|
|
"""
|
2020-03-17 20:22:37 +08:00
|
|
|
|
2020-03-19 17:23:46 +08:00
|
|
|
def __init__(self, actor, critic, optim,
|
|
|
|
dist_fn=torch.distributions.Categorical,
|
2020-03-20 19:52:29 +08:00
|
|
|
discount_factor=0.99, vf_coef=.5, ent_coef=.01,
|
2020-04-06 19:36:59 +08:00
|
|
|
max_grad_norm=None, **kwargs):
|
2020-04-08 21:13:15 +08:00
|
|
|
super().__init__(None, optim, dist_fn, discount_factor, **kwargs)
|
2020-03-19 17:23:46 +08:00
|
|
|
self.actor = actor
|
|
|
|
self.critic = critic
|
2020-03-20 19:52:29 +08:00
|
|
|
self._w_vf = vf_coef
|
|
|
|
self._w_ent = ent_coef
|
2020-03-18 21:45:41 +08:00
|
|
|
self._grad_norm = max_grad_norm
|
2020-03-17 20:22:37 +08:00
|
|
|
|
2020-04-06 19:36:59 +08:00
|
|
|
def __call__(self, batch, state=None, **kwargs):
|
|
|
|
"""Compute action over the given batch data.
|
|
|
|
|
|
|
|
:return: A :class:`~tianshou.data.Batch` which has 4 keys:
|
|
|
|
|
|
|
|
* ``act`` the action.
|
|
|
|
* ``logits`` the network's raw output.
|
|
|
|
* ``dist`` the action distribution.
|
|
|
|
* ``state`` the hidden state.
|
|
|
|
|
|
|
|
More information can be found at
|
|
|
|
:meth:`~tianshou.policy.BasePolicy.__call__`.
|
|
|
|
"""
|
2020-03-19 17:23:46 +08:00
|
|
|
logits, h = self.actor(batch.obs, state=state, info=batch.info)
|
2020-04-06 19:36:59 +08:00
|
|
|
if isinstance(logits, tuple):
|
|
|
|
dist = self.dist_fn(*logits)
|
|
|
|
else:
|
|
|
|
dist = self.dist_fn(logits)
|
2020-03-18 21:45:41 +08:00
|
|
|
act = dist.sample()
|
2020-03-19 17:23:46 +08:00
|
|
|
return Batch(logits=logits, act=act, state=h, dist=dist)
|
2020-03-17 20:22:37 +08:00
|
|
|
|
2020-04-06 19:36:59 +08:00
|
|
|
def learn(self, batch, batch_size=None, repeat=1, **kwargs):
|
2020-03-20 19:52:29 +08:00
|
|
|
losses, actor_losses, vf_losses, ent_losses = [], [], [], []
|
|
|
|
for _ in range(repeat):
|
|
|
|
for b in batch.split(batch_size):
|
|
|
|
self.optim.zero_grad()
|
|
|
|
result = self(b)
|
|
|
|
dist = result.dist
|
|
|
|
v = self.critic(b.obs)
|
|
|
|
a = torch.tensor(b.act, device=dist.logits.device)
|
|
|
|
r = torch.tensor(b.returns, device=dist.logits.device)
|
2020-03-26 11:42:34 +08:00
|
|
|
a_loss = -(dist.log_prob(a) * (r - v).detach()).mean()
|
2020-03-20 19:52:29 +08:00
|
|
|
vf_loss = F.mse_loss(r[:, None], v)
|
|
|
|
ent_loss = dist.entropy().mean()
|
2020-03-28 07:27:18 +08:00
|
|
|
|
2020-03-26 11:42:34 +08:00
|
|
|
loss = a_loss + self._w_vf * vf_loss - self._w_ent * ent_loss
|
2020-03-20 19:52:29 +08:00
|
|
|
loss.backward()
|
|
|
|
if self._grad_norm:
|
|
|
|
nn.utils.clip_grad_norm_(
|
|
|
|
self.model.parameters(), max_norm=self._grad_norm)
|
|
|
|
self.optim.step()
|
2020-04-03 21:28:12 +08:00
|
|
|
actor_losses.append(a_loss.item())
|
|
|
|
vf_losses.append(vf_loss.item())
|
|
|
|
ent_losses.append(ent_loss.item())
|
|
|
|
losses.append(loss.item())
|
2020-03-20 19:52:29 +08:00
|
|
|
return {
|
|
|
|
'loss': losses,
|
|
|
|
'loss/actor': actor_losses,
|
|
|
|
'loss/vf': vf_losses,
|
|
|
|
'loss/ent': ent_losses,
|
|
|
|
}
|