()
| 361 | # |
| 362 | |
| 363 | def optimize_model(): |
| 364 | if len(memory) < BATCH_SIZE: |
| 365 | return |
| 366 | transitions = memory.sample(BATCH_SIZE) |
| 367 | # Transpose the batch (see https://stackoverflow.com/a/19343/3343043 for |
| 368 | # detailed explanation). This converts batch-array of Transitions |
| 369 | # to Transition of batch-arrays. |
| 370 | batch = Transition(*zip(*transitions)) |
| 371 | |
| 372 | # Compute a mask of non-final states and concatenate the batch elements |
| 373 | # (a final state would've been the one after which simulation ended) |
| 374 | non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, |
| 375 | batch.next_state)), device=device, dtype=torch.bool) |
| 376 | non_final_next_states = torch.cat([s for s in batch.next_state |
| 377 | if s is not None]) |
| 378 | state_batch = torch.cat(batch.state) |
| 379 | action_batch = torch.cat(batch.action) |
| 380 | reward_batch = torch.cat(batch.reward) |
| 381 | |
| 382 | # Compute Q(s_t, a) - the model computes Q(s_t), then we select the |
| 383 | # columns of actions taken. These are the actions which would've been taken |
| 384 | # for each batch state according to policy_net |
| 385 | state_action_values = policy_net(state_batch).gather(1, action_batch) |
| 386 | |
| 387 | # Compute V(s_{t+1}) for all next states. |
| 388 | # Expected values of actions for non_final_next_states are computed based |
| 389 | # on the "older" target_net; selecting their best reward with max(1).values |
| 390 | # This is merged based on the mask, such that we'll have either the expected |
| 391 | # state value or 0 in case the state was final. |
| 392 | next_state_values = torch.zeros(BATCH_SIZE, device=device) |
| 393 | with torch.no_grad(): |
| 394 | next_state_values[non_final_mask] = target_net(non_final_next_states).max(1).values |
| 395 | # Compute the expected Q values |
| 396 | expected_state_action_values = (next_state_values * GAMMA) + reward_batch |
| 397 | |
| 398 | # Compute Huber loss |
| 399 | criterion = nn.SmoothL1Loss() |
| 400 | loss = criterion(state_action_values, expected_state_action_values.unsqueeze(1)) |
| 401 | |
| 402 | # Optimize the model |
| 403 | optimizer.zero_grad() |
| 404 | loss.backward() |
| 405 | # In-place gradient clipping |
| 406 | torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100) |
| 407 | optimizer.step() |
| 408 | |
| 409 | |
| 410 | ###################################################################### |
no test coverage detected