본문 바로가기
Unity/08.ML-Agents

04-기본 강화학습(Reinforcement Learning) 예제 만들기

by 레드아이™ 2021. 1. 19.

본 내용은 아래의 링크의 내용을 따라하는 내용임.

github.com/Unity-Technologies/ml-agents/blob/release_12_docs/docs/Learning-Environment-Create-New.md

 

Unity-Technologies/ml-agents

Unity Machine Learning Agents Toolkit. Contribute to Unity-Technologies/ml-agents development by creating an account on GitHub.

github.com

위의 예제는 플랫폼 환경을 만들고, 그 위에서 Agent(Sphere GameObject)가 플랫폼 외곽으로 떨어지지 않고 주어진 Cube 방향으로 굴러가는 예제임.

Unity 버전: 2019.4.18f (LTS), ML-Agent 버전: Release 12


1. 프로젝트 생성 및 기본 GameObject 생성하기

1-1. Unity Hub를 통해서 "RollerBall"의 이름을 가진 3D 프로젝트를 생성한다.

 

1-2. Window-Package Manager를 통해서 ML-Agents Unity 패키지를 프로젝트에 추가한다.

# 패키지 설치가 완료되면, 아래 그림처럼 Project 뷰에 Packages-ML Agents 패키지가 추가 되었음을 확인할 수 있다.

 

1-3. Agent가 활동할 플랫폼을 Plane을 사용해서 제작한다. Hierarchy 뷰에서 오른쪽 마우스 클릭 후, 3D Object-Plane을 선택한다.

1-4. GameObject의 이름을 "Floor"로 변경하고, Position, Rotation이 모두 0, Scale이 모두 1인지 확인한다.

 

1-5. 같은 방법으로 3D Object-Cube를 생성한 후, 이름을 "Target"으로 변경한 후, Position, Rotation, Scale을 아래와 같이 설정한다.

 

1-6. 같은 방법으로 3D-Object-Sphere을 생성한 후, 이름을 "RollerAgent"로 변경한 후, Position, Rotation, Scale을 아래와 같이 설정한다.

 

1-7. RollerAgent를 선택한 상태로, Inspector 뷰에서 Add Component 버튼을 클릭하여 (Command+Shift+A), Rigidbody 컴포넌트를 추가한다.

 

1-8. Hierarchy뷰에서 오른쪽 마우스 클릭 후, Create Empty 메뉴를 선택해서 빈 GameObject를 생성한다.

 

1-9. 이름을 "TrainingArea"로 변경한 후, 1-3~1-7에서 생성한 Floor, Target, RollerAgent GameObject를 선택하고 Drag & Drop으로 모두 하위에 위치 시킨다.


2. Agent 설정하기: Script를 추가하여 강화학습 기본 환경 설정

2-1. RollerAgent GameObject를 선택한 후, Inspector 뷰에서 Add Component를 클릭하여 New Script를 선택하여 "RollerAgent"라는 이름의 스크립트를 추가한다.

 

2-2. Project뷰에서 생성된 RollerAgent 스크립트를 더블클릭하여 코드에디터에서 수정한다.

 

2-3. 코드 에디터에서 아래와 같이, (1) ML-Agent 패키지를 불러오고, (2) 베이스 클래스를 MonoBehaviour에서 Agent로 변경한 후, (3) Update 부분은 삭제를 한다.

# ML-Agent를 Unity 프로젝트에 추가하기 위한 기본 스텝이다.

 


3. 스크립트 설정

3-1. Agent 초기화/리셋

ML-Agent Tookit에서의 학습 프로세스는 Agent(이 예제의 경우 Sphere)가 특정 Task를 해결하는 Episode가 진행되는 방식이다. 각각의 Episode는 Agent가 Task를 해결(이 예제의 경우 Cube까지 굴러가기)하거나, 실패(플랫폼 외부로 나가기)하거나, 시간초과(문제를 해결/실패하는데 시간이 너무 오래 걸릴 경우)가 될때까지 유지된다.

각각의 Episode가 시작될 때, OnEpisodeBegin() 함수가 환경설정을 위해 호출된다. 일반적으로 Scene은 Agent가 다양한 조건에서 Task를 진행하도록 무작위 방식으로 초기화 된다.

이 예제의 경우, Agent(Sphere)가 Target(Cube)에게 접근하게 되면 Episode는 종료하게 되며, Target(Cube)이 새로운 무작위 위치에 생성이 되면서 새로운 Episode가 시작된다. 또는, Agent가 플랫폼 외부로 나가게 되면, 마차가지로 Episode가 종료가 되고, Agent가 Floor에 재 위치하면서 새로운 Episode가 시작된다.

 

(1) Agent를 Target(Cube)쪽으로 이동시키기 위해서는, Agent의 Transform(위치, 방향, 스케일 값을 가지는)을 참조할 필요가 있게 되는데, 이를 위해 RollerAgent 클래스에 Transform 타입의 public field를 추가한다. 이렇게 함으로써, Inspector 윈도우에 public field로 정의된 component가 나타나게 되고, 여기에 특정 GameObject를 드래그 해서 Target으로 지정할 수 있게 된다.

 

(2) Agent의 velocity를 초기화하기 위해서(또한, Agent를 이동시키기 위한 Force를 적용하기 위해서), Rigidbody의 참조가 필요하게 되는데, Rigidbody가 현재 Agent GameObject에 추가 되어 있는 상태이므로, GameObject.GetComponent<Rigidbody>()를 활용하여 참조할 수 있다.

 

현재까지의 스크립트의 모습은 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;

public class RollerAgent : Agent
{
    
    // rBody라는 Rigidbody 함수 정의
    Rigidbody rBody;
    
    void Start()
    {
    	// rBody함수는 현재 GameObject의 Rigidbody Component를 참조
    	rBody = GetComponent<Rigidbody>(); 
    }

    // Target이라는 public Transform 함수를 선언하여 차후 Inspector 윈도우에서 지정 
    public Transform Target;
    public override void OnEpisodeBegin()
    {
    
    	// Agent가 플랫폼 외부로 떨어지면(Y 좌표가 0이하가 되면), angularVelocity/velocity=0으로, 위치를 초기 좌표로 리셋
        if (this.transform.localPosition.y < 0)
        {
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3 (0, 0.5f, 0);
        }

        // Target을 Random.value함수를 활용해서 새로운 무작위 위치에 이동
        Target.localPosition = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
    }

}

 

3-2. 환경 관찰 및 정보 수집

Agent는 주어진 환경에서 수집한 정보를 Brain으로 보내게 되고, 이를 활용해서 어떤한 결정을 내리게 된다. Agent를 새롭게 학습시키거나, 학습된 모델을 적용할 때에, data는 인공신경망에 feature vector형식으로 보내진다. Agent가 성공적으로 Task를 학습하기 위해서는, 정확한 정보를 제공할 필요가 있다. 어떠한 정보를 제공할지를 판단하는 좋은 방법은, 해당 문제의 분석적해법(Analytical Solution)을 계산하는데 필요한 것이 무엇인지 생각해 보는 것이다.

 

이 예제의 경우, Agent가 수집하는 정보(총 8개)에는 Target과 Agent 자신의 위치(총 6개), 그리고 Agent의 속도(X, Z축 속도, 총 2개)가 포함된다. Agent는 해당 정보를 활용해서 속도를 조절하는 법을 학습하게 되고, 이를 통해 Target으로 너무 빨리 접근해서 플랫폼 밖으로 나가버리는 상황을 방지하게 된다.

 

아래의 코드가 추가된다.

    public override void CollectObservations(VectorSensor sensor)
    {
        // Target/Agent의 위치 정보 수집
        sensor.AddObservation(Target.localPosition);
        sensor.AddObservation(this.transform.localPosition);

        // Agent의 velocity 정보 수집
        sensor.AddObservation(rBody.velocity.x);
        sensor.AddObservation(rBody.velocity.z);
    }

 

3-3. Actions 수행 및 Reward 정의

Agent가 X, Z축으로 움직여서 Target쪽으로 이동하기 위해서는 X, Z축으로 적용시킬 Force가 결정되어야 한다. (Y축, 즉 수직으로 이동할 필요가 있으면 Y축 Force도 정의한다.)

강화학습에서는 Agent가 취하는 Action에 보상(Reward)를 적용하여 원하는 결과쪽으로 학습하게 한다.

이 예제의 경우, Agent가 Target(Cube)에 도달하게 되면, 1.0의 보상 점수를 얻게 된다.

 

아래의 코드가 OnActionReceived() method으로 추가된다.

    public float forceMultiplier = 10;
    public override void OnActionReceived(ActionBuffers actionBuffers)
    {
    
    	// Agent가 Target쪽으로 이동하기 위해 X, Z축으로의 Force를 정의
        Vector3 controlSignal = Vector3.zero;
        controlSignal.x = actionBuffers.ContinuousActions[0];
        controlSignal.z = actionBuffers.ContinuousActions[1];
        rBody.AddForce(controlSignal * forceMultiplier);

        // Agent와 Target사이의 거리를 측정
        float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);

        // Target에 도달하는 경우 (거리가 1.42보다 작은 경우) Episode 종료
        if (distanceToTarget < 1.42)
        {
            SetReward(1.0f);
            EndEpisode();
        }

        // 플랫폼 밖으로 나가면 Episode 종료
        if (this.transform.localPosition.y < 0)
        {
            EndEpisode();
        }

    }

4. Editor에서 Agent 설정

Unity Editor에서 Agent 스크립트를 설정한다.

 

4-1. RollerAgent GameObject를 선택하여 RollerAgent 스크립트의 Target 부분에 Target GameObject를 끌어다놓는다.

 

4-2. RollerAgent GameObject가 선택되어 있는 상태에서 Add Component 버튼을 클릭해서(Command+Shift+A), 'Decision Requester' 스크립트를 추가하고, Decision Period을 10으로 설정한다. (10 step 마다 Decision을 요청하여 Observation-decision-action-reward 사이클이 돌아가게 된다.)

 

4-3. 마찬가지로, Add Component로 Behavior Parameters 스크립트를 추가하고, Behaviour Name: RollerBall, Vector Observation-Space Size: 8(총 8개의 정보를 수집함으로), Actions-Continous Actions: 2(X, Z축의 2개 축으로 이동)로 설정한다.


5. 스크립트 테스트

스크립트 설정이 완료된 후, 우선 키보드 조작을 통해 Agent를 이동시켜 구축한 환경을 테스트 해 본다.

이를 위해서는 스크립트 하단에 아래의 코드를 추가한다.

    public override void Heuristic(in ActionBuffers actionsOut)
    {
        var continuousActionsOut = actionsOut.ContinuousActions;
        continuousActionsOut[0] = Input.GetAxis("Horizontal");
        continuousActionsOut[1] = Input.GetAxis("Vertical");
    }

그리고 RollerAgent GameObject에 추가된 Behavior Parameters 컴포넌트의 Behavior Type을 Heuristic Only(사용자가 컨트롤 or rule-based 등 정해진 규칙에 따라 행동)로 변경한 후, Play 버튼을 눌러서 Scene을 테스트 해본다. 키보드 화살표 혹은 ASDW 버튼으로 공을 움직일 수 있으며, 플랫폼 밖으로 나가거나 Target(Cube)에 다다르게 되면 새로운 Episode가 시작된다.

 

# 테스트가 제대로 이루어진다면, 위의 스크립트를 삭제하고, Behavior Type을 다시 Default로 돌려놓고 아래의 모델 학습 부분을 게속해서 진행한다.

 


# C# 코드 작성 후, 아래의 에러 메세지가 나오는 경우가 있는데, com.unity.ml-agents 패키지의 버전이 낮기 때문이다. Actuators는 1.4.0-preview 패키지 버전에 추가되었다.

"The type or namespace name 'Actuators' does not exist in the namespace 'Unity.MLAgents' (are you missing an assembly reference?) [Assembly-CSharp]"

따라서, Window-Package Manager에서 Advanced 옵션의 'Show preview packages' 옵션을 선택한 후, ML Agents 1.4.0 Preview 이상 버전으로 업데이트 하면 된다.


6. 모델 학습하기

이제 모델 학습을 진행하기 위해 기존의 "03-3DBall 예제 활용하기" 포스팅에서 사용된 3DBall.yaml 파일을 활용할 것이다. 1번에서 다운받은 ML-Agents Release 12의 소스코드.zip파일의 압축을 풀면 /config/ppo/3DBall.yaml 파일이 있다.

본인이 원하는 예제 폴더를 새로 만들고, 그 아래에 /config/ppo/ 폴더를 동일하게 만들어 복사/붙여넣기 한 후, 파일명을 'rollerball_config.yaml' 변경한다.

 

6-1. 해당 yaml파일을 코드 에이터에서 열고 아래와 같이 behavior의 이름을 RollerBall로 변경하고, Hyperparameter 값을 변경한다.

(Configuration File의 각각의 Hyperparameter에 대한 설명은 링크에서 확인할 수 있다.)

 

6-2. 터미널에서 새로운 가상환경을 생성하거나, 기존의 생성한 가상환경을 재활용한다면 그 가상환경을 활성화 한다. (macOS 기준)

// 새로운 가상환경을 특정 폴더에 생성(예: mlagent-env 라는 가상환경 생성)
python3 -m venv ~/Documents/Workshop/MLAgents/python-envs/mlagent-env

// 가상환경 활성화
source ~/Documents/Workshop/MLAgents/python-envs/mlagent-env/bin/activate

// 아래의 코드로 ML-Agent가 실행되는지 확인
mlagents-learn --help

 

6-3. 아래의 명령어를 실행한 후, Unity Editor에서 Play 버튼을 눌러 학습을 시작한다.

// config 폴더가 위치한 상위 MLAgents로 이동
cd ~/Documents/Workshop/MLAgents

// rollerball_congif.yaml위치를 지정하고, 학습세션에 부여할 id를 지정
mlagents-learn config/ppo/rollerball_config.yaml --run-id=RollerBall

시간이 지날수록, Agent(Sphere)가 Target(Cube)로 접근하는 시간이 줄어든다.

# 터미널에 표시되는 Behavior 이름과 Hyperparameter가 지정 한대로 적용되었는지 확인한다.

 

6-4. 학습이 완료되면, 상위폴더(MLAgents) 아래에 results 폴더가 생기고 그 안에 지정한 id 세션 이름(RollerBall) 폴더가 생기고 RollerBall. onnx 모델 파일이 생성된 것을 확인할 수 있다.

6-5. 아래의 명령어를 터미널에 입력하고, 웹브라우저에서 localhost:6006 주소를 입력하면, 웹브라우저에서 텐서보드를 실행하여 시각적으로 결과를 확인할 수 있다.

tensorboard --logdir results

'Unity > 08.ML-Agents' 카테고리의 다른 글

03-3DBall 예제 활용하기  (1) 2021.01.17
02-ML-Agents Toolkit 설치  (0) 2021.01.17
01-가상환경(Virtual Environment) 구축  (0) 2021.01.17

댓글