forked from kklipski/ALHE-projekt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
evo.py
335 lines (274 loc) · 13.8 KB
/
evo.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
from statistics import mean
from src.td3.TD3 import TD3
from src.td3.utils import ReplayBuffer
import torch
import gym
import numpy as np
import random
import scipy.stats
import itertools
class EvolutionaryTD3:
def __init__(self, n_networks, max_buffer, max_episodes, max_steps, episodes_ready, explore_prob, explore_factors,
saved_scores=10, stop_condition=100):
self.n = n_networks # liczba sieci
self.max_buffer = max_buffer
self.max_episodes = max_episodes
self.max_steps = max_steps
self.episodes_ready = episodes_ready
if len(self.episodes_ready) < n_networks:
print("episodes_ready.len() != n_networks")
raise Exception
if min(self.episodes_ready) < saved_scores:
print("episodes_ready.min() < saved_scores")
raise Exception
self.explore_prob = explore_prob - int(explore_prob)
self.explore_factors = explore_factors
self.lr = 0.001
# początkowe ostatnie x cząstkowych wyników dla wszystkich sieci ustawiamy na -100
self.last_x_scores = [[-100 for _ in range(saved_scores)] for _ in range(self.n)]
self.envs = self.create_envs()
self.policies = self.create_policies()
self.replay_buffers = self.create_replay_buffers()
self.stop_condition = stop_condition
def create_envs(self):
envs = []
for i in range(self.n):
env = gym.make('BipedalWalker-v2')
envs.append(env)
return envs
def create_policies(self):
policies = []
for i in range(self.n):
state_dim = self.envs[i].observation_space.shape[0]
action_dim = self.envs[i].action_space.shape[0]
max_action = float(self.envs[i].action_space.high[0])
policies.append(TD3(self.lr, state_dim, action_dim, max_action))
return policies
def create_replay_buffers(self):
return [ReplayBuffer() for _ in range(self.n)]
def multiply_weights(self, idx, factor):
for param in self.policies[idx].actor.parameters():
param.data.mul_(factor)
for param in self.policies[idx].actor_target.parameters():
param.data.mul_(factor)
for param in self.policies[idx].critic_1.parameters():
param.data.mul_(factor)
for param in self.policies[idx].critic_1_target.parameters():
param.data.mul_(factor)
for param in self.policies[idx].critic_2.parameters():
param.data.mul_(factor)
for param in self.policies[idx].critic_2_target.parameters():
param.data.mul_(factor)
def copy_weights(self, model_from, model_to, picking):
indexes = []
for i in range(picking):
r = random.randint(0, 2)
while 2 * r in indexes: # żeby się indeksy nie powtarzały
r = random.randint(0, 2)
indexes.append(2 * r) # 0,2,4
for idx, (param_to, param_from) in enumerate(zip(model_to.parameters(), model_from.parameters())):
if idx in indexes or idx - 1 in indexes: # 0 w indexes dla 0,1 tutaj, 2 w indexes dla 2,3 tutaj, 4 w indexes dla 4,5 tutaj
param_to.data.copy_(param_from)
def exploit(self, idx):
"""
Eksploatacja polega na jednolitym próbkowaniu innego (losowo wybranego) agenta w populacji,
a następnie porównaniu ostatnich 10 cząstkowych nagród przy użyciu Welch’s t-test.
Jeśli próbkowany agent ma wyższą średnią cząstkową nagrodę i spełnia warunki t-test,
wagi z hiperparametrami są kopiowane do obecnego agenta.
:param idx: indeks sieci, dla której wywołujemy exploit()
"""
exploit_flag = False
exploit_num = 0
# losujemy indeks sieci różnej od obecnej
random_idx = random.randrange(self.n)
while random_idx == idx:
random_idx = random.randrange(self.n)
# wybieramy lepszą sieć
best_net_idx, mean_diff = self.pick_net(idx, random_idx)
exploit_idx = best_net_idx
# jeśli wylosowana sieć okazała się być lepsza
if idx != best_net_idx:
exploit_flag = True
# kopiujemy tylko część wag z nowej sieci - im większa jest różnica między średnimi cząstkowymi nagrodami
# dla rozpatrywanych sieci, tym więcej wag zostanie podmienionych (ich ilość zmienia się progowo)
# kopiujemy 1 albo 2 wylosowane tensory
if 0 <= mean_diff < 25:
to_pick = 1
else:
to_pick = 2
exploit_num = to_pick
self.copy_weights(self.policies[best_net_idx].actor, self.policies[idx].actor, to_pick)
self.copy_weights(self.policies[best_net_idx].actor_target, self.policies[idx].actor_target, to_pick)
self.copy_weights(self.policies[best_net_idx].critic_1, self.policies[idx].critic_1, to_pick)
self.copy_weights(self.policies[best_net_idx].critic_1_target, self.policies[idx].critic_1_target, to_pick)
self.copy_weights(self.policies[best_net_idx].critic_2, self.policies[idx].critic_2, to_pick)
self.copy_weights(self.policies[best_net_idx].critic_2_target, self.policies[idx].critic_2_target, to_pick)
print("<exploit", idx, "> Wczytano nowe wagi z sieci nr ", random_idx, "\t",
mean(self.last_x_scores[idx]), " vs. ",
mean(self.last_x_scores[best_net_idx]))
else:
print("<exploit", idx, "> Wagi zostają, obecne są lepsze od sieci nr ", random_idx, "\t",
mean(self.last_x_scores[idx]), " vs. ",
mean(self.last_x_scores[best_net_idx]))
return exploit_flag, exploit_idx, exploit_num
def explore(self, idx):
if random.random() < 0.5:
multiplier = self.explore_factors[0]
else:
multiplier = self.explore_factors[1]
self.multiply_weights(idx, multiplier)
print("<explore", idx, "> Przemnożono wagi przez ", multiplier)
return multiplier
def pick_net(self, idx1, idx2):
"""
Porównanie nagród cząstkowych dwóch sieci przy użyciu Welch's t-test
:param idx1: obecna sieć
:param idx2: losowo wybrana sieć
:return: indeks najlepszej sieci
"""
statistic, pvalue = scipy.stats.ttest_ind(self.last_x_scores[idx1], self.last_x_scores[idx2],
equal_var=False)
if pvalue <= 0.05:
# przeszło welch's t-test, teraz porównanie średnich z ostatnich 10 wyników
mean_curr = mean(self.last_x_scores[idx1])
mean_rand = mean(self.last_x_scores[idx2])
mean_diff = abs(mean_curr - mean_rand)
if mean_curr > mean_rand:
return idx1, 0 # obecna sieć lepsza
else:
return idx2, mean_diff # losowo wybrana sieć lepsza
else:
return idx1, 0 # nie przeszło welch's t-test
def train(self):
######### Hyperparameters #########
# env_name = "BipedalWalker-v2"
log_interval = 10 # print avg reward after interval
# random_seed = 0
gamma = 0.99 # discount for future rewards
batch_size = 100 # num of transitions sampled from replay buffer
exploration_noise = 0.1
polyak = 0.995 # target policy update parameter (1-tau)
policy_noise = 0.2 # target policy smoothing noise
noise_clip = 0.5
policy_delay = 2 # delayed policy updates parameter
max_timesteps = 2000 # max timesteps in one episode
directory = "./preTrained/" # save trained models
filename = "TD3"
###################################
log_f = open("log.txt", "w+")
avg_reward = np.zeros(self.n)
# Liczba iteracji algorytmu
for episode in range(self.max_episodes):
for idx in range(self.n):
# logging variables:
ep_reward = 0
exploit_flag = False
exploit_num = 0
exploit_idx = 0
explore_flag = False
explore_multiplier = 0
stuck_pen_flag = False
stuck_pen = 0
stand_flag = False
state = self.envs[idx].reset()
no_reward_counter = -100 # jeśli dojdzie do 100 uznajemy, że robot się nie porusza
start_no_reward_counter = 0
for t in range(max_timesteps):
# self.envs[idx].render()
# select action and add exploration noise:
action = self.policies[idx].select_action(state)
action = action + np.random.normal(0, exploration_noise, size=self.envs[idx].action_space.shape[0])
action = action.clip(self.envs[idx].action_space.low, self.envs[idx].action_space.high)
# take action in env:
next_state, reward, done, _ = self.envs[idx].step(action)
# jeśli robot stoi w miejscu przez stop_condition kroków, zakończ pętlę
if no_reward_counter == 0:
start_no_reward_counter = reward
# print("START=",start_no_reward_counter)
if start_no_reward_counter-0.05 < reward < start_no_reward_counter+0.05:
# print("COUNTER=",no_reward_counter)
no_reward_counter += 1
else:
no_reward_counter = 0
# jeśli robot stoi w miejscu dłuższy czas, ustaw flagę
stand_flag = no_reward_counter > self.stop_condition
if stand_flag:
stuck_pen_flag = True
if ep_reward <= 0:
reward = reward + -100
stuck_pen = -100
elif 0 < ep_reward <= 50:
reward = reward + -49
stuck_pen = -49
elif 50 < ep_reward <= 100:
reward = reward + -36
stuck_pen = -36
elif 100 < ep_reward <= 150:
reward = reward + -25
stuck_pen = -25
elif 150 < ep_reward <= 200:
reward = reward + -16
stuck_pen = -16
elif 200 < ep_reward <= 250:
reward = reward + -9
stuck_pen = -9
else:
reward = reward + -4
stuck_pen = -4
self.replay_buffers[idx].add((state, action, reward, next_state, float(done)))
state = next_state
avg_reward[idx] += reward
ep_reward += reward
# if episode is done then update policy:
if done or t == (max_timesteps - 1) or stand_flag:
# print("UPDATING POLICES...")
self.policies[idx].update(self.replay_buffers[idx], t, batch_size, gamma, polyak, policy_noise, noise_clip, policy_delay)
break
self.append_score(idx, ep_reward)
# print avg reward every log interval:
if episode % log_interval == 0:
print("Episode: {}\tAverage Reward: {}".format(episode, int(avg_reward[idx] / log_interval)))
avg_reward[idx] = 0
# każda sieć ma swój max epizodów, po których zostaną wywołane metody exploit i explore
if episode % self.episodes_ready[idx] == 0 and episode != 0:
exploit_flag, exploit_idx, exploit_num = self.exploit(idx)
if exploit_flag and random.random() < self.explore_prob:
explore_multiplier = self.explore(idx)
explore_flag = True
# logging updates:
log_f.write('{},{},{}'.format(idx, episode, ep_reward))
if stuck_pen_flag:
log_f.write(",stuck penalty:" + str(stuck_pen))
if exploit_flag:
log_f.write(",exploit:" + str(exploit_idx) + "," + str(exploit_num))
if explore_flag:
log_f.write(",explore:" + str(explore_multiplier))
if stand_flag:
log_f.write(",standing")
log_f.write("\n")
log_f.flush()
ep_reward = 0
# if ep reward > 300 then save and stop traning:
if ep_reward >= 300:
print("########## Solved! ###########")
name = filename + '_net_{}_solved'.format(idx)
self.policies[idx].save(directory, name)
log_f.close()
break
if episode % 100 == 0 and episode != 0:
self.save_nets(episode, directory, filename)
def append_score(self, idx, new_score):
"""
Usuwa ostatni wynik z 10 ostatnich cząstkowych wyników i dodaje nowy
:param idx: indeks sieci
:param new_score: nowy wynik
"""
self.last_x_scores[idx] = self.last_x_scores[idx][1:]
self.last_x_scores[idx].append(new_score)
def save_nets(self, episode, directory, filename):
idx_td3 = 0
for td3 in self.policies:
name = filename + '_ep_{}_net_{}'.format(episode, idx_td3)
td3.save(directory, name)
idx_td3 = idx_td3 + 1
print('Models saved successfully')