본문 바로가기

개발 프로젝트/Unity - Individual Museum

Unity 강화학습 ML-Agent (2) - Agent 클래스와 딥러닝 구조

이번 게시글에선 Ml-Agent의 Agent 클래스에 대해 설명하며,

Feature Vector를 활용하는 딥러닝의 구조에 대해 작성하겠습니다.

 

Agent 클래스를 상속받는 클래스는 최소 3개의 함수로 구성됩니다.

 

1. Ml-Agent FrameWork의 Agent 클래스를 상속하는 클래스 A작성

public class ButterflyAgentChase : Agent
{
	void Start() {
    	...
    }
    
    //Update는 사용하지 않습니다.
}

 

2. Agent 클래스의 OnEpisodeBegin을 override하여 새로운 Episode가 시작될 때 환경의 초기값 설정

// In Agent Class
.
.
 /// <summary>
 /// Implement `OnEpisodeBegin()` to set up an Agent instance at the beginning
 /// of an episode.
 /// </summary> 
 public virtual void OnEpisodeBegin() { }
 .
.
 public class ButterflyAgentChase : Agent
{
    public Transform Target;
    
    private bool isEndByBreak;
    private Rigidbody rBody;
    private Vector3 spawnPoint;
    
    
    void Start()
    {
        rBody = GetComponent<Rigidbody>();
        spawnPoint = Vector3.zero;
        isEndByBreak = false;
    }

    
    
    public override void OnEpisodeBegin()
    {
        
        //Agent가 너무 멀리 떨어지면 angularVelocity/velocity=0으로, 위치를 초기 좌표로 리셋
        if (isEndByBreak)
        {
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            
            isEndByBreak = !isEndByBreak;
        }
        // Target을 Random.value함수를 활용해서 새로운 무작위 위치에 이동
        this.transform.localPosition = new Vector3(Target.localPosition.x + Random.value * 30 - 15, Random.value * 3 + 0.3f, Target.localPosition.z + Random.value * 30 - 15);
        Target.localPosition = new Vector3(Random.value * 5, Random.value * 1 + 2.5f, Random.value * 8 - 3);
        this.spawnPoint = transform.localPosition;
    }
 }

OnEpisodeBegin() 함수는 Episode가 시작될 때 호출되는 콜백 메서드입니다. 해당 함수는 에이전트의 초기화와 관련된 작업을 수행하는 데 사용됩니다. 이 함수는 에피소드가 시작될 때마다 호출되므로, 초기화, 재설정 또는 다른 초기 단계의 작업을 수행하는 데 유용합니다.

 

 

 

3. Agent 클래스의 CollectObservations() 함수를 override하여 환경 관찰 정보를 Brain으로 전달하여 학습

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

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

CollectObservations 함수는 모델 생성에 있어서 가장 중요한 역할을 합니다.

 

CollectObservations 함수를 해석하기 위해선 인공지능에 대한 이해가 필요합니다. Agent는 주어진 환경에서 수집한 정보를 Brain으로 보내고, 이를 활용해서 Policy를 수정합니다. Agent를 새롭게 학습시키거나, 학습된 모델을 적용할 때, data를 인공신경망에 Feature Vector 타입(sensor 변수)으로 전달합니다.

 

Feature Vector(특성 벡터)란 머신러닝 및 패턴 인식과 같은 분야에서 사용되는 중요한 개념입니다. Feature Vector는 데이터 포인트를 나타내는 벡터이며, 각각의 요소는 해당 데이터 포인트의 여러 특성(특성)에 대한 값을 포함합니다. 각 특성은 해당 데이터에 대한 중요한 정보를 나타냅니다.  이는 객체에 대한 여러 정보를 포함하는 벡터로서 객체에 대한 특성 벡터를 정의하면 특성 공간을 구성할 수 있습니다.

 

Feature vectors물체를 수치적으로 표현하여 다양한 분석에 도움을 줍니다. 특성 벡터는 많은 비교 기술이 있기 때문에 분석에 용이합니다. 두 객체의 특성 벡터를 비교하는 간단한 방법 중 하나는 유클리드 거리를 취하는 것입니다.

 

아래는 Feature Vector 4가지 특징입니다.

  1. 차원(Dimension): Feature vector의 차원은 벡터의 길이 또는 포함된 특성의 수를 나타냅니다. 예를 들어, 3차원 feature vector는 세 개의 특성을 갖습니다.
  2. 특성(Feature): Feature는 데이터의 특징을 설명하는데 사용되는 입력 변수입니다. 예를 들어, 얼굴 이미지의 특성 벡터는 각 특성이 눈, 코, 입 등의 다양한 얼굴 특징을 나타내는 값을 포함할 수 있습니다.
  3. 분류 및 회귀: 머신러닝에서 feature vector는 주로 분류(Classification) 및 회귀(Regression) 문제에서 사용됩니다. 분류 문제에서는 각 클래스에 속하는 데이터를 구별하는 데 사용되고, 회귀 문제에서는 연속적인 결과를 예측하는 데 사용됩니다.
  4. 훈련 및 학습: Feature vector는 모델을 훈련시키고 학습시키는 데 사용됩니다. 모델은 이러한 feature vector를 기반으로 입력 데이터의 패턴을 학습하고 예측합니다.

 

https://brilliant.org/wiki/feature-vector/

 

Feature Vector | Brilliant Math & Science Wiki

In machine learning, feature vectors are used to represent numeric or symbolic characteristics, called features, of an object in a mathematical, easily analyzable way. They are important for many different areas of machine learning and pattern processing.

brilliant.org

 

이번 프로젝트에선 float타입의 rigidbody velocity와,  Vector3 타입의 localPosition , float 타입의 Distance를 Brain으로 전달합니다. Vector3 타입의 변수는 3개의 변수를 전달하는 것과 같으므로 (x , y, z 좌표 3개) 3 + 3 + 1

7개의 데이터 타입 즉 7차원 Feature Vector를 인공신경망에 전달합니다.  

 

4. Agent 클래스의 OnActionReceived를 통해 Action 수행 및 Reward 정의

public override void OnActionReceived(ActionBuffers actionBuffers)
{

    // Agent가 Target쪽으로 이동하기 위해 X, Z축으로의 Force를 정의
    Vector3 controlSignal = Vector3.zero;
    controlSignal.x = actionBuffers.ContinuousActions[0];
    controlSignal.y = actionBuffers.ContinuousActions[1] * 0.1f;
    controlSignal.z = actionBuffers.ContinuousActions[2];
    rBody.AddForce(controlSignal * forceMultiplier);

    // Agent와 Target사이의 거리를 측정
    float distanceToTarget = Vector3.Distance(this.transform.position, Target.position);
    
    // Target에 도달하는 경우 Episode 종료
    if (distanceToTarget <= 1.42f)
    {
        SetReward(1.0f);                
        EndEpisode();
    }

    // 플랫폼 밖으로 나가면 Episode 종료

     if (distanceToTarget >= 50.0f || this.transform.position.y <= - 0.1f || this.transform.position.y >= 15.0f)
    {
        isEndByBreak = !isEndByBreak;
        SetReward(-0.01f);
        EndEpisode();
    }
}

ActionBuffer는 ml-Agent 프레임워크에서 에이전트가 취할 수 있는 행동(action)을 저장하고 처리하는 데 사용되는 구조입니다. 이는 에이전트의 행동 정보를 담고 있으며 이를 통해 환경과 상호작용하며 학습합니다.

 

 //In Agent.cs
 
 /// When an agent uses continuous actions, the values in the ActionBuffers.ContinuousActions
 /// array are floating point numbers. You should clamp the values to the range,
 /// -1..1, to increase numerical stability during training.
 
 /// Struct containing the buffers of actions to be executed at this step.
 public virtual void OnActionReceived(ActionBuffers actions) { }
public ActionBuffers(float[] continuousActions, int[] discreteActions)

ActionBuffers는 구조체이며 오브젝트가 취할 수 있는 행동을 무작위로 buffer 형태로 저장합니다.

Action Buffer를 능동적으로 정의하여 다양한 Action을 수행하게 만들 수 있습니다.

 

가장 기본적인 형태의 ActionBuffers 사용 방법은 OnActionReceived()를 기본 형태로 선언하여

-1~1의 랜덤값을 호출하는 ActionBuffers를 활용하는 방법입니다.

즉  controlSignal.x = actionBuffers.ContinuousActions[0] 구문은

벡터의 X 좌표를 -1~1의 랜덤값으로 설정한다는 의미입니다.

이를 통해 ActionBuffer.ContinuousActions가 호출될 때 마다 무작위 3차원 벡터를 생성할 수 있습니다.

 

좀 더 쉽게 예를 들면 OnActionReceived() 함수는

Unity의 FixedUpdate와 비슷한 역할을 합니다.

물론 둘의 구조는 굉장한 차이가 있지만,

OnActionReceived 함수는 매초 약 60번 호출되고,

이 함수는 딥러닝 모델을 제작하거나 교육이 완료된 모델을 동작시킬 때

Agent를 움직이는 역할을 한다는 점에서 비슷하다고 생각합니다. (실제로 비슷하게 사용하고 있습니다.)

 

https://docs.unity3d.com/Packages/com.unity.ml-agents@2.0/api/Unity.MLAgents.Actuators.ActionBuffers.html

 

Struct ActionBuffers | ML Agents | 2.0.1

Struct ActionBuffers Assembly : solution.dll Syntax public struct ActionBuffers Constructors ActionBuffers(Single[], Int32[]) Construct an ActionBuffers instance with the continuous and discrete actions that will be used. /// Declaration public ActionBuffe

docs.unity3d.com

 

윗글을 요약하면

1. OnEpisodeBegin() Episode의 초기값을 설정

 

2. CollectObservations(VectorSensor sensor)모델 생성에 있어서 가장 중요한 환경 관찰 정보를 Brain으로 전달

 

3. OnActionReceived(ActionBuffers actionBuffers)딥러닝 모델의 Policy를 기반으로 Action 수행 및 Reward 획득

 

입니다.

 

아래는 전체 코드입니다.

 

namespace MeetAgain {
    public class ButterflyAgentChase : Agent
    {
        Rigidbody rBody;
        Vector3 spawnPoint;
        private bool isEndByBreak;
        public Transform Target;
        void Start()
        {
            rBody = GetComponent<Rigidbody>();
            spawnPoint = Vector3.zero;
            isEndByBreak = false;
        }
        public override void OnEpisodeBegin()
        {
            
            //Agent가 너무 멀리 떨어지면 angularVelocity/velocity=0으로, 위치를 초기 좌표로 리셋
            if (isEndByBreak)
            {
                this.rBody.angularVelocity = Vector3.zero;
                this.rBody.velocity = Vector3.zero;
                
                isEndByBreak = !isEndByBreak;
            }
            // Target을 Random.value함수를 활용해서 새로운 무작위 위치에 이동
            this.transform.localPosition = new Vector3(Target.localPosition.x + Random.value * 30 - 15, Random.value * 3 + 0.3f, Target.localPosition.z + Random.value * 30 - 15);
            Target.localPosition = new Vector3(Random.value * 5, Random.value * 1 + 2.5f, Random.value * 8 - 3);
            this.spawnPoint = transform.localPosition;
        }

        public override void CollectObservations(VectorSensor sensor)       // ml 에이전트의 환경 관찰 및 정보를 주는 것 (쉽게 말해 힘 조절을 해주게 하는 함수이다.)
        {
            // Target/Agent의 위치 정보 수집
            sensor.AddObservation(Vector3.Distance(this.transform.position, Target.position));
            sensor.AddObservation(transform.localPosition);

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

        public float forceMultiplier = 1;
        public override void OnActionReceived(ActionBuffers actionBuffers)
        {

            // Agent가 Target쪽으로 이동하기 위해 X, Z축으로의 Force를 정의
            Vector3 controlSignal = Vector3.zero;
            controlSignal.x = actionBuffers.ContinuousActions[0];
            controlSignal.y = actionBuffers.ContinuousActions[1] * 0.1f;
            controlSignal.z = actionBuffers.ContinuousActions[2];
            rBody.AddForce(controlSignal * forceMultiplier);

            // Agent와 Target사이의 거리를 측정
            float distanceToTarget = Vector3.Distance(this.transform.position, Target.position);
            
           
            if (distanceToTarget <= 1.42f)
            {
                SetReward(1.0f);                
                EndEpisode();
            }

            // 플랫폼 밖으로 나가면 Episode 종료

             if (distanceToTarget >= 50.0f || this.transform.position.y <= - 0.1f || this.transform.position.y >= 15.0f)
            {
                isEndByBreak = !isEndByBreak;
                SetReward(-0.01f);
                EndEpisode();
            }



        }

        
    }
}

 

다음 포스팅에선 Git Bash를 활용하여 딥러닝 모델을 생성하겠습니다.