pytorch로 구현하는 Q-network

현재 gym 라이브러리의 FrozenLake-v1 게임을 강화학습으로 해결하기 위해 Q-Network를 pytorch로 구현하고 있습니다. 학습을 진행하면서 Loss 값이 감소하고 게임 종료가 끝났는지 여부를 반환하는 done 변수도 True로 잘 받아오면서 종료가 되는데, 이상하게 Reward 값이 계속 0.0으로 출력이 됩니다. 코드 전문은 아래와 같습니다.

import gym
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.optim import SGD


# 모델 정의
class Network(nn.Module):
    def __init__(self, n_states, n_actions):
        super(Network, self).__init__()
        
        self.n_states = n_states
        self.n_actions = n_actions
        
        self.linear = nn.Linear(in_features=self.n_states, out_features=self.n_actions)
        
    def forward(self, x):
        x = torch.Tensor([x])
        x = F.one_hot(x.to(torch.int64), num_classes=self.n_states)
        x = x.to(torch.float32)
        y = self.linear(x)
        return y

# setting environment
env = gym.make("FrozenLake-v1")

# params 
n_states = env.observation_space.n
n_actions = env.action_space.n
num_episodes = 1
exploration_prob = 0.5
gamma = 0.99
lr = 0.1


# model, criterion, optimizer
network = Network(n_states, n_actions)
criterion = nn.MSELoss()
optimizer = SGD(params=network.parameters(), lr=lr)

# train
for i in range(num_episodes):
    done = False
    all_reward = 0
    
    # init state
    current_state, current_prob = env.reset()
    
    while not done:
        # init gradients in Q-network
        optimizer.zero_grad()
        
        # create label from Q-network using `current_state`
        Qs = network(current_state)
        
        # exploration & exploitation
        if torch.rand(1) < exploration_prob:
            action = env.action_space.sample()
        else:
            action = torch.argmax(Qs).item()
        
        # step to next-state
        next_state, reward, done, _, _ = env.step(action)
        
        if done:
            Qs[0, action] = reward
        else:
            # prediction from Q-network using `next_state`
            Qs_pred = network(next_state)
            Qs[0, action] = reward + gamma * torch.max(Qs_pred).item()

            # Loss
            loss = criterion(Qs, Qs_pred)
            loss.backward()

            # update params
            optimizer.step()
        
        # update next_state to current_state
        current_state = next_state
        all_reward += reward
        
        print("Loss:", loss.item(), "Reward:", reward, "done:", done)

위 코드의 마지막 줄에 print 문을 보면 보상을 출력시키도록 했는데, 코드를 실행시키면 게임 종료가 끝날 때까지 계속 보상은 0.0 으로 출력됩니다. 위 코드가 제대로 된 코드인 걸까요? 왜 보상이 계속 0.0이 나오는지 잘 모르겠습니다. 원인이 무엇인지 답변 부탁드립니다!

  • 아래 답변은 Anthropic의 Claude-2 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요. FrozenLake-v1 게임을 풀기 위해 Q-Network를 구현하는 과정에서 보상값이 0으로만 나오는 문제를 겪고 계시군요.

제가 보기에 코드 자체는 Q-Network를 잘 구현하신 것 같습니다. 다만 보상값을 다루는 부분에서 문제가 있는 것 같아요.

FrozenLake 게임의 보상 체계를 살펴보면, 목적 지점에 도달하면 1점의 보상을 주고, 그 외의 경우에는 보상이 0입니다. 즉 게임 도중에는 보상을 주지 않는 특성이 있죠.

여기서 문제는, 에피소드가 끝날 때만 1점의 보상을 주기 때문에, 에피소드 도중에는 보상이 계속 0으로 나오는 것입니다.

if done:
    Qs[0, action] = reward

위 코드 부분에서 done 변수가 True가 될 때만 보상값을 Qs 텐서에 업데이트 하고 있습니다.

따라서 에피소드가 끝나기 전까진 보상이 계속 0이 출력되는 것이 정상입니다.

해결방안은 에피소드가 끝났을 때가 아니라 매 타임스텝마다 보상값을 Qs 텐서에 반영하는 것입니다.

Qs[0, action] = reward + gamma * torch.max(Qs_pred).item()

위 코드처럼 매 순간 보상값과 함께 Qs를 업데이트 하면 정상적으로 보상이 반영될 것 입니다.

제 수준내의 답변이었던 것 같습니다. 더 자세한 해결이 필요하시다면 comment로 질문 부탁드립니다. 감사합니다!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요, young-hun-jo님. PyTorch를 사용하여 강화학습 문제를 해결하시는 과정에서 발생한 문제에 대해 도움을 요청하신 것으로 이해했습니다. FrozenLake-v1 게임에서 Q-Network를 구현하셨는데, 학습 과정에서 Loss는 감소하지만 보상 값이 계속 0으로 나오는 문제를 겪고 계신 것 같네요. 제공해주신 코드를 살펴보고 문제의 원인을 파악해 보겠습니다.

코드를 살펴본 결과, 몇 가지 문제점을 발견했습니다. 첫째, env.reset() 메서드는 초기 상태만 반환하고 확률 값을 반환하지 않습니다. 따라서 current_state, current_prob = env.reset() 부분을 current_state = env.reset()으로 수정해야 합니다. 둘째, env.step(action) 메서드는 네 개의 값(다음 상태, 보상, 게임 종료 여부, 추가 정보)을 반환합니다. 코드에서는 다섯 개의 값을 받으려고 하고 있으므로 이 부분도 수정해야 합니다. 셋째, 보상이 0으로 나오는 문제는 FrozenLake 게임의 특성상 대부분의 스텝에서 보상이 0이고, 목표 지점에 도달했을 때만 보상이 1로 주어지기 때문일 수 있습니다. 이는 게임의 특성상 정상적인 현상일 수 있습니다.

아래는 수정된 코드입니다:

import gym
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.optim import SGD

# 모델 정의
class Network(nn.Module):
    def __init__(self, n_states, n_actions):
        super(Network, self).__init__()
        self.linear = nn.Linear(in_features=n_states, out_features=n_actions)
        
    def forward(self, x):
        x = F.one_hot(torch.as_tensor(x), num_classes=self.linear.in_features)
        x = x.float()
        return self.linear(x)

# 환경 설정
env = gym.make("FrozenLake-v1")

# 파라미터 설정
n_states = env.observation_space.n
n_actions = env.action_space.n
num_episodes = 1
exploration_prob = 0.5
gamma = 0.99
lr = 0.1

# 모델, 손실 함수, 최적화기
network = Network(n_states, n_actions)
criterion = nn.MSELoss()
optimizer = SGD(network.parameters(), lr=lr)

# 학습
for i in range(num_episodes):
    done = False
    all_reward = 0
    current_state = env.reset()  # 수정된 부분
    
    while not done:
        optimizer.zero_grad()
        Qs = network(current_state)
        
        if torch.rand(1) < exploration_prob:
            action = env.action_space.sample()
        else:
            action = torch.argmax(Qs).item()
        
        next_state, reward, done, _ = env.step(action)  # 수정된 부분
        
        if done:
            Qs[0, action] = reward
        else:
            Qs_pred = network(next_state)
            Qs_target = Qs.clone()
            Qs_target[0, action] = reward + gamma * torch.max(Qs_pred).item()
            
            loss = criterion(Qs, Qs_target)
            loss.backward()
            optimizer.step()
        
        current_state = next_state
        all_reward += reward
        
        print("Loss:", loss.item(), "Reward:", reward, "done:", done)

위 코드에서는 env.reset()env.step(action)의 반환 값을 수정하였고, 손실 계산 시 Qs_pred 대신 Qs_target을 사용하여 업데이트하도록 변경하였습니다. 이렇게 하면 실제로 게임에서 얻은 보상이 학습에 반영될 것입니다.

추가적으로, 강화학습에서는 보상이 희소할 수 있으므로 여러 에피소드를 거쳐 학습이 진행됨에 따라 점차적으로 보상을 얻는 경우가 많습니다. num_episodes를 늘려서 학습을 진행해 보시는 것도 좋을 것 같습니다.

PyTorch 관련 문서나 튜토리얼을 참고하시려면 공식 PyTorch 튜토리얼 페이지(Welcome to PyTorch Tutorials — PyTorch Tutorials 2.2.0+cu121 documentation)를 방문하시거나 한국어로 된 튜토리얼(https://tutorials.pytorch.kr)을 참고하시면 도움이 될 것입니다.

제 답변이 정확하지 않을 수도 있으니, 문제가 계속 발생한다면 추가적인 정보를 제공해주시면 더 도움을 드릴 수 있을 것 같습니다. 감사합니다!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!

아하 게임 특성 상 보상이 0으로 주어지는 군요. 답변 감사합니다!