초기에 제대로 된 환경을 학습 모델이 알지 못하기 때문에 탐험의 비중을 높이고, 점차 학습해 나갈수록 학습 모델이 환경을 정확히 알아가기 시작한다고 가정하여 입실론을 감소시켜 나간다.
with torch.no_grad():
다음 Q 함수값을 계산할 때, 계산 그래프의 생성을 막고 있다. state2에 대한 Q Value는 그냥 훈련을 위한 목푯값을 계산하는데 필요한 것일 뿐, 계산 그래프를 통해 역전파를 수행하기 위함이 아니다. 따라서 torch.no_grad() 메소드를 이용해 계산 그래프의 생성을 막고 있다.
그럼 계산 그래프를 통한 역전파는 어디에 적용되고 있는걸까?
우리가 학습해야 하는 신경망의 매개변수들은 state2가 아니라 state1에 대해 훈련해야 한다. 따라서 계산 그래프를 통한 역전파는 model(state1)로 구한 Q 값에 대해서만 적용한다.
.detach() 메소드
지금 예제에서는 newQ 계산 시 torch.no_grad() 구문을 사용했으므로 꼭 이럴 필요가 없지만, 이처럼 계산 그래프에서 노드를 떼어내는(detach: 탈착) 코드는 중요하다. 노드를 제대로 떼어내지 않으면, 모델 훈련 시 버그를 유발하기 때문에, 명시적으로 detach 메소드를 호출하는 것이 중요하다.
이제 이 결과를 살펴보자.
손실 그래프가 다소 들쭉날쭉하지만, 그래도 이동 평균이 0으로 향한다는 추세는 뚜렷하다.
이제 이 모델로 Gridworld게임의 한 에피소드를 돌려보자.
def test_model(model, mode='static', display=True):
i = 0
test_game = Gridworld(size=4, mode=mode)
state_= test_game.board.render_np().reshape(1, 64) + np.random.rand(1, 64)/10.0
state = torch.from_numpy(state_).float()
if display:
print("Initial State:")
print(test_game.display())
status = 1
while(status == 1):
qval = model(state)
qval_ = qval.data.numpy()
action_ = np.argmax(qval_)
action = action_set[action_]
if display:
print('Move #: %s; Taking action: %s'%(i, action))
test_game.makeMove(action)
state_ = test_game.board.render_np().reshape(1, 64) + np.random.rand(1, 64)/10.0
state = torch.from_numpy(state_).float()
if display:
print(test_game.display())
reward = test_game.reward()
if reward != -1:
if reward > 0:
status = 2
if display:
print("Game won! Reward: %s" % (reward, ))
else:
status = 0
if display:
print("Game Lost. Reward: %s"% (reward, ))
i += 1
if (i>15):
if display:
print("Game Lost; too many moves.")
break;
win = True if status == 2 else False
return win
print(test_model(model))
무사히 학습에 성공한것을 알 수 있었다.
사실 이는 '정적 모드'에서의 움직임을 '암기'한 것으로, 게임의 구조를 이해한 것이 아니다. 그 증명으로 게임을 random 모드로 수행하면, 손실함수 그래프는 아래와 같이 전혀 수렴하지 않는다.
이에 대한 해소는 다음 글인 ER(Experience Replay)에서 해결해볼 예정이다.