#!/usr/bin/env python # coding=utf-8 ''' @Author: John @Email: johnjim0816@gmail.com @Date: 2020-06-09 20:25:52 @LastEditor: John LastEditTime: 2022-09-27 15:43:21 @Discription: @Environment: python 3.7.7 ''' import random import numpy as np import torch import torch.nn as nn import torch.optim as optim class DDPG: def __init__(self, models,memories,cfg): self.device = torch.device(cfg['device']) self.critic = models['critic'].to(self.device) self.target_critic = models['critic'].to(self.device) self.actor = models['actor'].to(self.device) self.target_actor = models['actor'].to(self.device) # copy weights from critic to target_critic for target_param, param in zip(self.target_critic.parameters(), self.critic.parameters()): target_param.data.copy_(param.data) # copy weights from actor to target_actor for target_param, param in zip(self.target_actor.parameters(), self.actor.parameters()): target_param.data.copy_(param.data) self.critic_optimizer = optim.Adam(self.critic.parameters(), lr=cfg['critic_lr']) self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=cfg['actor_lr']) self.memory = memories['memory'] self.batch_size = cfg['batch_size'] self.gamma = cfg['gamma'] self.tau = cfg['tau'] def sample_action(self, state): state = torch.FloatTensor(state).unsqueeze(0).to(self.device) action = self.actor(state) return action.detach().cpu().numpy()[0, 0] @torch.no_grad() def predict_action(self, state): ''' predict action ''' state = torch.FloatTensor(state).unsqueeze(0).to(self.device) action = self.actor(state) return action.cpu().numpy()[0, 0] def update(self): if len(self.memory) < self.batch_size: # when memory size is less than batch size, return return # sample a random minibatch of N transitions from R state, action, reward, next_state, done = self.memory.sample(self.batch_size) # convert to tensor state = torch.FloatTensor(np.array(state)).to(self.device) next_state = torch.FloatTensor(np.array(next_state)).to(self.device) action = torch.FloatTensor(np.array(action)).to(self.device) reward = torch.FloatTensor(reward).unsqueeze(1).to(self.device) done = torch.FloatTensor(np.float32(done)).unsqueeze(1).to(self.device) policy_loss = self.critic(state, self.actor(state)) policy_loss = -policy_loss.mean() next_action = self.target_actor(next_state) target_value = self.target_critic(next_state, next_action.detach()) expected_value = reward + (1.0 - done) * self.gamma * target_value expected_value = torch.clamp(expected_value, -np.inf, np.inf) value = self.critic(state, action) value_loss = nn.MSELoss()(value, expected_value.detach()) self.actor_optimizer.zero_grad() policy_loss.backward() self.actor_optimizer.step() self.critic_optimizer.zero_grad() value_loss.backward() self.critic_optimizer.step() # soft update for target_param, param in zip(self.target_critic.parameters(), self.critic.parameters()): target_param.data.copy_( target_param.data * (1.0 - self.tau) + param.data * self.tau ) for target_param, param in zip(self.target_actor.parameters(), self.actor.parameters()): target_param.data.copy_( target_param.data * (1.0 - self.tau) + param.data * self.tau ) def save_model(self,path): from pathlib import Path # create path Path(path).mkdir(parents=True, exist_ok=True) torch.save(self.actor.state_dict(), f"{path}/actor_checkpoint.pt") def load_model(self,path): self.actor.load_state_dict(torch.load(f"{path}/actor_checkpoint.pt"))