제어 문제
On-Policy
MC 제어
그 다음에 지금 우리가 mc나 td나 전부 정책은 고정이 돼 있고 가치를 추정하는 문제인데 결국에는 우리가 이제 정책을 찾아야 되거든요
그래서 제어 문제는 뭐냐면 정책 파일을 찾는 문제입니다
그래서 예측은 v를 찾는 거고 우리가 이제 해야 될 거는 정책을 찾아야죠 정책을 찾는 건 쉬운데 왜냐하면은 우리가 이미 정책 반복에서 배웠듯이 평가를 정확하게 할 수 있으면 최선의 행동을 하면 됩니다
예측을 잘 할 수 있으면 정책을 잘 하는 거는 그렇게 어려운 부분은 아니에요
근데 여기에도 이제 약간 여러 가지 복잡한 응용이 들어갑니다
그래서 우리가 이제 정책 반복을 했죠
정책 평가를 하고 정책 개선을 하고 이거를 반복하는 게 정책 반복인데 앞에 이제 일반화된 이라는 말이 붙는데 왜냐하면 아까의 정책 평가를 할 때는 우리가 전형률을 아는 상태에서 하는 거고 지금 전형률을 모르는 상태에서도 이 아이디어 자체는 적용할 수 있다
그래서 일반화된 정책 반복이라고 부릅니다
그러니까 구체적인 거는 넣기 나름이다
이거죠
그래서 여기 이벨유에이션은 평가 그래서 파이가 있으면 평가를 해서 V를 구하고 그 다음에 여기 그리지 V 이렇게 돼 있는데 이건 뭐냐면 V를 가지고 최선의 거를 하는 거예요 최선의 거를 하면은 이 파이가 나오는 거죠
그래서 왔다 갔다 하면은 우리가 어디에서 출발을 하더라도 이거를 평가를 정확하게 하면 이 수렴을 하면 평가가 정확해지겠죠
그럼 정확한 평가를 바탕으로 내가 최선의 행동을 한다고 치면 내 정책이 바뀔 겁니다
그럼
그걸 다시 평가하면 여기로 갈 거고 그러면 그걸 가지고 최선의 행동을 한다면 여기로 갈 거고 이걸 계속 반복하면 이렇게 수렴을 해서 최적 정책 최적 가치로 반드시 수렴을 하게 되죠
물론 조건이 몇 개 있긴 하지만 어쨌든 수렴을 하게 돼 있고 그러면 우리가 어디서 출발을 해도 여기서 출발을 하든 여기서 출발을 하든 여기서 출발을 하든 여기서 출발하든 이 정책 반복을 일반화된 정책 반복을 하면은 결국에는 최적 가치 최적 정책으로 수렴을 한다
이렇게 되고요 그러면은 우리가 이제 원래 정책 반복 오리지널 정책 반복은 정책 자체를 아주 정확하게 평가를 합니다
근데 가치 반복 가치 반복에서는 어떻게 하냐면 이 정책을 완벽하게 하지 않고 그냥 대충 해요
어차피 바꿀 정책인데 이거 평가 정확하게 해서 뭐하게 대충 하고 그래도 수렴을 한다는 거예요
가치 반복 그 다음에 이제 우리가 몬테카를로에다가 제어를 붙일 수가 있어요
몬테카를로 평가를 해서 몬테카를로 제어를 할 수 있는데 여기에는 이제 문제가 생깁니다
완벽한 정책을 찾을 수가 없어요
왜냐
우리는 맨날 똑같이 행동을 하기 때문에 그렇습니다
이렇게도 해보고 저렇게도 해봐야 되는데 우리는 항상 똑같이 하거든요
그러니까 정책 반복을 쓸 수가 없어요
그러면 이걸 알면 왜 정책 반복이 되느냐
알면 내가 이렇게 했을 때 이렇게 했을 때 어떤 상태가 될지
다 알잖아요
지금 나는 항상 이 방향으로 만 가니까 이 방향으로 가면 어떻게 되는지를 알 방법이 없단 말이에요 해봐야 알죠
그래서 여기 보시면 여기는 그리딘데 여기가 입실론 그리디로 되어 있거든요
그리디 입실론 그리디 뭐냐면 내가 안 해보면 모르니까 그걸 해보겠다 이거야 가끔씩 그래서 우리가 어제 MAB 했을 때 나왔던 입실론 그리디 기억나시죠
그래서 기본적으로 내 정책으로만 하면 다른 행동을 했을 때 어떻게 되는지를 모르니까 가끔은 내가 다른 행동도 해보겠다
이런 얘기입니다
그래서 이렇게 해도 또 수렴을 합니다
어떻게 수렴을 이렇게 해요
물론 여기도 또 일정 조건이 있습니다
그거는 좀 이따 얘기 드리고 그래서 이렇게 하는 거를 몬테카를로 제어라고 해요
그래서 기본적으로 몬테카를로 제어는 가치를 추정하는 거는 우리가 앞에서 했던 몬테카를로를 사용합니다
그 다음에 이제 행동을 좀 다르게 해봐야 되는데 여기는 보통 입실론 탐욕법을 써요
그래서 탐색을 이렇게 합니다
그래서 이제 몬테카를로 제어는 몬테카를로 제어는 코드를 보면 MC Prediction을 상속을 하는데요 여기 이제 Epsilon을 정해요
그래서 Epsilon은 여기 제가 Exploration Rate라고 했는데 탐색 비율이 되겠죠
얼마나 탐색을 할 거냐
그래서 예를 들면 여기를 지금 0.1로 했는데 10%의 경우에는 가끔 탐색을 한다
이렇게 됩니다
그래서 여기 Select Action이 두 가지로 나는데 Random Number를 만들어서 그 Random Number가 0에서 1 사이로 나오는데 여기 우리 정한 Epsilon보다 작으면 Epsilon이 0.1이니까 결국 0에서 0.1 사이에서는 이걸 하고 0.1에서 1.0 사이에서는 여기가 90%죠
90%의 경우에는 우리가 가진 Q들 중에 Q 값이 제일 큰 걸로 하게 됩니다
그래서 이 Select Action 부분이 몬테카를로, Epsilon Grid을 쓰게 되고요 아 quan가했네
그다음에 저희 Surprise 그 다음에 여기 업데이트 밸류를 다시 구현을 했는데 원래 업데이트 밸류랑 코드를 보시면 거의 똑같거든요
거의 똑같은데 뭐가 다르냐면 우리가 MC Prediction에서는 V를 추정합니다
상태가치를 추정을 하는데 MC 제어에서는 Q를 추정을 해요
행동가치를 추정을 합니다
왜냐하면 제어를 해야 되기 때문에 아까는 내가 뭘 했는지는 기억할 필요가 없어요
왜냐하면 나는 정책대로 행동할 거니까 그래서 Prediction을 할 때는 항상 정책대로 행동을 하니까 내가 무슨 행동을 했는지 기억할 필요가 없죠
정책대로 했겠죠
근데 MC 제어에서는 Q를 추정을 해서 무슨 행동을 했는지를 저장을 해 놨다가 그 행동의 가치를 계산합니다
여기 보시면 에피소드에 보면 여기 Action이 있거든요
내가 어떤 행동을 했는지를 다 기록을 해 놓습니다
그래서 왜냐하면 내가 행동을 정책대로 할 때도 있고 가끔 탐색을 할 때 새로운 행동을 할 때도 있고 내가 아까 뭐라고 했고 오늘 점심 뭐 먹었고 아침에 뭐 먹었더라 맨날 편의점에 먹는데 가끔 맥무닝을 먹었다
그러면 그거를 반영을 해야 되기 때문에 Action의 Return을 반영을 해 놓습니다
그래서 코드를 비교를 해 보시면 사실 거의 똑같아요
거의 여기 G는 0, G는 0 Reverse, Reverse 이렇게 가는데 여기 이제 왼쪽이 제어이고 오른쪽이 몬테카를로인데 몬테카를로 할 때는 여기 G는 Gamma, G, Plus, Rheode G는 Gamma, Plus, Rheode 똑같은데 여기서는 제어에서는 State하고 Action을 둘 다 해가지고 State하고 Action을 다 해가지고 좀 더 키워드 릴게요 State하고 Action을 다 해가지고 Q에다가 업데이트하죠
그리고 우리가 앞시간 어제 했던 거는 State만 가져와서 V만 업데이트합니다
그 차이를 아시겠죠
사실 뭐 큰 차이가 있는 건 아니에요
그래서 이런 식으로 되는 거고 그다음에 이제 Improved Policy가 MC Prediction에는 없습니다
MC Prediction은 그냥 정책은 고정되어 있는 거고 나는 무조건 정책대로 하는 건데 지금은 제어이기 때문에 정책도 업데이트합니다
정책 업데이트는 별거 없고 여기가 최선의 행동을 하는 걸로 바꾸는 거죠
그다음에 정책 평가도 달라지는데 정책 평가는 여기 Q를 트래킹을 해가지고 내 정책에 Q를 여러 가지 행동 중에서 내 정책의 행동에 가치가 얼마가 되냐
이렇게 됩니다
이것도 보시면 크게 달라지는 건 아닌데 MC Prediction에서는 정책 평가를 할 때 V를 추적을 하거든요
그러니까 근데 여기 제어에서는 Q를 추적을 합니다
여기서는 왜 V를 추정하느냐 나는 내 정책대로 행동할 거니까 Q를 추정하나
V를 추정하나 똑같아요
왜냐하면 나는 항상 똑같은 상황에 똑같은 방식으로 행동을 하니까 V를 해도 되는데 이때는 Q를 하게 되죠
그래서 이렇게 이 부분만 달라진다
나머지는 보시면 코드가 거의 라인 바이라인으로 똑같습니다
class MCControl(MCPrediction):
def __init__(self, env, gamma=0.9, epsilon=0.1):
super().__init__(env, gamma)
self.epsilon = epsilon # Exploration rate
self.Q = np.zeros((env.observation_space.n, env.action_space.n))
self.returns = defaultdict(list)
def select_action(self, policy, state):
if np.random.rand() < self.epsilon:
return self.env.action_space.sample()
else:
return np.argmax(self.Q[state])
def update_value(self, episode):
G = 0
for t in reversed(range(len(episode))):
state, action, reward, next_state, done = episode[t]
G = self.gamma * G + reward
if (state, action) not in [(x[0], x[1]) for x in episode[:t]]:
self.returns[(state, action)].append(G)
self.Q[state][action] = np.mean(self.returns[(state, action)])
def improve_policy(self):
policy = {}
for state in range(self.env.observation_space.n):
policy[state] = np.argmax(self.Q[state])
return policy
def evaluate_policy(self, policy, num_episodes=1000):
nS = self.env.observation_space.n
nA = self.env.action_space.n
self.Q_track = np.zeros((nS, nA, num_episodes))
for e in range(num_episodes):
episode = self.generate_episode(policy)
self.update_value(episode)
self.Q_track[:, :, e] = self.Q.copy()
return self.Q, self.Q_track
def control(self, policy, num_episodes=1000):
for _ in range(num_episodes):
episode = self.generate_episode()
self.update_value(episode)
return self.improve_policy()
실험
env = SlipperyWalk(9)
agent = MCControl(env)
LEFT, RIGHT = 0, 1
pi = {s: LEFT for s in range(env.observation_space.n)}
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 0, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
SARSA
그러면 이거 몬테카를로 대신에 Td를 써도 되겠네요
그래서 이제 Td 제어가 있는데 Td 제어는 이상하게도 이 Td 제어 알고리즘을 만든 사람들이 Td 제어라고 하지 않고 Salsa 라는 이름을 붙여놨어요 나쁜 사람들입니다
이렇게 하면 배우는 사람들이 헷갈리죠
Salsa는 또 뭐야
근데 장면을 진짜 거지가 채 놨는데 왜 Salsa냐 하면 우리가 state를 S로 쓰죠
행동을 A로 씁니다
그러면 뭔가 보상을 받겠죠
그 다음에 새로운 상태가 됩 니다
그 다음에 또 무슨 액션을 하겠죠
그래서 Salsa 이렇게 했는데 정말 성의없기 짝이 없는 장면인데 실제로는 Td 제어에요
왜 Td 제어냐 하면 원래는 여기서 계속 가잖아요
이렇게 Salsa 이렇게 하면 MC인데 여기까지만 한다 그러면 한 스텝만 했잖아요
그래서 Td 제어라고 하면 될 것을 여기까지만 한다고 해서 Salsa 까지만 여기까지죠
여기까지만 하니까 Salsa 까지만 하고 끝난다 해서 Td 제어인데 정말 의미 없는 이름입니다
그 다음에 이제 onpolish Td 제어라고 해놨는데 onpolish가 있고 offpolish가 있거든요
이거는 좀 이따가 설명을 드리고 일단은 Td 제어는 Salsa 라고 한다
그래서 해보시면 되고요 그 다음에 이제 쌀사 쌀사는 다시 얘기 드리지만 사실은 Td제어입니다
그래서 MC제어를 해봤는데 MC제어에서 그럼 뭐만 바꾸면 되겠어요?
추정하는 것만 바꾸면 되겠죠
그게 쌀사가 됩니다
쌀사는 여기 보시면 여기 업데이트 밸류를 보시면 업데이트 밸류에서 여기 Td타겟을 구해 가지고 이 Td타겟은 보상하고 그 다음에 다음 상태의 다음 행동에 q를 더해 가지고 그래서 실제로 끝까지 안 가본 거죠
실제로 끝까지 가보지 않고 그냥 q값을 가지고 다음에는 이렇게 되겠지
Td에로가 그만큼 반영을 해주시면 됩니다
그래서 여기 보시면 MC 컨트롤에서 나머지 우리가 입실론 그리디를 쓴다던가 이런 거는 상속을 받기 때문에 다시 구현하지 않습니다
그다음에 이제 제너레이트 에피소드가 좀 달라지는데 내가 다음 번에 어디로 가서 뭘 했는지가 저장이 돼 있어야 되거든요
에피소드에 그래서 보시면 4, R, 4를 이제 다 저장을 해놔야 됩니다
그래서 여기 제너레이 트 에피소드가 몬테카를로랑 좀 달라지는데 몬테카를로는 어차피 처음부터까지 다 할 거니까 그 이게 따로 저장할 필요가 없는데 Td에서는 내가 다음 번에 뭘 했는지를 알아야 그 오차를 계산을 하겠죠
그래서 이제 이름 그대로 4, R, 4가 제너레이트 에피소드에 저장 됩니다
이해 되시죠?
그래서 뭐 실험을 하시면 실험 결과는 뭐 비슷합니다
어차피 사실 이 문제는 그렇게 어려운 문제가 아니라서 항상 똑같이 답을 얻을 수 있습니다
class SARSA(MCControl):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1):
super().__init__(env, gamma, epsilon)
self.alpha = alpha
def generate_episode(self, policy):
episode = []
state, info = self.env.reset()
action = self.select_action(policy, state)
done = False
while not done:
next_state, reward, done, _, _ = self.env.step(action)
next_action = self.select_action(policy, next_state)
episode.append((state, action, reward, next_state, next_action, done))
state, action = next_state, next_action
return episode
def update_value(self, episode):
for t in range(len(episode)):
state, action, reward, next_state, next_action, done = episode[t]
td_target = reward + (self.gamma * self.Q[next_state][next_action] * (not done))
td_error = td_target - self.Q[state][action]
self.Q[state][action] += self.alpha * td_error
def control(self, policy, num_episodes=1000):
for _ in range(num_episodes):
episode = self.generate_episode(policy)
self.update_value(episode)
return self.improve_policy()
agent = SARSA(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
Off-Policy
우리가 지금까지는 온폴리시로 학습을 했는데 온폴리시는 폴리시가 정책이죠
정책 위에서 학습을 한다는 건데 뭐냐면 여기 숨어있는 말은 내 정책 위에서 학습을 하고 있다 이거야
그래서 행동정책이랑 학습정책이 똑같습니다
내가 지금 뭘 학습하냐면 가치를 학습하는 거죠
내가 가치를 학습하는 정책이 내가 행동하는 정책이에요
그래서 살살하던가
MC제어는 모두 온폴리시 학습입니다
내 정책을 가지고 학습을 하는 거예요
실제로 그 정책으로 행동을 하고 학습을 하는데 두 가지 문제가 있어요 첫 번째는 행동을 한다는 게 비용이 굉장히 크단 말이에요
우리가 로봇을 제어를 해가지고 어떤 공정에서 작업을 하는데 이거를 내가 강화학습으로 하겠다 그러면은 우리가 지금은 계속 환경이 컴퓨터 내에 있는 가상환경인데 실제 환경에서 로봇을 제어를 해야 되니까 비용이 굉장히 크겠죠
그리고 제어를 잘못해가지고 로봇이 부품에 손상을 입히거나 이러면 다 돈이란 말이에요
그래서 이거를 실제 라인에 적용을 할 수도 없고 그렇다고 그 로봇을 우리가 이 파이선으로 인바이러먼트를 구현을 하자니 너무 일이 복잡하죠
왜냐면 로봇이 발생시킬 수 있는 모든 상황을 내가 파이선 코드로 시뮬레이션을 해줘야 되잖아요
그럼 배보다 배꼽이 더 크죠
강화학습하는 것보다 그 다음에 또 다른 문제는 이제 입실론 탐색을 하는 비율을 정하는데 이 탐색을 하는 비율을 너무 크게 하면은 행동 가치가 제대로 추정이 안 됩니다
왜냐면은 가치라는 거는 내가 지금 말 그대로 쌀 사람 말이에요
그럼 내가 이 다음에 무슨 행동을 하느냐에 따라서 내가 지금 하는 행동의 가치가 결정이 되거든요
그러면 내가 내일 아침에 일찍 일어날 거기 때문에 지금 일찍 자는 행동이 가치가 있는 행동이 되는 거죠
그죠
예를 들면 여러분 내일 토요일에 늦게 일어나서 일찍 자는 행동이 되는 거죠
예를 들면 여러분이 내일 토요일이에요 늦게 일어나서 돼요
그럼 굳이 일찍 잘 필요 있습니까?
재밌게 놀고 늦게 자면 되잖아요
그래서 우리가 불금 하는 거잖아요
내일 늦게 일어나도 되니까 지금 불타는 금요일을 보내는 게 가치가 있는 행동이 된단 말이에요
다음에 행동에 따라서 내 행동의 가치가 결정이 되는데 입실론 그리디를 하면은 다음에 내가 할 행동이 A였다가 갑자기 A프라임으로 랜덤하게 바뀐단 말이에요
그러면은 이 행동의 가치가 달라지는 거죠 내일 아침에 원래는 내가 내일 토요일이라 가지고 지금 불금을 보낼 수 있는데 갑자기 아침에 랜덤하게 전화가 와서 갑자기 회사 나오라고 할 수도 있다
그러면은 지금 불금을 보내는 게 의미가 없게 되는 거죠
별로 좋은 가치가 있는 행동이 아니게 되는 거죠
근데 심지어 실제로 그러면 또 상관없는데 지금 입실론 그리디는 실제로 그런 것도 아니고 내가 그냥 단순히 탐색을 하기 위해서 다르게 해보는 거잖아요
사실 입실론 그리디는 실제에서는 쓰지 않을 거란 말이에요 현실에서는 없는 것이기 때문에 입실론이 너무 커버리면은 가치추정이 잘못되고 정책을 잘못 추정하게 됩니다
그래서 온폴리시는 이 두 가지 문제가 있어요
그래서 이거를 극복하는 방법이 뭐냐
오프폴리시 학습인데 오프폴리시는 오프라고 하면 탈 정책 이런 거죠
내 정책에서 오프 벗어나 있다
내 정책을 꺼버리는 거예요
그래서 이거는 내 정책인데 내가 지금 가치를 학습하려는 정책을 내 정책 내 정책을 하고 행동 정책이 달라요
그럼 이 행동 정책은 뭐냐
이건 남이죠
우리도 실제로 이렇게 하잖아요
남이 하는 거를 가만히 보는 거예요
저 사람이 어떻게 하나
이렇게 보고 아 저 사람이 하는 꼬라지를 보니까 내가 이렇게 하면 잘 되겠네
나는 저렇게 하지 말아야지 이렇게 하는 거잖아요
그거를 구현을 한 게 오프폴리시입니다
제가 항상 얘기하지만 강학습은 딴 얘기가 아닙니다
우리 맨날 하는 거예요
내가 예를 들면은 오토바이 같은 거 큰 오토바이 이런 거 보통 위험하다고 잘 안 타잖아요
여러분이 타서 넘어져보고 안 타는 게 아니라 시속 300km 되는 오토바이 이런 거 타고 남들이 저런 사고 나는구나
난 타지 말아야지 이렇게 되는 거잖아요
남이 하는 경험을 보면은 내가 어떻게 될지 알 수가 있습니다
그래서 다른 정책 남의 정책으로 한 행동을 가지고 내 거의 가치를 추정을 할 수 있고 그다음에 데이터를 재활용을 할 수 있습니다
내가 그래서 예를 들면 정책을 어떤 정책을 썼어요
그러면 이거를 우리가 정책을 계속 개선하기 때문에 내 정책도 고정된 게 아니라 계속 새로운 정책 더 새로운 정책 계속 바뀐단 말이에요
그럼 내가 예전에 했던 행동들을 보면서 이거를 내 거의 학습에다 써먹을 수가 있는 거죠
내가 지금은 더 이상 학생이 아니지만 그럼 나의 정책이 바뀌었죠
내가 학생 때를 생각해 보니까 그때 경험 중에 내가 지금 써먹을 만한 것들이 있단 말이에요
그때 데이터를 지금도 써먹을 수 있는 거죠
그러면은 더 효율성이 증가하겠죠 학습에 왜냐면 제가 방금 했던 얘기 중에 하나가 뭡니까
1000번에서 수렴을 안 하면 10,000번 하고 10,000번에서 수렴을 안 하면 10,000번 하고 10,000번에서 수렴을 하면 100,000번 하라고 했는데 그거를 좀 줄여줄 수가 있습니다
데이터를 효율적으로 쓰니까 그래서 이게 오프폴리시 학습이다 그러면은 아 좋다
이런 느낌이 들지만 세상에 좋은 것만 있지는 않습니다
당연히 오프폴리시 학습에도 수많은 문제들이 생겨요
그거를 또 해결하는 게 강학습에 굉장히 주된 과제가 됩니다
중요도 샘플링
그래서 오프폴리시 학습을 여러 가지 방법으로 할 수 있는데 한 가지 아이디 어는 중요도 샘플링이라는 것을 하는 거예요
임포턴스 샘플링이라고 하는데 이거는 우리의 정책, 우리 정책은 파이로 써놓은 거고 이 mu는 남의 정책입니다
여기 이제 내 정책, 남의 정책 그래서 어떻게 하냐면 어떤 경험을 반영을 할 때 이 비율만큼만 반영을 하자
이거야
mu는 남의 정책이고 pi는 내 정책의 비율인데 남의 정책에서는 10%를 하는데 내 정책에서는 30%를 한다 그러면은 어떤 반영을 할 때 3배 더 강하게 반영하면 되겠죠
반대로 그 사람은 예를 들면 이걸 많이 해요
50%를 하는데 나는 25% 밖에 안 한다 그러면은 그거를 반영할 때는 1분의 1만 반영하면 됩니다
그래서 이런 식으로 하는 게 중요도 샘플링이에요
그래서 남의 거에서 반영을 할 때 반영 비율을 확률의 비율대로 하자
이런 얘긴데 그러면은 여기서 바로 듣는 의문이 그럼 얘가 0이면 어떻게 돼?
이런 거죠
mu가 0이면 남의 절대 안 할 행동을 내가 하고 있다
그럼 이거 계산을 어떻게 해야 하나?
이런 문제가 생깁니다
그래서 이제 중요도 샘플링으로 오프폴리시 학습을 할 수 있는데 그러니까 쌀사의 경우에 원래는 여기가 TD5차죠
여기가 TD 목표죠 TD 목표를 계산을 한 다음에 남들이 이 행동을 할 확률하고 내가 이 행동을 할 확률을 계산해서 저 사람은 예를 들면 저런 행동을 많이 하는데 10번 하는데 나는 저 행동을 안 해
그러면 그 5차는 1분의 1만 반영을 하면 됩니다
근데 저 사람은 가끔 저런 행동을 하는데 나는 저거 많이 하는데 그러면 10배로 반영을 해 주면 됩니다
문제는 이거를 중요도 샘플링에 문제를 통해서 이거를 중요도 샘플링의 문제가 뭐냐면 그러면 남이 안 하는 걸 내가 하려면 어떻게 되느냐
계산이 안 되는 거죠
0분의...
무한대가 여기가 되어버려서 계산을 할 수가 없어요
그래서 아 좋다 말았네
이렇게 됩니다
그래서 이제 중요도 샘플링은 실제로 쓰지 않습니다
실제로 쓰지 않고 사람이 또 고민에 빠지는 거예요
중요도 샘플링으로 하면 될 줄 알았는데 안 되네
Q-Learning
그래서 이제 Q 학습이라는 게 나와요
Q 학습은 이름만 보면 Q가 행동가치니까 행동가치 당연히 학습하지
이렇게 생각할 수 있는데 이름만 보시고 예단을 하시면 곤란합니다
사실 이름만 따지면 MC제어도 Q는 학습을 하고요 우리 코드에서 봤죠
Salsa도 Q는 학습을 합니다
그래서 다 Q는 학습을 해요
그래야 정책을 짜니까 같이 평가를 해야 되잖아요
Q 학습의 정확한 이름은 오프 폴리시 T.D.제어 입니다
T.D.제어를 하는데 오프 폴리시를 하는 거예요
Q-학습에서, offpolicy.td 제어에서 관건은 뭐냐면 남의 행동을 가지고 내가 지금 내 행동에 가치를 추정을 해야 되는데 어떻게 하냐면 우리가 다음 상태에서 실제로 내가 한 행동을 사용을 하면 살사가 되고요 Q-러닝에서는 남의 행동을 보고 있잖아요
그래서 지금 남이 한 행동을 보고 있기 때문에 예를 들어서 나 같으면 여기서 여기 다음에 이걸 하고 그 다음에 이걸 했을 텐데 지금 남이 한 행동을 보고 있는데 남은 여기서 나는 여기서 a1을 하고 a2를 했을 텐데 남은 여기서 a3를 하고 앉았습니다
그러면 그 다음에 어떻게 됐을 거냐 한번 볼까요?
남은 여기서 a3를 하고 앉았습니다
그러면 그 다음에 어떻게 됐을 거냐 하면 남은 여기서 다시 a4를 했겠죠
그래서 이거는 뭐 어쩔 수 없고 네가 그렇게 행동하는 건 어쩔 수 없고 나 같았으면 여기서 a4를 안 하고 그 다음에 a5를 했을 거야
그래서 이 뒷부분에는 상대방이 실제로 한 행동이 아니라 나라면 이렇게 했을 거다
이거를 채워 넣었습니다
그래서 나는 어떻게 했겠어요?
나는 가치가 높은 행동을 했겠죠
Q가 나의 Q인데 지금 행동을 남의 행동을 보는 거지 Q는 남의 Q는 몰라요
여러분이 남들이 지금 뭘 하는지 그걸 학습하고 있는 건 아니어서 Q는 나의 Q인데 나의 Q를 기준으로 나는 이 행동을 했을 거야
하지만 이 a는 남의 행동이죠 남의 행동 남이 시점 T의 한 행동을 보고 너는 그렇게 했니?
나라면?
그거는 뭐 어쩔 수 없는데 나는 그 다음에 이렇게 했을 거야
그래서 이런 방식으로 하게 됩니다
그래서 이런 식으로 학습을 한다
이 Salsa는 a8도 내 행동 이것도 내 행동입니다
둘 다 내 행동이고 여기서는 Q러닝에서는 a8는 남의 행동이고 그래서 a8 플러스 1이 또 들어가려 하지 않고 그럼 남의 행동이잖아요
남의 행동을 안 놓고 나라면 이렇게 했을 거야
이거를 채워 넣는 거죠
그래서 예를 들면 어떤 게 있을까요?
예를 들면 누가 오토바이를 타다가 넘어지는 걸 봤어요
그럼 여러분이 넘어졌다면 어떻게 했습니까?
병원에 가겠죠
보통은 어떤 사람은 넘어져도 병원 안 가잖아요
그래서 아이고고고 하고 넘어진 걸 본 다음에 야 나 같았으면 저 다음에 병원에 갔겠지 그러면 내가 오토바이를 탈 때 얻을 수 있는 가치는 오토바이 타다 넘어지고 병원 가는 거 근데 그 사람은 병원 안 가요
그러면 그 사람은 병원 안 가요
그 사람은 야 오토바이 타다 넘어졌지만 난 병원 안 가니까 돈도 굳었네 아픈 건 뭐 참으면 되지
이러면 그 사람은 계속 오토바이 탈 건데 나 같으면 병원 가서 돈 깨지고 아프고 나 같으면 안 탄다
이렇게 되는 거죠
그래서 여기서 그 살사의 Q러닝 여기 함수를 보시면 업데이트 밸류 이게 달라지는데 리워드는 실제로 받은 리워드이기 때문에 상관이 없고 여기 보시면은 다음에 여기 곱해지는 데가 우리가 어떻게 다르냐면 계속 비교하면서 봐야 되니까 약간 어려운데요
살사에서 업데이트 밸류는 셀프 Q에 다음 상태에서 실제로 내가 다음 행동을 여기 Td타겟 계산할 때 사용을 합니다
Td타겟은 보상이란 그 V 다음 상태의 가치가 되는데 다음 상태에 실제로 내가 한 행동을 Td타겟으로 써요
근데 Q러닝에서는 어차피 지금 남의 행동을 보고 있기 때문에 이 액션은 지금 남의 한 행동입니다
그래서 나라면 이때 이렇게 쓸 텐데 이렇게 된 거 그래서 지금 보시면 여기는 그냥 Q에 NextState, NextAction인데 Q에 NextState 한 다음에 NextAction이 안 나오고 MP.max가 나오는 거죠
나는 최선행동을 할 거니까 나라면 저 상황에서 최선을 이렇게 할 거니까 그래서 나머지는 똑같습니다
타겟을 계산하면 그 다음에 타겟이랑 이거랑 반영하는 건 똑같아요
그 다음에 이제 사실 제너레이트 에피소드는 다시 구현할 필요가 없었는데 뭐가 달라졌냐면 약간 숨은 그림 찾기 같은데 원래 SARSA에서는 제너레이트 에피소드에 다음에 했던 행동을 저장을 해놓거든요
여기서는 네가 뭘 했는지는 나는 모르겠고 너 그 다음에 네가 무슨 행동한 건 나는 관심 없어
나는 그러면 이렇게 할 거야
이렇게 해 놓고 그래서 이 차이 때문에 하는데 사실 이거는 저장을 해도 그만이기 때문에 상관은 없습니다 상관은 없는데 그냥 제가 코드 짜다 보면은 그냥 약간 짤 때는 이걸 줄이는 게 합리적이라고 생각했는데 다시 보니까 괜히 쓸데없이 다시 짰네요
어쨌든 그 차이 밖에 없다
이거는 실질적인 차이는 없고 사실 Update Value 하나만 다시 짜주시면 됩니다
그래서 이제 Q학습을 해 보시면 어쨌든 오른쪽으로 가는 걸로 수렴하는데 지금은 사실 남의 정책이라고 하지만 사실은 그냥 내 정책 가지고 하고 있어요
내 정책 가지고 하는데 그게 왜 남의 정책입니까?
라고 할 수 있는데 정확하게 내 정책은 아닌 거죠
왜냐하면 실제로 이 구현을 보시면 결국에는 내 정책으로 하는데
아니 그럼 내 정책으로 내 정책을 하니까 사실 SARSA랑 똑같은 거 아닙니까 할 수 있지만 약간 다른 게 뭐냐면 내 정책에다가 지금 입실론 그리디가 붙어 있어요
이거 전체를 그냥 남의 정책으로 보는 거죠
입실론 그리디를 하는 건 내가 나중에 실제로 할 때는 실행할 부분이 아니잖아요
그냥 나는 지금 학습하고 있는 단계니까 이것도 해보고 저것도 해보는 거지
난 나중에 가면은 이것도 해보고 저것도 해보는 거는 하지 않을 거야
그래서 거기에서 생기는 괴리가 있습니다
그래서 그런 측면에서 남의 정책이라고 하는 거죠
그래서 SARSA에서는 예를 들면은 내가 내가 지금 어떤 행동을 하고 AT를 하고 AT 플러스 1에서는 여기서 탐색적으로 다른 행동을 하면 다른 행동한 거를 반영을 하는데 Q러닝 에서는 나는 그때 탐색 안 해
나는 최대로 가치가 있는 행동을 할 거야 탐색적 행동을 실제로 했지만 안 한 것처럼 이렇게 계산을 하게 됩니다
그게 Q러닝의 논리입니다
class QLearning(SARSA):
def generate_episode(self, policy):
episode = []
state, info = self.env.reset()
done = False
while not done:
action = self.select_action(policy, state)
next_state, reward, done, _, _ = self.env.step(action)
episode.append((state, action, reward, next_state, done))
state = next_state
return episode
def update_value(self, episode):
for t in range(len(episode)):
state, action, reward, next_state, done = episode[t]
td_target = reward + (self.gamma * np.max(self.Q[next_state]) * (not done))
td_error = td_target - self.Q[state][action]
self.Q[state][action] += self.alpha * td_error
agent = QLearning(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
GLIE
강화학습 알고리즘이 결국에는 학습을 하다 하다 하다 결국에는 왔다 갔다 왔다 갔다 하면서 계속 반복하면 최적 정책으로 수렴을 하는데 수렴하는 조건을 Greedy in the limit with infinite exploration 해서 두 가지 조건을 만족을 해야 됩니다
그래서 어느 조건에서 만족하냐 일단 탐색을 무한히 해야 되고요 실제로 무한히 아니면 되지만 언니가 탐색을 할 때에는 탐색을 할 때에는 탐색을 할 때에는 탐색을 해야 되고요 그래서 실제로 무한히 아니면 되지만 언젠가는 수렴을 하려면 재수 없으면 정말 무한히 해야 되는 거고 많이 하면 점점 수렴을 합니다
1번은 당연한 거고 우리가 지금 결국 몬테칼로든 템포럴 디퍼런스든 결국 해보고 그 경험을 바탕으로 하는 거니까 두 번째가 중요한데 정책은 탐욕 정책으로 수렴을 내야 된다
이건 무슨 말이냐면 탐욕 정책이라는 거는 가치가 큰 행동만 하는 거야 가치가 큰 행동만 한다
그래야 최적 행동이 되는 거지 최적 행동은 다시 얘기해 드리면 기억나실지 모르지만 정의가 뭡니까
최적 행동은?
모든 상태에서 그거보다 가치가 큰 행동이 없는 가장 가치가 큰 행동을 하는 정책이 최적 정책입니다
근데 우리가 입실론 탐욕법을 쓰면 여기서 괴리가 돼요
입실론 만큼 괴리가 됩니다
왜냐하면 모든 상태에서 최적 행동을 해야 되는데 가끔씩 입실론 만큼 탐색을 한단 말이야
그럼 그만큼 은 최적이 아닌 거죠
그래서 강학습에서 수렴을 하려면 입실론을 언젠가는 영으로 감쇄를 시키셔야 됩니다
그래야 최적 정책으로 수렴을 할 수 있어요
그래서 천천히 감쇄시키는 게 중요한데 너무 빠르게 감쇄하면 문제가 뭐냐면 탐색을 안 하는 거죠
너무 빨리 감쇄를 하면 탐색을 안 하니까 충분히 다양한 경험을 쌓지를 못합니다
그래서 최적 정책으로 수렴을 못하고 감쇄를 안 하면 계속 자꾸 뻘지사 하는 거죠
이상한 지사를 하는 거죠
안 해도 되는 거 그래서 2번을 만족을 못합니다
그래서 입실론 탐욕법은 입실론을 천천히 감쇄시키는 게 중요한데 Q학습은 조건 1만 만족하면 돼요 무한히 탐색하는 것만 하면 되고 탐욕정책으로 수렴 될 필요는 없습니다
그 얘기는 뭐냐면 감쇄를 굳이 안 해도 된다는 거죠
계속 탐색을 시키고 자
너는 탐색해라 나는 구경만 하려는다 그래서 그렇게 하면 망하는 거 나 이제 알았어
난 안 할 거야
잘 되는구나
난 저것만 해야지 남들이 망하는 걸 즐겁게 보면서 나는 쏙쏙 정부만 빼먹고 구경만 하면 되는 거죠
남들이 온갖 시행착오를 하는 걸 구경하는 겁니다
이중 Q-학습
Q학습은 최대화 편향이라는 게 있습니다
맥시마이제이션 바이오스라는 건데 그게 뭐냐면 사람들이 어떤 생각을 할 때 항상 오류가 있는데 남들이 어리석은 행동을 하면은 항상 이제 우리가 훈수를 주죠
아 나라면 그렇게 안 했을 건데 나라면 그렇게 안 했을 건데 뭐냐면 기본적으로 Q러닝은 R이 있고 그 다음에 이 다음에 무슨 행동을 하느냐고 했을 때 내가 제일 가치가 높은 행동을 할 거라고 항상 전제가 되어 있어요
그래서 이 Q에 최대값을 사용을 하는데 문제가 뭐냐면 공식을 다시 보시면 이 Q를 추정을 하는데 이 Q에 맥스 값을 쓰거든요
내가 할 수 있는 여러 가지 행동 중에 가장 최선 행동을 한다고 치고 그 값을 가지고 이 Q를 추정을 한다는 거죠
그러면은 쌀사하고 좀 차이가 보이시죠?
쌀사는 그냥 내가 실제로 이 행동을 했는데 해보니까 이렇더라
그거를 반영을 하는데 이건 항상 내가 다 최선 행동을 한다고 치고 이 Q를 추정을 해요
그럼 어떻게 되냐면 Q가 자꾸 커집니다
왜냐면 나는 항상 최선 행동을 하니까 그래서 이거를 최대화 편향이라고 해요
그래서 이 Q러닝에서 다음 단계에서 가치가 제일 큰 행동을 하는 걸 알아야 되는데 추정치를 제일 크게 하는 행동을 하는 걸로 추정을 하거든요
그러면 문제가 뭐냐면 정확하게는 최대값의 추정치를 알아야 되는데 추정치의 최대값을 사용을 한단 말이에요
그래서 문제가 뭐냐면 예를 들면 내가 이 Q가 계속 추정치이기 때문에 이 Q를 내가 좀 잘못 알고 있었어요
알고 보니까 Q가 그게 아니야
내 행동의 실제 행동 가치는 그게 아니야
내가 잘못 알고 있었어
근데 문제는 데이터가 좀 이상해 가지고 이게 좀 트여 가지고 굉장히 Q가 높게 나오는 그런 지점이 있었습니다
그러면 무조건 그거를 제대로 크게 하니까 어떻게 하냐면 에이 나라면 저때 저렇게 했을 텐데 그런데 그건 내 착각인 거죠
그게 최대한 좋은 행동이 아닌 거죠
그래서 결과적으로 어떻게 되냐면 가치함수가 자꾸 과대 추정이 됩니다
자꾸 Q가 실제보다 계속 커져요
그래서 이제 이 최저편향을 어떻게 억제하느냐
이런 문제가 생기는데요
요거에 대한 일반적인 간단한 해결책은 Q를 두 개를 쓰는 겁니다
그래서 공식 자체는 그 아까랑 똑같은데 원래 아까는 어떻게 했냐면 왔다 갔다 하면 헷갈리시니까 알파하고 알 플러스 맥스 에이 Q s,a 빼기 Q 여기 뒤에는 생략하고 이렇게 되거든요
그래서 Q를 여기서 맥스로 만드니까 얘가 같이 커져 버리잖아요
그래서 그 문제를 풀기 위해서 요식을 다시 쓰면 요식을 다시 쓰면 Q가 더 커져요
그래서 그 문제를 풀기 위해서 Q를 제일 크게 만드는 a란 말이에요
그래서 Q s,aq 이렇게 되는데 요걸 다시 쓰면 그럼 이 Q랑 이 Q랑 똑같으니까 문제다 라는 거죠
그래서 이 Q가 여기서 잘못 추정되서 크게 나오면 a도 크게 나오고, 요 Q도 크게 나오고, 요것도 커지고 다 커지잖아요
그래서 이 Q가 여기서 잘못 추정되서 크게 나오면 그러면 이 Q랑 이 Q를 다른 Q로 만들자
이겁니다
그래서 이걸 이중 Q학습이라고 하는데 Q를 두 세트를 만들어요
Q1, Q2 그래서 추정치를 두 개를 추정을 합니다
그래서 다음에 내가 뭘 했을까 나라면은 지금 남의 행동을 보고 있잖아요
계속 Q학습이기 때문에 남의 행동을 보고 있습니다 나라면 뭘 했을까는 Q1을 가지고 행동을 골라요
그러면 Q1을 가지고 행동을 골라요
나라면 뭘 했을까는 Q1을 가지고 행동을 골라요
그 다음에 이 행동의 가치는 얼마냐는 Q1으로 다시 계산하는 게 아니라 Q2로 계산을 합니다
왜 그렇게 하냐면 내가 Q1을 잘못 추정을 했어요
실제 가치는 여기인데 내 가 추정치를 좀 높게 잡았어요
그래서 예를 들면 a1이 있고 a2가 있는데 원래는 a2가 가치가 더 높아요
근데 내가 추정을 좀 잘못해 가지고 a1을 지금 더 높게 추정을 한단 말이에요
그럼 여기서는 a1이 뽑혀 나오겠죠
그럼
그건 좋아 여기서는 잘못 계산을 했어요
행동을 잘못 골랐는데 Q2는 어떻게 되느냐?
Q2도 추정이 잘못됐을 리는 없잖아요
물론 얘도 좀 잘못됐겠지만 완벽하게 똑같이 잘못되지는 않았을 거란 말이에요
Q2는 여기에 이렇게 추정이 됐다 그러면 이번에는 이만큼 깎이는 거죠
그래서 가치가 제일 큰 행동은 Q1으로 고르고 이 행동의 가치는 얼마냐?
Q2로 계산을 하게 됩니다
그러면 만약에 우리가 Q1을 추정을 잘못해 가지고 행동을 잘못 골라도 Q2도 뭔가 오차가 있긴 있겠지만 Q1하고 오차는 좀 다를 겁니다
오차라는 건 특성상 그때 그때 다른 게 오차잖아요
그래서 최소한 그만큼의 실수는 커버할 수 있다
이런 식의 아이디어가 됩니다
그래서 이걸 이중 Q학습이라고 합니다
그러면 좀 더 가치가 최대한 편향이 억제가 되니까 더 커지는 거는 막을 수 있다는 거죠
아니 반대로 작아질 수도 있지 않습니까?
작아질 수도 있는데 여기서는 작아질 수도 있고 커질 수도 있고 복불복이기 때문에 그게 그렇게 커지지는 않을 거다
이런 게 되고요 그래서 구현을 할 때 여기 보시면 QA하고 QB가 기대했는데 Q를 두 개를 만들어서 우리가 이제 랜덤하게 Q1, Q2를 어떨 때는 QA를 Q1으로 쓰고 어떨 때는 QB를 Q1으로 쓰고 이걸 랜덤하게 바꾸는 거예요
랜덤하게 바꿔도 되고 아니면 번갈아 하면서 해도 됩니다
아까는 QA를 Q1으로 했으면 이번에는 QB를 Q1으로 하고 이렇게 왔다 갔다 하셔도 되고 이거는 구현을 어떻게 하시냐
문제인데 저는 그렇게 하려면 에피소드 번호를 저장했는데 귀찮아서 랜덤하게 바꾸겠는데 지금 생각해보니까 이렇게 할 필요 없이 그냥 왔다 갔다 하면서 하고 싶으시면 어떻게 하면 되냐면 그건 되게 간단하게 고칠 수 있는데 이 품은에 이거를 그냥 T를 E로 나눠서 나머지가 0이다
이렇게 바꾸시면 그냥 번갈아 하면서 하게 됩니다
이거는 뭐 마음대로 구현하셔도 돼요
중요한 거는 여기 보시면 베스트, Next, Action을 고르는 부분하고 그건 Q1 기준으로 고르고 가치를 계산할 때는 Q2로 계산한다
이 차이만 있습니다
그다음에 최종적으로 내 행동 가치는 얼마로 하느냐 하면 이 두 개를 더해서 평균을 내요
그러면 QA는 좀 높게 추정하고 QB는 좀 낮게 추정하고 이럴 수 있는데 두 개를 평균을 내버리면 대충 맞게 추정이 되겠죠
그래서 이런 식으로 하는 게 이중 Q학습입니다
그래서 보면 WQ러닝도 업데이트 하는 방법만 다르지 나머지는 다 똑같아요
class DoubleQLearning(QLearning):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1):
super().__init__(env, gamma, alpha, epsilon)
self.QA = np.zeros((env.observation_space.n, env.action_space.n))
self.QB = np.zeros((env.observation_space.n, env.action_space.n))
def update_value(self, episode):
for t in range(len(episode)):
state, action, reward, next_state, done = episode[t]
if np.random.rand() < 0.5:
Q1, Q2 = self.QA, self.QB
else:
Q1, Q2 = self.QB, self.QA
best_next_action = np.argmax(Q1[next_state])
td_target = reward + (self.gamma * Q2[next_state][best_next_action] * (not done))
td_error = td_target - Q1[state][action]
Q1[state][action] += self.alpha * td_error
self.Q = (self.QA + self.QB) / 2
agent = DoubleQLearning(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
SARSA(λ)
Td람다제어를 SARSA(람다)라고 합니다
그러면 이제 Td람다를 어떻게 할까요?
적격 흔적을 남겨가지고 내가 어느 상황에서 무슨 행동을 했는지를 다 기억을 해놓습니다
다 기억을 해놓고 시간 지날 때마다 람다만큼 감쇄를 시키는 거에요
그래서 그만큼을 반영을 하는 거죠
그래서 이 델타 이게 Td오차인데 내가 이번에 해보니까 기대보다 좋구나 아니면 기대보다 나쁘구나
이게 Td오차인데 그거를 과거의 기억에다 다 반영을 해주는 거에요
그럼 어제는 이렇게 했어야 되고 그저께는 이렇게 했어야 되고 그저께는 이렇게 했어야 되고 저번주에는 어떻게 됐더라?
기억이 안 나네?
반영 안 하고 그래서 구현을 보시면 이 구현은 제가 맞게 백오드뷰로 잘 했습니다
왜 이거는 맞게 하고 앞에 거는 이상하게 보면은 이게 엘리자빌리티 트레이스인데 엘리자빌리티 트레이스인데 적격 흔적 적격 흔적을 제대로 맞게 앞으로 빼놨죠
class SARSALambda(SARSA):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1, lambd=0.9):
super().__init__(env, gamma, alpha, epsilon)
self.lambd = lambd
self.E = np.zeros((env.observation_space.n, env.action_space.n))
def update_value(self, episode):
self.E.fill(0)
for t in range(len(episode)):
state, action, reward, next_state, next_action, done = episode[t]
td_target = reward + (self.gamma * self.Q[next_state][next_action] * (not done))
td_error = td_target - self.Q[state][action]
self.E[state][action] += 1
for s in range(self.env.observation_space.n):
for a in range(self.env.action_space.n):
self.Q[s][a] += self.alpha * td_error * self.E[s][a]
if next_action is not None:
self.E[s][a] *= self.gamma * self.lambd
agent = SARSALambda(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
왓킨스의 Q(λ)
과거의 기억을 그 다음에 살사 람다가 있으면 이제 q람다도 있어야 되는데 q람다를 구현하기가 좀 까다롭습니다
그래서 q람다를 구현하는 게 까다로운데 그냥 q학습에다가 그 람다를 집어넣으면 어 문제가 뭐냐면 우리가 지금 남의 정책을 보고 있단 말이에요
그 기억 흔적을 어떻게 관리해야 되느냐가 되게 골치압품 문제가 됩니다
그니까 왜냐하면 q학습은 남의 걸 보고 내 걸 업데이트 해야 되는데 이 기억 흔적을 어떻게 관리해야 되냐
그럼 도대체 무슨 기억을 어떻게 해야 되냐
이게 학습정책하고 행동정책이 괴리되어 있기 때문에 이 그 이 기억흔적을 관리하는 게 문제인데 와킨스 이제 q학습을 만든 사람인데 크리스 와킨스가 생각한 방법은 어 탐색 행동을 하면은 그거는 기억을 초기화를 시켜갖고 그의 정책을 정리로 바꿔버리는 게 맞다
이렇게 생각을 했어요
근데 이 사람의 주장을 받아들인다면은 문제가 뭐냐면 우리가 람다보상을 하는 거는 과거의 기억을 가지고 이 과거의 어떤 행동에 대해서 q를 업데이트를 해주기 때문에 그게 장점이 되는 건데 어 우리가 탐색을 할 때마다 이 엘리저빌리티 트레이스를 초기화를 해버리면 그냥 다 기억을 날려버린거잖아요
내가 이 기억을 하다가 안하던 짓을 할 때마다 랜덤하게 우리가 10%의 확률로 안하던 짓을 하는 건데 그때마다 기억을 다 초기화를 시켜버리면 뭐하러 람다보상을 주냐
이거죠
이 q람다는 뭐하러 하냐
이거죠
그리고 어 특히 우리가 요 아이고 아이고 입실론을 감
쇄를 시키는데 입실론을 이렇게 서서히 감
쇄를 시키는데 초반에는 그럼 입실론이 크단 말이에요
그 얘기는 탐색을 자주 한다는 얘기고 탐색을 자주 한다는 거는 초기화를 자주 하는 거니까 기껏 q람다를 하는데 그냥 q학습을 하는 거랑 똑같습니다
그러면은 뭔 짓이야
약간 이렇게 되는 거죠
그래서 이 람다의 장점을 살리면서도 q학습하고 결합을 하는게 굉장히 까다로운 문제가 돼요
그래서 여러가지 아이디어가 나오는데요
첫번째는 징팽이라는 사람이 제안한 q람다가 있습니다
근데 이거는 굉장히 복잡해요
그래서 이런 방법이 있고 다른 사람들이 제안한 단순 q람다라는게 있는데 이거는 정말 이름이 나이부 q람다잖아요
나이부 하다는 건 약간 욕이 거든요
참 사람이 나이부해
이러면은 기생충 보셨는지 모르겠지만 그 기생충 영화 보셨나요?
거기보면은 사모님이 참 심플해 뭐 이런 얘기 하잖아요
혹시 안보신 분도 있을 수 있는데 거기보면 기생충 내용이 송강훈의 집안 아들이 부잣집 과외선생으로 가는데 친구가 그 과외자리를 소개해 주면서 해주는 얘기가 그 집 사모님이 좀 심플해 약간 이러는데 사실 심플하기보다는 약간 맹하죠
잘 속고 나이부하다는게 그런 의미의 단순한 겁니다
사람이 단순해 어디 가면 사기당하기 딱 좋겠어
이런 느낌의 단순한 건데 q람다는 어떻게 생각하냐면 원래는 이거를 탐색을 하는 경우는 그냥 활용을 하는 경우랑 다르게 처리를 해줘야 되는데 그냥 안합니다
아무것도 안해요
마치 탐색을 하는게 정상적인 것처럼 근데 재밌는 거는 단순 q람다를 해도 신기하게도 수학적으로 증명되거나 이런건 아닌데 잘 돌아갑니다
왜 잘 돌아가는지는 모르겠지만 대충 잘 돌아가요 아마도 이제 어쨌든 탐색을 그렇게 자주 하는건 아니니까 어쨌든 우리가 어차피 모든게 정확하지 않단 말이에요
다 근사치고 다 추정치기 때문에 조금 추정치가 틀려도 어쨌든 대세에는 지장이 없는게 아닌가
이 정도로 생각을 합니다
class WatkinsQLambda(SARSA):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1, lambd=0.9):
super().__init__(env, gamma, alpha, epsilon)
self.lambd = lambd
self.E = np.zeros((env.observation_space.n, env.action_space.n))
def update_value(self, episode):
self.E.fill(0)
for t in range(len(episode)):
state, action, reward, next_state, next_action, done = episode[t]
best_next_action = np.argmax(self.Q[next_state])
td_target = reward + (self.gamma * self.Q[next_state][best_next_action] * (not done))
td_error = td_target - self.Q[state][action]
self.E[state][action] += 1
for s in range(self.env.observation_space.n):
for a in range(self.env.action_space.n):
self.Q[s][a] += self.alpha * td_error * self.E[s][a]
# 탐색을 하면 0으로 바꿈
if best_next_action == next_action:
self.E[s][a] *= self.gamma * self.lambd
else:
self.E[s][a] = 0
# 탐색을 할 때도 0으로 바꾸지 않으면, 단순 Q(lambda)
agent = WatkinsQLambda(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
모형기반 강화학습
그 다음에 이제 우리가 지금까지 하면서 계속 어떤 걸 가정했냐면 우리의 전이 확률을 알거나 모르버나 이문법적으로 생각했어요.
알면 동척계법 같은 거를 쓰면 되고 모르면 우리가 지금 쓰고 있는 mc나 Td 같은 거를 쓰면 된다.
라고 생각을 하고 있는데 그럼 이 중간 정도도 좀 있을 수 있겠죠.
제대로 아는 건 아니지만 완전히 모르는 것도 아닌 상태.
우리도 현실에서 보면 세상이 어떻게 돌아가는지를 전혀 모르고 오로지 보상과 처벌만으로 반응하는 건 아니잖아요.
대충 어떻게 일이 어떻게 될지 예상이 가능하단 말이에요.
정확하게는 몰라도.
그래서 우리가 이제 환경이 앞으로 어떻게 돌아갈지를 모형을 따로 만들 수가 있습니다.
대충 이렇게 되겠지.
그래서 이런 모형이 있느냐 없느냐를 가지고 모형 기반, 모형 없음
이렇게 납니다.
모델 베이스드, 모델 프리.
사실 용어는 제가 여러분들 이해를 돕기 위해서 한국어로 하긴 하는데 기억은 영어로 해주셔야 돼요.
왜냐하면 이게 공식적인 번역이 있는 게 아니기 때문에 한국어로만 외워두시면 나중에 뭔 소리지?
왜냐하면 어떤 사람은 모델 프리를 그냥 한글로 모델 프리 이렇게 씁니다.
그래서 한글로만 알아두시면 안 되고 영어로 알아두시고 아 이게 이런 뜻이구나.
하셔야 누가 모델 프리 이렇게 써놔도 아 이거 모델 프리 말하는 거구나.
그래서 이제 모형 기반이라는 거는 내가 환경에 대해서 환경이 앞으로 이렇게 될 거야. 라는 것을 학습을 따로 해가지고 그걸 바탕으로 하는 거고.
모델 프리는 어떻게 되는지는 몰라요.
그냥 내가 이렇게 하니까 잘 되는데 이렇게 하는 거예요.
그래서 여기서 이제 가치 기반, 정책 기반 이렇게 나누는데.
가치 기반은 세상이 어떻게 돌아갈지는 모르겠고.
하지만 내가 이렇게 하면 세상은 나에게 좋은 걸 주더라.
그럼 난 계속 이렇게 할 거야.
정책 기반도 마찬가지예요.
정책 기반도 세상이 어떻게 돌아갈지는 모르겠고.
내가 이걸 하면 얼마나 좋은지도 모르겠어.
가치도 잘 몰라.
하지만 나는 이걸 할 거야.
이런 거죠.
그러니까 우리가 이제 인간의 행동으로 얘기하면 이런 거는 다 습관입니다.
습관.
여러분들이 어떤 습관적인 행동을 할 때 생각을 하고 하는 건 아니잖아요.
근데 여러분이 어떤 습관이 있는 이유는 뭐냐면 그냥 그걸 오랫동안 하면서 거기에 어떤 보상을 받아왔기 때문이에요.
그러면 그 행동을 그냥 계속 하는 겁니다.
그런데 이제 모형 기반은 여러분 생각을 하면서 하는 거죠.
앞으로 이렇게 될 거야.
저렇게 될 거야.
그래서 강화학습에서 모형 기반, 모형 없음
이런 용어를 많이 쓰는데 앞에 항상 생략된 말은 환경에 모형이 없다.
환경에 모형이 있다.
이런 얘기입니다.
그래서 가치나 정책에 대한 모형을 말하는 건 아니에요.
이거 약간 헷갈리기 쉬우신 부분입니다.
그래서 모형이 없는 강화학습 이러면 아 그러면 가치에 대한 모형이 없구나
이런 얘기가 아니에요.
환경에 대한 모형이 없다는 얘기예요.
그래서 모형 기반 강화학습도 종류가 여러 가지가 있는데요.
분포 모형이 있고 표본 모형이 있습니다.
이런 건 용어 어려우니까 외우실 필요는 없고 그냥 그렇구나 생각하시면 되는데 분포 모형은 뭐냐면 모든 가능성과 확률을 다 모형화해서 하는 거예요.
내가 이렇게 하면 이렇게 되고 저 렇게 해도 되고 이렇게 되고.
이거를 다 가지고 있으면 분포 모형이라고 합니다.
있으면 제일 베스트죠.
왜냐하면 우리가 전유 확률을 다 하는 거거든요.
이거는 딱 생각만 해도 그 확률을 어떻게 다 하나.
모든 가능한 경우의 수를 어떻게 다 하나.
모르겠죠.
그래서 그다음은 표본 모형인데 표본 모형은 좀 더 만들기가 쉬운데 표본 모형은 어떤 한 가지 가능성은 생각할 수 있는 거예요.
이렇게 되겠다.
우리가 살면서 대부분 우리는 이거 한 중간 정도 되는 거죠.
어떤 내가 이렇게 하면 이렇게 될 수도 있고 저렇게 될 수도 있겠다까지는 아닌 거죠.
몇 가지 가능성은 우리가 알고 있습니다.
약간 표본 모형 중에 분포 모형에 가까운 하지만 안전한 거는 모르는.
그래서 분포 모형이 더 강력하지만 표본 모형이 만들기가 더 쉽습니다.
예를 들면 비교를 해보면 여러분이 주사위 12개를 굴렸을 때 그러면 합계가 얼마까지 나올 수 있으니까 합계가 6에서 72 사이로 나올 수 있죠.
그 모든 경우의 수에 대해서 각각 확률이 얼마 됩니까?
물어보면 고등학교 때 배웠는데 이러면서 갑자기 괴로워요.
근데 주사위 12개를 굴렸을 때 나올 수 있는 결과 하나를 얘기해봐라.
그런 건 쉽죠.
아니면 그냥 주사위 12개 굴려보면 됩니다.
떼구르르르 굴리면 바로 나오죠.
그래서 표본 모형이 대체로 만들기가 쉽고 그래서 환경에 대한 모형이 있으면 우리가 환경을 시뮬레이션 해볼 수 있습니다.
정확하게 똑같지는 않겠지만 대략 이렇게 될 거다.
이런 걸 알 수 있습니다.
그래서 우리가 이걸 이용해서 계획을 세울 수 있는데 강화학습에서 계획이라고 하는 거는 뭔가
이런 모형을 이용해서 정책을 개선하는 거예요.
우리가 지금 정책을 개선하는 방법이 두 가지가 있는데 전이 확률을 알면 그냥 반복해서 정책 평가해서 계산을 하면 됩니다.
전이 확률을 모르면 어떻게 하냐면 그냥 시행착오를 해서 직접 두들겨 맞으면서 개선을 하면 됩니다.
그래서 이제 계획은 뭐냐면 전이 확률을 직접 아는 건 아니에요.
하지만 요거에 대한 모형이 있는 거죠.
실제 확률이 아니라 우리가 그거를 얼마쯤 되는 것 같아.
전이 확률이 이 정도 되는 것 같아.
어떤 추정치가 있어서 그 추정치를 기반으로 개선을 하는 겁니다.
그래서 모형에서 우리가 시뮬레이션 된 경험을 구할 수 있고 이걸 가지고 가치를 계산하고 그걸로 정책을 개선하는 겁니다.
지금 사실 근데 우리가 하고 있는 슬리퍼링워크나 이런 데서는 모형 기반 강화학습이 가지는 장점이 별로 없습니다.
왜냐하면 어차피 지금 이쪽의 환경 자체가 시뮬레이션이거든요.
시뮬레이션이라서 사실 우리가 모형을 따로 만들어서 시뮬레이션 해봐야 아무 의미가 없어요.
어차피 둘 다 컴퓨터에서 돌아가는데 뭔 차이가 있습니까?
똑같지.
근데 이제 이런 거는 언제 의미가 있냐면 예를 들면 자율주행을 한다.
그럼 실제 차를 갖다가 세상에 내보내서 들이박으면서 시행창고를 하면서 자율주행을 습득해라.
이거는 말이 안 돼요.
그죠?
그러면 실제 자동차가 운행을 할 때 현실에서 있을 수 있는 일을 뭔가 모형화해가지고 머신러닝으로 모형화를 해서 모델이 학습하게 이렇게 할 수가 있겠죠.
제가 방금 말을 하니까 생각이 나는 건데 뭐 하나 보 여드리면 이거 같다.
게임 중에 둠이라고 아시죠?
이 게임 아시죠?
이거 게임 화면 같잖아요.
이 게임 화면이 아닙니다.
그 인공신경망이 게임의 장면을 모델링을 한 거예요.
완전히 전혀 게임을 하지 않고 어떻게 하는 거냐면 이 순간에 어떤 장면이 있잖아요.
이 장면을 뉴럴레이션으로 만들어서 이 장면을 뉴럴레이션으로 만들어서 이 장면을 뉴럴레이션으로 만들어서 여기서 만약에 사용자가 앞으로 가는 반키를 누르면 바로 다음 장면에 어떤 화면이 나올지를 네가 예상을 해봐라.
그럼 그걸 계속 이어붙이면 게임처럼 되겠죠.
그래서 게임처럼 보이는데 이건 전부 뉴럴레이션이 모두 예상입니다.
다 예상, 모델링을 한 거예요.
그래서 보면 가끔 숫자 같은 게 갑자기 확 바뀌거든요.
그런 게 있고 그다음에 하다 보면 약간 알고 보면 되게 이상한 데가 조금씩 있는데 몬스터 나오는 데가 조금 이상했는데 몬스터 나오는 데가 어디에다?
하여간 이쪽 장면에 있었구나.
여기 총 쏘는 데 보면 또 지나갔네.
하여간 중간 중간에 약간씩 장면이 어색한 데가 있어요.
예측을 제대로 못 한 거죠.
모델이 틀려서.
그래서 이런 거는 우리가 리얼타임 게임인지 이렇게 했는데 강화학습하고 모델링하고 환경에 대한 모델하고 결합을 한 겁니다.
그래서 이걸 실제로 어떻게 학습을 시키냐면 실제 둠 게임 있잖아요.
둠 게임을 강화학습으로 게임을 하게 해요.
그럼 게임을 얘가 되게 잘 하겠죠.
그리고 게임을 하면서 이것저것 다양하게 해봅니다.
그 데이터가 쌓이는 거를 다른 신경망이 이 게임을 하면 이때 앞버튼을 누르면 화면이 이렇 게 바뀌는구나.
여기서 점프를 하면 이렇게 바뀌는구나.
이거를 학습하는 모양이 따로 있어요.
그래서 얘가 그걸 학습을 하는 거죠.
그래서 이 게임을 어떻게 하는지 내가 여기서 주먹을 이렇게 하면 어떻게 돼?
그럼 몬스터가 이렇게 피하겠지.
그럼 여기서 총을 쏘면 어떻게 돼?
그러면 이렇게 되겠지.
이거를 지원자 혼자서 머릿속으로 다 할 수 있는 거예요.
약간 우리가 상상으로 이미지 트레이닝 하듯이.
그래서 예를 들면 자율주행 이런 거 한다고 하면 똑같이 할 수 있겠죠.
자동차를 사람이 운전하면 카메라 하나 달아놓고 영상을 쫙 촬영을 해놨다가 이 상황에서 핸들을 왼쪽으로 끌고 이렇게 하면 이렇게 되는 거죠.
그래서 이렇게 하면 이렇게 되는 거죠.
그래서 우리가 자율주행 이런 거 하려면 그런 데이터를 많이 쌓아놓고 있으면 많이 유리하겠죠.
테슬라 이런 회사들이 유리한 게 그런 데이터를 굉장히 많이 쌓고 있기 때문에 사실 유리한 점이 있습니다.
근데 유리하다고 잘하는 건 아닌데.
그래서 이런 게 유리한 점이 있어요.
그래서 우리가 자율주행을 하는 게 자율주행을 하는 게 유리한 점이 있습니다.
그래서 우리가 자율주행을 하는 게 유리한 점이 있습니다.
그래서 이것도 지금 보시면 구글에서 하고 구글 딥마인드에 상관했죠.
며칠 전에 나온 따끈따끈한 논문인데 한 일주일쯤 됐습니다.
대단하구만
이런 생각이 항상 들더라고요.
어쨌든 우리가 실제로 사실 게임까지도 게임도 저희 컴퓨터 돌려서 하는 거니까 별로 의미 없지만 현실의 상황에서 할 수 있는 거를 할 수 있다.
그래서 우리가 정말 나중에 강학습이 지금보다 좀 더 기술적으로 쉬워지면 제 생각에는 요즘에 보면 경찰들 바디캠 같은 거 차고 다니잖아요.
이런 라인에 있는 작업자들한테 다 바디캠 같은 거 채우지 않을까.
그러면 라인에서 작업할 때 있는 상황들을 데이터로 학습을 할 수 있으니까 그거를 시뮬레이션 해서 강학습을 시키면 나중에 인간 작업자가 하던 걸 로봇한테 시킬 수가 있겠죠.
지금부터 로봇을 넣으려면 약간 곤란한 그런 상황들을 그렇게 가상현실 같은 걸로 학습을 할 수 있지 않겠냐.
Dyna-Q
그래서 우리가 이런 모델 기반 학습 모델 중에 하나가 다이나 Q라고 있는데 다이나 Q는 그림을 보시면 좀 그림이 복잡한데 여기서 환경이 있고요.
환경이 있으면 환경에서 실제로 경험을 얻습니다.
리얼 익스피리언스를 얻고요.
그래서 입실론 탐욕법으로 행동을 선택해서 내가 이 상황에서 행동을 해보는 거야.
그러면 이 경험이 있는데 이 경험을 가지고 Q학습을 해서 이렇게 하면 그냥 Q학습입니다.
실제로 가치를 개선을 하는 거죠.
여기까지만 하면 Q함수인데 여기서 우리가 경험을 했으니까 이 경험을 가지고 우리가 가지고 있는 모형을 지도학습 같은 형태로 업데이트를 하는 거야.
그래서 모형을 학습을 시킨 다음에 이 모형에서 시뮬레이션을 하는 겁니다.
시뮬레이션을 하는데 시뮬레이션하는 방법이 여러 가지가 있는데 그 중에 하나는 뭐냐면 우리가 이미 해봤던 여러 가지 상태 중에 하나를 골라서 그때 액션을 이렇게 하면 어떻게 되지?
이 걸 생각을 해보는 거예요.
그러면 이런 R이 나오겠구나.
그러면 그걸 가지고 Q함수를 다시 수정을 할 수가 있겠죠.
그래서 이런 식으로 하는 거를 다이나 Q라고 합니다.
생각해보면 우리도 실제로 많이 하는 거잖아요.
현실에서 경험으로 부딪혀서 배우는 게 있고 내가 상상해서 그때 이렇게 하면 그게 더 좋았을까?
왜 그 생각 못했을까?
이러면서 배우는 게 있잖아요.
두 가지를 다 하는 겁니다.
그래서 다이나 Q 코드를 보면 여기 플래닝 스텝이라는 게 있는데 플래닝 스텝이라는 게 있는데 내가 상태 중에 하나를 앤덤하게 고르고요.
행동 중에 하나를 앤덤하게 골라요.
그다음에 나의 모델을 가지고 이 상태에서 이렇게 행동하면 어떤 보상이 나올 거고 어떤 결과가 될지를 상상을 해보는 거예요.
모델을 이용해서.
여기서는 이 모델이 머신러닝 모델이 아니고 그냥 되게 단순한 표입니다.
그냥 표로 하나 만들어졌어요.
표로 만들어서 이 상태일 때 이 행동하면 이 정도가 예상이 된다
이런 표를 만들었어요.
실제로는 나중에 이걸 머신러닝 모델로 바꿔야겠죠.
그래서 이걸 가지고 그다음에 리워드 계산하고 타깃 계산하고 하는 거는 그냥 Q학습이랑 똑같습니다.
다만 이 리워드는 진짜로 받은 리워드가 아니고 내가 상상한 시뮬레이션한 리워드고 이 상태도 시뮬레이션한 상태입니다.
그다음에 업데이트 밸류는 이거는 진짜 경험을 가지고 하는 거죠.
그래서 여기서 스테이트 액션은 내가 진짜로 해본 겁니다.
진짜로 해봤더니 이런 보상이 나오고 이런 다음 상태가 나오더라
그래서 그렇게 하는 거 그래서 이거를 반복해서 하는 거다
이런 얘기고 이것도 우리 이제 제어 문제에 있죠.
모형 기반 강학습 그래서 플래닝 스텝 이렇게 있습니다.
그래서 이 플래닝 스텝을 몇 번 하느냐
이것도 한 가지 문제인데 여기서 이제 N플래닝 스텝을 다섯 번 이렇게 했거든요.
그래서 플래닝 스텝을 밟을 때마다 이 짓을 다섯 번씩 합니다.
그래서 여기 보시면 업데이트 밸류를 할 때 내가 한번 실제 경험을 가지고 Q를 업데이트를 하면 그 다음에 플래닝 스텝을 밟는데
그럼 어떻게 되냐면 내가 실제 경험으로 한번 학습하면 다섯 번은 상상으로 학습을 하는 거예요.
1대 5의 비율로 내가 한번 누구한테 맞았어요.
그럼 내가 왼쪽으로 이렇게 하면 오른쪽으로 하면 이렇게 머리 싹 피하면 이걸 다섯 번 해보고 그다음에 또 한 대 맞고 나서 또 한 대 실제로 맞았어.
다섯 번 상상을 해보는 거죠.
그러면 만약에 모델이 굉장히 정확하다
플래닝 스텝을 많이 밟으시는 게 좋겠죠.
그리고 모델이 별로 정확하지 않다
그럼 이건 되게 쓸데없는 짓입니다.
내가 싸움을 되게 잘하는데 그럼 상상으로 이렇게 하면 피할 수 있어요.
이렇게 상상하는 게 말이 되는데 내가 싸움 못 하는데 이렇게 피하면 돼.
하면 또 맞겠죠.
아무 쓸모가 없습니다.
그래서 이거를 우리가 모델의 성능에 따라서 플래닝 스텝은 조절할 수 있다.
import random
class DynaQ(QLearning):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1, n_planning_steps=5):
super().__init__(env, gamma, alpha, epsilon)
self.n_planning_steps = n_planning_steps
self.model = defaultdict(lambda: defaultdict(lambda: (0, 0, 0)))
def update_model(self, state, action, reward, next_state, done):
self.model[state][action] = (reward, next_state, done)
def planning_step(self):
for _ in range(self.n_planning_steps):
if len(self.model) == 0:
continue
# 상태, 행동을 무작위로 샘플링
state = random.choice(list(self.model.keys()))
action = random.choice(list(self.model[state].keys()))
reward, next_state, done = self.model[state][action]
td_target = reward + (self.gamma * np.max(self.Q[next_state]) * (not done))
td_error = td_target - self.Q[state][action]
self.Q[state][action] += self.alpha * td_error
def update_value(self, episode):
for t in range(len(episode)):
state, action, reward, next_state, done = episode[t]
td_target = reward + (self.gamma * np.max(self.Q[next_state]) * (not done))
td_error = td_target - self.Q[state][action]
self.Q[state][action] += self.alpha * td_error
self.update_model(state, action, reward, next_state, done)
self.planning_step()
def control(self, policy, num_episodes=1000):
for _ in range(num_episodes):
episode = self.generate_episode(policy)
self.update_value(episode)
return self.improve_policy()
agent = DynaQ(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}
Trajectory Sampling
자 그 다음에 우리가 이제 다이나 큐에서 코드를 보면은 아까 플래닝 스텝을 보면 상태랑 행동을 그냥 랜덤하게 고르거든요
그럼 어떤 사람들을 이렇게 생각하는 거죠?
아니 이렇게 랜덤하게 고르면 우리가 별로 있을 법하지 않은 상태에 있을 법하지 않은 행동도 자꾸 고르게 되겠죠
그래서 상태 행동을 균일하게 샘플링을 하는데 상태가 굉장히 많은 경우에 이건 좀 비효율적이란 말이에요
왜냐면 이상한 게 나오니까 그래서 이거를 수정한 게 궤적을, 트라젝토리를 샘플링을 하는 건데 무슨 말이냐면 우리가 이제 코드를 보시면 여기 샘플 트라젝토리에서 어디 갔죠?
여기서 처음에 어떤 출발점에서 시작을 해가지고 우리가 모델로 다음 상태를 예측을 한 다음에 그 다음에 다시 그 다음 상태에 이어지는 상태를 또 어떻게 되는지를 추적을 하는 겁니다
그러니까 아까는 아까는 어떻게 하냐면 그냥 매번 무작위로 뽑아서 예를 들면 아까는 어떻게 하냐면 그냥 매번 무작위로 뽑아서 예를 들면 싸움을 하는데 상대방이 왼쪽 주먹을 뻗으면 어떡하지?
오른쪽 주먹을 뻗으면 어떡하지?
이걸 랜덤하게 생각을 하는 건데 트라젝토리 샘플링에는 상대방이 왼쪽 주먹을 뻗으면 내가 이렇게 피할 건데 그럼 내가 피하는 걸 보고 상대방이 다시 왼쪽 주먹으로 이렇게 공격을 하면 이런 식으로 생각을 하는 거예요
이어지게 생각을 하는 거예요
그래서 이 트라젝토리, 궤적이라는 말이 그래서 나오는 거예요
그래서 이런 식으로 샘플링을 하는 거고 그래서 여기 보시면 시작점에서 시작해서 Next State를 뽑는데 Next State를 다시 State를 덮었으면 앞으로 다시 돌아갔을 때 이 State에 대해서 모델로 예측을 하는 거죠
다시 또 Next State를 예측하고 이걸 계속 뱅글뱅글뱅글뱅글 돌면서 트라젝토리를 샘플링을 하는 거죠
그래서 이렇게 하면 현재 정책에서 더 자주 방문하는 상태 더 자주 방문하는, 자주 하는 행동을 더 많이 업데이트를 하게 되고 그래서 이렇게 하면 초기에는 트라젝토리 샘플링이 가치가 높은 정책을 찾는데 도움이 많이 됩니다
왜냐면 초반에는 우리가 데이터가 별로 안 쌓였으니까 있을 법한 상황을 집중적으로 데이터를 더 추가하는 게 도움이 되겠죠
근데 이제 시간이 지나가지고 충분히 샘플이 많이 확보되면 이제 많이 상상을 해봤잖아요
이미지 트레이닝을 많이 실제로도 많이 경험을 쌓고 상상으로도 많이 해봤기 때문에 사실 이제 더 그걸 많이 하는 게 큰 도움이 안 됩니다
그래서 후반에 가면 다양한 상황을 생각해보는 게 좋아요
여러분들이 초보자라고 하면 초보자면 기초를 튼튼하게 하는 게 중요하겠죠
근데 여러분이 기초가 쌓였으면 어떤 예외적인 상황, 다양한 상황 이런 거를 이제 생각을 할 수 있어야겠죠
그래서 태권도 도장 가면 정권 찌르기만 계속 하잖아요
그러다가 실력이 쌓이면 더 복잡한 기술을 하듯이 그래서 여기 그림을 보시면 이 유니폼하게 뽑는 게 초반에는 더 성능이 떨어지는데 뒤로 가면은 성능을 이렇게 역전을 합니다 뒤로 가면은 역전을 해요
그래서 이렇게 하는 것이 초반에 더 좋지만 후반 가면 좋지 않다 그러면은 우리가 이것도 다이나큐도 바꿀 수가 있겠죠
예를 들면 초반에는 트랙토리 샘플링을 하다가 어느 정도 지나면 유니폼하게 하거나 아니면 몇 댐에서도 섞을 수도 있겠죠
그래서 처음에는 예를 들면 99%는 이쪽에서 하고 1%는 이쪽에서 하다가 이 비율을 서서히 바꾼다든지 이런 식으로 이제 여러분들이 알고리즘을 바꾸실 수가 있습니다
class DynaQTrajectory(DynaQ):
def __init__(self, env, gamma=0.9, alpha=0.1, epsilon=0.1, n_planning_steps=5,
trajectory_length=5):
super().__init__(env, gamma, alpha, epsilon, n_planning_steps)
self.trajectory_length = trajectory_length
def sample_trajectory(self, start_state, start_action):
trajectory = []
state = start_state
action = start_action
for _ in range(self.trajectory_length):
if state not in self.model or action not in self.model[state]:
break
reward, next_state, done = self.model[state][action]
trajectory.append((state, action, reward, next_state, done))
if done:
break
state = next_state
action = np.argmax(self.Q[state])
return trajectory
def planning_step(self):
for _ in range(self.n_planning_steps):
if len(self.model) == 0:
continue
start_state = random.choice(list(self.model.keys()))
start_action = random.choice(list(self.model[start_state].keys()))
trajectory = self.sample_trajectory(start_state, start_action)
for t in range(len(trajectory)):
state, action, reward, next_state, done = trajectory[t]
td_target = reward + (self.gamma * np.max(self.Q[next_state]) * (not done))
td_error = td_target - self.Q[state][action]
self.Q[state][action] += self.alpha * td_error
agent = DynaQTrajectory(env)
agent.evaluate_policy(pi)
new_pi = agent.improve_policy()
new_pi
{0: 0, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0}