-
Notifications
You must be signed in to change notification settings - Fork 17
/
core.py
163 lines (135 loc) · 6.67 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/python
# -*- coding: utf-8 -*-
import torch.nn as nn
import torch
import numpy as np
class ActorPPO(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim), )
layer_norm(self.net[-1], std=0.1) # output layer for action
# the logarithm (log) of standard deviation (std) of action, it is a trainable parameter
self.a_logstd = nn.Parameter(torch.zeros((1, action_dim)) - 0.5, requires_grad=True)
self.sqrt_2pi_log = np.log(np.sqrt(2 * np.pi))
def forward(self, state):
return self.net(state).tanh() # action.tanh()
def get_action(self, state):
a_avg = self.net(state)
a_std = self.a_logstd.exp()
noise = torch.randn_like(a_avg)
action = a_avg + noise * a_std
return action, noise
def get_logprob_entropy(self, state, action):
a_avg = self.net(state)
a_std = self.a_logstd.exp()
delta = ((a_avg - action) / a_std).pow(2) * 0.5
logprob = -(self.a_logstd + self.sqrt_2pi_log + delta).sum(1) # new_logprob
dist_entropy = (logprob.exp() * logprob).mean() # policy entropy
return logprob, dist_entropy
def get_old_logprob(self, _action, noise): # noise = action - a_noise
delta = noise.pow(2) * 0.5
return -(self.a_logstd + self.sqrt_2pi_log + delta).sum(1) # old_logprob
class ActorDiscretePPO(nn.Module):
def __init__(self, mid_dim, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, action_dim))
self.action_dim = action_dim
self.soft_max = nn.Softmax(dim=-1)
self.Categorical = torch.distributions.Categorical
def forward(self, state):
return self.net(state) # action_prob without softmax
def get_action(self, state):
a_prob = self.soft_max(self.net(state))
# dist = Categorical(a_prob)
# action = dist.sample()
samples_2d = torch.multinomial(a_prob, num_samples=1, replacement=True)
action = samples_2d.reshape(state.size(0))
return action, a_prob
def get_logprob_entropy(self, state, action):
a_prob = self.soft_max(self.net(state))
dist = self.Categorical(a_prob)
a_int = action.squeeze(1).long()
return dist.log_prob(a_int), dist.entropy().mean()
def get_old_logprob(self, action, a_prob):
dist = self.Categorical(a_prob)
return dist.log_prob(action.long().squeeze(1))
class CriticAdv(nn.Module):
def __init__(self, mid_dim, state_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.ReLU(),
nn.Linear(mid_dim, mid_dim), nn.Hardswish(),
nn.Linear(mid_dim, 1))
layer_norm(self.net[-1], std=0.5) # output layer for Q value
def forward(self, state):
return self.net(state) # Q value
class ReplayBuffer:
def __init__(self, max_len, state_dim, action_dim, if_discrete):
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.max_len = max_len
self.now_len = 0
self.next_idx = 0
self.if_full = False
self.action_dim = 1 if if_discrete else action_dim # for self.sample_all(
self.tuple = None
self.np_torch = torch
other_dim = 1 + 1 + self.action_dim + action_dim
# other = (reward, mask, action, a_noise) for continuous action
# other = (reward, mask, a_int, a_prob) for discrete action
self.buf_other = np.empty((max_len, other_dim), dtype=np.float32)
self.buf_state = np.empty((max_len, state_dim), dtype=np.float32)
def append_buffer(self, state, other): # CPU array to CPU array
self.buf_state[self.next_idx] = state
self.buf_other[self.next_idx] = other
self.next_idx += 1
if self.next_idx >= self.max_len:
self.if_full = True
self.next_idx = 0
def extend_buffer(self, state, other): # CPU array to CPU array
size = len(other)
next_idx = self.next_idx + size
if next_idx > self.max_len:
self.buf_state[self.next_idx:self.max_len] = state[:self.max_len - self.next_idx]
self.buf_other[self.next_idx:self.max_len] = other[:self.max_len - self.next_idx]
self.if_full = True
next_idx = next_idx - self.max_len
self.buf_state[0:next_idx] = state[-next_idx:]
self.buf_other[0:next_idx] = other[-next_idx:]
else:
self.buf_state[self.next_idx:next_idx] = state
self.buf_other[self.next_idx:next_idx] = other
self.next_idx = next_idx
def extend_buffer_from_list(self, trajectory_list):
state_ary = np.array([item[0] for item in trajectory_list], dtype=np.float32)
other_ary = np.array([item[1] for item in trajectory_list], dtype=np.float32)
self.extend_buffer(state_ary, other_ary)
def sample_batch(self, batch_size):
indices = rd.randint(self.now_len - 1, size=batch_size)
r_m_a = self.buf_other[indices]
return (r_m_a[:, 0:1], # reward
r_m_a[:, 1:2], # mask = 0.0 if done else gamma
r_m_a[:, 2:], # action
self.buf_state[indices], # state
self.buf_state[indices + 1]) # next_state
def sample_all(self):
all_other = torch.as_tensor(self.buf_other[:self.now_len], device=self.device)
return (all_other[:, 0], # reward
all_other[:, 1], # mask = 0.0 if done else gamma
all_other[:, 2:2 + self.action_dim], # action
all_other[:, 2 + self.action_dim:], # action_noise or action_prob
torch.as_tensor(self.buf_state[:self.now_len], device=self.device)) # state
def update_now_len(self):
self.now_len = self.max_len if self.if_full else self.next_idx
def empty_buffer(self):
self.next_idx = 0
self.now_len = 0
self.if_full = False
def layer_norm(layer, std=1.0, bias_const=1e-6):
torch.nn.init.orthogonal_(layer.weight, std)
torch.nn.init.constant_(layer.bias, bias_const)