Actor-Critic
자 그래서 리인폴스 알고리즘이 이름도 이따위로 지어서 마음에 안 드는데 이런 문제도 있어 가지고 잘 안 써요
다행히도 잘 안 쓰는데 그래서 이제 이 리인폴스를 actor critic 이라는 멋있는 이름으로 확장을 해서 보통 씁니다
이름이 좀 멋있죠
actor critic 어떻게 확장을 하느냐 일단 아까도 얘기 드렸듯이 만약에 g가 항상 플러스다
그럼 확률을 항상 올리는 문제가 있어요
그래서 여기다가 어떤 적당한 숫자를 빼줍니다
이걸 이제 기저선이라고 해요
적당한 숫자를 빼줘도 되느냐 수학적으로는 여기서 뭘 빼든 말든 b가 액션 행동에 영향만 안 받으면 수학적으로는 아무 상관이 없습니다
그래
그러면은 예를 들면 우리 아까 식당의 예를 생각해 보면 a 식당도 맛있고 b 식당도 맛있는데 a 식당은 보통 한 70점 정도 되고 b 식당은 보통 90점 된다고 해 보죠
항상 70점은 아니고 어떨 땐 70점 어떨 땐 60점 어떨 땐 80점 이러겠죠
b 식당은 보통 90점이지만 100점도 나오고 80점도 나오게 돼요
그러면은 대충 봐가지고 한 80점을 양쪽에서 다 빼 버리는 거야
그럼 어떻게 되냐면 a 식당은 원래는 80점 짜리 식당이었는데 80점을 빼 버리니까 0점이 되어버립니다
아니면 마이너스 10, 마이너스 20, b 식당은 플러스 20, 플 러스 10, 0 그러면 a 식당은 제일 맛있어 봐요 0점이에요
그러면 확률이 전혀 안 오르겠죠
a 식당 갔는데 오 a 식당이 이렇게 맛있어
하지만 넌 0점이다
이 자식아 그럼 a 식당의 확률은 안 올라가는 거죠
그리고 a 식당 갔는데 70점은 나쁘지 않잖아요
오늘 괜찮네
근데 a 식당 더 오지 말아야겠다 이렇게 되는 거죠 괜찮은데 마이너스 80 해 가지고 확률이 오히려 깎여요 확률이 높았는데 아니 70점이 되는데 왜 깎입니까 나는 80점 빼 이렇게 그리고 얘네도 90점 맞아 봐야 플러스 10점만 쳐 주는 거에요
그러면은 어떤 내가 정한 수준이 있어서 그 수준보다 못하면 확률이 플러스라도 확률이 깎이고 내가 어떤 정한 수준보다 높아야만 확률을 올려주는 거죠
그러면은 더 학습이 빨리 잘 되겠죠
그래서 기저선을 뭘로 할까
이게 문제가 됩니다
기저선을 뭘로 할까
그래서 이 기저선이 가져야 될 조건은 행동에 따라 뭔가 변화하면 안 되고 그 외에는 상관 없습니다
그냥 s 에 대한 어떤 상태에 대한 함수기만 하면 되요 상태에 대한 함수라 뭘 할까 사람들이 생각을 하는 거죠
여기까지는 누가 증명을 했어요
뭘 할까 하다가 흠 우리는 어차피 강학습 하는 사람들이란 말이에요
그러면은 가만히 생각해보니까 강학습에 상태에 따라 달라지는 함수가 하나 있습니다
뭐죠 상태 같이 함수죠 상태 같이 함수는 s에 따라 달라지니까 행동에 영향을 받지 않는 함수잖아요
그렇죠
어라?
음 상태 같이 함수를 추정을 해야 된다고 그래서 액터 크리틱이 나오는데 액터 크리틱은 이제 같이 함수도 추정을 하죠
상태 같이 함수도 추정을 하고 정책도 추정 을 합니다
그래서 정책 기반 강화학습과 가치 기반 강화학습이 합쳐지는 거에요
여기서 그래서 이 액터 크리틱 이란 이름은 액터 하고 크리틱 두가지를 합친 건데 모델이 두 가지로 이루어져 있다
이런 얘기고 액터 행동자는 환경에서 어떤 행동을 취할지
결정 그러니까 간단히 말하면 파이 정책을 말합니다
정책을 학습하는 모형이 되는 거구요 그 다음에 비평자는 이 행동이 얼마나 좋은지 비평을 평가를 하는 거에요
잘했네
못했네를 따지는 거에요
그래서 이거는 가치, v를 학습을 합니다
그러면은 정책도 학습하고 가치도 학습을 하는 거죠
그래서 이제 정책 기반의 장점하고 가치 기반의 장점을 두 가지를 다 합치게 되는데 그래서 이제 식으로 나타내면 이런 식으로 나타내면 되요
그래서 원래는 여기 지이가 들어가거든요
근데 여기서 이제 원래 그 베이스라인을 빼주는 거죠
근데 베이스라인을 가치로 바꿔요 vs로 바꿔요 여기서 끝나지 않고 g도 생각해 보니까 재기적 형태로 쓸 수 있단 말이에요
g를 재기적 형태로 쓰면 어떻게 됩니까
r 플러스 간마 g 프라임 이렇게 되겠죠
그럼
g 프라임이 있는데 이거 좀 거슬리잖아요
이거를 g가 아니라 v 프라임 이렇게 바꾸면 시간차 학습 형태로 바꿀 수가 있죠
그래서 의도치 않은 장점이 생기게 되는데 원래는 아까 우리가 베이스라인으로 뭘 넣을까 하다가 이왕 하는 거 상태 가치로 하면 되겠다 라고 해서 했더니 어?
시간차 학습도 되네?
그럼 원래는 리인폴스가 mc제어였잖아요
근데 갑자기 스윽 하고 td제어로 바뀌게 됩니다
td제어의 장점은 끝까지 안 가봐야 된다는 거죠
바로바로 학습을 할 수 있고 약간 쌀쌀하고 좀 비슷한데 쌀쌀하고 똑같진 않지만 약간 쌀쌀하고 좀 비슷한 그런 느낌이 있는 그런 걸로 바뀌게 됩니다
그래서 보시면 어떤 아이디어라는 게 사실 대부분의 아이디어는 그런데 연구되서 나오고 나면 되게 뻔한 아이디어에요
왜 우리가 이걸 몰랐을까?
액터크리틱도 이 논리의 흐름을 쭉 들어보시면 당연히 그렇게 되겠네 싶겠지만 리인폴스에서 액터크리틱까지 한 20년인가 걸리거든요
이 논리의 스텝을 다 결과를 듣고 나면 뭐 그렇게 되겠지 싶은데 아이디어 하나하나가 진전하는 게 굉장히 고통스러운 과정 20년인가
10년인가 되게 오래 걸렸어요
항상 그 발전이 굉장히 느린데 우리가 이제 다 만들어 놓고 보면 쉽다 남이 해놓은 걸 보면 여러분들이 좀 이 강의가 어려워 느껴지시겠지만 사실 이게 수십년의 발전상을 이렇게 5일만에 압축해서 배우니까 좀 어렵긴 합니다
그래서 이제 아마 다음 주쯤 되시면 이제 수업 내용이 사람이 한 일주일만 돼도 되게 길거든요
일주일 전에 들었던 내용은 원래 잘 기억 안 나요
다음 주만 돼도 사실 기억이 가물가물 하실 텐데 그래도 이제 뭐 가치라던가 정책이라던가 MC라던가 시간차라던가 이런 단어만 기억나셔도 나중에 다시 보시면 또 기억이 새롭사로 나거든요
그래서 너무 스트레스 안 받으셨으면 좋겠습니다
어쨌든 그래서 이제 이 액터크리틱의 장단점이 있는데요
일단 장점이 수렴을 굉장히 잘해요
수렴이 굉장히 빠르고 굉장히 효율적이고 그 다음에 얘는 그 두 가지의 장점을 합친 거기 때문에 확률론적 정책도 학습할 수 있습니다
근데 이제 단점이 있는데 뭐냐면 첫 번째로 약간 국소 최적에 빠지는 경향이 좀 있어요
왜냐하면 그 얘가 평가를 하기 때문에 평가하는 거랑 정책이랑 서로 이렇게 맞물려 돌아가는 경우에 얽혀가지고 얘는 이렇게 하고 그 결과를 보고 학습하니까 그게 좋다고 하고 서로 이렇게 딱 맞물려버리면 거기서 벗어나질 못합니다
그래서 이제 그런 거를 국소 최적이라고 하는데 어떤 특정한 상황에 빠져 가지고 잘 빠져나오질 않아요
그 다음에 정책평가 V를 학습하는 게 대체로 좀 비효율적이고 분산도 크고 잘 안 됩니다
그 다음에 이제 기본적으로 이게 액터크리틱은 온폴리시 학습이거든요
그래서 DQN은 오프 폴리시라서 데이터를 막 섞을 수가 있단 말이에요
우리가 리플레이 버퍼 지난 시간에 있던 리플레이 버퍼가 그건데 막 섞을 수가 있는데 온폴리시는 막 섞는 게 잘 안 되는 거죠
왜냐하면 남의 정책으로 하는 게 아니라 내 정책까지 학습을 해야 되니까 우리가 아까 함수 근사의 문제, 똑같은 여기 이제 순서를 반대로 써줬는데 이게 이제 아까 함수 근사의 IID 문제고 이거는 이게 이제 I의 문제고 이게 ID의 문제인데 그래서 IID 문제나 있는데 이거를 극복을 할 수가 없어요
리플레이 버퍼를 또 쓸 수도 없습니다
리플레이 버퍼는 오프 폴리시에서 쓰는 거고 온폴리시는 해당 사항이 없어요
그래서 이제 액터크리틱 좋은데 이 문제를 또 어떻게 극복을 할 거냐
A3C
계속 뭔가 문제가 있고 극복이 있고 문제가 있고 극복이 있고 그래서 A3C라는 모델이 나옵니다
A3C는 Asynchronous, Advantage, Actors Critic 이렇게 되는데 일단 Actors Critic 앞에 두 개가 붙었어요
Advantage랑 Asynchronous가 붙었는데 Advantage는 왜 붙었냐면 그냥 V로 하지 말고 Advantage로 하자 Advantage가 뭡니까 이득이죠 이득 그래서 이득으로 하자
이거고 그 다음에 Asynchronous는 뭐냐면 온폴리시에서는 리플레이 버퍼를 못 쓴단 말이에요
이전에 다른 정책으로 한 거는 써 먹을 수가 없습니다
그러면 그냥 내 정책까지 데이터를 쌓아 가지고 내 정책으로 내가 학습을 하면 되지
근데 워커를 여러 개를 만들어요
워커를 여러 개 만들어서 내 정책을 얘네한테 복사를 해줍니다
복사를 해주고 얘네가 그러면 각자 상호작용을 해요
각자 상호작용을 해가지고 각자 학습을 합니다
그 다음에 이 글로벌 폴리시하고 얘네가 학습한 폴리시하고 약간 반영을 해주는 거에요
이 폴리시에 있는 내용하고 글로벌 폴리시를 일종의 평균 같은 걸 해가지고 여기 있는 얘가 학습한 거를 여기다 합쳐줍니다
얘가 학습한 걸 합쳐주고 얘가 학습한 걸 합쳐주고 또 반대로도 얘 거를 여기다 반영하고 얘 거를 여기다 반영하고 얘 거를 여기다 반영하고 그러니까 남의 데이터로 학습을 할 수 없으니까 남이 학습한 정책의 파라미터만 가져와서 파라미터를 섞어주는 거에요
그래서 근데 그거를 동기적으로 하지 않고 여기 Asynchronous, 비동기적으로 한다는 건 뭐냐면 동기적으로 한다는 거는 동기적으로 하는 건 딱딱 맞춰서 한다는 거거든요
그래서 동작 그만 다 멈춰 자 정책을 복사해 주겠다
다 했지?
시작해 그 다음에 동작 그만 자 너 정책 내놔 너 정책 내놔 너 정책 내놔
이러면 이제 동기적으로 하는 거고 비동기적으로 하는 거는 그냥 각자 알아서 돌아가고 있습니다 컴퓨터가 3대가 있으면 3대가 알아서 돌아가고 있고 가끔 그냥 이렇게 왔다 갔다 하는데 그게 이제 딱 정해지지 않은 식으로 하는 거에요
왜 비동기적으로 하느냐 하면 동기적으로 하면 컴퓨터를 다 멈춰야 되니까 비율적이란 말이에요
그래서 알아서 다들 돌아가고 있어 내가 필요할 때 가서 니네 정책 복사해 올 거야 알아서 돌아가고 있어 이런 식으로 하는 거에요
그래서 이제 이런 식으로 돌아가는 게 이제 A3C입니다
그 다음에 이제 A3C가 몇 가지 이전하고 달라진 게 있는데 A3C에 엔트로피 손실이라는 게 들어있어요 엔트로피 손실은 뭐냐면 기존에는 보상만 많이 받으면 장땡이었거든요
근데 앞에서도 얘기 드렸지만 액터 크리틱의 단점 중에 하나가 국소체적에 빠진 거야
국소체적에 빠지면 어떤 특정 하던 대로만 하게 됩니다
그래서 엔트로피 손실은 뭐냐면 애초에 이 손실 함수 또는 보상에다가 엔트로피를 추가를 해버리는 거예요
그래서 아까도 얘기 드렸지만 엔트로피가 무질서 도잖아요
이 경우에는 어떻게 되냐면 골고루 있으면 엔트로피가 증가하는 거거든요
그래서 일종의 다양성을 보장을 하는 겁니다
다양성이 높으면 엔트로피가 증가해서 그거 자체가 일종의 보상이 되는 거예요
그래서 사람 중에도 성격이 뭔가 새로운 경험을 하는 것 그 자체의 가치를 두는 사람들이 있거든요
그러니까 그냥 맨날 지겨워요
맨날 맛있는 어떤 사람은 그냥 맛만 있으면 장땡인데 어떤 사람은 똑같은 식당에 두 번 가는 거를 너무 지겨워요
어떤 사람은 맛있으면 그걸로 하루 일주일 내내도 먹습니다
사람마다 성격이 다른데 기존의 강화학습은 보상만 중시했어요
근데 A3C에서는 추가로 엔트로피 손실을 한 걸 넣어서 내가 계속 같은 것만 하고 있으면 지겨워요
얘가 지겨움을 느낍니다
엔트로피라는 게 지겨움의 척도라고 생각하시면 돼요
그래서 다양하게 보면 엔트로피가 증가해서 보상을 받습니다
근데 하나만 하면 엔트로피가 감소하는데 그럼 얘는 지겨워요
얘는 엔트로피가 증가해야 어지르고 이것도 해보고 저것도 해보고 어제 이거 했으면 오늘 이거 해보고 이래야 즐거운 거예요
그래서 이걸 두 개를 섞어서 인위적으로 탐색을 장려하게 합니다
그 다음에 앞에서 이 A싱크로너스하게 비동기적으로 서로 정책을 섞어준다
이런 얘기를 했는데 그거를 호그와일드라는 느낌표까지 포함하는 이름인데요 이런 알고리즘을 써요
그래서 어떤 식으로 돌아가냐면 우리가 전역정책이 있고 워커멸 정책이 있는데 전역정책에 각자 컴퓨터가 열어되어 있으면 얘네끼리 혼자 강화학습 각자 하고 있다가 얘네가 학습한 정책을 글로벌정책에 업데이트를 해야 되거든요
그러면 얘가 업데이트를 했는데 저장하기도 전에 바로 얘가 업데이트를 해버리면 얘가 업데이트한 걸 덮어 써버리겠죠
그러면 얘가 업데이트한 거는 사라지잖아요
그래서 얘가 업데이트하는 동안 얘가 업데이트를 못하게 하려면 ROG이라는 걸 걸어가지고 야 다 손때 이거는 내가 저장할 거야
이렇게 해야 됩니다
여러분들 컴퓨터에서도 보면 파이팅이 되거든요
ROG을 걸어서 그렇게 되는 겁니다
그래서 ROG을 걸어야 되는데 ROG을 걸면 우리가 지금 비동기적으로 하고 있는데 얘네가 다 기다려야 되잖아요
얘가 끝날 때까지 그러면 우리가 굉장히 비효율적인 문제가 생깁니다
강화학습은 이게 학습을 되게 오래 돌려야 되기 때문에 쓸데없이 기다리는 시간 이런게 발생하면 굉장히 그게 누적되면 성능차이가 많이 나거든요
그래서 우리가 학습을 되게 오래 돌려야 되니까 굉장히 그게 누적되면 성능차이가 많이 나거든요
예를 들면 여러분들이 본부장님한테 뭔가 결제를 받아야 되는데 누가 본부장님 결제를 받고 있는 동안 남들은 다 기다려야 된다
근데 그게 한두 명이 아니다
그러면 그게 굉장히 시간 낭비잖아요
다 복도에서 결제판 들고 요즘에는 안 그렇겠죠
요즘에 아직도 관공서 이런 데 가면 결제판 들고 기관장실 앞에 사람들 줄 서있고 이런데 다 뭐하는 시간 낭비니까 그래서 호그와일드 알고리즘은 어떻게 하는 거냐면 랍을 안 걸려고 파라미터가 있으면 여기다가 전체적으로 반영하는 게 아니라 이 중에 일부에다가 랜덤하게 반영을 하는 거야
나는 여기만 써야지
그럼 얘는 나는 여기만 써야지 우연히 겹치면 덮어 쓰기가 되는데 대부분은 파라미터가 하도 많으니까 잘 안 겹치거든요
그래서 각자 서로 다른데다가 쓰기를 하는 겁니다
그래서 저장을 하는 방식이에요
그 다음에 우리가 이제 어드밴티지를 이익을 계산을 하는데 이익을 추정을 하는 것도 A3C에서 수정이 되는데 어떻게 하냐면 원래는 그냥 G랑 V랑 빼거든요
근데 이렇게 하면은 또 편향분산 교환에 걸려버립니다
그래서 어떻게 할까 하다가 우리가 편향분산 교환을 조절하기 위해서 Td랍다를 예전에 만들었어요
그러면 그 아이디어를 그대로 가져와서 쓰면 되겠네 라고 해서 Td랍다랑 약간 비슷한 방식으로 이런 식으로 해가지고 이전에 Td오차에다가 랍다로 이렇게 적용을 해서 이익을 계산을 하게 됩니다
그래서 우리가 이제 이 랍다가 0에서 1까지 값이죠
그래서 랍다가 0이면 Td가 되고 랍다가 1이면 mc랑 똑같이 되는데 랍다가 0에서 Td가 되면은 편향은 높지만 분산은 작게 되고 mc로 가면은 편향은 적지만 분산이 크게 되죠
그래서 랍다를 이렇게 조절을 해서 적당하게 학습을 하게 만들 수가 있습니다
그래서 뭔가 좋은건 다 집어넣는 이런 느낌이 있죠
지금 보시면은 원래는 폴리시그랜터 정책 경사 방법인데 거기다가 가치도 추정을 하고 그 다음에 이익을 계산할 때 랍다도 집어넣고 시간차도 집어넣고 비동기적인 업데이트도 집어넣고 좋은건 다 집어넣습니다
그래서 실제 구현을 보면은 여기 컴퓨터 리턴즈 앤드 어드밴티지라는 함수가 있는데 여기 보면은 델타가 Td오차를 계산을 한 다음에 요거를 요거에다가 그 다음에 이제 랍다 곱하기 해가지고 이전 상태 구한값이랑 더해주는 이런식으로 해서 누적을 시키는 방식을 사용을 해요
A2C
그 다음에 우리가 a3c가 나왔는데 실제로 해보니까는 비동기적으로 업데이트 하는게 비동기적으로 업데이트 하려고 온몸 비틀기를 했는데 본부장님한테 결제 받으려고 줄 서있는거가 너무 시간 낭비다
그래서 어떻게 하냐면 본부장님은 가만히 앉는데 각자 다른 페이지를 결제를 받고 있는거죠
본부장님은 한번은 이쪽팀 1페이지에 사인하고 한번은 이쪽팀 3페이지에 사인하고 이런식으로 정신된 산악교 이렇게 했는데 막상 실험을 해보니까 별 쓸모가 없었습니다
약간 뻘짓이었어요
그래서 이 앞에 있던 a3c가 a가 3개가 있다고 a3c인데 async, 비동기가 별로 필요 없으니까 쓱 지웁니다
그래서 a가 하나 줄어서 a3c에서 a2c로 바뀌어요
그래서 a2c는 어떻게 하냐면 그냥 각자 하면서 데이터를 쌓습니다
데이터를 쌓아놓고 그래서 얘네는 각자 자기 정책은 똑같아요
정책은 똑같은데 똑같은 정책으로 똑같이 행동하고 그 다음에 파라미터를 보내지 않고 데이터만 보냅니다
그리고 어차피 같은 정책으로 행동했으니까 데이터가 서로 다르거나 이러지 않아요
그래서 그 데이터로 얘가 학습을 합니다
그래서 아까 a3c와의 차이는 a3c에서는 얘네가 각자 롤러아웃도 하고 정책을 수행하는 실행도 하고 학습도 하고 각자 해요
그 다음에 파라미터만 공유했는데 a2c에서는 실행은 각자 하지만 학습은 중앙으로 모아서 합니다
그 다음에 파라미터는 일괄적으로 뿌려주는 거죠
왜냐하면 어차피 학습은 여기서만 하니까 그래서 이런 식으로 한 게 a2c고 그렇습니다
# 병렬 환경
from stable_baselines3.common.env_util import make_vec_env
vec_env = make_vec_env("CartPole-v1", n_envs=4)
# 학습
from stable_baselines3 import A2C
model = A2C("MlpPolicy", vec_env, verbose=1)
model.learn(total_timesteps=25000, progress_bar=True)
시각화
import tqdm
import gymnasium as gym
env = gym.make("CartPole-v1", render_mode="rgb_array")
render_episode(env, model)