핸즈 온 머신러닝 (Hands-On Machine Learning with Scikit-Learn & TensorFlow) / 오렐리앙 제론 지음 , 박해선 옮김

을 읽고, 추후 기억을 되살릴 수 있게끔 나만의 방법으로 내용을 리뷰한다. 따라서 리뷰의 내용 별 비중이 주관적일 수 있다.

챕터 16. 강화 학습

강화 학습 (Reinforcement Learning)은 요즘 머신러닝에서 흥미진진한 분야이며 오래된 분야이다. 2017년 5월 알파고가 바둑 세계챔피언 커제를 이기며 성과의 절정에 다다랐다. 강화 학습 분야에 강력한 딥러닝을 적용했더니 상상 이상의 성능을 낸 것이다.

 

16.1 보상을 최적화하기 위한 학습

에이전트(agent)는 관측(observation)을 하고 주어진 환경(environment)에서 행동(action)을 한다. 그 결과로 보상(reward)를 받는다. 이 것이 강화학습의 주 내용이다. 이는 다양한 종류의 작업에 적용할 수 있는 정의로, 보행 로봇을 제어할 수 있고 미스 팩맨 혹은 바둑게임을 제어하는 프로그램일 수도 있으며 목표 온도를 맞추는 인공지능일 수도 있다.

 

16.2 정책 탐색

에이전트가 행동을 결정하기 위해 사용하는 알고리즘을 정책(policy)라고 한다. 이는 예를 들면 신경망 등이 될 수 있다. 정책은 결정적이지 않고 확률적일 수 있다. 이 때는 확률을 학습하게 된다. 모든 조합의 파라미터를 시도해보는 것이 정책 탐색의 한 예인데 이는 효율적이지 않다. 보통 정책 공간이 매우 크기 때문이다.

 

정책 탐색의 다른 방법으로는 유전 알고리즘이 있다. 성능이 좋은 정책이 자식 정책을 생산하도록 하는 것인데 (하나의 부모가 있으면 무성 생식, 둘 이상의 부모가 있으면 유성 생식이라고 한다.) 이 때 자식은 부모를 복사하여 무작위성을 더한 것이다. 낮은 성능의 정책을 살리는 것이 무작위성에 도움이 될 때도 있다.

 

또 다른 방법으로는 정책 파라미터에 대한 보상의 그래디언트를 평가하여 경사 상승법을 적용하는 것으로, 이를 정책 그래디언트 (policy gradient)라고 한다.

16.3 OpenAI 짐

OpenAI 짐은 다양한 종류의 시뮬레이션 환경을 제공하는 툴킷이다. pip install을 통해 virtualenv에 OpenAI를 설치라고 사용한다. (pip install --upgrade gym)

 

import gym
env = gym.make("CartPole-v0") #환경 만들기

obs = env.reset() #환경 초기화
env.render() #환경 시각화

img = env.render(mode="rgb_array") #numpy 이미지 배열

env.action_space #액션 수

action = 1
obs, reward, done, info = env.step (action) #obs : 관측값, reward : 보상, done : 종료 여부, info : 추가 정보

 

16.4 신경망 정책

CartPole 예제에서는 action이 0 또는 1이므로 0을 선택할 확률을 출력하는 신경망을 만들 수 있다. 입력은 obs에서 관측 가능한 네 값이다. 출력 확률에 따라 랜덤하게 행동을 선택한다. 행동을 랜덤으로 선택하는 이유는 새로운 행동을 탐험하는 것과 잘 할 수 있는 행동을 활용하는 것 사이에 균형을 맞추기 위해서이다. 한 번 먹어본 음식이 맛있다고 계속 먹으면 더 맛있는 음식을 평생 먹어볼 수 없다.

 

텐서플로를 이용해 신경망 구조를 정의하고, 신경망을 구성하여 추정된 확률을 기반으로 랜덤하게 행동을 선택하는 코드를 쉽게 짤 수 있다. 행동을 선택할 때에는 tf.multinomial 함수를 사용한다.

 

16.5 행동 평가: 신용 할당 문제

각 스텝에서 가장 좋은 행동이 알고 있다면 신경망을 평소처럼 훈련시킬 수 있지만, 강화 학습에서는 보상밖에 알 수 없다. 보상은 보통 드물고 지연되어 나타나기 때문에 행동이 보상에 얼마나 기여하였는지 알 수 없다. 이를 신용 할당 문제 (credit assignment problem)이라고 한다. 이 문제를 해결하기 위해 흔히 사용하는 전략은 행동이 일어난 후 각 단계마다 할인 계수 gamma를 적용한 보상을 모두 합하여 행동을 평가하는 것이다. 할인 계수가 1에 가까울수록 미래의 보상이 현재의 보상만큼 중요하게 평가된다. 주로 0.95에서 0.99의 값을 사용한다. 좋은 행동 뒤에 나쁜 행동이 뒤따라서 좋은 행동의 점수도 낮아질 수 있지만, 수 많은 행동을 평가하다보면 좋은 행동은 평균적으로 높은 점수를 받게 될 것이다.

 

16.6 정책 그래디언트

PG 알고리즘은 높은 보상을 얻는 방향의 그래디언트로 파라미터를 최적화하는 알고리즘이다. 신경망 정책이 여러 번에 걸쳐 게임을 플레이하고 매 스텝마다 선택된 행동이 더 높은 가능성을 가지도록 만드는 그래디언트를 계산한다. 또한 행동의 점수를 할인율을 적용하여 계산한다. 여러 번의 게임에 걸친 많은 행동들에 대한 점수를 정규화한다. 각 파라미터에 대하여 [그래디언트 * 점수]를 평균내어 그래디언트를 계산하고, 경사 하강법 (정확히는 상승법. 음수를 곱하여 하강법으로 구현함) 스텝을 수행한다. 나쁜 행동은 점수가 음수가 되므로 해당 행동을 행하지 않는 방향으로 경사 하강법 스텝이 수행될 것이다.

 

텐서플로를 사용하여 카트 위의 막대가 균형을 잡도록 학습하는 경우를 보자. 위에서 0을 선택할 확률을 p로 잡았으므로, 타깃값 y = 1 - action 이다. y = 1 일 때 log p를 커지게 하고 y = 0 일 때 log (1-p)를 커지게 하는 것은 cross entropy를 작게 하는 것과 같으므로 cross entropy에 경사 하강법을 적용하여 준다. 하지만 optimizer를 바로 minimize() 하지 않고, compute_gradients() 메서드를 호출한 후 rewards를 곱해주고 apply_gradients를 적용하여야 한다.

 

rewards를 곱하기 위해 모든 변수에 대해 placeholder를 만들어 리스트에 담는다. 이후 원본 보상을 받아서 할인된 전체 보상을 계산하는 함수와 여러 에피소드에 걸친 점수를 정규화하는 두 개의 함수를 만든다.

 

이제 정책을 훈련시켜야 한다. 훈련 반복마다 10번의 에피소드에 대해 정책을 실행하고 (최대 스텝 수 지정), 각 행동이 최선인 양 그래디언트를 계산하고 10번의 에피소드가 실행된 후 점수들을 discount 계산하고 정규화한다. 훈련될 모든 변수에 모든 에피소드와 스텝에 걸쳐 점수를 곱하고 placeholder에 주입 후 그래디언트 훈련 연산을 실행한다.

 

이렇게 정책을 훈련해보면 250번만 반복해도 정책이 잘 학습된다. 하지만 화면 밖으로 사라지므로 훈련을 더 반복하여 해소해야한다. 이 알고리즘은 간단하지만 강력하며, 알파고는 비슷한 PG 알고리즘인 MonteCarlo TreeSearch를 기반으로 했다. PG 알고리즘이 직접적으로 정책을 최적화시킨다면 덜 직접적으로 각 상태에 대한, 혹은 각 상태와 행동 쌍에 대한 할인된 미래 보상의 합을 추정하는 알고리즘도 있다. 이 알고리즘을 이해하려면 마르코프 결정 과정(MDP)을 알아야 한다.

 

16.7 마르코프 결정 과정

메모리가 없는 확률 과정인 마르코프 연쇄 (Markov chain)은 정해진 개수의 상태를 가지고 있으며, 각 스텝마다 한 상태에서 다른 상태로 랜덤하게 전이된다. 각 확률은 고정되어 있으며, 과거 상태와 상관없이 두 상태 (s, s')에만 의존한다. 영원히 특정 상태에 남게 된다면 그 상태를 terminal state라고 한다. 마르코프 연쇄는 다양한 역학 관계를 모델링할 수 있어서 많은 분야에서 사용된다.

 

마르코프 결정 과정은 마르코프 연쇄와 비슷하지만 각 스텝에서 에이전트는 여러 가능한 행동 중 하나를 선택할 수 있고, 행동에 따라 전이 확률이 달라진다. 또한 어떤 전이는 보상을 반환하며 에이전트는 보상을 최대화하는 정책을 찾는다.

 

마르코프 결정 과정이 주어질 때 상태 s의 최적의 상태 가치 (state value) V* (s)를 추정할 수 있다. 이 값은 에이전트가 상태 s에 도달한 후 최적으로 행동한다고 가정하고 평균적으로 기대할 수 있는 할인된 미래 보상의 합으로, 벨만 최적 방정식이 적용된다.

 

V*(s) : max SIGMA T(s, a, s') [R(s, a, s') + gamma V* (s')]

 

V*(s) 값을 0으로 초기화한 뒤 위 식에 따라 반복적으로 업데이트하면 최적의 값에 수렴하게 된다. 최적의 상태 가치는 정책을 평가할 때 유용하지만 에이전트가 해야할 일을 알려주지는 않는다. Q-Value라는 (s, a)쌍의 최적 가치를 추정하는 값을 정의하였다. 이는 상태 s에서 행동 a를 취한 후 평균적으로 기대할 수 있는 할인된 미래 보상의 합니다. V*(s)와 마찬가지로 식을 정의한 후 반복적으로 업데이트하여 수렴시킨다.

 

Q_(k+1) (s, a) : SIGMA T(s, a, s') [R(s, a, s') + gamma * max Q_k(s', a')]

 

Q를 구하면 최적의 정책은 Q*(s, a)를 최대화하는 action을 찾는 것이다.

 

16.8 시간차 학습과 Q-러닝

보통 강화 학습 문제는 마르코프 결정 과정으로 모델링될 수 있지만 전이 확률과 보상에 대해 알지 못한다. 따라서 이를 추정하기 위해 여러 번 경험을 해야한다. 시간차 학습 (Temporal Difference Learning)은 가치 반복 알고리즘과 매우 비슷하지만, MDP에 대해 일부 정보만 알고 있을 경우 사용하는 방법이다. 에이전트는 탐험 정책을 사용해 MDP를 탐험하고 실제 관측된 전이와 보상에 근거하여 업데이트한다.

 

V_(k+1) (s) : (1-alpha) V_k(s) + alpha (r + gamma * V_k(s'))

 

비슷하게 Q-Learning 알고리즘은 다음과 같다.

 

Q_(k+1) (s, a) : (1-alpha)Q_k(s,a) + alpha (r + gamma * max Q_k(s', a'))

 

alpha는 학습률이다.

 

이 때 행동 a를 훈련된 정책에 따라 결정하지 않고 랜덤으로 정한다면 off-policy 알고리즘이라고 부른다. 

 

16.8.1 탐험 정책

완전한 랜덤 정책이 모든 상태와 전이를 여러 번 경험할 수는 있지만 극단적으로 오랜 시간이 걸릴 수 있다. eps-greedy policy는 각 스텝에서 입실론 확률로 랜덤하게 행동하거나 1-입실론 확률로 정책에 따른 행동을 취한다. 이 방법의 장점은 Q-Value 추정이 점점 더 향상되기 때문에 환경에서 관심 있는 부분을 살피면서도 알려지지 않은 지역을 방문하는데 일정 시간을 쓴다는 점이다. 처음에는 eps을 크게하고 점점 작게 하는 방법이 좋다.

 

혹은, 이전에 많이 시도하지 않았던 행동을 시도하도록 식을 고치는 것으로 max Q_k(s', a') 대신 max f(Q(s', a'), N(s', a'))가 되는 행동 a'을 찾아 적용한다. 이 때 f(q,n) 은 q + K/(1+n)과 같은 탐험 함수이다.

 

16.8.2 근사 Q-러닝과 딥 Q-러닝

모든 Q-Value에 대해 추정값을 기록할 수는 없으므로 근사하는 함수를 찾아야한다. 이를 Approximate Q-Learning 이라고 한다. 상태에서 직접 뽑아낸 특성들을 선형 조합하는 방식이 권장되었었지만, 딥마인드는 심층 신경망을 사용해 특성공학 없이 더 나은 결과를 보였다. 이를 위해 사용하는 DNN을 심층 Q-네트워크 (DQN)이라고 하고, 이 과정을 심청 Q-러닝 (Deep Q-Learning) 이라고 한다.

 

DQN을 학습하는 방법은 다음과 같다. (s, a)쌍에 대해 DQN이 계산한 Q-Value는 상태 s에서 행동 a를 실행했을 때 관측된 보상 r과 그 이후에 최적으로 행동해서 얻은 할인된 가치의 합에 가까워져야한다. 이 타겟 값을 추정하기 위해 다음 상태 s'와 모든 가능한 행동 a'에 대해 DQN을 실행한 후 가장 높은 것을 골라 식을 적용한다. 즉, 타겟 값 y(s, a) = r + gamma * max Qt (s', a') 이다(Qt는 근사한 Q). 이제 타겟 값이 있으므로 경사하강법을 이용하여 DQN을 학습 시킬 수 있다.

 

딥마인드의 DQN 알고리즘에는 두 가지 변경사항이 추가로 있다.

 

- 경험하며 DQN을 훈련하는 대신, 대규모 재현 메모리에 경험 데이터를 저장하고 재현 메모리에서 batch sample을 꺼내 학습한다. 이는 배치 데이터들의 상관관계를 감소시켜 훈련을 향상시킨다.

- DQN을 두 개 훈련한다. 하나는 Online DQN으로 원래의 훈련 DQN이다. 하나는 타겟 값을 생성하는 target DQN으로, Online DQN과 같은 그래프이지만 파라미터들을 일정한 간격으로 복사해온다. 이는 피드백 루프를 감소시키고 훈련 과정을 안정화시킨다. 

 

16.9 DQN 알고리즘으로 미스 팩맨 플레이 학습하기

책의 내용이 코드 위주로 진행되고 있으므로 간단한 절차만 언급하겠다. 

 

1. OpenAI 짐의 모든 환경을 설치한다. 이후 짐을 설치하고 MsPacman-v0 환경을 만든다.

2. 이미지를 좀 잘라 줄이고 대비를 향상시키기 위한 전처리 함수를 만든다.

3. DQN은 입력을 받아 세 개의 합성곱 층을 통과시키고 512 뉴런의 완전 연결층와 9 뉴런의 완전 연결층을 통과시킨다. 원래 (s, a)쌍을 입력으로 받고 Q(s, a)를 반환하지만 MsPacman의 경우 항상 action이 9가지 이므로 s만을 입력으로 받고 9가지 액션에 대해 Q value를 추정한다.

4. 네트워크를 만드는 q_network() 함수를 정의하여 두 번 실행한다(Online DQN, target DQN). q_network()에서 모든 훈련 가능한 변수를 담고 있는 dictionary를 같이 리턴한다.

5. dictionary를 이용하여 온라인 DQN을 타겟 DQN으로 복사하는 연산을 만든다. 이 연산들을 tf.group으로 모아서 하나의 연산으로 만들면 편리하다.

6. 각 상태-행동에 대한 Q-value를 반환하기 위해서, action을 one-hot vector로 바꾸고 Q-value 출력 벡터를 곱한 뒤 원소를 첫 번째 축을 기준으로 더하는 연산을 만든다.

7. 타겟 Q-value placeholder를 만들고 손실 함수를 계산한다. 이 문제에서는 0 근처에서 2차함수고 그 밖에서는 선형인 손실을 사용한다. 이는 큰 오차로 인한 영향을 줄여주고 훈련이 안정되도록 도와준다.

8. nesterov 가속 경사 옵티마이저를 만든다. global_step 변수를 만들어 optimizer에 넣으면 변수가 증가된다.

9. init, Saver 객체를 만든다.

10. 재현 메모리를 만든다. deque를 사용하여 만들면 좋다.

11. 게임을 탐험할 에이전트를 만든다. eps-greedy 정책을 사용하고 스텝을 입력받아 점진적으로 감소시킨다.

12. 이제 훈련 단계를 만들어야 한다. 먼저 파라미터들을 설정한다.

13. 체크포인트 파일이 있으면 모델을 복원하고 아니면 변수를 초기화하여 online DQN을 target DQN으로 복사한다.

14. 전체 훈련 스텝 횟수 iteration 과 훈련 시작 이후 전체 훈련 스텝 횟수 step 이 존재한다. step이 n_step보다 크면 종료한다.

15. 게임이 재시작 되는 경우 게임을 리셋하고 게임의 도입 부분 (특정 프레임)을 건너뛴다.

16. 온라인 DQN이 어떤 행동을 할지 평가하고, 플레이 해서 그 경험을 재현 메모리에 저장한다.

17. 일정한 간격으로 온라인 DQN이 훈련 스텝으로 들어간다.

18. 메모리에서 배치를 샘플링하고 target DQN을 통해 각 데이터의 '다음 상태'에 대해 가능한 모든 행동의 Q-Value를 구해 y_val을 계산한다. 만약 게임이 끝났다면 y_val을 0으로 만들어야한다. 이후 훈련 연산을 실행한다.

19. 일정 간격으로 online DQN을 target DQN으로 복사하고 모델을 저장한다.

+ Recent posts