-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathReinforcementLearningCartpole.py
More file actions
128 lines (104 loc) · 4.6 KB
/
Copy pathReinforcementLearningCartpole.py
File metadata and controls
128 lines (104 loc) · 4.6 KB
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
# Based on: https://gym.openai.com/evaluations/eval_EIcM1ZBnQW2LBaFN6FY65g/
# Python 3.11: Coursera Lab exercise.
# check readme file for more details
from collections import deque
import random
import math
import gym
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
class DQN(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(4, 24)
self.fc2 = nn.Linear(24, 48)
self.fc3 = nn.Linear(48, 2)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.relu(x)
x = self.fc3(x)
return x
class DQNCartPoleSolver:
def __init__(self, n_episodes=1000, n_win_ticks=195, max_env_steps=None, gamma=1.0, epsilon=1.0, epsilon_min=0.01, epsilon_log_decay=0.995, alpha=0.01, alpha_decay=0.01, batch_size=64, monitor=False, quiet=False):
self.memory = deque(maxlen=100000)
self.env = gym.make('CartPole-v0')
if monitor: self.env = gym.wrappers.Monitor(self.env, '../data/cartpole-1', force=True)
self.gamma = gamma
self.epsilon = epsilon
self.epsilon_min = epsilon_min
self.epsilon_decay = epsilon_log_decay
self.alpha = alpha
self.alpha_decay = alpha_decay
self.n_episodes = n_episodes
self.n_win_ticks = n_win_ticks
self.batch_size = batch_size
self.quiet = quiet
if max_env_steps is not None: self.env._max_episode_steps = max_env_steps
# Init model
self.dqn = DQN()
self.criterion = torch.nn.MSELoss()
self.opt = torch.optim.Adam(self.dqn.parameters(), lr=0.01)
def get_epsilon(self, t):
return max(self.epsilon_min, min(self.epsilon, 1.0 - math.log10((t + 1) * self.epsilon_decay)))
def preprocess_state(self, state):
return torch.tensor(np.reshape(state, [1, 4]), dtype=torch.float32)
def choose_action(self, state, epsilon):
if (np.random.random() <= epsilon):
return self.env.action_space.sample()
else:
with torch.no_grad():
return torch.argmax(self.dqn(state)).numpy()
def remember(self, state, action, reward, next_state, done):
reward = torch.tensor(reward)
self.memory.append((state, action, reward, next_state, done))
def replay(self, batch_size):
y_batch, y_target_batch = [], []
minibatch = random.sample(self.memory, min(len(self.memory), batch_size))
for state, action, reward, next_state, done in minibatch:
y = self.dqn(state)
y_target = y.clone().detach()
with torch.no_grad():
y_target[0][action] = reward if done else reward + self.gamma * torch.max(self.dqn(next_state)[0])
y_batch.append(y[0])
y_target_batch.append(y_target[0])
y_batch = torch.cat(y_batch)
y_target_batch = torch.cat(y_target_batch)
self.opt.zero_grad()
loss = self.criterion(y_batch, y_target_batch)
loss.backward()
self.opt.step()
if self.epsilon > self.epsilon_min:
self.epsilon *= self.epsilon_decay
def run(self):
scores = deque(maxlen=100)
for e in range(self.n_episodes):
state = self.preprocess_state(self.env.reset())
done = False
i = 0
while not done:
if e % 100 == 0 and not self.quiet:
self.env.render()
action = self.choose_action(state, self.get_epsilon(e))
next_state, reward, done, _ = self.env.step(action)
next_state = self.preprocess_state(next_state)
self.remember(state, action, reward, next_state, done)
state = next_state
i += 1
scores.append(i)
mean_score = np.mean(scores)
if mean_score >= self.n_win_ticks and e >= 100:
if not self.quiet: print('Ran {} episodes. Solved after {} trials ✔'.format(e, e - 100))
return e - 100
if e % 100 == 0 and not self.quiet:
print('[Episode {}] - Mean survival time over last 100 episodes was {} ticks.'.format(e, mean_score))
self.replay(self.batch_size)
if not self.quiet: print('Did not solve after {} episodes 😞'.format(e))
return e
if __name__ == '__main__':
agent = DQNCartPoleSolver()
agent.run()
agent.env.close()