본문 바로가기

유니티

정말 힘겹게 실습한 유니티 ML-Agent 1.0 - 텐서플로우 연동

빨강색이 추적자(CCTV)이고 노란색이 추적대상(사람)

 

월요일에 유니티 코리아에서 진행하는 Dev Weeks의 ML-Agent 주제를 듣고나서 조금 관심이 생겨 한번 다뤄보고 싶었다. 

 

그래서 간단한 예제를 만들어보기 위해 열심히 구글링을 하였지만

인터넷에 돌아다니는 ML-Agent 강의나 자료들은 1.0과는 조금 차이가 있었다.

특히 유니티 ML-Agent가 단기간에 발전하면서 버전이 업그레이드 될 때마다 대격변을 거쳤다고 들었다.

 

그래서 유니티 내부에서 돌아가는 로직을 작성하는데에도 애를 좀 먹었다.

여러개를 찾아 창을 띄어놓고 교차검증을 해가면서 1.0에 맞는 코드를 완성하긴 하였다. (맞는것인지는 모름)

 

 

추적임무를 수행할 CCTV 클래스 (난잡해서 죄송.. 빠르게 하고 싶기도 했고 이것저것 가져다 쓰느라..)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
 
public class CCTV : Agent
{
    Vector3 StartPos;
    Vector3 EndPos = new Vector3(0f, 12f, 4f);
 
    new Rigidbody rigidbody;
 
 
    public GameObject people;
    public GameObject plane;
 
    int score = 0;
 
    float moveSpeed = 2f;
 
 
    private void Awake()
    {
        rigidbody = GetComponent<Rigidbody>();
    }
 
    public override void Initialize()
    {
        rigidbody = GetComponent<Rigidbody>();
        StartPos = transform.position;
    }
 
    public override void CollectObservations(VectorSensor sensor)
    {
        Vector3 disToPeople = people.transform.position - transform.position;
 
        sensor.AddObservation(Mathf.Clamp(disToPeople.x / 5f, -1f, 1f));
        sensor.AddObservation(Mathf.Clamp(disToPeople.z / 5f, -1f, 1f));
 
        Vector3 relPos = plane.transform.position - transform.position;
 
        sensor.AddObservation(Mathf.Clamp(relPos.x / 5f, -1f, 1f));
        sensor.AddObservation(Mathf.Clamp(relPos.z / 5f, -1f, 1f));
 
        sensor.AddObservation(Mathf.Clamp(rigidbody.velocity.x / 10f, -1f, 1f));
        sensor.AddObservation(Mathf.Clamp(rigidbody.velocity.z / 10f, -1f, 1f));
    }
 
    private void FixedUpdate()
    {
        RequestDecision();
    }
 
    public override void OnActionReceived(float[] vectorAction)
    {
        AddReward(-0.001f);
 
        float horizontal = vectorAction[0];
        float vertical = vectorAction[1];
 
        rigidbody.AddForce(horizontal * moveSpeed, 0f, vertical * moveSpeed);
    }
 
    public override void OnEpisodeBegin()
    {
        Reset();
    }
 
    public override void Heuristic(float[] actionsOut)
    {
        actionsOut[0= 0;
    }
 
    void ResetPeople()
    {
        Vector3 randPos = new Vector3(Random.Range(-5f, 5f), 1f, Random.Range(-5f, 5f));
 
        people.transform.position = randPos + plane.transform.position;
    }
 
    void ResetAgent()
    {
        Vector3 randPos = new Vector3(Random.Range(-5f, 5f), 0.5f, Random.Range(-5f, 5f));
 
        transform.position = randPos + plane.transform.position;
 
        rigidbody.velocity = Vector3.zero;
 
        ResetPeople();
    }
 
    private void Reset()
    {
        ResetAgent();
    }
 
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            score--;
            Debug.Log("감소 : " + score);
            AddReward(-1.0f);
            EndEpisode();
        }
 
    }
    private void OnTriggerEnter(Collider other)
    {
        if(other.gameObject.CompareTag("People"))
        {
            score += 1;
            Debug.Log("증가 : " + score);
            ResetPeople();
            AddReward(1.0f);
        }
    }
cs

대충 유니티에서 기계학습이 돌아가는 루틴만 감으로 파악하고 구자료들을 교차검증하면서 감으로 써내려갔다.

솔직히.. 위의 메소드중 아직 이게 왜 여기서 호출하는지 파악하지 못한 것도 있긴 하다.

 

일단

훈련 시작 Initialize()

미리 설정된 탐색 조건(?) CollectObservations() : 물체를 탐색하는데 아무런 힌트를 주지않으면 학습의 효율이 떨어질 것이다.

에피소드 시작 OnEpisodeBegin() : 여기서 게임오브젝트들의 위치를 리셋

탐색자가 행동을 취함 OnActionReceived()

Hueristic() : 정확히 모르겠다.. 아마 사용자가 인공지능에 개입하는 이미테이션 방식에 쓰이는 것 같다.

 

OnCollisionEnter에서 탐색자가 탐색범위를 벗어나면 보상을 빼앗고 에피소드 종료

마찬가지로 대신 사람을 포착하였다면 사람의 위치를 랜덤하게 리셋시키고 보상을 지급한다.

 

그래서 이걸 수만번을 반복하면 점점 똑똑해진다.

그 모습을 바라보는 나는 부모가 된 기분 ㅋㅋ

 

-----------------------------------------------------------

 

아무튼 유니티만 작동한다고 되는 것이 아니다.

텐서플로우와 유니티가 통신을 해야하는데 일단 텐서플로우 설치 자체가 굉장히 까다로웠다.

처음 설치를 한다면 이런 느낌 '뭐야.. 뭐가 계속 업서.."

 

수 많은 시행착오를 거쳐 결국 연동에 성공했다.

 

유니티에디터의 플레이 버튼을 눌러서 훈련을 시작하라는 메시지가 보이면 성공!

 

좀 더 빠른 결과를 얻기위해 훈련장도 10개로 늘려주고 플레이버튼을 누르면

 

아나콘다 프롬프트 상에서 아래와 같은 훈련 진행도가 계속 표시됨을 확인할 수 있었다.

 

 

그리고 텐서보드를 통해 시각화 정보를 얻을 수도 있었다.

 

구자료들은 유니티 프로젝트의 summary 폴더안에 시각화 정보를 위한 로그가 있다고 하였지만

1.0 버전에서는 results 안의 훈련 id폴더안 각자 지어준 behaviour의 이름 폴더안에 

이렇게 적힌 파일을 텐서플로우 명령을 통해 실행하면 되었다.

 

----------------------------------------------------

 

훈련이 끝나면 nn 파일을 얻을 수 있는데

그 파일을 유니티 프로젝트에 끌어다놓고 Behaviour Parameter안의 Model 칸을 찾아 바인딩을 해놓으면

미리 훈련된 모델을 사용하여 실행할 수 있게 된다.

 

30만 스텝 훈련 모델
60만 스텝 훈련 모델

별 차이는 없는 것 같지만.. 그래도 60만 모델이 좀 더 정확성을 보이긴 했다.

적절한 훈련값 등을 설정해줘야 학습의 효율이 올라간다고 하지만

 

아직까지는 이제 한번 다뤄본 나에게는 너무나도 어려운 일이여서 시행하진 못했다.

그냥 이 값을 바꾸면 움직임이 어떻게 바뀔까? 하는 정도에서 몇 번 조정해본게 전부이다.

 

그래도 어찌해서 연동도 잘 되었고 데이터도 잘나왔으니 경험해본다는 입장에서는 나름 만족

좀 더 제대로 다뤄보는 것은 유니티 2020버전에 정식으로 나온다고 하니 그때 이후로 바라볼 것 같다.

 

혹시 프로젝트가 필요하신분들을 위해서 Github에 업로드하였습니다.

https://github.com/LHEALP/MLA_exercise

 

LHEALP/MLA_exercise

Contribute to LHEALP/MLA_exercise development by creating an account on GitHub.

github.com

 

 

 

(이거 한번 해보겠다고 밤샜다..)