Curso de Inteligencia Artificial con ML-Agents de UNITY. Introducción.
Creación del script del Agente
Cualquier duda o comentario de la lección podéis dejarla en el foro del curso.
En el vídeo hemos creado y explicado el script de nuestro Agente. Este script es el encargado de definir las acciones que realiza el agente, indicar que variables tiene que observar, y dar los premios o los castigos.
Como estamos con un modelo muy simple, la única acción que realizamos es mover en dos ejes la esfera, por eso estamos usando el modo Continuous en el Space Type del Vector Action. En este modo, la función OnActionReceived, tan solo recibe un array de floats entre 0 y 1 del numero de posiciones que le indiquemos en el dato Space Size de Vector Action de Behaviour Parameters.
en el script incorporamos las librerías MlAgents i MLagents.sensor. La clase hereda de Agent.
Las funciones llamadas por el motor de MachineLearning son OnActionReceived, y CollectObservations. La función Heuristic se usa para que el agente se pueda controlar por parte de un humano y no el motor de Machine Learning.
Los premios y los castigos se dan con la función AddReward, que sea un premio o un castigo depende del signo del valor que le pasemos. Es importante balancear correctamente el valor de estos premios y no castigar en exceso al Agente ya que lo podemos colapsar y que decida no hacer nada por miedo a unos castigos demasiado severos.
using System.Collections; using System.Collections.Generic; using UnityEngine; using Unity.MLAgents; using Unity.MLAgents.Sensors; public class AgenteML : Agent { [SerializeField] private float _fuerzaMovimieto = 200; [SerializeField] private Transform _target, _isla; public bool _training = true; private Rigidbody _rb; public override void Initialize() { _rb = GetComponent<Rigidbody>(); //MaxStep forma parte de la clase Agent if (!_training) MaxStep = 0; } public override void OnEpisodeBegin() { _rb.velocity = Vector3.zero; _rb.angularVelocity = Vector3.zero; MoverPosicionInicial(); } /// <summary> /// El vectorAction nos sirve para construir un vector de desplazamiento /// [0]: X. /// [1]: Z. /// </summary> /// <param name="vectorAction"></param> public override void OnActionReceived(float[] vectorAction) { //Construimos un vector con el vector recibido. Vector3 movimiento = new Vector3(vectorAction[0], 0f, vectorAction[1]); //Sumamos el vector construido al rigidbody como fuerza _rb.AddForce(movimiento * _fuerzaMovimieto * Time.deltaTime); } public override void CollectObservations(VectorSensor sensor) { //Calcular cuanto nos queda hasta el objetivo Vector3 alObjetivo = _target.position - transform.position; //Un vector ocupa 3 observaciones. sensor.AddObservation(alObjetivo.normalized); } public override void Heuristic(float[] actionsOut) { float moveHorizontal = Input.GetAxis("Horizontal"); float moveVertical = Input.GetAxis("Vertical"); Vector3 movement = new Vector3(moveHorizontal, 0f, moveVertical); actionsOut[0] = movement.x; actionsOut[1] = movement.z; } private void OnTriggerEnter(Collider other) { if (_training) { if (other.CompareTag("target")) { AddReward(1f); } if (other.CompareTag("borders")) { AddReward(-0.1f); } } } private void OnTriggerStay(Collider other) { if (_training) { if (other.CompareTag("target")) { AddReward(0.5f); } if (other.CompareTag("borders")) { AddReward(-0.05f); } } } private void MoverPosicionInicial() { bool posicionEncontrada = false; int intentos = 100; Vector3 posicionPotencial = Vector3.zero; while (!posicionEncontrada || intentos >= 0) { intentos--; posicionPotencial = new Vector3( transform.parent.position.x + UnityEngine.Random.Range(-3f, 3f), 0.555f, transform.parent.position.z + UnityEngine.Random.Range(-3f, 3f)); //en el caso de que tengamos mas cosas en el escenario checker que no choca Collider[] colliders = Physics.OverlapSphere(posicionPotencial, 0.05f); if (colliders.Length == 0) { transform.position = posicionPotencial; posicionEncontrada = true; } } } }
Actualización ml-agents 1.7.
La función OnActionReceived ha cambiado, y ahora puede recibir al mismo tiempo dos arrays, uno de tipo Discrete y el otro de tipo continous. No hace falta usar los dos pero si que se pueden configurar los dos a la vez. Esto abre nuevas posibilidades de acciones a los ML-Agents.
Nuestra función OncollectObservation quedaria de la siguiente forma:
public override void OnActionReceived(ActionBuffers actions) { //Construimos un vector con el vector recibido. Vector3 movimiento = new Vector3(actions.ContinuousActions[0], 0f, actions.ContinuousActions[1]); //Sumamos el vector construido al rigidbody como fuerza _rb.AddForce(movimiento * _fuerzaMovimieto * Time.deltaTime); }
También cambia la forma de informar el array en el IDE de Unity: