ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [강화학습] chatgpt랑 공부하는 강화학습: Q-learning, FrozenLake - 2
    배우기 - computer로 하는거 2025. 4. 10. 22:27

    지난번에는 학습, 실행 코드를 정리했었다. 이번 글에서는 하나씩 천천히 살펴보도록 하겠다. (전체 코드는 이전 글 참조) 

    1. main 함수:

    우선 메인함수는 아래와 같다. main 함수는 학습을 수행하고, 실제로 특정 환경에서 학습된 agent를 동작시키는 역할을 한다.

    def main():
        train_env = gym.make("FrozenLake-v1", map_name="4x4", is_slippery=True)
        play_env = gym.make("FrozenLake-v1", map_name="4x4", is_slippery=True, render_mode="human")
    
        q_table = train_on_frozen_lake(train_env)  # 학습만 수행
        play_trained_agent(play_env, q_table)      # 학습된 q_table로 결과 보기
    
    if __name__ == '__main__':
        main()

    train_env는 학습하는 환경, play_env는 실행하는 환경이다. render_mode는 각 환경에서 실행되는 결과를 눈으로 확인할수 있는 부분이다. render_mode 옵션으로는 ansi, human 등을 지정할 수 있는데 둘의 차이는 아래와 같다.

    ansi human
    🚶 에이전트 경로:
      (Left)
    SFFF
    FHFH
    FFFH
    HFFG

      (Left)
    SFFF
    FHFH
    FFFH
    HFFG


    ...

      (Left)
    SFFF
    FHFH
    FFFH
    HFFG

      (Down)
    SFFF
    FHFH
    FFFH
    HFFG

    🎉 성공적으로 Goal 도달!


    ansi에서 각 필드 위의 방향과 결과가 다른 것은 is_slippery 옵션을 True 했기 때문에 랜덤으로 움직일 확률이 있어서 그렇다.
    이때, render_mode="human"으로 하기 위해서는 pygame 을 설치해야 한다.

    pip install pygame 명령어로 설치할수도 있는데, "gymnasium[toy-text]" 방식이 더 깔끔하고 추천된다고 한다.


    2. 학습

    학습 코드는 아래와 같다.

    def train_on_frozen_lake(env):
        rewards = []
    
        # 하이퍼파라미터 설정
        alpha = 0.8     # 학습률 (learning rate)
        gamma = 0.99    # 할인율
        epsilon = 1.0   # 탐험 확률 (epsilon-greedy)
        epsilon_min = 0.1
        epsilon_decay = 0.9995
        episodes = 10000 # 에피소드 수
    
        # Q-테이블 초기화: 상태 수 x 행동 수
        q_table = np.zeros([env.observation_space.n, env.action_space.n])
    
        # 학습 루프
        for _ in tqdm(range(episodes)):
            state, _ = env.reset()
            done = False
            total_reward = 0
    
            while not done:
                # 행동 선택: 탐험 or 활용 (epsilon-greedy)
                if np.random.uniform(0, 1) < epsilon:
                    action = env.action_space.sample()  # 무작위 행동
                else:
                    action = np.argmax(q_table[state])
    
                # 환경에 행동 적용 -> 다음 상태, 보상, 종료 여부 등 받기
                next_state, reward, terminated, truncated, _ = env.step(action)
                done = terminated or truncated
    
                # Q 테이블 업데이트
                q_table[state, action] = q_table[state, action] + alpha * (
                    reward + gamma * np.max(q_table[next_state]) - q_table[state, action]
                )
    
                # 상태 업데이트
                state = next_state
    
                # epsilon 감소
                if epsilon > epsilon_min:
                    epsilon *= epsilon_decay
    
                total_reward += reward
    
            rewards.append(total_reward)
    
        # 학습 완료 후 Q 테이블 출력
        print("Q-table")
        print(q_table)
    
        # 누적 성공률 (100 에피소드마다 평균)
        window = 100
        moving_avg = [np.mean(rewards[i - window:i + 1]) for i in range(window, len(rewards))]
    
        plt.plot(moving_avg)
        plt.title("🎯 Moving Average of Success Rate")
        plt.xlabel("Episode")
        plt.ylabel("Success Rate")
        plt.grid(True)
        plt.show()
    
        return q_table

     

    2.1. 하이퍼 파라미터

    학습에 사용하는 하이퍼파라미터는 아래와 같다.

    • alpha: 학습률 (learning rate)
    • gamma: 할인율
    • epsilon: 탐험 확률 (epsilon-greedy)
    • episodes: 에피소드 수

    alpha는 일반적인 기계학습, 딥러닝에사용하는 학습률인것 같고, 에피소드 수는 epoch, 이나 몇 스텝을 돌릴지에 대한 값이다.

    gamma, epsilon은 생소해서 chatGPT한테 물어봤다.


    2.1.1 gamma:

    라고 물어보니,

    좋은 질문이야! gamma = 0.95 처럼 나오는 할인율(Discount Factor) 은 **"미래 보상을 얼마나 중요하게 생각할 것인가"**를 정하는 값이야.

    라고 한다. 뒤에 더 길게 설명해줬는데 정리하면 
    gamma가 크면 미래 보상을 더 중요시하고
    gamma가 작으면 당장 눈앞의 이득만 추구한다
    고 보면 되겠다. (0 ~ 1 로 세팅)


    2.1.2. epsilon:


    2.2. 학습

    하이퍼 파라미터 이후 코드는 아래와 같다.

    2.2.1. q_table

        # Q-테이블 초기화: 상태 수 x 행동 수
        q_table = np.zeros([env.observation_space.n, env.action_space.n])

    q_table은 최초에 전부 0으로 설정한다. 그리고 q_table이란

    저러고나서 한참 또 설명하는데, 내가 한번 보면서 정리를 해보겠다.

    Q-table: 어떤 상태(State)에서 행동(action)을 취했을 때 얻을수 있는 보상(reward, Q)이 들어있는 테이블이다. 즉,

    이런 식이다.

    2.2.2. 학습 루프

    그다음 바로 학습 루프 안으로 들어가는데,

        # 학습 루프
        for _ in tqdm(range(episodes)):
            state, _ = env.reset()
            done = False
            total_reward = 0

    총 episodes 만큼 반복하고, 현재 에피소드에서 상태(state)를 초기화한다.

    2.2.2.1. action

    이제 학습 코드의 나머지 부분을 살펴보겠다. 

            while not done:
                # 행동 선택: 탐험 or 활용 (epsilon-greedy)
                if np.random.uniform(0, 1) < epsilon:
                    action = env.action_space.sample()  # 무작위 행동
                else:
                    action = np.argmax(q_table[state])

    epsilon에 따른 분기처리하는 부분은 위에서 살펴봤고, action은 1~4 사이의 값이고 각 값은 방향을 나타낸다. 처음 chatGPT가  FrozenLake 설명해줄때 말해줬는데, 까먹어서 또 물어봤다.

    근데 글 쓰면서 확인해보니 앞에서는 chatGPT가 다르게 말했었다.. 이런

    일단 다 정리하고 다시 확인해보겠다. chatGPT 랑 공부할때는 이런 디테일한 부분은 잘 확인해봐야겠다.

    2.2.2.2. 상태 업데이트

    다음 코드는 아래와 같다. (while문 내부)

                # 환경에 행동 적용 -> 다음 상태, 보상, 종료 여부 등 받기
                next_state, reward, terminated, truncated, _ = env.step(action)
                done = terminated or truncated
    
                # Q 테이블 업데이트
                q_table[state, action] = q_table[state, action] + alpha * (
                    reward + gamma * np.max(q_table[next_state]) - q_table[state, action]
                )
    
                # 상태 업데이트
                state = next_state

    일단 env.step(action)을 통해 action을 입력받고, action에 대해 다음 상태, 보상, 종료 여부 등을 

    라고 한다.

    q_table 을 실제로 업데이트하는 아래 부분에 대해서 chatGPT는 

    라고 설명해줬다.

    그리고 state = next_state를 통해 state를 next_state로 업데이트하고, 한 에피소드 당 이 과정을 done = True일 때까지 반복한다. (epsilon 관련 부분은 위 하이퍼파라미터 부분 참조)

    즉, 매 에피소드마다 구멍에 빠지거나 골에 도달할때까지 q_table에 있는 값(액션에 대한 보상)에 따라 action을 선택하고 그 action으로 다시 q_table을 업데이트하는 것을 반복하는 것이다. 그리고 각 에피소드의 reward (성공 1, 실패 0)을 저장해둔다. (필수는 아님)

    2.3. 학습결과 확인

        # 누적 성공률 (100 에피소드마다 평균)
        window = 100
        moving_avg = [np.mean(rewards[i - window:i + 1]) for i in range(window, len(rewards))]
    
        plt.plot(moving_avg)
        plt.title("🎯 Moving Average of Success Rate")
        plt.xlabel("Episode")
        plt.ylabel("Success Rate")
        plt.grid(True)
        plt.show()

     

    100 에피소드 당 성공률의 이동평균

    위 그래프를 보면 6,000 스텝까지는 성공률이 거의 0이다가 이후에 성공하기 시작하며 학습 완료시점에는 20~30퍼센트까지 올라가는 것을 확인할 수 있다.

    다음 글에서는 학습이 끝난 q_table 을 이용해 agent가 실제로 어떻게 동작하는지 확인해보겠다.

    [강화학습] chatgpt랑 공부하는 강화학습: Q-learning, FrozenLake - 3

     

     
Designed by Tistory.