CHƯƠNG 3: ÁP DỤNG HỌC TĂNG CƯỜNG VÀO GAME MARIO
3.5 Triển khai các thuật toán
3.5.1 Định nghĩa lớp DQN:
- Đầu tiên ta xây dựng mô hình Deep Learning để phân tích dữ liệu đầu vào.
Mạng nơ ron dùng để ước lượng có các lớp sau:
28
self.online = nn.Sequential(
nn.Conv2d(in_channels=4, out_channels=32, kernel_size=(8,8), stride=4),
nn.ReLU(),
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=(4,4), stride=2),
nn.ReLU(),
nn.Conv2d(in_channels=64, out_channels=64, kernel_size=(3,3), stride=1),
nn.ReLU(),
nn.Flatten(),
nn.Linear(3136, 512),
nn.ReLU(),
nn.Linear(512, output_dim), )
- Ban đầu mô hình mạng nơ ron để tính giá trị đích sẽ sao chép giá trị tham số của mô hình giá trị ước lượng:
self.target = copy.deepcopy(self.online)
- Các tham số của mô hình này không cần tính đạo hàm nên ta để chức năng tự động tính đạo hàm của nó là False:
for p in self.target.parameters():
p.requires_grad = False
- Đầu tiên xây dựng class DQN, trong hàm khởi tạo __init__(), ta tạo và gán giá trị cho các thông số sau:
+ Số lượng hành động
self.action_size = action_size
+ Một bộ nhớ có cấu trúc dạng deque với bộ nhớ 50000 self.replay_buffer = deque(maxlen=5000)
+ Hệ số suy giảm:
self.gamma = 0.95
+ Thời gian cập nhật tham số của giá trị đích:
self.sync_period = 10000
29
+Mạng nơ ron ta đã tạo ở trên:
self.network = DDQNSolver(self.action_dim).cuda()
- Tiếp theo là phương thức để lưu dữ liệu trải nghiệm vào bộ nhớ replay buffer và phương thức để lấy ra dữ liệu từ bộ nhớ:
def remember(self,state,next_state,action,reward, done):
def recall(self):
- Phương thức quan trọng nhất là optimize_model – dùng để tính toán và cập nhật các tham số của mạng nơ ron:
def experience_replay(self, step_reward):
+ Tính tổng phần thưởng đạt được trong một episode:
self.current_episode_reward += step_reward
+ Cứ sau 10000 hành động thì lại cập nhật tham số của giá trị đích:
if self.current_step % self.sync_period == 0:
self.network.target.load_state_dict (self.network.online.state_dict()) + Nếu như dữ liệu bên trong replay buffer chưa đủ thì không thực hiện phương thức này:
if self.batch_size > len(self.memory): return + Lấy dữ liệu từ bộ nhớ replay buffer để train:
state, next_state, action, reward, done = self.recall()
+ Cứ sau 10000 hành động thì lại cập nhật tham số của giá trị đích:
q_estimate = self.network(state.cuda(), model="online")[np.arange(0, self.batch_size), action.cuda()]
30
+ Chọn hành động có giá trị lớn nhất sử dụng mạng nơ ron của giá trị ước lượng và tính giá trị đích. Hàm no_grad () được gọi để đảm bảo torch không tính đạo hàm trong quá trình tính toán:
with torch.no_grad():
best_action = torch.argmax(self.network(next_state.cuda(), model="online"),
dim=1)
next_q = self.network(next_state.cuda(), model="target")[np.arange(0, self.batch_size), best_action]
q_target = (reward.cuda() + (1 - done.cuda().float()) * self.gamma *
next_q).float()
+Tính được td error nhờ vào giá trị đích và giá trị ước lượng có được:
td_error = q_estimate - q_target
+ Tính giá trị mất mát :
value_loss = td_error.pow(2).mul(0.5).mean()
+ Chuyển toàn bộ gradient bên trong bộ nhớ đệm về bằng 0:
self.optimizer.zero_grad()
+ Tính giá trị đạo hàm của hàm mất mát (lan truyền ngược):
value_loss.backward()
+ Cập nhật giá trị của các tham số 𝜃:
self.optimizer.step()
- Nếu muốn lưu lại giá trị các tham số của mạng nơ ron, ta viết thêm 2 phương thức nữa là:
def load_checkpoint(self, path):
def load_checkpoint(self, path):
31
3.5.2 Dueling DDQN
- Với thuật toán Dueling, mô hình mạng nơ ron thay đổi khá nhiều nên ta phải viết một mô hình mới. Ở mô hình này, giai đoạn cuối ta chia mô hình ra làm 2 output: 1 ouput của giá trị trạng thái V và 1 output bao gồm giá trị lợi thế của các hành động
self.CNN_layer = nn.Sequential(
nn.Conv2d(state_dim[0], 32, kernel_size=(8, 8), stride=4),
nn.ReLU(),
nn.Conv2d(32, 64, kernel_size=(4, 4), stride=2),
nn.ReLU(),
nn.Conv2d(64, 64, kernel_size=(3, 3), stride=1),
nn.ReLU(),
)
#Build ANN layer
self.fc1 = nn.Linear(3136, 512)
# DUELING
self.V = nn.Linear(512, 1)
self.A = nn.Linear(512, action_dim)
- Ta tính giá trị Q của các hành động ở phương thức forward:
def forward(self, state):
t = self.CNN_layer(state) t = F.relu(self.fc1(t))
A = self.A(t)
V = self.V(t).expand_as(A)
Q = V + A - A.mean(1, keepdim=True).expand_as(A) return Q