본문 바로가기
유니티/mlAgents

유니티 머신러닝 개발 ML Agents 13편, 목표 찾기 예제 개선. 7 여러 타깃 사용해보기, 관리자 만들기

by NGVI 2021. 4. 14.

유니티 머신러닝 개발 ML Agents 13편, 목표 찾기 예제 개선. 7 여러 타깃 사용해보기, 관리자 만들기

직전 수정은 사실 프로그램의 결과값, 즉 눈으로 보는 프로그램의 변화는 거의 없습니다.

로직 변화가 있지만요, 엄밀히 말하면 정말 같은 것은 아니지만, 그냥 보시기에는 거의 차이가 없는 수정이죠

 

출동 검증 방식이 바뀐 것이었으니까요.

 

이번에는 타겟을 추가할 수 있도록 수정하려 합니다.

타깃을 관리하는 관리 소스를 만들 예정입니다.

다음 이름의 cs 파일을 추가합니다.

gTargetManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class gTargetManager : MonoBehaviour
{
    public GameObject targetPrefab = null;
    Transform transformP = null;

    public int targetCount = 1;
    public int goalCount = 1;

    List<gTarget> targetList = new List<gTarget>();

    public void OnEpisodeBegin()
    {
        transformP = this.transform;
        if (targetCount != targetList.Count)
        {
            for (int i = 0; i < targetCount; i++)
            {
                targetList.Add(GameObject.Instantiate(targetPrefab, transformP).GetComponent<gTarget>());
            }
        }

        foreach (var target in targetList)
        {
            float rx = 0;
            float rz = 0;

            rx = Random.value * 16 - 8;
            rz = Random.value * 16 - 8;

            target.transform.localPosition = new Vector3(rx,
                                               0.5f,
                                               rz);

            target.gameObject.SetActive(true);
        }
    }
}

프리 팹을 등록해서 프로그램 실행 시 오브젝트를 생성할 수 있도록 구성합니다.

 

생성 시 자식 오브젝트로 귀속할 예정입니다.

 

public int targetCount = 1;
생성 수량을 받아서 그만큼 생성시킬 예정입니다.

 

public int goalCount = 1;

에이전트가 몇 개를 먹으면 에피소드를 종료할 것인지에 대한 수치입니다.

 

그리고 에피소드 시작시 타깃들을 위치가 새로 지정되게 소스가 구성되어 있습니다.

 

추가로 TrainingArea 자체를 관리하는 관리자도 만들도록 하겠습니다.

gTrainingArea.cs 파일을 생성합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class gTrainingAreaManager : MonoBehaviour
{
    public gTargetManager targetManager = null;

    // Start is called before the first frame update
    void Start()
    {
        EpisodeStart();
    }

    public void EpisodeStart()
    {
        targetManager.OnEpisodeBegin();
    }

    public void needEpisodeEnd() 
    {
        EpisodeStart(); //종료이후 에피소드 재시작
    }
}

요 녀석의 하나의 단위의 최고 관리자라고 보시면 됩니다.

Target 관리자는 Target만 관리합니다. TrainingArea관리자가 Target관리자를 관리하고 일단은 게임이 최상위에서 일을 진행하도록 할 것입니다.

 

프로젝트가 시작되면 에피소드를 시작시킵니다.

 

에피소드가 끝나면, 에피소드를 다시 시작하는 역할을 합니다.

 

추가로 TrainingAreaManager를 경유해서 다양한 관리자를 찾을 수 있게도 할 계획입니다.

 

유니티 에디터로 돌아옵니다.

참조 자료

TrainingArea에 작성한 컴퍼넌트를 붙입니다.

gTrainingAreaManager의 targetManager 다시 자신을 물려서 활성화시켜줍니다.

 

TrainingArea에 자식으로 전재하는 Target 프리팹화 시킵니다.

gTargetManager의 Prefab에 넣어줍니다.

 

gRollerAgent.cs에서

타깃 관련 소스를 지웁니다.

trainingAreaManager 추가하여 알려줍니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
//mlAgent 사용시 포함해야 됨

public class gRollerAgent : Agent
{
    Rigidbody rBody;
    void Start()
    {
        rBody = GetComponent<Rigidbody>();
    }

    public gTrainingAreaManager trainingAreaManager = null;

    public GameObject viewModel = null;

    int Pointlast = 0;
    int Point = 0;

    public override void OnEpisodeBegin()
    {
        //새로운 애피소드 시작시, 다시 에이전트의 포지션의 초기화
        // If the Agent fell, zero its momentum
        if (this.transform.localPosition.y < 0) //에이전트가 floor 아래로 떨어진 경우 추가 초기화
        {
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3(0, 0.5f, 0);
        }

        Pointlast = 0;
        Point = 0;
    }

    /// <summary>
    /// 강화학습을 위한, 강화학습을 통한 행동이 결정되는 곳
    /// </summary>
    public float forceMultiplier = 10;
    float m_ForwardSpeed = 1.0f;

    /// <summary>
    /// 타겟안에 에이전트가 들어오면 타겟이 호출하는 함수
    /// </summary>
    public void EnteredTarget()
    {
        Point++;
    }

    public override void OnActionReceived(ActionBuffers actionBuffers)
    {
        if (Pointlast < Point)
        {
            SetReward(1.0f);
            
        }

        if (trainingAreaManager.targetManager.goalCount <= Point)
        {
            trainingAreaManager.needEpisodeEnd();
            EndEpisode();
        }

        MoveAgent(actionBuffers.DiscreteActions);
    }
    public void MoveAgent(ActionSegment<int> act)
    {
        var dirToGo = Vector3.zero;
        var rotateDir = Vector3.zero;

        var forwardAxis = act[0];
        var rotateAxis = act[1];

        switch (forwardAxis)
        {
            case 1:
                dirToGo = transform.forward * m_ForwardSpeed;
                break;
        }

        switch (rotateAxis)
        {
            case 1:
                rotateDir = transform.up * -1f;
                break;
            case 2:
                rotateDir = transform.up * 1f;
                break;
        }

        transform.Rotate(rotateDir, Time.deltaTime * 100f);
        rBody.AddForce(dirToGo * forceMultiplier, ForceMode.VelocityChange);
    }

    /// <summary>
    /// 해당 함수는 직접조작 혹은 규칙성있는 코딩으로 조작시키기 위한 함수
    /// </summary>
    /// <param name="actionsOut"></param>

    public override void Heuristic(in ActionBuffers actionsOut)
    {
        var discreteActionsOut = actionsOut.DiscreteActions;
        discreteActionsOut.Clear();
        //forward
        if (Input.GetKey(KeyCode.W))
        {
            discreteActionsOut[0] = 1;
        }

        //rotate
        if (Input.GetKey(KeyCode.A))
        {
            discreteActionsOut[1] = 1;
        }
        if (Input.GetKey(KeyCode.D))
        {
            discreteActionsOut[1] = 2;
        }
    }
}

위는 전체의 소스입니다.

 

public override void OnActionReceived(ActionBuffers actionBuffers)만 따로 보도록 합니다.

public override void OnActionReceived(ActionBuffers actionBuffers)
    {
        if (Pointlast < Point)
        {
            SetReward(1.0f);
        }

        if (trainingAreaManager.targetManager.goalCount <= Point)
        {
            trainingAreaManager.needEpisodeEnd();
            EndEpisode();
        }

        MoveAgent(actionBuffers.DiscreteActions);
    }

포인트가 증가되면 보상을 줍니다.

그리고 다시 비교합니다. 현재 내가 누적한 타깃수가 골 타깃 수 인지.

 

grollerAgent의 maxSetp를 일단 0으로 돌려줍니다.

특정 행위를 하는 동안 보상 변경이 없으면 해당 에이전트의 에피소드가 종료되는데, 일단 0으로 설정하여 꺼둡니다.

에피소드 단위를 trainingArea로 할 계획이라, 여러 고민이 되니까 일단은 기능은 꺼둡니다.

아쉽게도 이러면 바보같이 벽면에 좀 많이 비비는 느낌이 나오긴 합니다.

 

태스티 및 교육시켜보기

그럼 이제

Heuristic Only 모드 등으로 테스트해봅니다. 

 

TrainingArea 컴 터넌트의 값들을 변경하며 테스트해봅니다.

 

 

Target Count는 훈령장에 타깃이 몇 개 등장할 것인지며,

Goal Count는 몇 개를 먹어야 에피소드가 종료되는지입니다.

 

당연히 골이 적어야겠죠.

 

저는 4,2로 해두고 다시 재교육을 시켜보겠습니다.

 

쌘서때문에 서로 멀리들 두고 교육

아래는 교육 데이터로 실행해봄

30만 스텝을 수련했던 친구

30만 스텝을 수련했던 친구

흠.. 더 수련이 필요한 듯 보인다.

 

이번에는 타깃을 여러 개를 설치하는 행위를 해보았습니다.

그를 관리하기 위해 상위 관리자들도 만들었고요

 

봐주셔서 감사합니다.

댓글