Learn to leverage Artificial Intelligence to enhance your Unity projects
Unity3D/ml-agent 2020. 7. 7. 10:16
Unity Learn 플랫폼에 새로 추가 된 기능은 강화 학습 및 AI를 사용하여 게임 개발 문제를 해결하고 더 똑똑한 게임을 만드는 방법을 알려줍니다.
머신 러닝 에이전트의 힘 활용
우리는 지속적으로 흥미로운 컨텐츠를 Unity Learn 플랫폼에 추가하고 있으며, 가장 최근에 추가 된 것은 무료 ML- 에이전트 : 펭귄 프로젝트로, 고품질 Unity 학습 컨텐츠를 만드는 팀인 Immersive Limit와 공동으로 제작되었습니다. 우리는 머신 러닝 경험이 거의 또는 전혀없고 Unity에서 AI 의사 결정을 시작하려는 열망이있는 중간 Unity 개발자를 위해이 프로젝트를 설계했습니다.
이 프로젝트에서는 펭귄 (에이전트)을 훈련시켜 수영 물고기를 잡아서 아기가 먹이를 먹을 수있는 거리 내에있게합니다. 이것은 에이전트로부터의 훈련 및 입력에 기초하여 "추론"(결정)을 할 수있는 신경망을 생성한다. 다음 방법을 배우게됩니다.
Anaconda 및 Unity ML-Agent 환경 설정
Unity ML- 에이전트를 사용하는 프로젝트 만들기
기계 학습을 사용하여 에이전트 교육
최초의 오픈 소스 머신 러닝 제품인 Unity ML- 에이전트는 강화 학습을 Unity 프로젝트에 제공합니다.
강화 학습 (Reinforcement Learning)은 게임 개발자가 NPC (Playable and Non-Playable Character) 모두에 대해 복잡하고 흥미로운 행동을 만들도록 도와주는 흥미로운 인공 지능 분야입니다.
최근 Go 및 StarCraft와 같은 게임에서 최고의 플레이어를 이길 수있게 됐다는 소식이 전해졌습니다.
Unity ML- 에이전트는 간단한 Python API를 통해 강화 학습 및 진화 방법으로 에이전트를 교육합니다.
에이전트를 사용하여 게임 난이도를 동적으로 조정하는 등의 문제를 해결하는 게임 개발자 로봇 공학, 자율 주행 차량 및 기타 산업 응용 분야를위한 대규모 병렬 교육 체제를 구현하는 산업 및 엔터프라이즈 연구원 시각적 내용과 현실적인 물리학에서 복잡한 행동을 연구하는 학술 연구원
Start Learning 버튼을 누르고 다음 프로젝트를 확인 한다
총 5단계로 구성되어 있는 튜토리얼
1. ML-Agents And Anaconda 설정 하기
이 학습서에서는 Windows 용 Unity 기계 학습 에이전트 툴킷 (ML-Agent) 및 Anaconda 2019.10을 설정하는 방법을 학습합니다. ML-Agents는 사전 학습에 대한 사전 지식없이 게임에서 의사 결정에 신경망 및 강화 학습을 사용할 수있게하는 코드 저장소입니다. Anaconda는 Python 설치를 관리하고 별도의 환경을 생성하여 Python을 설치하지 않고도 ML 에이전트를 쉽게 훈련시킬 수있는 도구입니다. 본질적으로 다른 환경에 특정 Python 라이브러리가 설치된 특수 명령 줄입니다. 여러 번 파이썬 스크립트가 특정 버전의 파이썬 라이브러리에 의존하기 때문에 유용합니다. 모든 시스템을 수용하도록 시스템을 설정하는 것은 매우 어렵습니다. 이 자습서를 마치면 ML-Agent를 다운로드하고 Anaconda가 ML-Agent를 교육 할 준비가 된 Python 환경으로 설정하게됩니다.
https://github.com/Unity-Technologies/ml-agents/releases
Source code (zip) 파일 다운로드 받고 압축풀기
Anaconda 다운로드
Python 3.7 버전을 다운 받는다
https://www.anaconda.com/products/individual
설치 프로그램을 실행하십시오. 설치 단계는 플랫폼에 따라 다르므로 필요한 경우 자세한 단계는 Anaconda 설명서 설치 페이지로 이동하는 것이 좋습니다.
설치가 완료되면 설치 확인 : Anaconda 지시 사항을 따르십시오. 이 지침은 운영 체제에서 Anaconda 프롬프트를 시작하는 방법을 알려줍니다. 이 프롬프트는 이후 자습서에서 교육을 실행하는 데 사용할 것입니다.
https://docs.anaconda.com/anaconda/install/verify-install/#conda
Anaconda 프롬프트를 시작하십시오 (이전 단계에서 아직 열지 않은 경우).
Anaconda 프롬프트를 사용하면 ML-Agent 교육 스크립트를위한 Python 환경을 만들 수 있습니다.
Anaconda 프롬프트에 다음 명령을 입력하고 키보드에서 Enter를 누르십시오.
이 명령은 ml-agents라는 새로운 Python 3.7 환경을 만듭니다.
원하는 경우 이름을 바꿀 수 있습니다.
예를 들어 이전 버전의 ml-agents 환경이 이미있을 수 있으므로이 환경 ml-agents-release_3을 호출 할 수 있습니다.
프로그램은 설치 될 패키지 목록을 보여줍니다. ‘y’를 입력하고 Enter를 눌러 계속 진행하십시오
설정이 완료된 후 명령을 입력하여 환경을 활성화하십시오.
ml-agents 소스 코드를 압축 해제 한 디렉토리로 이동하십시오.
ML-Agent를 설치하려면 Anaconda 프롬프트에서 다음 명령을 실행하십시오.
이 명령은 상위디렉토리로 이동합니다.
이 명령은 ml-agents 하위 디렉토리로 이동합니다.
이 명령은 ml-agents 디렉토리의 내용을 설치합니다.
이 명령은 상위 디렉토리로 이동합니다.
ML-Agent 교육 코드가 Anaconda Python 환경에 완전히 설치되었습니다. 나중에 Training and Inference 자습서에서 사용합니다. 어떤 이유로 Anaconda 프롬프트를 닫으면 위에서 사용한 것과 동일한 명령으로 다시 활성화 할 수 있습니다.
이 명령으로 환경 목록을 볼 수도 있습니다.
Anaconda 환경 관리에 대한 추가 지시 사항은 환경 관리 Anaconda 문서를 방문하십시오.
https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html
2. Unity 프로젝트 설정과 어셋 임포트 하기
유니티 허브를 엽니다
새 프로젝트를 만듭니다
2019.3버전 또는 높은 버전을 사용하세요
3D template 를 선택 하고 프로젝트 명은 Penguins 라고 합니다.
생성 버튼을 누릅니다
Window > Package Manager를 엽니다
advanced를 클릭 하고 Show preview packages를 선택 합니다
Barracuda 최신 패키지를 찾아 선택 하고 설치 합니다.
튜토리얼 어셋을 다운로드 합니다
Penguins 폴더를 만들고 어셋을 넣습니다
모든 매쉬들을 씬에 추가 합니다
이름을 변경 합니다
baby_penguin > BabyPenguin
fish > Fish
heart > Heart
penguin > Penguin
penguin_area > PenguinArea
regurgitated_fish > RegurgitatedFish
그리고 Penguin이라는 이름으로 폴더를 만듭니다
이름이 바뀐 모든 오브젝트를 프리팹 폴더에 넣습니다
Original Prefab을 선택 합니다.
PenguinArea을 제외한 모든 오브젝트를 씬에서 지워줍니다.
3. 코드 작성하기
4. SceneConstruction
5. Training and Interface
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
public class PenguinAgent : Agent
{
[Tooltip("How fast the agent moves forward")]
public float moveSpeed = 5f;
[Tooltip("How fast the agent turns")]
public float turnSpeed = 180f;
[Tooltip("Prefab of the heart that appears when the baby is fed")]
public GameObject heartPrefab;
[Tooltip("Prefab of the regurgitated fish that appears when the baby is fed")]
public GameObject regurgitatedFishPrefab;
private PenguinArea penguinArea;
new private Rigidbody rigidbody;
private GameObject baby;
private bool isFull; // If true, penguin has a full stomach
private float feedRadius = 0f;
EnvironmentParameters m_ResetParams;
// Start is called before the first frame update
void Start()
{
}
public override void Initialize()
{
base.Initialize();
penguinArea = GetComponentInParent<PenguinArea>();
baby = penguinArea.penguinBaby;
rigidbody = GetComponent<Rigidbody>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
AgentReset();
}
public override void OnEpisodeBegin()
{
base.OnEpisodeBegin();
}
public override void OnActionReceived(float[] vectorAction)
{
// Convert the first action to forward movement
float forwardAmount = vectorAction[0];
// Convert the second action to turning left or right
float turnAmount = 0f;
if (vectorAction[1] == 1f)
{
turnAmount = -1f;
}
else if (vectorAction[1] == 2f)
{
turnAmount = 1f;
}
// Apply movement
rigidbody.MovePosition(transform.position + transform.forward * forwardAmount * moveSpeed * Time.fixedDeltaTime);
transform.Rotate(transform.up * turnAmount * turnSpeed * Time.fixedDeltaTime);
// Apply a tiny negative reward every step to encourage action
AddReward(-1f / 1000); //max step
}
public override void CollectObservations(VectorSensor sensor)
{
// Whether the penguin has eaten a fish (1 float = 1 value)
sensor.AddObservation(isFull);
// Distance to the baby (1 float = 1 value)
sensor.AddObservation(Vector3.Distance(baby.transform.position, transform.position));
// Direction to baby (1 Vector3 = 3 values)
sensor.AddObservation((baby.transform.position - transform.position).normalized);
// Direction penguin is facing (1 Vector3 = 3 values)
sensor.AddObservation(transform.forward);
// 1 + 1 + 3 + 3 = 8 total values
}
private void FixedUpdate()
{
// Test if the agent is close enough to to feed the baby
if (Vector3.Distance(transform.position, baby.transform.position) < feedRadius)
{
// Close enough, try to feed the baby
RegurgitateFish();
}
}
public void AgentReset()
{
isFull = false;
penguinArea.ResetArea();
//feedRadius = penguinAcademy.FeedRadius;
}
/// <summary>
/// When the agent collides with something, take action
/// </summary>
/// <param name="collision">The collision info</param>
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.CompareTag("fish"))
{
// Try to eat the fish
EatFish(collision.gameObject);
}
else if (collision.transform.CompareTag("baby"))
{
// Try to feed the baby
RegurgitateFish();
}
}
private void EatFish(GameObject fishObject)
{
if (isFull) return; // Can't eat another fish while full
isFull = true;
penguinArea.RemoveSpecificFish(fishObject);
AddReward(1f);
}
/// <summary>
/// Check if agent is full, if yes, feed the baby
/// </summary>
private void RegurgitateFish()
{
if (!isFull) return; // Nothing to regurgitate
isFull = false;
// Spawn regurgitated fish
GameObject regurgitatedFish = Instantiate<GameObject>(regurgitatedFishPrefab);
regurgitatedFish.transform.parent = transform.parent;
regurgitatedFish.transform.position = baby.transform.position;
Destroy(regurgitatedFish, 4f);
// Spawn heart
GameObject heart = Instantiate<GameObject>(heartPrefab);
heart.transform.parent = transform.parent;
heart.transform.position = baby.transform.position + Vector3.up;
Destroy(heart, 4f);
AddReward(1f);
if (penguinArea.FishRemaining <= 0)
{
AgentReset();
this.EndEpisode();
}
}
}
|
cs |
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class PenguinArea : MonoBehaviour
{
public float fishSpeed;
[Tooltip("The agent inside the area")]
public PenguinAgent penguinAgent;
[Tooltip("The baby penguin inside the area")]
public GameObject penguinBaby;
[Tooltip("The TextMeshPro text that shows the cumulative reward of the agent")]
public TextMeshPro cumulativeRewardText;
[Tooltip("Prefab of a live fish")]
public Fish fishPrefab;
private List<GameObject> fishList;
private void Update()
{
// Update the cumulative reward text
cumulativeRewardText.text = penguinAgent.GetCumulativeReward().ToString("0.00");
}
public void ResetArea()
{
RemoveAllFish();
PlacePenguin();
PlaceBaby();
SpawnFish(4, fishSpeed);
}
public void RemoveSpecificFish(GameObject fishObject)
{
fishList.Remove(fishObject);
Destroy(fishObject);
}
public int FishRemaining
{
get { return fishList.Count; }
}
public static Vector3 ChooseRandomPosition(Vector3 center, float minAngle, float maxAngle, float minRadius, float maxRadius)
{
float radius = minRadius;
float angle = minAngle;
if (maxRadius > minRadius)
{
// Pick a random radius
radius = UnityEngine.Random.Range(minRadius, maxRadius);
}
if (maxAngle > minAngle)
{
// Pick a random angle
angle = UnityEngine.Random.Range(minAngle, maxAngle);
}
// Center position + forward vector rotated around the Y axis by "angle" degrees, multiplies by "radius"
return center + Quaternion.Euler(0f, angle, 0f) * Vector3.forward * radius;
}
private void RemoveAllFish()
{
if (fishList != null)
{
for (int i = 0; i < fishList.Count; i++)
{
if (fishList[i] != null)
{
Destroy(fishList[i]);
}
}
}
fishList = new List<GameObject>();
}
/// <summary>
/// Place the penguin in the area
/// </summary>
private void PlacePenguin()
{
Rigidbody rigidbody = penguinAgent.GetComponent<Rigidbody>();
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
penguinAgent.transform.position = ChooseRandomPosition(transform.position, 0f, 360f, 0f, 9f) + Vector3.up * .5f;
penguinAgent.transform.rotation = Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f);
}
/// <summary>
/// Place the baby in the area
/// </summary>
private void PlaceBaby()
{
Rigidbody rigidbody = penguinBaby.GetComponent<Rigidbody>();
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
penguinBaby.transform.position = ChooseRandomPosition(transform.position, -45f, 45f, 4f, 9f) + Vector3.up * .5f;
penguinBaby.transform.rotation = Quaternion.Euler(0f, 180f, 0f);
}
/// <summary>
/// Spawn some number of fish in the area and set their swim speed
/// </summary>
/// <param name="count">The number to spawn</param>
/// <param name="fishSpeed">The swim speed</param>
private void SpawnFish(int count, float fishSpeed)
{
for (int i = 0; i < count; i++)
{
// Spawn and place the fish
GameObject fishObject = Instantiate<GameObject>(fishPrefab.gameObject);
fishObject.transform.position = ChooseRandomPosition(transform.position, 100f, 260f, 2f, 13f) + Vector3.up * .5f;
fishObject.transform.rotation = Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f);
// Set the fish's parent to this area's transform
fishObject.transform.SetParent(transform);
// Keep track of the fish
fishList.Add(fishObject);
// Set the fish speed
fishObject.GetComponent<Fish>().fishSpeed = fishSpeed;
}
}
}
|
cs |
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
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fish : MonoBehaviour
{
[Tooltip("The swim speed")]
public float fishSpeed;
private float randomizedSpeed = 0f;
private float nextActionTime = -1f;
private Vector3 targetPosition;
private void FixedUpdate()
{
if (fishSpeed > 0f)
{
Swim();
}
}
/// <summary>
/// Swim between random positions
/// </summary>
private void Swim()
{
// If it's time for the next action, pick a new speed and destination
// Else, swim toward the destination
if (Time.fixedTime >= nextActionTime)
{
// Randomize the speed
randomizedSpeed = fishSpeed * UnityEngine.Random.Range(.5f, 1.5f);
// Pick a random target
targetPosition = PenguinArea.ChooseRandomPosition(transform.parent.position, 100f, 260f, 2f, 13f);
// Rotate toward the target
transform.rotation = Quaternion.LookRotation(targetPosition - transform.position, Vector3.up);
// Calculate the time to get there
float timeToGetThere = Vector3.Distance(transform.position, targetPosition) / randomizedSpeed;
nextActionTime = Time.fixedTime + timeToGetThere;
}
else
{
// Make sure that the fish does not swim past the target
Vector3 moveVector = randomizedSpeed * transform.forward * Time.fixedDeltaTime;
if (moveVector.magnitude <= Vector3.Distance(transform.position, targetPosition))
{
transform.position += moveVector;
}
else
{
transform.position = targetPosition;
nextActionTime = Time.fixedTime;
}
}
}
}
|
cs |
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
|
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
public class Ball3DAgent : Agent
{
[Header("Specific to Ball3D")]
public GameObject ball;
Rigidbody m_BallRb;
EnvironmentParameters m_ResetParams;
public override void Initialize()
{
m_BallRb = ball.GetComponent<Rigidbody>();
m_ResetParams = Academy.Instance.EnvironmentParameters;
SetResetParameters();
}
public override void CollectObservations(VectorSensor sensor)
{
sensor.AddObservation(gameObject.transform.rotation.z);
sensor.AddObservation(gameObject.transform.rotation.x);
sensor.AddObservation(ball.transform.position - gameObject.transform.position);
sensor.AddObservation(m_BallRb.velocity);
}
public override void OnActionReceived(float[] vectorAction)
{
var actionZ = 2f * Mathf.Clamp(vectorAction[0], -1f, 1f);
var actionX = 2f * Mathf.Clamp(vectorAction[1], -1f, 1f);
if ((gameObject.transform.rotation.z < 0.25f && actionZ > 0f) ||
(gameObject.transform.rotation.z > -0.25f && actionZ < 0f))
{
gameObject.transform.Rotate(new Vector3(0, 0, 1), actionZ);
}
if ((gameObject.transform.rotation.x < 0.25f && actionX > 0f) ||
(gameObject.transform.rotation.x > -0.25f && actionX < 0f))
{
gameObject.transform.Rotate(new Vector3(1, 0, 0), actionX);
}
if ((ball.transform.position.y - gameObject.transform.position.y) < -2f ||
Mathf.Abs(ball.transform.position.x - gameObject.transform.position.x) > 3f ||
Mathf.Abs(ball.transform.position.z - gameObject.transform.position.z) > 3f)
{
SetReward(-1f);
EndEpisode();
}
else
{
SetReward(0.1f);
}
}
public override void OnEpisodeBegin()
{
gameObject.transform.rotation = new Quaternion(0f, 0f, 0f, 0f);
gameObject.transform.Rotate(new Vector3(1, 0, 0), Random.Range(-10f, 10f));
gameObject.transform.Rotate(new Vector3(0, 0, 1), Random.Range(-10f, 10f));
m_BallRb.velocity = new Vector3(0f, 0f, 0f);
ball.transform.position = new Vector3(Random.Range(-1.5f, 1.5f), 4f, Random.Range(-1.5f, 1.5f))
+ gameObject.transform.position;
//Reset the parameters when the Agent is reset.
SetResetParameters();
}
public override void Heuristic(float[] actionsOut)
{
actionsOut[0] = -Input.GetAxis("Horizontal");
actionsOut[1] = Input.GetAxis("Vertical");
}
public void SetBall()
{
//Set the attributes of the ball by fetching the information from the academy
m_BallRb.mass = m_ResetParams.GetWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetWithDefault("scale", 1.0f);
ball.transform.localScale = new Vector3(scale, scale, scale);
}
public void SetResetParameters()
{
SetBall();
}
}
|
cs |
'Unity3D > ml-agent' 카테고리의 다른 글
ml-agents v1.0 : Installation (0) | 2020.12.28 |
---|---|
ml-agents v1.0 (0) | 2020.12.28 |
Unity ML-Agents Release 3 자습서 (2) (0) | 2020.06.30 |
[ml-agent] Imitation Learning (0) | 2019.06.05 |
[ml-agent] Build-in reinforcement learning (0) | 2019.06.05 |