머신러닝 강화학습은 위와 같은 사이클로 동작한다.
Agent는 테스트 환경을 관찰하여 데이터를 얻고(Observation), 그걸 바탕으로 결정을 내린다(Decision).
결정은 바로 행동으로 이어지고(Action), 행동이 테스트 의도와 맞을 경우 보상을 받는다(Reward).
이러한 과정의 반복을 통해 Agent는 어떤 결정과 행동이 가장 높은 결과를 내는지 학습한다.
ML-Agents를 이용하여 간단한 머신러닝을 해보자.
파란색 네모는 Player Agent, 하얀색 구는 Goal이다.
Player가 Goal에 도달하는 것이 목적인 게임이다.
# MoveToGoalAgent.cs
using Unity.MLAgents;
public class MoveToGoalAgent : Agent
{
}
Unity.MLAgents.Agent를 상속받은 MoveToGoalAgent 클래스를 생성하고, 스크립트를 Agent Object에 넣어준다.
Agent를 상속받는 스크립트를 넣어주면 자동으로 Behavior Parameters 스크립트도 추가된다.
Agent가 될 MonoBehaviour 스크립트를 먼저 넣어두고, Agent를 나중에 상속받으면 Behavior Parameters는 추가되지 않으니 주의.
1) Observation
먼저 Agent의 Observation을 설정해 보자.
게임은 Player가 Goal에 도달해야 하므로, Agent는 Player와 Goal의 Position 값을 알고 있으면 된다.
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using UnityEngine;
public class MoveToGoalAgent : Agent
{
[SerializeField] private Transform targetTransform;
public override void CollectObservations(VectorSensor sensor)
{
sensor.AddObservation(transform.localPosition);
sensor.AddObservation(targetTransform.position);
}
}
CollectObservations에서 VectorSensor에 관찰할 데이터를 모두 넣어준다.
Behavior Paramters의 Vector Observation 항목에서 VectorSensor에 넣어준 데이터 갯수만큼 사이즈를 맞춰준다.
x, y, z값 3개로 이루어진 Vector3 데이터를 두 개 넣었으니 Space Size는 6이 된다.
Stacked Vectors는 Agent가 다음 추론에 지난 관찰 데이터를 사용하도록 하는 파라미터다. Stacked Vectors가 2일 경우 바로 전 관찰까지 참조하게 된다.
position을 사용하는 이 게임에서는 Agent가 이전 position과 현재 position을 알게 되므로, Player의 방향을 추론에 이용할 수 있게끔 된다는 소리 같다. 아주 간단한 수준의 이 게임에서는 1로 두고 쓴다.
2) Decision
Action을 하기 위해선 Decision이 필요하다.
Agent에 Decision Requester 스크립트를 넣어 Decision 기능을 수행한다. Decision Requester는 일정한 시간마다 Decision Request를 날리고, Action을 수행하도록 한다.
/// <summary>
/// The DecisionRequester component automatically request decisions for an
/// <see cref="Agent"/> instance at regular intervals.
/// </summary>
/// <remarks>
/// Attach a DecisionRequester component to the same [GameObject] as the
/// <see cref="Agent"/> component.
///
Decision을 내리는 방법은 다양하지만 이 게임에서는 간단하게 Decision Requester 스크립트를 사용한다.
/// The DecisionRequester component provides a convenient and flexible way to
/// trigger the agent decision making process. Without a DecisionRequester,
/// your <see cref="Agent"/> implmentation must manually call its
/// <seealso cref="Agent.RequestDecision"/> function.
3) Action
다음으로 Vector Action 값을 설정한다.
여기서 사용하는 머신러닝 알고리즘은 오직 숫자에 의해서만 동작하며, Behavior Parameters > Vector Action를 통해 Input을 받을 수 있다.
Vector Action은 정수만 사용하는 Discrete(이산적)와 -1.0f ~ 1.0f값을 사용하는 Continuous(연속적)의 SpaceType을 설정할 수 있다.
예를 들어 보자.
Discrete : 고정된 방향으로만 움직이는 자동차 AI를 만든다. Branch 0을 이용하여 앞 또는 뒤 2가지 선택사항을 만든다고 하면, Size를 2로 한다. 그럼 VectorAction의 0번 데이터는 0 또는 1이된다. 왼쪽, 중앙, 오른쪽 방향 데이터도 넣는다고 하면, Branch 1 Size를 3으로 하고, 0, 1, 2의 데이터를 받을 수 있다.
Continuous : 자동차가 360도 어느 방향으로도 갈 수 있게 -1.0f ~ 1.0f 값을 받아 사용한다.
이 게임에서 Agent는 x, z축으로 이동하는데, 이동값을 Continuous Type으로 각각 받아서 사용할 예정이다.
public class MoveToGoalAgent : Agent
{
...
public override void OnActionReceived(float[] vectorAction)
{
float moveX = vectorAction[0];
float moveZ = vectorAction[1];
transform.localPosition += new Vector3(moveX, 0, moveZ) * Time.deltaTime * moveSpeed;
}
}
OnActionReceived에서 Agent로부터 받은 VectorAction값을 transform를 이동하는데 사용한다.
이제 게임이 돌아가는 에피소드를 작성한다.
public class MoveToGoalAgent : Agent
{
...
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent<Goal>(out Goal goal))
{
SetReward(1f);
}
if (other.TryGetComponent<Wall>(out Wall wall))
{
SetReward(-1f);
}
EndEpisode();
}
}
Player가 Goal과 충돌할 경우 SetReward(1f)를 이용하여 Positive 보상을 주고, 맵을 벗어나 Wall과 충돌할 경우 -1에 해당하는 Negative 보상을 준다.
어떤 경우라도 EndEpisode()를 호출하여 이번 에피소드를 종료한다.
public class MoveToGoalAgent : Agent
{
...
public override void OnEpisodeBegin()
{
transform.localPosition = new Vector3(-2, 1, 0);
}
}
에피소드가 시작될 때는 Player의 위치를 초기위치로 돌려주어 게임을 처음부터 다시 시작한다.
public class MoveToGoalAgent : Agent
{
...
public override void Heuristic(float[] actionsOut)
{
actionsOut[0] = Input.GetAxisRaw("Horizontal");
actionsOut[1] = Input.GetAxisRaw("Vertical");
}
}
마지막으로 테스트를 위해 OnActionReceived로 들어온 Action을 수정하는 메서드를 작성한다.
Heuristic()에서 actionsOut에 넣은 데이터는 OnActionReceived의 파라미터로 전달된다.
기능 작업은 완료되었고, 테스트를 해보자.
Behavior Parameters > Behavior Type에서 Default 또는 HeuristicOnly를 선택한다.
Default는 Python ML Agent가 돌아가지 않을 때 HeuristicOnly와 동일하게 동작하며, HeuristicOnly는 Agent.Heuristic() 메서드를 동작시킨다. Default 또는 HeuristicOnly에서 플레이하면 방향키 조작으로 Player를 움직일 수 있다.
InferenceOnly는 기존 학습 모델로 동작하는 방식이다.
이제 Python ML Agent를 실행하여 실제 머신러닝을 돌려보자.
우선 Python 가상환경을 실행한다.
D:\Workspace\Projects\MLAgent>venv\Scripts\activate
(venv) D:\Workspace\Projects\MLAgent>
mlagents-learn을 실행하는데, --run-id 옵션으로 저장할 모델의 이름을 지정한다.
이미 사용한 id일 경우 --force를 지정하면 run-id를 덮어쓰고, --resume을 지정하면 해당 id의 모델을 다시 실행한다.
(venv) D:\Workspace\Projects\MLAgent>mlagents-learn --run-id=MoveToGoal
정상적으로 작동할 경우 아래와 같이 Unity 로고가 나오면서 Unity의 Play모드 진입을 기다린다.
Play모드에 진입하면 Agent는 앞서 작성한 코드에 따라 랜덤으로 방향을 움직이기 시작한다.
한 가지 고려해야할 사항이 있다. Agent가 끝끝내 Goal을 터치하지 못해 Positive Reward를 얻는 경우가 없게 되면, Agent는 Negative Reward를 피하기 위해 초기 Position 주변을 영원히 맴돌 수 있다.
이런 경우를 방지하기 위해 Agent의 MaxStep값을 명시해주어야 한다.
Max Step은 기본적으로 0으로 설정되어 있으며, 0은 Unlimited를 의미한다. Agent의 Step은 MonoBehaviour.FixedUpdate()와 마찬가지로 초당 50회 동작한다.
적당히 1000회 정도로 Step을 제한하면 될 것 같다.
마지막으로 Agent의 학습 속도를 높여주기 위해 시뮬레이션을 여러 개 배치한다.
일정 Step마다 학습 결과가 출력되고, 500000번째 Step에서 모델은 저장/종료된다.
2021-02-24 00:45:16 INFO [stats.py:139] MoveToGoal. Step: 50000. Time Elapsed: 19.279 s. Mean Reward: 0.148. Std of Reward: 0.986. Training.
2021-02-24 00:45:30 INFO [stats.py:139] MoveToGoal. Step: 100000. Time Elapsed: 33.543 s. Mean Reward: 0.113. Std of Reward: 0.992. Training.
2021-02-24 00:45:44 INFO [stats.py:139] MoveToGoal. Step: 150000. Time Elapsed: 47.892 s. Mean Reward: 0.316. Std of Reward: 0.948. Training.
2021-02-24 00:45:59 INFO [stats.py:139] MoveToGoal. Step: 200000. Time Elapsed: 62.353 s. Mean Reward: 0.653. Std of Reward: 0.758. Training.
2021-02-24 00:46:14 INFO [stats.py:139] MoveToGoal. Step: 250000. Time Elapsed: 77.498 s. Mean Reward: 0.935. Std of Reward: 0.356. Training.
2021-02-24 00:46:29 INFO [stats.py:139] MoveToGoal. Step: 300000. Time Elapsed: 92.672 s. Mean Reward: 0.989. Std of Reward: 0.150. Training.
2021-02-24 00:46:44 INFO [stats.py:139] MoveToGoal. Step: 350000. Time Elapsed: 107.837 s. Mean Reward: 0.995. Std of Reward: 0.104. Training.
2021-02-24 00:47:00 INFO [stats.py:139] MoveToGoal. Step: 400000. Time Elapsed: 123.088 s. Mean Reward: 0.986. Std of Reward: 0.165. Training.
2021-02-24 00:47:15 INFO [stats.py:139] MoveToGoal. Step: 450000. Time Elapsed: 138.620 s. Mean Reward: 0.996. Std of Reward: 0.094. Training.
2021-02-24 00:47:30 INFO [stats.py:139] MoveToGoal. Step: 500000. Time Elapsed: 153.751 s. Mean Reward: 0.995. Std of Reward: 0.100. Training.
2021-02-24 00:47:30 INFO [model_serialization.py:104] Converting to results\Test5\MoveToGoal\MoveToGoal-499984.onnx
2021-02-24 00:47:30 INFO [model_serialization.py:116] Exported results\Test5\MoveToGoal\MoveToGoal-499984.onnx
2021-02-24 00:47:30 INFO [model_serialization.py:104] Converting to results\Test5\MoveToGoal\MoveToGoal-500050.onnx
2021-02-24 00:47:30 INFO [model_serialization.py:116] Exported results\Test5\MoveToGoal\MoveToGoal-500050.onnx
2021-02-24 00:47:30 INFO [torch_model_saver.py:116] Copied results\Test5\MoveToGoal\MoveToGoal-500050.onnx to results\Test5\MoveToGoal.onnx.
2021-02-24 00:47:30 INFO [trainer_controller.py:85] Saved Model
학습 결과는 프로젝트 Root/results 경로에 mlagents-learning의 id 이름 폴더에 저장된다.
모델은 Agent이름.onnx로 저장된다.
해당 모델은 BehaviorParameters > BehaviorType을 InferenceOnly로 두고, Model에 지정하여 바로 사용해 볼 수 있다.
git) github.com/JJ03/MoveToGoal
참고)
1. CodeMonkey - How to use Machine Learning AI in Unity! (ML-Agents) - youtu.be/zPFU30tbyKs
2. UnityDocs - ML Agents 1.7.2-preview - docs.unity3d.com/Packages/com.unity.ml-agents@1.7/api/Unity.MLAgents.html
'Unity & C# > Machine Learning' 카테고리의 다른 글
Unity ML-Agents 설치 (0) | 2021.02.18 |
---|