조금씩 차근차근 2025. 9. 21. 23:10

지난 글에선 REINFORCE 알고리즘을 공부했다.
이 알고리즘은 간단한 CartPole 예제에는 잘 작동했지만, 좀 더 복잡한 환경의 강화학습에는 그리 잘 통과하지 않는다.

 

한편, DQN의 경우 이산적인 동작 공간에서 상당히 효과적이지만, 입실론-그리디 정책같은 개별적인 정책 함수가 필요하다는 단점이 있다.

 

이번 글에서는 REINFORCE의 장점과 DQN의 장점을 합친 actor-critic(행위자-비평자)라는 알고리즘을 소개한다.
이 모델은 여러 문제 영역에서 최고 수준의 성과를 낸 바 있다.


REINFORCE 알고리즘은 일반적으로 일회적 알고리즘(에피소딕, episodic algorithm)으로 구현된다.
이는 에이전트가 하나의 에피소드 전체를 끝낸 후에야 그 에피소드에서 수집한 보상들로 모델의 매개변수들을 갱신한다는 뜻이다.

 

확률적 강화학습 접근 방식들에서 정책은 상태를 받고 동작들에 관한 확률분포를 돌려주는 하나의 함수였다.
더 나은 동작일수록 더 높은 확률이 배정되므로, 그 확률분포에서 무작위로 동작을 추출(표집)하면 더 나은 동작이 더 자주 뽑힌다.
에피소드의 끝에서는 그 에피소드의 총 수익(return)을 계산한다.
본질적으로 수익은 에피소드 보상들의 가중합인데, 공식은 다음과 같다.

사실 이 식은 '부트스트래핑'을 적용하지 않아, 미래 수익에 대한 기대 가치를 적용하지 않았다.
이는 이전에 TD법을 다룰 때 한번 설명했는데,
이번 글에서도 추후 부트스트래핑에 대해 다시 언급할 예정이다.

 

[강화학습] 시간차 학습(TD), SARSA, Q-Learning

시작하기 전에, 간단하게 강화학습에서의 몬테 카를로와 DP의 특징에 대해 짚고 넘어가겠다.DP다이나믹 프로그래밍의 점화식을 통한 증분 계산을 활용한다.장점따라서 에피소드 진행 중 평가와

dev.go-gradually.me

 

 

보상과 수익은 좀 더 나은 확률분포를 산출할 수 있도록 정책 함수를 개선하는 데 쓰인다.
예를 들어 어떤 한 상태에서 동작 1을 취해서 +10의 보상을 받았다면, 그 상태에서의 동작 1의 확률이 조금 더 높아진다.
반대로, 그 상태에서 동작 2를 취해서 -20의 보상을 받았다면 동작 2의 확률은 더 낮아진다.

 

이러한 학습을 위해 알고리즘이 최소화하는 손실 함수는 다음과 같다.

우변은 "주어진 상태 S에서 동작 a의 확률의 로그에 수익 R을 곱한 값"에 음의 부호가 붙은 것이다.
따라서 이 손실을 최소화한다는 것은 그 로그 확률과 수익의 곱을 최대화하는 것에 해당한다.
REINFORCE 알고리즘은 각 에피소드에서 동작들과 보상들을 수집하고 이 손실이 최소가 되도록 정책 함수의 매개변수들을 갱신하는 과정을 여러 번 반복함으로써, 보상이 큰 동작에 더 높은 확률을 배정하도록 정책 함수를 개선한다.

 

 

이번 글에서는 Advantage Actor-Critic (A2C)라는 또 다른 종류의 정책 기울기 방법을 소개한다.
이 방법은 DQN처럼 온라인 학습의 장점을 가지되, 경험 재현이 필요하지 않다.
게다가 동작들에 관한 확률분포에서 직접 동작을 선택한다는 정책 기울기 방법의 장점도 갖고있다.

가치 함수와 정책 함수의 결합

Q-Learning 의 위력 중 하나는 환경에서 얻은 정보(보상)에서 직접 배운다는 것이다.

 

기본적으로 Q-Learning 모델은 동작의 향후 보상을 예측하는 방법을 배우는데, 그런 향후 보상을 가치(value)라 부른다.

 

예를 들어 핀볼 게임에 적용된 심층 Q 신경망은 두 가지 동작, 즉 왼쪽 패들 조작과 오른쪽 패들 조작의 가치를 배운다. 그 가치들을 동작 선택에 어떻게 활용하는지는 응용 방식에 따라 다르겠지만, 대부분의 경우는 가치가 더 큰 동작을 선택한다.

 

그와 반대로 정책 기울기 방법에서 정책 함수는 어떠한 동작들이 더 나은지를 "다소 암묵적인" 방식으로 학습한다. 핀볼에서 왼쪽 패들로 공을 쳐올려서 큰 점수를 얻었다면, 그 동작이 긍정적으로 강화되어서 다음번에 게임이 비슷한 상태가 되었을 때 그 동작이 선택될 확률이 커진다.

 

다른 말로 하면, DQN과 같은 Q-Learning 기반 알고리즘들은 훈련 가능한 함수를 이용해서 주어진 상태에서의 한 동작의 가치를 직접적으로 모델화한다.
상태들과 보상들만 관찰하면 된다는 관점에서 이는 대단히 직관적인 MDP 해법이다.
보상들을 예측하고 기대 보상이 가장 큰 동작을 선택한다는 것은 아주 자연스럽다.

 

그렇지만, 이전 글에서 보았듯이 Policy Gradient 기반 방법들도 장점이 뚜렷하다. 동작들에 관한 조건부 확률분포 P(a|S) 를 충실하게 근사할 수 있다면, 그로부터 직접 동작을 추출할 수 있다.


Advantage (이익)과 A2C

두 접근 방식 모두 나름의 장점이 있으므로, 누군가 그 둘을 결합한 방법을 만들어 낸 것은 당연한 일일 것이다.
누군가가 그런 결합된 가치-정책 학습 알고리즘을 만들어낸 과정을 따라가보자.

 

출발점은 정책망 학습 모형이다. 정책망 학습 모형의 안정성을 위해서는 다음 두 문제를 해결해야 한다.

  • 갱신 빈도를 높여서 표본 효율성을 개선해야 한다.
  • 모델 매개변수 갱신에 사용할 보상들의 분산을 줄여야 한다.

여기서 말하는 분산은 업데이트에 쓰이는 신호(리턴, 어드밴티지 등)의 산포도, 더 정확히는 그로부터 계산되는 정책 그라디언트 추정치의 분산을 의미한다.

 

보상의 분산은 수집한 표본의 크기에 의존하므로, 두 문제는 연관되어 있다.
결합된 가치-정책 알고리즘의 핵심은 가치 학습 모형을 이용해서 보상들(정책망의 훈련에 쓰이는)의 분산을 줄인다는 것이다.
좀 더 구체적으로, REINFORCE 의 손실함수에는 에피소드에서 관측한 총 수익 R을 그대로 곱하지만, 가치-정책 알고리즘에서는 다음처럼 수익에서 하나의 '기준선(baseline)'가치를 뺀 값을 곱한다.

여기서, 아래 수식 부분을 이익이라고 부른다.


즉, 이익(Advantage)이란 특정 행동 a가 그 상태 s에서 평균적인 가치 V보다 얼마나 더 좋은가를 나타낸다.

 

이산적인 동작들과 작은 이산적 상태 공간을 가진 Gridworld 게임에 대한 정책을 학습한다고 생각해보자.
각 상태에 대해 그 상태를 방문한 이후 받은 보상들의 평균을 구하고, 그 평균들을 각 상태를 인덱스로 하는 목록에 담아서 하나의 상태-가치 참조표를 만들 수 있을것이다.
그러면 참조표도 상태에 대한 가치를 돌려주므로 하나의 V(S)이다.

 

 

어떤 상태에서 정책에 따라 동작 1을 선택해서 +10의 보상이 관측되었으며, 참조표에서 조회한 그 상태의 가치가 +4라고 하면, 그 상태에서 동작 1의 이익은 10 - 4 = +6이다. 이는 주어진 상태에서 동작 1을 선택했을 때의 보상이 그 상태에서의 기존 보상들로 예측한 기대 보상보다 상당히 크다는 뜻이다.
한편, 다른 어떤 상태에서 동작 1을 취해서 역시 +10을 얻었지만 해당 상태의 기대 가치가 +15라고 하면, 이익은 10 - 15 = -5가 된다. 비록 직접적인 보상이 크더라도, 이 상태에서는 이 동작이 상대적으로 나쁜 동작인 것이다.

 

실제 응용에서는 단순한 참조표 대신, 신경망처럼 주어진 상태에 대한 기대 보상들을 예측하도록 훈련할 수 있는 "매개변수화된 모델"을 사용한다.
이 접근 방식에서는 두 개의 신경망, 즉 정책 신경망과 상태 가치 신경망(또는 동작 가치 신경망)을 함께 훈련한다.


이러한 종류의 알고리즘을 액터-크리틱 방법이라고 부르는데, 정책이 '액터'(행위자)이고(동작을 결정하는 단위라는 점에서) 가치 함수가 '크리틱'(비평자)이다(행위자의 동작이 얼마나 좋은지를 말해준다는 점에서).
수익이나 가치를 따로 사용하는 것이 아니라 이익(가치에서 수익을 뺀 값)을 정책 학습에 사용하므로, 이를 어드밴티지 액터-크리틱(advantage actor-critic, A2C)방법이라고 부른다.

부트스트래핑과 편향, 분산

정책망의 손실함수는 에피소드 끝에서 수집된 보상들에 의존한다.
에피소드가 길어서 보상이 희소한 환경에서는, "현재 보상"만 학습하게 되면 학습이 거의 진행되지 않을 위험이 있다.

 

 

Gridworld 학습을 예시로 생각해보자.
의미 있는 보상(+10 또는 -10)은 에피소드당 한 번만 주어지고 나머지 모든 동작의 보상이 -1이므로, 단순한 정책 기울기 방법은 승리 또는 패배에 결정적으로 기여한 동작을 충분히 강화하지 못한다.
반면 DQN은 부트스트래핑덕분에 보상이 희박해도 적절한 Q 가치들을 학습할 수 있다.

 

 

부트스트래핑은 예측 결과로부터 또 다른 결과를 예측하는 것을 말한다.
예를 들어 모레의 기온을 예측하는 한 가지 방법은 먼저 내일 기온을 예측하고 그로부터 모레의 기온을 예측하는 것이다.


이것이 부트스트래핑의 예이다.
첫 예측이 나쁘다면 둘째 예측은 더욱 나쁠 것이므로, 부트스트래핑은 편향(bias)의 근원이 된다.
여기서 편향은 어떤 대상(지금 예에서는 Q 값)에 대한 예측이 그 참값과 얼마나 벗어났는지를 나타내는 체계적 오차(systemic error)이다.
한편, 예측값으로 또 다른 예측을 하면 일종의 자기 일관성(self-consistency)이 생겨서 분산(variance)이 낮아진다.
온도의 예에서, 모레 기온을 내일 기온 예측값에 근거해서 예측한다면, 두 기온은 그리 다르지 않을(즉, 분산이 낮을)것이다.

 

편향과 분산은 심층학습이나 심층강화학습 뿐만 아니라 모든 기계학습에서 중요한 개념이다.
일반적으로 편향을 줄이면 분산이 커지고, 분산을 줄이면 편향이 커진다.


실제로, 기계학습 알고리즘들은 소위 정규화(Regularization)항을 두어서 훈련 도중 매개변수들의 크기를 제한하는 기법을 흔히 사용한다.
이는 너무 크거나 0보다 작은 매개변수들에 대한 벌점으로 적용해서 매개변수들의 분산을 줄이는 역할을 한다.
이러한 정규화는 기계학습 모델의 overfitting을 완화하는 데 도움이 된다.


A2C의 구성

잠재적으로 편향이 크고 분산이 작은 가치 예측과 잠재적으로 편향이 작고 분산이 큰 정책 예측을 결합한다면 편향과 분산이 둘 다 적당한, 따라서 단계별 학습 방식에서도 잘 작동하는 뭔가를 만들어 낼 수 있다.
그것이 바로 액터-크리틱 모델이다.

 

이제 비평자의 역할이 어느 정도 이해가 될 것이다.
행위자(정책망)가 동작을 취하면 비평자(상태 가치 신경망, 줄여서 가치망)는 그 동작이 좋았는지 나빴는지 말해 준다.
따라서 가끔씩만 보상 신호를 제공하는 환경에서도 행위자는 적절한 피드백을 받을 수 있다.

 

구현에서 비평자는 행위자의 손실함수의 한 항이 된다.
Q 학습에서처럼 비평자는 환경이 제공하는 보상 신호들로부터 직접 배우지만, 그 보상들은 행위자가 취한 동작들에 의존한다. 따라서 행위자도 비평자에 영향을 끼친다.
이러한 상관관계를 그림으로 표현하면 아래와 같다.

행위자는 부분적으로 비평자가 제공한 신호에 따라 훈련된다.
그렇다면, 익숙한 동작 가치 함수 대신 상태 가치 함수를 훈련에 사용하려면 어떻게 해야 할까?
동작 가치에서는 주어진 상태-동작 쌍의 기대 수익(할인된 향후 보상들의 합)을 계산해서 그 상태-동작 쌍이 바람직한 긍정적 보상으로 이어질지 아니면 나쁜 부정적 보상으로 이어질지(또는 그 중간의 어떤 보상으로 이어질지) 예측했다.
그런데 DQN에서는 가능한 각 동작에 대해 개별적인 동작 가치를 돌려주므로, 입실론-탐욕 같은 적절한 정책을 적용하면 상태 가치는 사실상 가장 큰 동작 가치에 해당한다.
즉, 상태 가치 함수는 각 동작의 동작 가치를 개별적으로 계산하는 것이 아니라 주어진 상태에서 가장 큰 동작 가치를 계산하는 함수이다.

A2C

이제 본격적으로 A2C를 구현해보자.

앞에서는 행위자와 비평자를 개별적인 두 함수로 표현했지만, 실제로는 그 둘을 출력 '단자'가 두 개인 하나의 신경망으로 결합할 수 있다.
이번 내용의 예제는 실제로 그러한 신경망을 사용할 것이다.
보통의 신경망은 하나의 벡터를 돌려주지만, 이 이중 출력 신경망은 두 개의 벡터를 돌려준다.

  • 정책 벡터
  • 가치 벡터
    가치를 계산하는 데 필요한 정보 중 일부는 주어진 정책에 따라 최선의 동작을 예측하는 데에도 유용하므로, 이처럼 정책 함수와 가치 함수가 매개변수들을 공유하게 하면 전체적인 처리 속도가 빨라진다.

일단 수도코드를 보자.

gamma = 0.9
for i in epochs:
    state = environment.get_state() # 환경의 현재 상태를 얻는다.
    value = critic(state) # 상태의 가치를 예측한다.
    policy = actor(state) # 상태의 동작들에 대한 확률분포를 예측한다.
    action = policy.sample() # 정책의 동작 확률분포에 따라 동작 하나를 추출한다.
    next_state, reward = environment.take_action(action)
    value_next = critic(next_state) # 다음 상태의 가치를 예측한다.
    advantage = reward + (gamma * value_next - value) # 보상에 다음 상태 가치와 현재 상태 가치의 차이를 더한 값을 advantage로 둔다.
    loss = -1 * policy.logprob(action) * advantage # 계산된 이익에 기초해서 동작을 강화한다.
    minimize(loss)

아주 단순화된 수도코드지만, 이 알고리즘의 핵심을 잘 보여준다.

 

중요한 부분은 advantage 계산이다.
현재 상태에서 어떤 동작을 취해서 +10의 보상을 받았으며, 현재 상태의 예측 가치가 +5이고 다음 상태의 예측 가치가 +7이라고 하자.
다음 상태의 예측은 현재 관측된 보상보다 덜 중요하므로, 다음 상태 가치에 할인률을 곱한다.
할인률이 0.9라고 하면 이득은


이다.

이 예에서는 다음 상태 가치와 현재 상태 가치의 차이가 양수이므로, 해당 동작의 전체적인 가치가 높아진다. 따라서 그 동작이 좀 더 강화된다.
미래의 상태에 대한 예측에 기초해서 현재 상태와 동작의 가치를 계산한다는 점에서 이러한 이익 계산은 부트스트래핑 효과를 낸다.

 

이번 예제가 환경으로 사용하는 CartPole은 일회적 게임이다.
따라서 에피소드 하나가 다 끝나야 갱신을 수행하는 완전한 몬테카를로 접근 방식을 사용한다면, 마지막 동작에 대한 value_next는 항상 0일 것이다(에피소드가 끝나면 다음 상태라는 것이 없으므로).
그런 경우 advantage = reward - value 가 된다.
이것이 바로 baseline value이다.

 

완전한 advantage value는 다음과 같다.


이는 1-step 학습 및 N-step 학습에 쓰인다.

N-step 학습은 에피소드의 개별 단계마다 학습을 진행하는 DP식 학습과 에피소드 전체가 끝나야 학습을 진행하는 몬테카를로 방식의 중간에 해당한다.
이름에서 짐작하듯이, N-step 학습에서는 게임을 N개의 단계만큼 진행해서 누적한 보상들로 손실을 계산하고 역전파를 수행한다.

이번 글에선 에피소드 전체(몬테카를로급)에 대한 행위자-비평자 알고리즘을 살펴보자.

위 그림은 A2C의 개요이다.
행위자-비평자 모델은 상태 가치와 동작 확률분포를 산출해야 한다.
동작 확률분포로 동작을 선택해서 보상을 받고, 그것을 상태 가치와 비교해서 Advantage를 계산한다.


그럼 CartPole 플레이를 위한 A2C 모델을 구현해보자.
전체적인 과정은 다음과 같다.

  1. 행위자-비평자 모델을 이중 출력 신경망의 형태로 구축한다.
    이 모델은 CartPole 상태를 입력받는다. 이 상태는 하나의 4차원 실수 벡터이다.
    행위자 출력 노드는 두 가지 동작에 관한 이산 확률분포에 해당하는 2차원 벡터를 출력한다.
    비평자 출력 노드는 상태 가치에 해당하는 하나의 수치를 출력한다.
    비평자를 v(s) 로, 행위자를 pi(s)로 표기하자.
  2. 에피소드 실행
    1. 하이퍼파라미터 gamma를 정의한다.
    2. 새 에피소드를 시작한다.
    3. 현재 상태 s_t의 가치 v(s_t)를 계산해서 리스트에 저장한다.
    4. 확률분포 pi(s_t)를 계산해서 목록에 저장하고, 동작 a_t를 추출해서 실행한다.
    5. 새 상태 s_t+1과 보상 r_t+1을 얻고, 보상을 리스트에 저장한다.
  3. 훈련
    1. 수익을 0으로 초기화한다. 보상 목록을 거꾸로 훑으면서 수익 R = r_i + gamma * R을 계산한다.
    2. 행위자 손실을 최소화한다.
    3. 비평자 손실을 최소화한다.
  4. 전체 에피소드 횟수를 넘지 않았다면 3번부터 다시 반복한다.

이를 파이썬 코드로 구현한 것은 다음과 같다.

이를 그림으로 표현하면 다음과 같다.

worker 함수는 새 CartPole 환경에서 하나의 에이전트를 실행한다.

각 프로세스는 각자의 환경에서 worker 함수를 실행한다.
worker를 실행하는 각 "worker 프로세스"는 개별적인 CartPole 환경과 옵티마이저를 사용하되, 둘째 인수로 주어진 A2C 모델을 공유한다.
이를 그림으로 표현하면 아래와 같다.

run episode와 update params 메소드는 아래와 같다.

def run_episode(worker_env, worker_model):  
    state = torch.from_numpy(worker_env.env.state).float()  
    values, logprobs, rewards = [], [], []  
    done = False  
    j = 0  
    while done == False:  
        j += 1  
        policy, value = worker_model(state)  
        values.append(value)  
        logits = policy.view(-1)  
        action_dist = torch.distributions.Categorical(logits=logits)  
        action = action_dist.sample()  
        logprob_ = policy.view(-1)[action]  
        logprobs.append(logprob_)  
        state_, _, terminated, truncated, info = worker_env.step(action.item())  
        state = torch.from_numpy(state_).float()  
        done = terminated or truncated  
        if done:  
            reward = -10  
            worker_env.reset()  
        else:  
            reward = 1  
        rewards.append(reward)  
    return values, logprobs, rewards  

def update_params(worker_opt, values, logprobs, rewards, clc=0.1, gamma=0.95):  
    rewards = torch.Tensor(rewards).flip(dims=(0,)).view(-1)  
    logprobs = torch.stack(logprobs).flip(dims=(0,)).view(-1)  
    values = torch.stack(values).flip(dims=(0,)).view(-1)  
    Returns = []  
    ret_ = torch.Tensor([0])  
    for r in range(rewards.shape[0]):  
        ret_ = rewards[r] + gamma * ret_  
        Returns.append(ret_)  
    Returns = torch.stack(Returns).view(-1)  
    Returns = F.normalize(Returns, dim=0)  
    actor_loss = -1 * logprobs * (Returns - values.detach())  
    critic_loss = torch.pow(values - Returns, 2)  
    loss = actor_loss.sum() + clc * critic_loss.sum()  
    loss.backward()  
    worker_opt.step()  
    return actor_loss, critic_loss, len(rewards)

이를 통해 시간에 따른 손실값 변화를 그래프로 그려보면 아쉽게도 감소 추세가 그리 뚜렷하게 나타나지 않는데, 이는 행위자와 비평자가 서로 경쟁하기 때문이다.
이는 행위자와 비평자가 서로 경쟁하기 때문이다.

  • 비평자는 최선의 수익을 산출하려 한다.
    • 그리고 수익은 행위자가 선택한 동작들에 의존한다.
  • 행위자는 비평자의 기대를 깨려 한다.
    • 행위자는 "Advantage"에 집중한다.

행위자가 비평자보다 더 빠르게 개선되면 비평자의 손실이 커지고, 비평자가 더 빠르게 개선되면 행위자의 손실이 커진다.
즉, 둘 사이에는 어느 정도 대립관계가 성립한다.

이런 대립적 훈련은 강화학습뿐만 아니라 기계학습의 다른 여러 분야에서도 위력을 발휘하는 기법이다.
예를 들어 비지도학습 알고리즘의 하나인 GAN은 행위자 및 비평자와 비슷하게 작동하는 두 모형을 이용해서 사실적인 표본을 생성하는 방법을 훈련 데이터로부터 학습한다.

요약

  • Q-Learning 알고리즘은 주어진 상태와 동작의 할인된 보상을 예측하는 방법을 학습한다.
  • 정책 학습 방법은 주어진 상태의 동작 확률분포를 배운다.
  • 행위자-비평자 모델은 Q 러닝과 Policy 러닝의 장점을 결합한다.
  • 이익(Advantage)이란 특정 행동 a가 그 상태 s에서 평균적인 가치 V보다 얼마나 더 좋은가를 나타낸다.
  • A2C 모델은 주어진 동작의 기대 가치와 실제로 관측된 보상을 비교해서 이익을 계산한다.
    • 예를 들어 기대 보상이 -1이지만 실제로는 +10의 보상으로 이어지는 동작의 이익이 기대보 상이 +9이고 이후 +10의 보상으로 이어지는 동작의 이익보다 크다.

본 내용은 심층 강화학습 인 액션도서를 참고하여 작성되었습니다.

 

심층 강화학습 인 액션 - 예스24

프로젝트로 배우는 심층 강화학습의 이론과 실제!이 책 『심층 강화학습 인 액션』은 환경이 제공하는 직접적인 피드백에 기반해서 환경에 적응하고 자신을 개선해 나가는 에이전트의 구현 방

www.yes24.com