public override Locomotion.MoveDirection GetNextMove(BoardInfo boardInfo, CellInfo currentPos, CellInfo[] goals) { if (!startedLearn) { if (File.Exists("qtable.csv") && !forgetPreviousLearning) { qtable = QTable.LoadFromCsv("qtable.csv", boardInfo); episode = numberOfEpisodes; } else { StartCoroutine(Learn(boardInfo)); } startedLearn = true; } if (episode == numberOfEpisodes) { // Proxima celda aleatoria Locomotion.MoveDirection nextDirection = (Locomotion.MoveDirection)qtable.GetHighestQDirection(currentPos); return(nextDirection); } return(Locomotion.MoveDirection.None); }
/// <summary> /// Lee un fichero csv para generar una QTable /// </summary> /// <param name="path">El fichero a leer</param> /// <param name="boardInfo">Información del tablero de juego</param> /// <returns>QTable con los valores almacenados en el fichero csv</returns> public static QTable LoadFromCsv(string path, BoardInfo boardInfo) { QTable qtable = new QTable(boardInfo); StreamReader streamReader = new StreamReader(path); string line; float[] data; int nLine = 0; qtable.table.Clear(); streamReader.ReadLine(); // Saltar cabeceras while ((line = streamReader.ReadLine()) != null) { data = qtable.GetCsvValues(line); qtable.table.AddRange(data); } return(qtable); }
/// <summary> /// Función para aprender mediante QLearning. /// </summary> /// <param name="boardInfo">Tablero de juego</param> /// <returns></returns> private IEnumerator Learn(BoardInfo boardInfo) { if (subTitle) { subTitle.text = String.Format(cultureInfo, "Número de episodios: {0:n0}", numberOfEpisodes); } if (loadingPanel) { loadingPanel.SetActive(true); } yield return(null); CellInfo nextState, currentState; // Estado actual y próximo Locomotion.MoveDirection direction; // Dirección proximo movimiento float Q, r; // Valor Q y recompensa qtable = new QTable(boardInfo); // Nueva tabla Q float maxQValue = float.MinValue; // valor máximo Q en toda la tabla float totalQValue = 0f; // Suma de todos los valores Q de la tabla var epsilon = this.epsilon; // Epsilon clone for (episode = 0; episode < numberOfEpisodes; episode++) // Episodios { // Elección de una celda de inicio aleatoria para el episodio currentState = boardInfo.CellInfos[ Random.Range(0, boardInfo.NumColumns), Random.Range(0, boardInfo.NumRows) ]; bool endOfEpisode = false; do { // Elige una nueva dirección de forma aleatoria o mediante los valores Q del estado actual // en función de epsilon, el ratio de aprendizaje-exploracion if (Random.Range(0.0f, 1.0f) < epsilon) { // Elegimos una dirección aleatoria direction = (Locomotion.MoveDirection)Random.Range(0, 4); } else { // Elegimos la mejor posición según la tabla Q direction = (Locomotion.MoveDirection)qtable.GetHighestQDirection(currentState); } // Valor Q actual para la posición (estado) actual y la nueva dirección (accion) a tomar Q = qtable[currentState, direction]; // Calculamos recompensa para la próxima posición (estado) nextState = currentState.WalkableNeighbours(boardInfo)[(int)direction]; r = GetReward(nextState); // Máximo valor de Q para el próximo estado float nextQMax = nextState != null?qtable.GetHighestQValue(nextState) : 0; // Actualizamos tabla Q float QValue = (1 - alpha) * Q + alpha * (r + gamma * nextQMax); qtable[currentState, direction] = QValue; totalQValue += QValue; maxQValue = QValue > maxQValue ? QValue : maxQValue; // Nos desplazamos al siguiente estado currentState = nextState; // Condición de parada, hemos ido a una celda no navegable o hemos llegado al final if (r == -1 || r == 100) { endOfEpisode = true; } } while (!endOfEpisode); // Reducimos epsilon, para cada vez explorar menos y usar mas lo aprendido if (epsilon >= epsilonMinimumValue) { epsilon *= epsilonDecayRate; } // Actualizamos avance float pct = (episode + 1.0f) / numberOfEpisodes; if (progressBar != null) { progressBar.value = pct; } if (progressText != null) { progressText.text = (int)(pct * 100) + "%"; } if (iterationsText != null) { iterationsText.text = "Episodios: " + (episode + 1); } if (episode % 100 == 0) { yield return(null); } } if (loadingPanel) { loadingPanel.SetActive(false); } qtable.SaveToCsv("qtable.csv"); }