1. 강화학습의 기본 흐름
환경을 관찰하고, 그 환경을 바탕으로 결정을 하고, 그 결정을 바탕으로 행동을 한다. 취해진 행동으로 인해 변경된 상태는 보상과 새로운 환경을 갖는다. 이 새로운 환경을 관찰하는 것으로 다시 돌아와 이 과정을 반복하게 되는데 결과적으로 agent는 reward를 극대화 하는 방향으로 action을 취하도록 학습하게 될 것이다.
이 각 단계들을 어떻게 구현할까?
우선 이 모든것을 하는 주체인 Agent를 정의해주어야 한다.
2. Agent 정의
Unity.MLAgetns를 import한다음, 여기에 포함된 커스텀 Agent class를 정의해야한다. 여기서 Agent란 행동을 하는 객체이다 Agent라고 class를 정의하고 visual studio의 see definition을 이용하여 Import한 파일을 보면, Agent와 함수들이 어떤 이름으로 정의되어 있는지 확인할 수 있다 이 파일엔 위 모든 과정들을 행하는 함수들이 모두 포함되어 있다. 이 함수들을 오버라이딩 하여 우리는 우리의 문제에 맞게 AI를 학습시키는 것이다.
3. 환경 관찰
환경 관찰은 MLAgent의 CollectObservation을 오버라이딩하면 되는데, 우선 관찰할 주체가 필요하고 그 주체가 어떤것을 관찰해줘야할지를 정의해주어야 한다. 관찰할 주체는 MLAgetns.Sensors에 정의된 센서들을 통해 관찰된다. sensor에 우리가 관찰하고자 하는 값들을 등록함으로써 문제에 맞게 관찰할 수 있게 되는것이다.
정리하자면 sensor는 Agent가 어떻게 환경을 바라보는지를 정의해주는 것이고, 문제에 따라 다른 관찰할 변수를 이 센서에 문제에 맞게 정의하고 등록해줘야 하는 것이다.
예시로 캐릭터가 낙사하지 않고 공에 접근하는 이 문제를 확인해보자. 캐릭터가 낙사하지 않고 공에 접근하게 하기 위해서는 캐릭터의 위치와 공의 위치를 알아야 할 것이다. 이 변수를 sensor에 추가(addobservation)하여 sensor가 이 변수들을 관찰하도록 명시해주자.
각 컴포넌트의 위치라고 해서 관찰할 변수를 2개만 받으면 된다고 생각할 순 있지만 그렇지 않다. 왜냐하면 위치는 x,y,z로 구성되기 때문에 Space size는 6으로 설정해주어야 한다. 추가적으로 이전 환경값을 함께 이용하여 학습하고싶다면 stacked vector를 1이 아닌 다른 값으로 구성해주면 될 것이다. 이 값은 component에 transform을 등록해주고 위의 함수에서 sensor의 addObservation을 통해 등록해준다
이제 sensor를 등록했으니, 행동을 결정할 때 어떤 값을 받을지와 어떤 행동을 할지 OnActinoReceived에 정의해주자.
4. 행동 결정 및 행동
OnActinoReceived함수는 Action을 ActionBuffers로 받아, Action이 행해졌을 때의 "결정"을 결정하는 로직을 설정할 수 있다. 여기서 MLAgent는 모든 행동 / 보상 등 모든 값을 Number로만 인지할 수 있고, 행동으로는 이해할 수 없기 때문에 각 행동이 어떤 값을 갖고 어떻게 변화하는지는 우리가 문제에 맞게 우리가 정의해주어야 한다는 사실을 미리 리마인드 하고가자. 이렇게 OnActinoReceived함수가 정의된 스크립트를 Unity Inspector를 통해 보면, Behavior Parameters를 설정할 수 있을것이다.
여기서 Behavior Parameters란, Action을 받았을 때의 이벤트를 정의하는 파라미터로, 받을 행동값의 유형 (discrete or continuous)이나, 받을 입력의 갯수(branch)와 받을 값의 범위(branch I size)등을 정의해줄 수 있을것이다.
아까 이전에 확인했던 ActionBuffers는 이 행동값의 유형에 따라 참조하는 멤버 버퍼가 달라지는데, 만약 discrete라면 actions.DiscreteActions로 접근해야한다
환경 관찰에서 어떤 값을 관찰할지 문제에 맞게 정의하였으니 행동이 취해졌을 때 이 값중에서 행동 결정에 필요한 변수들을 가져와서 결정을 내리면 된다.
이제 행동(Action)을 정의해야하는데 이 문제에 필요한 값은 x와 z(가로 세로)뿐이므로 2개의 Action space size를 갖을 것이고 이 값들이 연속적이므로 continuous로 받아와 주도록 해야 할 것이다. 우선 간단하게 이 sensor를 통해 관찰되고, buffer로써 들어온 action 중, x,z를 이용해서 player의 transform을 이동시켜 주도록 하자
4. 보상
어떤 상태가 보상(reward)을 받을 수 있는 상태인지를 정의해주어, AI가 그 상태를 찾도록 유도해야 한다. 공에게 is_triggered 상태를 정의해주고, player가 이 공을 충돌하면 reward를 전달해주도록 정의해줄 것이다.
collider에 충돌하면 호출되는 함수인 onTriggerEnter에 Agent에 정의된 SetReward (정확한 reward값을 설정해주는 함수)와 AddReward(현재 값에서 인자로 전달된 수만큼 값을 증가시키는 함수)함수를 호출하여 reward를 정의할 수 있다.
그리고 공에 닿으면 그 에피소드가 성공했다는 의미이므로 EndEpisode를 통해 에피소드를 종료시켜주어야 한다.
또한 실패 에피소드를 정의해주기 위해 wall component를 정의해주고, 위의 ball과 같이 모든 것을 정의해줄것이다.
에피소드의 시작과 종료에 대해서 살펴보자
에피소드가 시작되면 다시 모든 값들을 초기화 시켜주어야 하므로 OnEpisodeBegin함수를 오버라이딩 하여 이를 구현해주어야 한다. 여기서는 값을 기억하여 푸는 것을 방지하고자 위치를 random으로 지정해줄 것이다.
5. 휴리스틱 함수를 이용한 테스트
이제 어떻게 모든 것을 정의했다.
이대로 트레이닝을 진행해도 되지만, 우선 지금까지 정의한 것들이 정확하게 잘 되는지 확인해보자.
MLAgent에 정의된 Heuristic함수를 통해 이것이 가능하다. 이를 통해서 테스트를 해볼 수 있다
이 함수는 단순히 이동의 continuous값을 사용자의 input 값을 actionbuffer로 전달하는 함수이다. 이를 통해 action, reward, obsercation의 동작을 확인할 수 있다.
이제 이렇게 정의한 heuristic함수를 함수를 Behavior parameters에서 behavior type으로써 정의해준다.
이제 실행해보고 이것들이 제대로 적용된다면, 다시 Behavior parameters에서 behavior type를 default로 정의해주고, python에서 ml-agent를 돌리면 강화학습을 활용한 Unity 환경에서의 AI학습이 완성된다.
6. 디테일
이제 조금 더 디테일하게 가보자. 만약에 위 예제에서 ball을 건들지 않으면 어떻게 될까? 그러면 그냥 단순히 패널티를 주는 벽을 피하는 방식으로 학습하게 되어 결국 멈추게 될것이다. 이를 어떻게 해결할까?
안움직이는 모델을 방지하고자 max step을 정의하여 ball을 찾지 않으면 패널티를 줌으로써 ball을 찾도록 강제할것이다. 여기론 1000으로 정의했다고 가정하자.
이제 모든 환경을 완성시켰다. 이제부터는 이 모든 것을 하나의 환경(Enviroment)을 정의하고 안에 넣은 후 asset으로 넣어 prefab으로 만들자. 이를 통해 동일한 환경을 여러개로 복사하여 독립적으로 학습할 수 있다. 뭐 GA에서 population을 늘리는 것과 같은 개념인가보다. 독립적인 환경을 조성하기 위해서 global position이 아닌, local position으로 설정해주어야 할 것이다. 모든 transform에 대하여 position을 localposition으로 변경해준다.
추가적으로 실패, 성공 시마다 환경 중, Platform의 색을 바꿔줌으로써 가시성을 높여줄 수 있다.
위와 같이 정의된 코드를 end episode가 호출될 때 reward의 값에 따라 floormeshrenderer를 win/lose Material로 바꿔주도록 한다.
또한 값 뿐만이 아니라 실제 platform의 색을 변경해주기 위해서 이 변수들을 enviroments의 Inspector에서 찾은 후, floor mesh renderer에 platform을, win/lose material에 matarials를 등록시켜준다.
이 디테일 들을 등록시켜주고 ml-agent와 unity실행을 통해 학습을 시켜주면 된다. 결과를 보면 잘 학습되는 것을 볼 수 있다. 종료하면 이 모델이 어디에 저장되는지 보여줄 것이다.
이 모델이 어떤 값들을 보여주는지 확인해보자.
Neural network model이 정의되어 있음을 확인할 수 있다.
이 모델을 agent의 model에 넣고 inference only 모드로 명시해주면 학습한 모델을 추가적인 학습없이 바로 사용할 수 있다.
하지만 이 모델에는 문제가 있다. 만약 ball의 위치가 바뀌면 오른쪽으로만 가면 reward를 얻는다고 학습했던 agent는 잘못된 행동만 하게 될 것이다. 이에 대한 원인은 ball과 player의 위치를 고정하여 학습을 시켰다는 점과 config을 설정해주지 않았기 때문이다. 위치의 고정을 해결하기 위해서 에피소드가 실행될 때 호출되는 메소드인 onEpisodeBegin에 각 컴포넌트의 transform을 랜덤하게 정의해주고, 설정의 부재를 해결하기 위해서 learning에 필요한 parameter의 이름과 값들이 정의된 yaml 파일을 정의하고, ml-agent에 옵션으로써 전달해야 한다.
추가로 이전 모델을 추가적으로 학습하고 싶다면 --initialize-from=id이름 옵션을 줌으로써 추가 학습이 가능하다.
지금까지의 학습의 과정을 그래프로써 보고싶다면 tensorboard --logdir 위치 를 이용하면 된다.
7. Reference
'ML' 카테고리의 다른 글
ML-Agent 학습 환경 디자인 (0) | 2021.05.28 |
---|---|
트리 탐색과 유전 알고리즘을 활용한 외판원 문제 근접해 구하기 (0) | 2021.05.12 |
DQN의 입력 (0) | 2021.04.07 |
TensoFlow를 활용한 DQN 실습 Breakout 적용 - 2 (0) | 2021.04.03 |
TensoFlow를 활용한 DQN 실습 Breakout 적용 - 1 (0) | 2021.04.03 |