You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ML-For-Beginners/translations/ko/8-Reinforcement/2-Gym
leestott aa4b119744
🌐 Update translations via Co-op Translator
2 weeks ago
..
solution 🌐 Update translations via Co-op Translator 3 weeks ago
README.md 🌐 Update translations via Co-op Translator 2 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago
notebook.ipynb 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

사전 준비

이번 강의에서는 OpenAI Gym이라는 라이브러리를 사용하여 다양한 환경을 시뮬레이션할 것입니다. 이 강의의 코드는 로컬 환경(예: Visual Studio Code)에서 실행할 수 있으며, 이 경우 시뮬레이션이 새 창에서 열립니다. 온라인으로 코드를 실행할 경우, 여기에 설명된 대로 코드를 약간 수정해야 할 수도 있습니다.

OpenAI Gym

이전 강의에서는 게임의 규칙과 상태가 우리가 직접 정의한 Board 클래스에 의해 제공되었습니다. 이번에는 시뮬레이션 환경을 사용하여 균형 잡힌 막대의 물리학을 시뮬레이션할 것입니다. 강화 학습 알고리즘을 훈련하기 위한 가장 인기 있는 시뮬레이션 환경 중 하나는 Gym이며, 이는 OpenAI에서 관리합니다. 이 Gym을 사용하면 카트폴 시뮬레이션부터 아타리 게임까지 다양한 환경을 생성할 수 있습니다.

참고: OpenAI Gym에서 제공하는 다른 환경은 여기에서 확인할 수 있습니다.

먼저 Gym을 설치하고 필요한 라이브러리를 가져옵니다. (코드 블록 1):

import sys
!{sys.executable} -m pip install gym 

import gym
import matplotlib.pyplot as plt
import numpy as np
import random

연습 - 카트폴 환경 초기화하기

카트폴 균형 문제를 다루기 위해서는 해당 환경을 초기화해야 합니다. 각 환경은 다음과 관련됩니다:

  • 관찰 공간: 환경에서 얻는 정보의 구조를 정의합니다. 카트폴 문제에서는 막대의 위치, 속도 및 기타 값을 받습니다.

  • 행동 공간: 가능한 행동을 정의합니다. 우리의 경우 행동 공간은 이산적이며, 왼쪽오른쪽 두 가지 행동으로 구성됩니다. (코드 블록 2)

  1. 초기화를 위해 다음 코드를 입력하세요:

    env = gym.make("CartPole-v1")
    print(env.action_space)
    print(env.observation_space)
    print(env.action_space.sample())
    

환경이 어떻게 작동하는지 확인하려면 100단계 동안 짧은 시뮬레이션을 실행해 봅시다. 각 단계에서 action_space에서 무작위로 선택한 행동을 제공합니다.

  1. 아래 코드를 실행하고 결과를 확인하세요.

    이 코드는 로컬 Python 설치에서 실행하는 것이 좋습니다! (코드 블록 3)

    env.reset()
    
    for i in range(100):
       env.render()
       env.step(env.action_space.sample())
    env.close()
    

    다음과 유사한 이미지를 볼 수 있어야 합니다:

    균형을 잡지 못하는 카트폴

  2. 시뮬레이션 중에는 행동을 결정하기 위해 관찰값을 얻어야 합니다. 실제로 step 함수는 현재 관찰값, 보상 함수, 시뮬레이션을 계속할 가치가 있는지 여부를 나타내는 완료 플래그를 반환합니다: (코드 블록 4)

    env.reset()
    
    done = False
    while not done:
       env.render()
       obs, rew, done, info = env.step(env.action_space.sample())
       print(f"{obs} -> {rew}")
    env.close()
    

    노트북 출력에서 다음과 같은 결과를 볼 수 있습니다:

    [ 0.03403272 -0.24301182  0.02669811  0.2895829 ] -> 1.0
    [ 0.02917248 -0.04828055  0.03248977  0.00543839] -> 1.0
    [ 0.02820687  0.14636075  0.03259854 -0.27681916] -> 1.0
    [ 0.03113408  0.34100283  0.02706215 -0.55904489] -> 1.0
    [ 0.03795414  0.53573468  0.01588125 -0.84308041] -> 1.0
    ...
    [ 0.17299878  0.15868546 -0.20754175 -0.55975453] -> 1.0
    [ 0.17617249  0.35602306 -0.21873684 -0.90998894] -> 1.0
    

    시뮬레이션의 각 단계에서 반환되는 관찰 벡터는 다음 값을 포함합니다:

    • 카트의 위치
    • 카트의 속도
    • 막대의 각도
    • 막대의 회전 속도
  3. 이러한 숫자의 최소값과 최대값을 확인하세요: (코드 블록 5)

    print(env.observation_space.low)
    print(env.observation_space.high)
    

    또한 각 시뮬레이션 단계에서 보상 값이 항상 1임을 알 수 있습니다. 이는 우리의 목표가 가능한 한 오랫동안 막대를 수직에 가깝게 유지하는 것이기 때문입니다.

    실제로, 카트폴 시뮬레이션은 100번의 연속적인 시도에서 평균 보상이 195에 도달하면 해결된 것으로 간주됩니다.

상태 이산화

Q-Learning에서는 각 상태에서 무엇을 해야 할지를 정의하는 Q-테이블을 구축해야 합니다. 이를 위해 상태는 이산적이어야 하며, 더 정확히는 유한한 수의 이산 값으로 구성되어야 합니다. 따라서 관찰값을 이산화하여 유한한 상태 집합으로 매핑해야 합니다.

이를 수행하는 몇 가지 방법이 있습니다:

  • 구간으로 나누기: 특정 값의 범위를 알고 있다면 이 범위를 여러 구간으로 나누고, 해당 값이 속하는 구간 번호로 값을 대체할 수 있습니다. 이는 numpy의 digitize 메서드를 사용하여 수행할 수 있습니다. 이 경우 디지털화에 선택한 구간 수에 따라 상태 크기를 정확히 알 수 있습니다.

값을 특정 유한 범위(예: -20에서 20)로 선형 보간한 다음, 반올림하여 정수로 변환할 수 있습니다. 이는 입력 값의 정확한 범위를 모를 경우 상태 크기에 대한 제어가 약간 줄어듭니다. 예를 들어, 우리의 경우 4개의 값 중 2개는 상한/하한이 정의되어 있지 않아 상태 수가 무한해질 수 있습니다.

우리의 예제에서는 두 번째 접근법을 사용할 것입니다. 나중에 알 수 있듯이 상한/하한이 정의되지 않았음에도 불구하고, 이러한 값들은 특정 유한 범위를 벗어나는 경우가 드뭅니다. 따라서 극단적인 값의 상태는 매우 드물게 나타납니다.

  1. 모델에서 관찰값을 받아 4개의 정수 값 튜플을 생성하는 함수는 다음과 같습니다: (코드 블록 6)

    def discretize(x):
        return tuple((x/np.array([0.25, 0.25, 0.01, 0.1])).astype(np.int))
    
  2. 구간을 사용한 또 다른 이산화 방법을 탐색해 봅시다: (코드 블록 7)

    def create_bins(i,num):
        return np.arange(num+1)*(i[1]-i[0])/num+i[0]
    
    print("Sample bins for interval (-5,5) with 10 bins\n",create_bins((-5,5),10))
    
    ints = [(-5,5),(-2,2),(-0.5,0.5),(-2,2)] # intervals of values for each parameter
    nbins = [20,20,10,10] # number of bins for each parameter
    bins = [create_bins(ints[i],nbins[i]) for i in range(4)]
    
    def discretize_bins(x):
        return tuple(np.digitize(x[i],bins[i]) for i in range(4))
    
  3. 짧은 시뮬레이션을 실행하고 이산화된 환경 값을 관찰해 봅시다. discretizediscretize_bins를 모두 시도해 보고 차이가 있는지 확인하세요.

    discretize_bins는 0부터 시작하는 구간 번호를 반환합니다. 따라서 입력 변수 값이 0 근처일 경우 구간의 중간 값(10)을 반환합니다. discretize에서는 출력 값의 범위에 대해 신경 쓰지 않았기 때문에 값이 음수가 될 수 있으며, 0은 0에 해당합니다. (코드 블록 8)

    env.reset()
    
    done = False
    while not done:
       #env.render()
       obs, rew, done, info = env.step(env.action_space.sample())
       #print(discretize_bins(obs))
       print(discretize(obs))
    env.close()
    

    환경 실행을 보고 싶다면 env.render로 시작하는 줄의 주석을 제거하세요. 그렇지 않으면 백그라운드에서 실행할 수 있으며, 이 방식이 더 빠릅니다. Q-Learning 과정에서는 이 "보이지 않는" 실행을 사용할 것입니다.

Q-테이블 구조

이전 강의에서는 상태가 0에서 8까지의 숫자 쌍으로 간단했기 때문에 Q-테이블을 8x8x2 형태의 numpy 텐서로 표현하는 것이 편리했습니다. 구간 이산화를 사용하는 경우 상태 벡터의 크기도 알려져 있으므로 동일한 접근법을 사용하여 상태를 20x20x10x10x2 배열 형태로 표현할 수 있습니다(여기서 2는 행동 공간의 차원이며, 첫 번째 차원은 관찰 공간의 각 매개변수에 대해 선택한 구간 수에 해당합니다).

하지만 관찰 공간의 정확한 차원이 알려지지 않은 경우도 있습니다. discretize 함수의 경우, 일부 원래 값이 제한되지 않았기 때문에 상태가 특정 한계 내에 유지된다는 것을 확신할 수 없습니다. 따라서 약간 다른 접근법을 사용하여 Q-테이블을 딕셔너리로 표현할 것입니다.

  1. (state, action) 쌍을 딕셔너리 키로 사용하고, 값은 Q-테이블 항목 값에 해당합니다. (코드 블록 9)

    Q = {}
    actions = (0,1)
    
    def qvalues(state):
        return [Q.get((state,a),0) for a in actions]
    

    여기서 qvalues()라는 함수를 정의하여 특정 상태에 대해 가능한 모든 행동에 해당하는 Q-테이블 값을 반환합니다. Q-테이블에 항목이 없으면 기본값으로 0을 반환합니다.

Q-Learning 시작하기

이제 피터에게 균형 잡는 법을 가르칠 준비가 되었습니다!

  1. 먼저 몇 가지 하이퍼파라미터를 설정합시다: (코드 블록 10)

    # hyperparameters
    alpha = 0.3
    gamma = 0.9
    epsilon = 0.90
    

    여기서 alpha학습률로, 각 단계에서 Q-테이블의 현재 값을 어느 정도 조정해야 할지를 정의합니다. 이전 강의에서는 1로 시작하여 훈련 중에 alpha를 낮은 값으로 줄였습니다. 이번 예제에서는 단순성을 위해 이를 일정하게 유지할 것이며, 나중에 alpha 값을 조정하는 실험을 할 수 있습니다.

    gamma할인 계수로, 현재 보상보다 미래 보상을 얼마나 우선시해야 하는지를 나타냅니다.

    epsilon탐험/활용 계수로, 탐험을 선호할지 활용을 선호할지를 결정합니다. 알고리즘에서는 epsilon 비율만큼 Q-테이블 값을 기준으로 다음 행동을 선택하고, 나머지 경우에는 무작위 행동을 실행합니다. 이를 통해 이전에 탐색하지 않은 검색 공간 영역을 탐험할 수 있습니다.

    균형을 잡는 관점에서 보면, 무작위 행동(탐험)은 잘못된 방향으로의 무작위 펀치처럼 작용하며, 막대는 이러한 "실수"에서 균형을 회복하는 방법을 배워야 합니다.

알고리즘 개선

이전 강의의 알고리즘을 두 가지 방식으로 개선할 수 있습니다:

  • 평균 누적 보상 계산: 여러 시뮬레이션에 걸쳐 평균 누적 보상을 계산합니다. 5000번의 반복마다 진행 상황을 출력하며, 해당 기간 동안 누적 보상의 평균을 계산합니다. 만약 195점 이상을 얻는다면 문제를 해결했다고 간주할 수 있으며, 요구된 품질보다 더 높은 품질로 해결한 것입니다.

  • 최대 평균 누적 결과 계산: Qmax를 계산하고, 해당 결과에 해당하는 Q-테이블을 저장합니다. 훈련을 실행하면 평균 누적 결과가 때때로 감소하는 것을 알 수 있으며, 이는 상황을 악화시키는 값으로 이미 학습된 Q-테이블 값을 "파괴"할 수 있음을 의미합니다.

  1. 각 시뮬레이션에서 누적 보상을 rewards 벡터에 수집하여 나중에 그래프를 그립니다. (코드 블록 11)

    def probs(v,eps=1e-4):
        v = v-v.min()+eps
        v = v/v.sum()
        return v
    
    Qmax = 0
    cum_rewards = []
    rewards = []
    for epoch in range(100000):
        obs = env.reset()
        done = False
        cum_reward=0
        # == do the simulation ==
        while not done:
            s = discretize(obs)
            if random.random()<epsilon:
                # exploitation - chose the action according to Q-Table probabilities
                v = probs(np.array(qvalues(s)))
                a = random.choices(actions,weights=v)[0]
            else:
                # exploration - randomly chose the action
                a = np.random.randint(env.action_space.n)
    
            obs, rew, done, info = env.step(a)
            cum_reward+=rew
            ns = discretize(obs)
            Q[(s,a)] = (1 - alpha) * Q.get((s,a),0) + alpha * (rew + gamma * max(qvalues(ns)))
        cum_rewards.append(cum_reward)
        rewards.append(cum_reward)
        # == Periodically print results and calculate average reward ==
        if epoch%5000==0:
            print(f"{epoch}: {np.average(cum_rewards)}, alpha={alpha}, epsilon={epsilon}")
            if np.average(cum_rewards) > Qmax:
                Qmax = np.average(cum_rewards)
                Qbest = Q
            cum_rewards=[]
    

이 결과에서 다음을 알 수 있습니다:

  • 목표에 근접: 100번 이상의 연속 실행에서 195 누적 보상을 얻는 목표에 매우 근접하거나 실제로 달성했을 수 있습니다! 더 작은 숫자를 얻더라도, 우리는 5000번의 실행에서 평균을 내기 때문에 공식 기준에서는 100번의 실행만 필요합니다.

  • 보상이 감소하기 시작: 때때로 보상이 감소하기 시작하는데, 이는 상황을 악화시키는 값으로 이미 학습된 Q-테이블 값을 "파괴"할 수 있음을 의미합니다.

이 관찰은 훈련 진행 상황을 그래프로 나타내면 더 명확하게 보입니다.

훈련 진행 상황 그래프 그리기

훈련 중에 각 반복에서 누적 보상 값을 rewards 벡터에 수집했습니다. 이를 반복 번호에 대해 그래프로 나타내면 다음과 같습니다:

plt.plot(rewards)

원시 진행 상황

이 그래프에서는 아무것도 알 수 없습니다. 이는 확률적 훈련 과정의 특성상 훈련 세션 길이가 크게 변하기 때문입니다. 이 그래프를 더 이해하기 쉽게 만들기 위해, 예를 들어 100번의 실험에 대해 이동 평균을 계산할 수 있습니다. 이는 np.convolve를 사용하여 편리하게 수행할 수 있습니다: (코드 블록 12)

def running_average(x,window):
    return np.convolve(x,np.ones(window)/window,mode='valid')

plt.plot(running_average(rewards,100))

훈련 진행 상황

하이퍼파라미터 조정

학습을 더 안정적으로 만들기 위해 훈련 중에 일부 하이퍼파라미터를 조정하는 것이 좋습니다. 특히:

  • 학습률 alpha의 경우, 1에 가까운 값으로 시작한 다음 점차적으로 이 값을 줄이는 것이 좋습니다. 시간이 지나면 Q-테이블에서 좋은 확률 값을 얻을 수 있으며, 이를 약간만 조정하고 새 값으로 완전히 덮어쓰지 않아야 합니다.

  • epsilon 증가: epsilon을 천천히 증가시켜 탐험을 줄이고 활용을 늘리는 것이 좋습니다. 낮은 epsilon 값으로 시작하여 거의 1에 가까운 값으로 이동하는 것이 합리적일 수 있습니다.

작업 1: 하이퍼파라미터 값을 조정해 보며 더 높은 누적 보상을 얻을 수 있는지 확인하세요. 195점을 넘기고 있나요? 과제 2: 문제를 공식적으로 해결하려면 100번의 연속 실행에서 평균 보상이 195에 도달해야 합니다. 훈련 중에 이를 측정하고 문제를 공식적으로 해결했는지 확인하세요!

결과 확인하기

훈련된 모델이 실제로 어떻게 작동하는지 보는 것은 흥미로울 것입니다. 시뮬레이션을 실행하고 훈련 중과 동일한 행동 선택 전략을 따라 Q-테이블의 확률 분포에 따라 샘플링해 봅시다: (코드 블록 13)

obs = env.reset()
done = False
while not done:
   s = discretize(obs)
   env.render()
   v = probs(np.array(qvalues(s)))
   a = random.choices(actions,weights=v)[0]
   obs,_,done,_ = env.step(a)
env.close()

다음과 같은 결과를 볼 수 있을 것입니다:

균형 잡힌 카트폴


🚀도전

과제 3: 여기서는 최종 Q-테이블을 사용했지만, 이것이 최상의 결과를 제공하는 것은 아닐 수 있습니다. 우리가 가장 성능이 좋은 Q-테이블을 Qbest 변수에 저장했다는 것을 기억하세요! QbestQ로 복사하여 최상의 Q-테이블을 사용해 동일한 예제를 실행하고 차이를 확인해 보세요.

과제 4: 여기서는 각 단계에서 최상의 행동을 선택하지 않고, 해당 확률 분포에 따라 샘플링했습니다. 항상 Q-테이블 값이 가장 높은 최상의 행동을 선택하는 것이 더 합리적일까요? 이를 위해 np.argmax 함수를 사용하여 Q-테이블 값이 가장 높은 행동 번호를 찾을 수 있습니다. 이 전략을 구현하여 균형 잡기에 개선이 있는지 확인해 보세요.

강의 후 퀴즈

과제

Mountain Car 훈련하기

결론

우리는 이제 에이전트가 게임의 원하는 상태를 정의하는 보상 함수만 제공받고, 검색 공간을 지능적으로 탐색할 기회를 통해 좋은 결과를 얻는 방법을 배웠습니다. 우리는 Q-러닝 알고리즘을 이산적 환경과 연속적 환경에서 성공적으로 적용했으며, 이산적 행동을 사용했습니다.

행동 상태도 연속적이고 관찰 공간이 훨씬 더 복잡한 상황, 예를 들어 아타리 게임 화면의 이미지를 다루는 경우를 연구하는 것도 중요합니다. 이러한 문제에서는 종종 신경망과 같은 더 강력한 머신러닝 기술을 사용해야 좋은 결과를 얻을 수 있습니다. 이러한 더 고급 주제는 앞으로 진행될 고급 AI 과정에서 다룰 예정입니다.


면책 조항:
이 문서는 AI 번역 서비스 Co-op Translator를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서를 해당 언어로 작성된 상태에서 권위 있는 자료로 간주해야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다.