Full text
Trabajo de Fin de Grado en Ingenier ´ ıa Inform´ atica Facultad de Inform´ atica Universidad Complutense de Madrid Evaluaci´on de rendimiento de arquitecturas paralelas y de prop´osito espec´ıfico para el aprendizaje por refuerzo en juegos Performance evaluation of parallel and specific-purpose architectures for reinforcement learning in games Autor: Javier Guzm´ an Mu˜ noz Profesor director: Francisco Igual Pe˜ na Profesor codirector: Luis MªCostero Valero Doble Grado en Ingenier ´ ıa Inform´ atica - Matem´ aticas Curso 2020-2021
2
3 Resumen Las aplicaciones de aprendizaje por refuerzo se usan en la actualidad para resolver problemas de todo tipo en campos muy diversos. Sin embargo, una de las principales desventajas que presentan es el elevado coste computacional del entrenamiento de los modelos necesarios. Con este trabajo de fin de grado se pretende mejorar este proceso mediante la paralelizaci´on de los algoritmos empleados y el uso de distintas arquitecturas hardware que variar´an los tiempos requeridos. Los modelos entrenados pueden aplicarse para obtener la mejor secuencia de acciones que podemos realizar sobre un entorno y mejorar la recompensa obtenida. Este proceso, que se denomina inferencia, aunque tiene menor complejidad computacional, se realiza muchas m´as veces, por lo que se han desarrollado procesadores de prop´osito espec´ıfico para llevar a cabo esta tarea. Por ello, tambi´en es conveniente evaluar su rendimiento en estos soportes y compararlos con otras unidades de procesamiento m´as generales. Tras definir el escenario en el que nos vamos a mover y los recursos necesarios para ello, se proponen una serie de experimentos de los procesos de entrenamiento e inferencia que nos permitir´an evaluar el rendimiento en t´erminos del tiempo empleado, de la utilizaci´on de los recursos disponibles y del consumo de energ´ıa de distintas arquitecturas hardware, viendo cu´al es m´as conveniente usar en cada caso. Palabras clave Aprendizaje por refuerzo, algoritmo PPO, red neuronal de convoluci´on, Ray RLlib, entornos Gym, TPU Google Coral, aceleradores hardware. Abstract Nowadays, reinforcement learning applications are used to solve all kinds of problems in a wide variety of fields. However, one of their main disadvantages is the high computational cost of training the necessary models. This Bachelor’s thesis aims at improving this process by parallelizing the involved algorithms and by using different hardware architectures, which will differ in the amount of time used. We can run previously trained models to obtain the best sequence of actions to interact with the environment in order to improve the reward obtained. Although this process, called inference, has a lower computational complexity, it is usually repeated many times and requires a fast response. In order to execute inference in an efficient way, specific-purpose processors have been developed, so it is convenient to evaluate its performance on these devices and compare them with more general processing units. After defining the scenario and the resources needed, we propose a series of experiments to test the training and inference processes, evaluating the performance in terms of the time spent, the resource usage and the power consumption when using different architectures, analyzing which is the best option in each case. Keywords Reinforcement learning, PPO algorithm, convolutional neural network, Ray RLlib,Gym environments, Google Coral TPU, hardware accelerators.
4
´ Indice general 1. Introducci´on 7 2. Fundamentos 15 2.1. Aprendizajeporrefuerzo.................................... 15 2.1.1. Pol´ıticas en el aprendizaje por refuerzo . . . . . . . . . . . . . . . . . . . . . . . . 16 2.2. Algoritmo de Optimizaci´on de Pol´ıtica Pr´oxima (PPO) . . . . . . . . . . . . . . . . . . 17 2.2.1. Algoritmos de gradiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.2. Algoritmos de regi´on de confianza . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.2.3. Fundamentos del algoritmo PPO . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.3. Redes Neuronales de Convoluci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.1. Redes de convoluci´on y aprendizaje por refuerzo . . . . . . . . . . . . . . . . . . 24 2.4. Tensorflow............................................ 24 2.4.1. Keras .......................................... 25 2.4.2. TensorflowLite..................................... 25 2.5. RayyRLlib........................................... 25 2.5.1. RLlib .......................................... 26 2.5.2. Algoritmo PPO en RLlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.6. EntornosGym ......................................... 28 2.7. TPUyGoogleCoral...................................... 30 2.8. Cuantizaci´ondemodelos.................................... 32 3. Implementaci´on 35 3.1. Descripci´on de los entornos de pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2. Descripci´on de los modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.2.1. Descripci´on del entorno del agente . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.2.2. Modelo de Tensorflow Keras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.2.3. Modelospropuestos .................................. 39 3.2.4. Modelosseleccionados ................................. 40 3.3. An´alisisdelrendimiento .................................... 41 3.3.1. Implementaci´on de los experimentos de entrenamiento . . . . . . . . . . . . . . . 41 3.3.2. Implementaci´on de los experimentos de inferencia . . . . . . . . . . . . . . . . . . 44 4. Resultados 51 4.1. Resultados del proceso de entrenamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.1.1. Uso de los recursos disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.1.2. Tiemposempleados .................................. 57 4.1.3. An´alisis del uso que se hace de las distintas CPUs . . . . . . . . . . . . . . . . . 66 4.1.4. Conclusiones extra´ıdas de los experimentos de entrenamiento . . . . . . . . . . . 68 5
6´ INDICE GENERAL 4.2. Resultados de la inferencia de modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 4.2.1. InferenciaenRLlib................................... 68 4.2.2. Inferencia sobre el acelerador Google Coral . . . . . . . . . . . . . . . . . . . . . 70 5. Conclusiones 73 A. Funcionamiento de los scripts de Python 79 A.1.Scriptdeentrenamiento .................................... 79 A.2. Script de inferencia en RLlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 A.3. Scripts de exportaci´on y cuantizaci´on de modelos para la TPU . . . . . . . . . . . . . . 82 A.3.1. Script de exportaci´on de modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 A.3.2. Script de creaci´on de modelos de Tensorflow Lite . . . . . . . . . . . . . . . . . . 83 A.3.3. Script de creaci´on de datasets para la cuantizaci´on . . . . . . . . . . . . . . . . . 83 A.3.4. Script de cuantizaci´on de modelos de Tensorflow Lite . . . . . . . . . . . . . . . . 83 A.4. Scripts de inferencia de modelos de Tensorflow Lite . . . . . . . . . . . . . . . . . . . . . 84 Bibliograf´ıa 88
Cap´ıtulo 1 Introducci´on Hoy en d´ıa, el aprendizaje autom´atico omachine learning est´a presente en pr´acticamente todos los campos del conocimiento (ingenier´ıa, finanzas, medicina, . . . ). Esta rama de la computaci´on trata de emular la manera en la que el cerebro humano aprende a partir de la informaci´on que le llega de su entorno. Son m´ultiples los paradigmas que existen dentro de este campo. En este trabajo nos vamos a centrar en uno de ellos, el aprendizaje por refuerzo. Su idea fundamental es llevar a cabo el aprendizaje por medio de la interacci´on continua y el intercambio de informaci´on entre un agente y un entorno con el que desea aprender a interaccionar. El agente recibe observaciones del entorno y, bas´andose en ellas, entrena una pol´ıtica que determinar´a una acci´on que realizar´a en el entorno y por la que obtendr´a una recompensa. El objetivo final ser´a desarrollar un modelo que maximice las recompensas obtenidas, esto es, que dada una observaci´on del entorno sepa cual es la mejor acci´on que nos llevar´a a optimizar la recompensa final acumulada. Para obtener un modelo de aprendizaje autom´atico hay que realizar un proceso de entrenamiento, en el que se configura el propio modelo a trav´es de interacciones sucesivas con el entorno en el que va mejorando su toma de decisiones. Una vez concluya este proceso ya podemos usar nuestro modelo para realizar inferencias, esto es, interacciones con el entorno en las que en cada momento se escoja la mejor acci´on de acuerdo con la pol´ıtica que hemos entrenado. Existen diversos modelos (algoritmos) que se pueden usar en problemas de aprendizaje. Uno de ellos son las redes neuronales de convoluci´on, que reciben im´agenes y son capaces de capturar dependencias espaciales y temporales en ellas. El escenario de aprendizaje por refuerzo puede aplicarse a todo tipo de problemas de aprendizaje. En este trabajo se adaptar´a el problema para el caso en el que queremos aprender a jugar a videojuegos sencillos a partir de las observaciones de la pantalla y de las puntuaciones obtenidas. Para modelar el entorno del problema nos ayudaremos de la biblioteca Gym1, que proporciona entornos sobre los que podemos desarrollar escenarios de aprendizaje por refuerzo. Los entornos que tomaremos nos suministrar´an datos en forma de im´agenes como observaciones del entorno, lo que propiciar´a tambi´en el uso de redes neuronales de convoluci´on. El soporte hardware sobre el que se pueden desarrollar estos procesos es muy variado: desde procesadores gen´ericos (CPUs) hasta otros de prop´osito espec´ıfico para tareas concretas (como por ejemplo la inferencia), pasando por aceleradores gr´aficos como GPUs (Graphics Processing Units). 1https://gym.openai.com/ 7
8CAP´ ITULO 1. INTRODUCCI ´ ON Motivaciones Uno de los principales problemas que presenta el desarrollo de modelos de aprendizaje por refuerzo es el elevado coste computacional de los algoritmos empleados para el proceso de entrenamiento. Para ello, se tratan de acelerar los c´alculos implicados con el uso de GPUs. Adem´as, la estructura de muchos de los algoritmos de entrenamiento habituales en este tipo de problemas va a permitir una paralelizaci´on que mejore tambi´en su rendimiento. Sin embargo, es necesario adaptar el proceso de entrenamiento para que sea posible su correcta ejecuci´on en este soporte y la paralelizaci´on de los algoritmos, que en muchos casos no ser´a tarea sencilla. Y aqu´ı es donde aparece la biblioteca RLlib2de Ray, que elimina estas dificultades. Por otro lado, introducir recursos como las GPUs incrementa notablemente el consumo de potencia de nuestro sistema, por lo que es un factor tambi´en a tener en cuenta cuando configuremos el entrenamiento. En cuanto al proceso de inferencia, aunque una inferencia individual tenga un coste mucho menor que una iteraci´on de entrenamiento, las inferencias se realizan un n´umero muy elevado de veces, por lo que, en conjunto, el coste de las inferencias suele ser mayor que el del entrenamiento (al fin y al cabo, el entrenamiento lo realizamos una vez pero las inferencias siempre que queramos usar nuestro modelo para resolver el problema de aprendizaje). Por ello, es importante realizar las inferencias en dispositivos en los que el coste de cada inferencia individual sea muy bajo, para que una diferencia de coste casi imperceptible no se convierta en un problema al verse multiplicada cuando realizamos gran cantidad de pasos de inferencia. As´ı, cabe la posibilidad de que variando el hardware sobre el que se ejecute se mejore su rendimiento en este aspecto, al igual que en el entrenamiento. Y es que existe hardware espec´ıfico para realizar este proceso, como la TPU de Google Coral3, ideada como un procesador de matrices que resulta id´oneo para ejecutar inferencias sobre modelos de aprendizaje autom´atico. Adem´as, otra de las ventajas que ofrece es el ahorro en consumo de energ´ıa respecto a las CPUs y GPUs. El uso de este dispositivo a˜nade la dificultad de que es necesario adaptar los modelos para que podamos ejecutar inferencias en ´el, y este proceso podr´ıa en muchos casos no ser trivial. Objetivos Motivados por lo expuesto anteriormente, el objetivo principal del presente trabajo de fin de grado es evaluar el rendimiento de los procesos de aprendizaje e inferencia en el escenario del aprendizaje por refuerzo en diferentes arquitecturas hardware, analizando las ventajas e inconvenientes de cada una de ellas. As´ı, tendremos informaci´on ´util a la hora de entrenar o de aplicar modelos de aprendizaje por refuerzo que nos permitir´a optimizar el tiempo, la utilizaci´on de recursos o el consumo de energ´ıa en cada caso concreto. Listamos a continuaci´on algunos de los objetivos espec´ıficos en los que podemos desglosar este objetivo principal: 1. Modelar correctamente el escenario de aprendizaje haciendo uso de las bibliotecas RLlib yGym. 2. Ejecutar un mismo proceso de entrenamiento sobre diferentes arquitecturas hardware haciendo uso de las utilidades de Ray. 3. Evaluar el rendimiento variando los par´ametros propios de los modelos que se consideran. 4. Analizar y extraer conclusiones sobre el proceso de entrenamiento, que permitan determinar las ventajas e inconvenientes en t´erminos de tiempo de aprendizaje y utilizaci´on de los recursos de cada uno de los escenarios evaluados. 2https://docs.ray.io/en/master/rllib.html 3https://coral.ai/products/
9 5. Ejecutar procesos de inferencia haciendo uso de las utilidades de Ray, variando los recursos hardware empleados para ello. 6. Ser capaces de ejecutar inferencias en el acelerador Google Coral sobre modelos previamente entrenados con RLlib y posteriormente cuantizados. 7. Extraer conclusiones respecto a la inferencia de modelos y analizar las ventajas e inconvenientes que supone el uso de aceleradores de prop´osito espec´ıfico. Metodolog´ıa Para poder lograr estos objetivos trataremos de manera separada los procesos de entrenamiento e inferencia. Elegiremos una serie de modelos (dados como redes neuronales de convoluci´on) que se distingan entre s´ı fundamentalmente por el tama˜no de las entradas que reciben. Una vez elegidos los modelos, propondremos una serie de experimentos que permitan evaluar el rendimiento de los procesos de entrenamiento e inferencia variando los recursos hardware empleados, desarrollando el c´odigo Python necesario para ello. Posteriormente, transformamos algunos de los modelos obtenidos como resultado de los experimentos de entrenamiento para que puedan ser ejecutados sobre la TPU Google Coral, proceso que, como veremos, no es trivial. En todo momento, nos preocuparemos tambi´en de tener en cuenta qu´e aspectos vamos a medir (m´etricas) en cada uno de los procesos y c´omo obtendremos esta informaci´on. Tras plantear los experimentos se procede a su ejecuci´on en servidores remotos del Departamento de Arquitectura de Computadores y Autom´atica, almacenando los datos obtenidos como resultado de estas ejecuciones. Seguidamente, debemos organizar los datos para proceder a su an´alisis. Se elaboran una serie de scripts de Python que agrupan los datos y generan gr´aficas y ficheros tabulares de los mismos, comparando los resultados de los distintos experimentos y facilitando as´ı la extracci´on de conclusiones. Para ello, se hace uso de bibliotecas espec´ıficas de Python para el tratamiento y la informaci´on de datos, como Pandas yMatplotlib. Estructura del trabajo Este trabajo de fin de grado esta compuesto por la presente memoria y por el c´odigo empleado para la realizaci´on de los distintos experimentos antes mencionados y la obtenci´on de resultados, que se encuentra en un repositorio de Github del usuario javigm98 y al que se puede acceder mediante la siguiente URL: https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico. La memoria se organiza en cinco cap´ıtulos. Esta introducci´on, en la que se presenta el tema y los objetivos que se pretenden conseguir, constituye el primero de los cap´ıtulos. En el segundo de ellos se presentan los fundamentos de muchos de los conceptos con los que vamos a trabajar. As´ı, comenzamos definiendo de manera te´orica el paradigma del aprendizaje por refuerzo y el algoritmo PPO que usaremos durante el entrenamiento. Tambi´en, se explica en qu´e consisten las redes neuronales de convoluci´on y c´omo las usaremos para procesar observaciones en nuestro escenario de aprendizaje por refuerzo. Adem´as, se introducen los frameworks y bibliotecas de Python que se utilizar´an para la implementaci´on del trabajo: Tensorflow,Ray,RLlib yGym. Por ´ultimo, se describe en qu´e consiste el proceso de cuantizaci´on de modelos, que ser´a necesario para poder ejecutar inferencias sobre el acelerador TPU de Google Coral, cuyas caracter´ısticas y las ventajas que ofrece se detallan en una ´ultima secci´on. En el tercer cap´ıtulo exponemos los detalles de implementaci´on tenidos en cuenta para la consecuci´on de los objetivos del trabajo. Tras detallar los sistemas de los que disponemos para la
16 CAP´ ITULO 2. FUNDAMENTOS entorno en ese momento) y realiza una acci´on en la que interacciona con dicho entorno. Como consecuencia de esta acci´on, el agente recibe del entorno una recompensa espec´ıfica para la acci´on realizada y una nueva observaci´on con el estado del entorno tras realizarse la interacci´on del agente. El agente aprende tras sucesivos intentos, desde una observaci´on inicial hasta un estado final del entorno, que bien puede ser de ´exito o de fracaso en la tarea a realizar. El objetivo del agente es tratar de maximizar las recompensas obtenidas y ser capaz de determinar qu´e acciones tomar en cada momento para alcanzar ese objetivo. Desde el punto de vista te´orico, podemos modelar el problema de aprendizaje por refuerzo como un proceso de decisi´on de Markov (MDP, del ingl´es Markov Decision Process) con los siguientes elementos: Un conjunto de estados S, que puede ser infinito. Un conjunto de acciones Aque puede realizar el agente, que puede ser tambi´en infinito. Probabilidad de transici´on (en tiempo t) del estado sal estado s0tomando la acci´on a, que denotaremos por Pa(s, s0) = P[st+1 =s0|st=s, at=a]. Recompensa obtenida al pasar del estado sas0tras tomar la acci´on a. Lo denotamos por Ra(s, s0). stst+1 st+2 at/rt+1 at+1/rt+2 Figura 2.2: Reperesentaci´on de las transiciones entre estados s∈Sen un proceso de decisi´on de Markov, donde en cada estado tomamos una acci´on ai∈Ay recibimos una recompensa ri∈R. 2.1.1. Pol´ıticas en el aprendizaje por refuerzo El principal problema que debe resolver un agente de de aprendizaje por refuerzo es determinar qu´e acci´on tomar en cada estado. Para ello, introducimos el concepto de pol´ıtica que nos servir´a para decidir la acci´on que se toma en cada momento. Definici´on 2.1 (Pol´ıtica).Dado un problema de aprendizaje por refuerzo, una pol´ıtica es una funci´on π:S→∆(A), donde ∆(A) es un conjunto de distribuciones de probabilidad sobre el conjunto A, esto es, funciones A→[0,1]. Para un estado sy una acci´on a,π(s)(a) = P[a|s] denota la probabilidad de tomar la acci´on asi nos encontramos en el estado s. El agente, a trav´es de sucesivas interacciones con el entorno, entrenar´a una pol´ıtica que ser´a capaz de determinar las acciones que tendr´an que tomar los agentes para maximizar la recompensa acumulada. Aunque los agentes s´olo reciben por parte del entorno la recompensa inmediata para una acci´on tomada, la pol´ıtica debe determinar la secuencia de acciones a tomar tras cada observaci´on para que la recompensa final al completar un episodio se maximice. El agente se enfrentar´a al dilema de elegir entre exploraci´on yexplotaci´on, esto es, explorar estados desconocidos para el agente para tratar de ganar m´as informaci´on sobre el entorno y las recompensas o centrarse en explotar la informaci´on de la que ya dispone de pasos anteriores para maximizar las recompensas. Normalmente los algoritmos empleados propiciar´an que los agentes vayan alternando ambas opciones.
2.2. ALGORITMO DE OPTIMIZACI ´ ON DE POL´ ITICA PR ´ OXIMA (PPO) 17 Para los problemas de aprendizaje por refuerzo que trataremos ser´a necesario definir una pol´ıtica no estacionaria, dada por una sucesi´on de funciones pol´ıtica πt:S→∆(A), haciendo referencia a la pol´ıtica vigente en cada instante tdel problema. Esta pol´ıtica se ir´a modificando durante el proceso de entrenamiento, actualiz´andose con los resultados que obtenemos tras interaccionar con el entorno siguiendo la pol´ıtica v´alida en ese momento. El objetivo del agente de un problema de aprendizaje por refuerzo ser´a encontrar una pol´ıtica que maximice la recompensa esperada, esto es, no s´olo la recompensa de la pr´oxima acci´on sino la suma de estas a largo plazo, pues al final ´este es el objetivo del aprendizaje por refuerzo. As´ı, en cada estado spodemos definimos el valor de la pol´ıtica πen ese estado, Vπ(s) como el valor esperado para la recompensa total obtenida desde el estado s. Formalmente: Vπ(s) = E at∼π(st)"∞ X t=0 γtrat(st, st+1)|s0=s# En vista de esta expresi´on, observamos que el valor de la pol´ıtica viene dado por el valor de la esperanza de la recompensa total tras una serie de pasos comenzando desde el estado s, esto es la recompensa que esperamos obtener seleccionando las acciones de acuerdo con la distribuci´on dada por la pol´ıtica para cada estado. Adem´as, multiplicamos la recompensa en cada paso por γt, donde γ∈[0,1) se denomina factor de descuento y es un valor constante que se emplea para dar m´as peso a las recompensas m´as inmediatas. Los agentes en cada estado sbuscar´an una pol´ıtica πcon el mayor valor posible de Vπ(s). As´ı, diremos que una pol´ıtica π∗es ´optima si su valor es maximal para todo estado s∈S, esto es, para cualquier otra pol´ıtica πy cualquier estado s∈Sse tiene que Vπ∗(s)≥Vπ(s). De hecho, se puede probar que, si los conjuntos de estados y acciones del problema de decisi´on de Markov con el que modelamos nuestro problema de aprendizaje por refuerzo son finitos, existe una pol´ıtica que para cualquier estado inicial ses ´optima. Adem´as, se prueba tambi´en la existencia en esas condiciones de una pol´ıtica ´optima determinista, esto es, que para cualquier estado sexiste una acci´on atal que π(s)(a) = 1. Esta pol´ıtica nos indicar´ıa la secuencia exacta de acciones a tomar que maximizar´ıan la recompensa total del problema. La siguiente cuesti´on en la que nos centraremos ser´a elaborar un algoritmo que vaya construyendo esta pol´ıtica ´optima durante el proceso de aprendizaje. Los agentes ir´an interaccionando con el entorno siguiendo una pol´ıtica determinada y obteniendo los valores para las recompensas tras cada acci´on, usando esta informaci´on para mejorar esta pol´ıtica. Dise˜naremos un algoritmo iterativo que parta de una pol´ıtica inicial que determine las acciones a realizar sobre el entorno y que “aprenda” con la informaci´on recolectada para ir mejorando su valor. 2.2. Algoritmo de Optimizaci´on de Pol´ıtica Pr´oxima (PPO) Presentamos a continuaci´on uno de los algoritmos m´as eficientes para optimizar la pol´ıtica en el aprendizaje por refuerzo: el algoritmo de Optimizaci´on de Pol´ıtica Pr´oxima, PPO (del ingl´es Proximal Policy Optimization). Veremos algunos fundamentos te´oricos del mismo y tambi´en la manera en la que se implementa en RLlib. El algoritmo PPO aparece por primera vez en 2017 en el art´ıculo [12]. De esta fuente y otros art´ıculos ([3], [15]) se extrae la informaci´on que presentamos a continuaci´on. El algoritmo PPO se encuadra dentro de lo que denominamos como m´etodos de gradiente. En lo que sigue, denotaremos la pol´ıtica que usa nuestro agente en cada momento por πθ, donde θhace referencia a los par´ametros que definen la propia funci´on de la pol´ıtica y que se ir´an actualizando para optimizarla.
18 CAP´ ITULO 2. FUNDAMENTOS Los algoritmos con los que obtendremos la pol´ıtica ´optima para nuestro problema de aprendizaje son en realidad algoritmos de optimizaci´on. Podemos clasificarlos en dos subclases: los algoritmos de gradiente y los algoritmos de regi´on de confianza. 2.2.1. Algoritmos de gradiente Estos algoritmos se basan en elegir siempre la direcci´on en la que se produzca un mayor descenso en el valor del gradiente de la funci´on objetivo a optimizar, en este caso la pol´ıtica, y se avanza en esa direcci´on. Esto hace, en muchos casos, que lleguemos de manera r´apida a la soluci´on ´optima, pero en ocasiones podemos acabar en estados peores que de los que part´ıamos. Por ejemplo, imaginemos que estamos escalando una monta˜na y en cada etapa tenemos que avanzar un n´umero fijo de pasos. Si elegimos ascender en la direcci´on con mayor pendiente siempre, la l´ogica nos invita a pensar que llegaremos r´apidamente a la cima. Pero, sin embargo, si elegimos una direcci´on por ser la de mayor pendiente y avanzamos un n´umero de pasos excesivo en esa direcci´on, podremos caer y aparecer en un nivel incluso m´as bajo del que part´ıamos. Desde el punto de vista te´orico, estos m´etodos se basan en calcular un estimador del gradiente de la pol´ıtica y usarlo en un algoritmo de descenso de gradiente [1]. El estimador que m´as se usa en la pr´actica es el siguiente: ˆg=ˆ Eth∇θlog πθ(at|st)ˆ Ati, donde πθes la pol´ıtica, ˆ Ates un estimador del valor de la funci´on de ventaja en el paso tyˆ Et[. . . ] denota el valor de la media emp´ırica sobre un conjunto de valores en distintos pasos t. La funci´on de ventaja mide c´omo de buena o mala es la decisi´on de tomar una acci´on en un determinado estado, esto es, qu´e ventaja obtenemos al tomar esta acci´on. Puede expresarse como A(s, a) = E"ra(s0, s1) + ∞ X t=1 γtr(st, at)|s0=s, a0=a#−E"∞ X t=0 γtrat(st, st+1)#, esto es, la ventaja para un estado sy una acci´on amide la diferencia entre la recompensa total esperada empezando en el estado ssi la primera acci´on que tomamos es ay la recompensa total esperada partiendo del estado ssin indicar cual es esa primera acci´on. En este contexto, se define la funci´on objetivo LP G(θ) = ˆ Ethlog πθ(at|st)ˆ Ati, cuyo gradiente coincide con el valor de ˆgque present´abamos antes, y se optimiza su valor mediante uno de estos algoritmos. 2.2.2. Algoritmos de regi´on de confianza Estos algoritmos, en lugar de buscar optimizar el valor de la funci´on objetivo de manera lineal, definen una regi´on de confianza a la que restringimos las soluciones, y se busca optimizar la soluci´on en ese subconjunto. Se itera definiendo nuevas regiones de confianza (de distintos tama˜nos) hasta converger a una soluci´on ´optima. La principal diferencia frente a los algoritmos de gradiente es, que en este caso, el avance no es lineal y que la “distancia”que se avanza en cada iteraci´on no est´a fijada de antemano, sino que depende de la regi´on de confianza considerada en cada momento. As´ı, el tama˜no de la regi´on de confianza a considerar se reajusta din´amicamente en cada iteraci´on, haci´endose m´as peque˜no si vemos que se producen variaciones considerables en la pol´ıtica. En este tipo de algoritmos debemos limitar de alguna manera cu´anto puede variar la pol´ıtica en cada iteraci´on respecto a la anterior. Para ello, introducimos el concepto de divergencia-KL, que mide la diferencia existente entre dos distribuciones de probabilidad PyQy que se define como sigue: KL(P, Q) = E xlog P(x) Q(x).
2.2. ALGORITMO DE OPTIMIZACI ´ ON DE POL´ ITICA PR ´ OXIMA (PPO) 19 Los algoritmos de regi´on de confianza aplicados para encontrar la pol´ıtica ´optima en problemas de aprendizaje por refuerzo impondr´an restricciones en el valor de la divergencia-KL para evitar variaciones excesivas de la pol´ıtica en cada iteraci´on. El algoritmo TRPO (Trust Region Policy Optimization) [13] se basa en este m´etodo y trata de resolver el siguiente problema de optimizaci´on, compuesto por una funci´on objetivo y una restricci´on que imponemos al valor de la divergencia-KL: m´ax θ ˆ Etπθ(at|st) πθold (at|st)ˆ At s. a.: ˆ Et[KL(πθold (·|st), πθ(·|st))] ≤δ, donde θold representa el vector de par´ametros que defin´ıan la pol´ıtica antes de actualizarla en cada iteraci´on. Se prueba que podemos obtener una soluci´on aproximada eficiente para este problema usando un algoritmo de gradiente conjugado despu´es de aproximar linealmente la funci´on objetivo y mediante una funci´on cuadr´atica la restricci´on. Adem´as, cuando se presenta este algoritmo, se sugiere modelizar el problema a˜nadiendo una penalizaci´on a la funci´on objetivo en lugar de la restricci´on que ten´ıamos, dando lugar al problema de optimizaci´on sin restricciones siguiente: m´ax θ ˆ Etπθ(at|st) πθold (at|st)ˆ At−βKL(πθold (·|st), πθ(·|st)),(2.1) para alg´un coeficiente β. Sin embargo, TRPO usa la versi´on con la restricci´on en lugar de esta ´ultima con la penalizaci´on por la dificultad que supone elegir un valor de βque funcione bien para todos los problemas concretos. 2.2.3. Fundamentos del algoritmo PPO Para realizar las aproximaciones que hemos mencionado antes y que resolv´ıan el problema de optimizaci´on del algoritmo TRPO dando una soluci´on aproximada del problema usamos el desarrollo de Taylor de orden dos tanto de la funci´on objetivo como de la restricci´on. Adem´as, dado que el t´ermino de orden dos de la funci´on objetivo va a ser mucho m´as peque˜no que el de la divergenciaKL, podemos ignorarlo. Sin embargo, resolver este problema implicar´ıa calcular la derivada de segundo orden de la funci´on divergencia-KL e invertir la matriz resultante, lo cual es un c´alculo bastante costoso desde el punto de vista computacional. Este problema se trata de abordar de dos maneras: Aproximar los c´alculos que impliquen a la segunda derivada y su inversa para reducir la complejidad. Hacer que la soluci´on aproximada implique s´olo el c´alculo de derivadas de primer orden (como el descenso de gradiente) a˜nadiendo restricciones al problema. En el algoritmo TRPO se toma la primera soluci´on, mientras que la novedad de PPO es que su aproximaci´on se parece m´as la segunda idea expuesta. As´ı, aplicaremos m´etodos que s´olo impliquen la primera derivada como el descenso de gradiente, pero a˜nadiendo restricciones que fuercen a que la optimizaci´on siga realiz´andose dentro de una regi´on de confianza determinada. PPO con penalizaci´on KL adaptada En esta aproximaci´on resolveremos el problema del algoritmo TRPO pero sustituyendo la restricci´on del problema de optimizaci´on por una penalizaci´on en la funci´on objetivo, tal y como ten´ıamos en la expresi´on (2.1). El valor de βcontrola la penalizaci´on que introducimos al valor de la funci´on objetivo
20 CAP´ ITULO 2. FUNDAMENTOS y que iremos ajustando din´amicamente. As´ı, si el valor de la divergencia-KL entre la nueva pol´ıtica πθ y la pol´ıtica antigua πθold aumenta en exceso (fijamos un valor δque marque el valor m´aximo de la divergencia-KL que toleramos) disminuimos el valor de β. Simplificando mucho las cosas, un esquema de cada iteraci´on de actualizaci´on de la pol´ıtica en un algoritmo PPO con penalizaci´on KL ser´ıa el siguiente: 1. Computar varias etapas del algoritmo minibatch SGD optimizar la funci´on objetivo con penalizaci´on KL: LKLP EN (θ) = ˆ Etπθ(at|st) πθold (at|st)ˆ At−βKL(πθold (·|st), πθ(·|st))(2.2) 2. Calcular d=ˆ Et[KL(πθold (·|st), πθ(·|st))]. Ahora ajustamos el valor de βdependiendo del valor de d: Si d < δ 1.5,β←β/2 Si d > 1.5×δ,β←2×β. El valor de βactualizado se usa para la siguiente iteraci´on. Los valores 1.5 y 2 se eligen heur´ısticamente y aunque el valor de βinicial es un hiperpar´ametro del algoritmo este no se ve afectado por una mala elecci´on del mismo, pues r´apidamente se ajusta su valor. PPO con objetivo sustituto recortado (clipped surrogate objective) Para un instante de tiempo ten el que nos encontramos en un estado sty para una determinada acci´on atdenotamos por rt(θ) = πθ(at|st) πθold (at|st). Observamos que rt(θold) = 1. Con esta notaci´on, la funci´on objetivo del problema de optimizaci´on del algoritmo TRPO se puede expresar como: L(θ) = ˆ Et[rt(θ)ˆ At] (2.3) Sin imponer ninguna restricci´on, la maximizaci´on de esta funci´on L(θ) podr´ıa dar lugar a incrementos muy grandes de la pol´ıtica entre iteraciones. Para evitar esto, en esta aproximaci´on lo que se va a hacer es penalizar los cambios en la pol´ıtica que hagan que el valor de rt(θ) se aleje de 1. As´ı, se propone la funci´on objetivo LCLIP (θ) = ˆ Ethm´ın{rt(θ)ˆ At,clip(rt(θ),1−ε, 1 + ε)ˆ At}i,(2.4) donde εes un hiperpar´ametro del algoritmo (habitualmente ε= 0.2). La funci´on clip(rt(θ),1−ε, 1+ε) hace que el valor de rt(θ) se mantenga siempre en el intervalo [1 −ε, 1 + ε], esto es, clip(rt(θ),1−ε, 1 + ε) = 1−εsi rt(θ)≤1−ε 1 + εsi rt(θ)≥1 + ε rt(θ) en otro caso Lo que hacemos, es seleccionar el m´ınimo entre el resultado de aplicar la funci´on clip al radio de probabilidades entre la pol´ıtica nueva y la antigua y el valor de ese radio. Esto provoca un comportamiento en el valor de la funci´on objetivo como el mostrado en la figura 2.3. As´ı, el algoritmo se basa en maximizar el valor de esta funci´on objetivo, tarea que se puede llevar a cabo con algoritmos de descenso de gradiente de manera sencilla. Frente a otros algoritmos que se pueden aplicar para la optimizaci´on de la pol´ıtica en el aprendizaje por refuerzo, PPO ofrece simplicidad y eficiencia, en un algoritmo con el que, en la mayor´ıa de los casos, se obtienen muy buenos resultados. Cuando m´as adelante describamos las utilidades de la biblioteca de aprendizaje por refuerzo RLlib detallaremos algunos aspectos m´as t´ecnicos de su implementaci´on y c´omo se puede optimizar su rendimiento paralelizando partes de su ejecuci´on.
2.3. REDES NEURONALES DE CONVOLUCI ´ ON 21 Figura 2.3: Valores de la funci´on objetivo para distintos valores del radio de probabilidades en un instante tconcreto. Obs´ervese el efecto de la funci´on clip en los casos en los que el estimador de ventaja es positivo o negativo. Esta imagen se obtiene del paper en el que se presenta el algoritmo PPO [12]. 2.3. Redes Neuronales de Convoluci´on Las redes neuronales de convoluci´on (CNN, del ingl´es Convolutional Neural Network) son un algoritmo de aprendizaje profundo que toma im´agenes como datos de entrada, extrae informaci´on de ellas y es capaz de diferenciar unas de otras para realizar predicciones. Describiremos de manera breve c´omo y por qu´e funcionan, siguiendo en parte lo expuesto en [8], y tambi´en como las integraremos en nuestro escenario de aprendizaje por refuerzo. Una red neuronal est´a formada por una serie de capas, cada una con un n´umero determinado de neuronas, que reciben datos, los multiplican por una serie de pesos y pasan la informaci´on a las neuronas de siguientes capas a las que est´an interconectadas. Una imagen no es m´as que una matriz de p´ıxeles, esto es, una matriz cuyos valores representan los p´ıxeles de la imagen. Podr´ıamos reordenar los elementos de esta matriz dando lugar a un vector unidimensional que sirviese como entrada de una red neuronal al uso. Sin embargo, haciendo esto estar´ıamos perdiendo mucha informaci´on sobre dependencias espaciales de muchos de los elementos de las im´agenes, por lo que debemos ser capaces de procesar la imagen en su formato habitual como matriz n-dimensional de p´ıxeles (cada capa de color es una matriz bidimensional y podemos tener varias capas). Los elementos de entrada de una red neuronal de convoluci´on vendr´an dados por tensores (vectores) de cuatro dimensiones (n´umero de entradas, ancho de la imagen, alto de la imagen y n´umero de canales de color de la misma). Normalmente, el n´umero de entradas ser´a 1. El elemento fundamental de las redes neuronales de convoluci´on son las capas de convoluci´on, que dan nombre al algoritmo. En estas capas se extraen las caracter´ısticas fundamentales de los datos de entrada y se crea con ellas lo que denominaremos mapa de caracter´ısticas. Para ello, las capas de convoluci´on cuentan con un n´ucleo ofiltro (en ingl´es kernel), que viene dado por una matriz bidimensional de menor tama˜no que la imagen y cuyos valores son par´ametros entrenables. Tendremos un filtro para cada canal de color. La funci´on de este filtro es extraer las caracter´ısticas relevantes de las distintas partes de la imagen. Para ello, es necesario definir las dimensiones del filtro y lo que queremos que se desplace el filtro cada vez que lo movamos (stride). Supongamos que tenemos una entrada de tama˜no (1 ×ancho ×alto ×canales), un kernel de tama˜no (dim ×dim) (con dim < m´ın(alto, ancho)) y un desplazamiento con valor s. Comenzamos colocando el filtro sobre los p´ıxeles correspondientes a la esquina superior izquierda del primer canal de color de la imagen y multiplicamos cada valor en el kernel por el correspondiente en esa posici´on en la imagen, sumando todos esos valores y almacenando el resultado. Repetimos la misma acci´on para el resto de canales y sumamos los valores obtenidos, siendo el valor resultante el primer elemento del mapa de caracter´ısticas que obtendremos como salida
22 CAP´ ITULO 2. FUNDAMENTOS Figura 2.4: Obtenci´on de la primera caracter´ıstica del mapa en una imagen con 3 canales de color y un filtro de dimensiones 3 ×3. para esta capa. Volviendo al primer canal de color, ahora desplazamos el filtro sp´ıxeles a la derecha y repetimos la operaci´on. Una vez no tengamos m´as p´ıxeles por la derecha hacia los que desplazar el filtro (ya habremos completado la primera fila del mapa de caracter´ısticas) volvemos a la primera posici´on en la que lo colocamos y lo desplazamos sp´ıxeles hacia abajo, comenzando nuevamente el proceso anterior hasta completar la segunda fila del mapa de caracter´ısticas. Esto es, vamos recorriendo cada capa de la imagen de izquierda a derecha y de arriba a abajo obteniendo la “caracter´ıstica”de cada posici´on que ocupa el filtro (ver figura 2.4). Adem´as, hay una par´ametro m´as que debemos indicar a las capas de convoluci´on y que determinar´a el tama˜no de la salida: el relleno opadding. Este par´ametro puede tomar dos valores: same yvalid. Con el primero de ellos, forzamos a que el mapa de caracter´ısticas tenga las mismas dimensiones que la imagen (en ancho y alto) si el stride fuese 1, ampliando por sus cuatro lados con filas y columnas de ceros las capas de la imagen, para que el filtro pueda desplazarse hasta obtener tantas caracter´ısticas como p´ıxeles ten´ıa la imagen original. Con valid padding, la salida tendr´a por dimensi´on las veces que se haya podido mover el filtro con el stride sen esa direcci´on sin salirse de la imagen y en general el tama˜no del mapa de caracter´ısticas ser´a menor que el de la imagen de entrada. Un par´ametro adicional que reciben las capas de convoluci´on es el n´umero de filtros a aplicar, esto es, indicamos cuantos filtros con las mismas dimensiones vamos a aplicar a la imagen, obteniendo para cada uno de ellos un mapa de caracter´ısticas. Con este par´ametro indicamos tambi´en la cuarta dimensi´on de la salida de la capa. As´ı, en general, si tenemos unos datos de entrada de tama˜no (n×w×h×c) para una capa de convoluci´on con un kernel de tama˜no (k1×k2), un stride sy aplicamos mfiltros a la imagen, la salida de la capa tendr´a un tama˜no, dependiendo del tipo de padding de:
2.3. REDES NEURONALES DE CONVOLUCI ´ ON 23 (a) Valid padding, stride=1 (b) Same padding, stride=1 (c) Valid padding, stride=2 (d) Same padding, stride=2 Figura 2.5: Diferentes configuraciones de la capa de convoluci´on, con valid ysame padding ystrides de 1 y 2. Las im´agenes se toman de [2]. Same padding:n×lw sm×h s×m. Valid padding:n×w−k1+ 1 s×h−k2+ 1 s×m En cuanto al n´umero de par´ametros entrenables de la capa (pesos), este vendr´a dado por el n´umero total de filtros que tengamos multiplicado por el tama˜no de cada filtro. En el caso general, tenemos c×m×k1×k2par´ametros entrenables. Adem´as, a la salida de cada filtro se le suma tambi´en un vector de sesgo, que tiene tama˜no my cuyos valores son par´ametros entrenables tambi´en, por lo que en realidad esta cantidad viene dada por c×m×k1×k2+m. Adem´as de las capas de convoluci´on las redes neuronales cuentan con otro tipo de capas: Capas de pooling: Estas capas se encargan de reducir las dimensiones de las salidas de las capas de convoluci´on, reduciendo la informaci´on que nos ofrece esta salida. As´ı, se toma la salida de la capa de convoluci´on y convertimos la porci´on de imagen cubierta por el kernel en un ´unico valor, que puede ser la media (average pooling) o el m´aximo (max pooling) de los valores presentes en esa porci´on de la salida. Las capas de pooling suelen aparecer entre dos capas de convoluci´on. Capas completamente conectadas (fully connected): Estas capas, habituales en redes neuronales, conectan todas las neuronas de entrada con todas las de salida y en las redes neuronales de convoluci´on suelen aparecer como las ´ultimas capas de la red, para una vez hayamos extra´ıdo las caracter´ısticas de la imagen podemos establecer relaciones entre ellas que faciliten y precisen la predicci´on final sobre los datos. Las redes neuronales de convoluci´on funcionan extraordinariamente bien como modelos de aprendizaje autom´atico con im´agenes de entrada, y en general sirven para todos aquellos datos de entrada en los que queramos capturar dependencias espaciales entre los elementos, que convirtiendo la imagen en un array de datos ser´ıa imposible capturar. M´as adelante, veremos que las redes neuronales de convoluci´on que emplearemos no reciben exactamente im´agenes con 3 canales de color, sino que lo que reciben es una pila de 4 im´agenes con una capa de color (escala de grises), pero que a efectos pr´acticos se corresponde con una imagen con cuatro capas. Al fin y al cabo aqu´ı los colores no son tan importantes y nos centramos m´as en las relaciones existentes entre las im´agenes que nos devuelve el entorno como observaciones en iteraciones sucesivas.
24 CAP´ ITULO 2. FUNDAMENTOS Figura 2.6: Esquema general de una red neuronal de convoluci´on y sus capas. Imagen obtenida de [8]. 2.3.1. Redes de convoluci´on y aprendizaje por refuerzo Dentro del marco de aprendizaje por refuerzo, las redes neuronales modelizar´an la pol´ıtica y su funci´on de valor. As´ı, mantendremos en nuestro modelo dos redes neuronales que reciben la misma entrada: una observaci´on del entorno. Una de ellas tiene tantos valores de salida como acciones posibles pueda realizar el agente en el entorno, indicando el valor de cada salida la probabilidad de realizar esa acci´on en el estado representado por la observaci´on (π(a|s)), es lo que llamamos red de pol´ıtica (policy network). La segunda red tiene una ´unica salida, que indica el valor de la pol´ıtica actual que a la larga queremos maximizar y recibe el nombre de red de valor (value network). Lo que en realidad hacemos es entrenar una red neuronal con una serie de par´ametros θ(los que dec´ıamos que determinaban la pol´ıtica en el algoritmo PPO) que defina la pol´ıtica en ese momento y su valor, con el objetivo de encontrar una pol´ıtica ´optima que maximice la recompensa esperada. Los modelos que dise˜nemos aplicar´an los mismos filtros de convoluci´on a las entradas para cada una de las dos redes, diferenci´andose s´olo en la ´ultima capa (fully connected) que tendr´a distinto n´umero de salidas en cada una de ellas. 2.4. Tensorflow Tensorfow es una biblioteca de c´odigo abierto para computaci´on num´erica que permite desarrollar aplicaciones de machine learning de manera r´apida y sencilla. Expondremos brevemente algunas detalles de su funcionamiento y en la siguiente secci´on veremos como se integra dentro de RLlib. La exposici´on siguiente se extrae de [18] y de la propia documentaci´on1. Desarrollada por Google, usa Python para proporcionar una API sencilla para el desarrollo de aplicaciones y C++ para la ejecuci´on de las mismas. La idea fundamental de Tensorflow son los grafos de datos. Cada nodo en el grafo representa una operaci´on matem´atica y cada arista entre nodos es un array multidimensional llamado 1https://www.tensorflow.org/
2.5. RAY Y RLLIB 25 tensor. Estos nodos y tensores son objetos de Python pero las operaciones se ejecutan en C++, que proporciona un mayor rendimiento de c´alculo. Con Tensorflow podemos modelar, entrenar y ejecutar inferencias sobre modelos de aprendizaje profundo como redes neuronales, usando para ello la API de keras. 2.4.1. Keras Keras2es la API de alto nivel de Tensorflow para crear y entrenar modelos de aprendizaje profundo (redes neuronales). Algunas de sus ventajas son la simplicidad de su interfaz, que es bastante accesible al usuario, y la facilidad para configurar la creaci´on de modelos y para extender modelos previamente creados. Podemos crear modelos de aprendizaje autom´atico, como redes neuronales, de manera sencilla con la idea del grafo de Tensorflow, y as´ı es como lo hace keras. Los nodos se corresponden con cada una de las capas de la red y las aristas con el flujo de datos entre capas. Cada nodo representa las operaciones que hay que hacer sobre los datos de entrada, modeladas por una serie de pesos entrenables, cuyos valores se van ajustando durante las iteraciones de entrenamiento. 2.4.2. Tensorflow Lite Tensorflow Lite es un framework de aprendizaje profundo que permite ejecutar modelos previamente entrenados en Tensorflow en dispositivos en los que se pueden optimizar costes de inferencia (dispositivos m´oviles que usen Android oIOs, sistemas como Rapsberry Pi o aceleradores como la TPU Google Coral). Los modelos de Tensorflow se pueden convertir en modelos de Tensorflow Lite, que tienen un formato especial que permite introducir optimizaciones en los modelos. Una de las ventajas que nos ofrecer´a Tensorflow Lite ser´a la cuantizaci´on de modelos, que en la secci´on 2.8 explicaremos. 2.5. Ray y RLlib Ray es un framework de c´odigo abierto que pretende crear una API universal para aplicaciones distribuidas. Este framework est´a constituido por varias bibliotecas que ofrecen funcionalidades muy diversas. Nosotros nos centraremos en la biblioteca de Python RLlib (Reinforcement Learning Library) que da soporte a aplicaciones relacionadas con el aprendizaje por refuerzo. Ray cuenta con su “n´ucleo”(Ray Core) que gestiona la planificaci´on de tareas de manera distribuida y los recursos disponibles y sobre ´el se desarrollan bibliotecas muy variadas, entre las que se encuentra la ya mencionada RLlib.Ray proporciona primitivas simples para construir estas aplicaciones distribuidas y para poder paralelizar de manera sencilla c´odigo escrito para una sola m´aquina. La API de Ray est´a disponible en Python yJava y experimentalmente en C++. Nosotros usaremos la API de Python a lo largo de todo este trabajo. Frente a otros frameworks y bibliotecas existentes para la computaci´on distribuida, Ray cuenta con la ventaja de la facilidad que ofrece para paralelizar c´odigo que originalmente no se escribi´o con con esta intenci´on. Ray transforma un c´odigo compuesto por clases y funciones en una serie de actores que se realizan tareas, permitiendo as´ı la paralelizaci´on. Esta manera de crear los actores y las tareas bas´andose en en la estructura de clases y funciones del c´odigo simple aporta a Ray esta ventaja que mencion´abamos antes. Aunque Ray proporciona soporte para escalar la ejecuci´on a estructuras distribuidas con varios nodos, nosotros explotaremos su funcionalidad en una sola m´aquina, llevando a cabo esa paralelizaci´on a nivel de recursos de la propia m´aquina. 2https://www.tensorflow.org/guide/keras
32 CAP´ ITULO 2. FUNDAMENTOS Figura 2.10: Google Coral M.2 Accelerator A+E Key, usado para mejorar la inferencia sobre modelos de Tensorflow Lite cuantizados. concretas de este producto pueden consultarse en la propia p´agina de Google Coral13. El uso de la TPU como tarjeta M.2 en lugar de la versi´on USB del acelerador tiene ventajas adicionales, como el hecho de eliminar el sobrecoste de la interconexi´on USB. A todas las ventajas ya mencionadas de estos aceleradores hemos de a˜nadir su bajo precio, que podemos adquirir por un precio de 24.99$en la propia web de Google Coral14. Una vez tengamos el producto hay que seguir una serie de pasos para instalarlo y poder usarlo en nuestro sistema15, que b´asicamente consisten en instalar el driver PCIe, el runtime de Edge TPU y PyCoral, una biblioteca de Python desarrollada sobre Tensorflow Lite que simplifica las interacciones con la TPU. 2.8. Cuantizaci´on de modelos Como hemos visto en la secci´on anterior, la TPU que usaremos para acelerar la inferencia s´olo trabaja con valores enteros de 8 bits, pero los datos de los modelos que entrenemos (pesos de la red neuronal, entradas, salidas, . . . ) van a venir representados por n´umeros en punto flotante de 32 bits. Ser´a necesario hacer una transformaci´on de estos valores float32 aint8 para poder llevar a cabo esta inferencia sobre el acelerador de Google Coral. Este proceso va a ser lo que denominamos como cuantizaci´on y a continuaci´on exponemos con m´as detalle en qu´e consiste y por qu´e funciona, bas´andonos en lo mostrado en [9]. Cuando representamos datos num´ericos en un ordenador siempre lo hacemos de manera discreta, pues el n´umero de valores que podemos representar es finito. Por tanto, no existe una representaci´on para cada n´umero real, y de hecho varios n´umeros son representados de la misma manera. El n´umero de bits del que dispongamos para representar nuestro conjunto de n´umeros determinar´a una mayor o menor precisi´on en la representaci´on, esto es, si podemos o no distinguir dos n´umeros pr´oximos entre s´ı. El proceso de cuantizaci´on consiste en reducir la precisi´on a la hora de representar estos valores, disminuyendo el n´umero de valores disponibles para representar los n´umeros reales y haciendo que el conjunto de n´umeros distintos con el que trabajamos sea menor. Esta reducci´on de la precisi´on trae consigo un decremento evidente en el volumen de memoria necesario para almacenar cada n´umero, pues estamos disminuyendo el n´umero de bits necesarios para representar cada valor, y la posibilidad de almacenar m´as datos en las caches o registros, reduciendo los accesos a memoria. Nosotros aplicaremos la cuantizaci´on a los valores de una red neuronal, que en general suelen ser 13https://coral.ai/docs/m2/datasheet/ 14https://coral.ai/products/m2-accelerator-ae 15https://coral.ai/docs/m2/get-started#1-connect-the-module
2.8. CUANTIZACI ´ ON DE MODELOS 33 bastante robustas frente a peque˜nas perturbaciones de esos valores. Y es que la cuantizaci´on, si se realiza correctamente, s´olo trae consigo una peque˜na p´erdida de precisi´on en la representaci´on de los valores que no afecta al comportamiento general del modelo. Los modelos que entrenemos con RLlib tendr´an una serie de par´ametros dados por valores en punto flotante de 32 bits. Estos bits se dividen en tres conjuntos: signo, exponente y mantisa (ver figura 2.11), y el valor que representan viene dado por la siguiente expresi´on: (−1)b31 ×2(b30b29 ...b23 )2−127 ×(1.b22b21 . . . b0)2 S Exponente Mantisa 31 30 23 22 0 Figura 2.11: Representaci´on de un valor en punto flotante de 32 bits. Como podemos observar, el exponente permite representar un amplio rango de n´umeros mientras que la mantisa fija la precisi´on de los mismos. Para poder usar el modelo en la TPU necesitamos que todos estos valores vengan dados por enteros de 8 bits con signo. Aqu´ı es donde entra en juego la cuantizaci´on, que va a consistir en mapear todos los valores que toman los par´ametros del modelo en valores enteros en el intervalo [-127,128] (que son los que podemos representar con enteros de 8 bits). A la hora de definir esta funci´on hemos de tener en cuenta dos aspectos: 1. Debe ser lineal para poder realizar la transformaci´on inversa de manera directa. 2. El 0 en punto flotante debe estar representado de manera correcta, esto es, debe corresponderse con una de los valores cuantizados. Al cuantizar y descuantizar valores s´olo 256 = 28de ellos volver´an a tomar el mismo valor que ten´ıan antes de la cuantizaci´on. Si aseguramos que el 0 en la representaci´on en punto flotante sea uno de ellos obtendremos mejores resultados al cuantizar, ya que el 0 tiene un significado diferente al resto de valores en muchos de los par´ametros de estas redes. Asignaremos a los valores extremos de los datos sin cuantizar los valores extremos que pueden tomar los datos cuantizados, definiendo as´ı un factor de escala del que ser´an m´ultiplo todos los n´umeros reales en el proceso de descuantizaci´on. Esto es, los extremos m´aximo y m´ınimo de ambos conjuntos de datos se mapean a los del otro y el 0 se mapea a uno de los valores cuantizados, asignando valores en punto flotante al resto de valores cuantizados. As´ı, los valores reales que no tengan un valor entero asignado se redondean al valor m´as pr´oximo que s´ı que lo tenga, y se les asigna ese valor, perdi´endose precisi´on ya que dos valores distintos pero pr´oximos en el modelo sin cuantizar pasan a representar el mismo valor en el modelo cuantizado. Podemos relacionar los valores cuantizados qy descuantizados rpor medio de la siguiente expresi´on: r=s×(q−z), donde zse denomina zero-point y se corresponde con el valor entero que asignamos al valor 0 en punto flotante y ses el factor de escala, que viene dado por el cociente entre el rango de valores reales y los que podemos representar de manera cuantizada, esto es, s=rmax −rmin 128 −(−127) =rmax −rmin 255 , con rmax yrmin los valores m´aximo y m´ınimo del conjunto de valores a cuantizar.
34 CAP´ ITULO 2. FUNDAMENTOS Los modelos que empelaremos en este trabajo estar´an formados por varias capas que se implementan con valores en punto flotante: Tensores con los datos de la capa de convoluci´on, que son constantes. Tensores con los datos de entrada. Tensores con el resultado de la capa para los datos de entrada. Seg´un lo expuesto antes, es tarea f´acil convertir los par´ametros propios de la red (pesos) en valores cuantizados una vez entrenada, pues sabemos de antemano el rango de valores que van a tomar. Los valores que no son constantes (como los de las entradas y salidas de cada capa) no se pueden conocer con exactitud y no se puede proceder del mismo modo que con los pesos. Sin embargo, s´ı que se puede estimar el rango en el que se van a mover observando los valores que toman en distintas ejecuciones. Esto es, tras una serie de ejecuciones con valores en punto flotante, podemos estimar el rango de valores que han tomado y considerar que este va a ser el rango aproximado en cualquier ejecuci´on y realizar la cuantizaci´on con estos valores. As´ı, esta informaci´on para la cuantizaci´on puede obtenerse de dos formas: durante y despu´es del entrenamiento. Dado que la fase de entrenamiento de nuestros modelos la vamos a realizar con RLlib optamos por la segunda opci´on, al no tener esa facilidad para configurar este entrenamiento cuantizado. Para ello, una vez entrenados los modelos, durante el proceso de cuantizaci´on, se ejecutar´an unas cuantas inferencias con un conjunto de datos de entrada para el problema que resuelve el modelo, y los valores que tomen la distintas entradas y salidas en estas ejecuciones se utilizar´an para llevar a cabo una correcta cuantizaci´on. M´as adelante se detallar´a la implementaci´on concreta de este proceso en nuestros modelos.
Cap´ıtulo 3 Implementaci´on El objetivo del trabajo ser´a evaluar el rendimiento de los procesos de entrenamiento y de inferencia sobre modelos de aprendizaje por refuerzo. Realizaremos estas evaluaciones sobre diferentes arquitecturas hardware, que nos permitir´an variar los recursos empleados. Con todo ello, obtendremos una serie de conclusiones en las que veremos qu´e es mejor en cada caso y los beneficios e inconvenientes de cada una de las opciones que consideremos. 3.1. Descripci´on de los entornos de pruebas Tras haber realizado unas primeras pruebas para familiarizarse con el entorno de Ray y la biblioteca RLlib, el grueso del trabajo se realiza de manera remota en tres servidores del Departamento de Arquitectura de Computadores y Autom´atica de la Facultad de Inform´atica de la Universidad Complutense de Madrid. Describiremos a continuaci´on las caracter´ısticas de cada uno de estos sistemas: Servidor esfinge: El servidor cuenta con 16 CPUs Intel(R) Xeon(R) CPU E5-2670 01de 2.60GHz y una GPU GeForce GTX 10802. Servidor volta1: Este servidor es el que m´as recursos nos ofrece: 40 CPUs Intel(R) Xeon(R) Gold 6138 CPU3de 2 GHz y dos GPUs de ´ultima generaci´on: GeForce RTX 30904y Tesla V100-PCIE-32GB5. Servidor artecslab001: En este servidor es donde se encuentra instalado el acelerador Google Coral sobre el que realizaremos el an´alisis de la inferencia. Adem´as, cuenta con 16 CPUs Intel(R) Core(TM) i9-9900K CPU de 3.60GHz6. 1https://ark.intel.com/content/www/es/es/ark/products/64595/intel-xeon-processor-e5-2670-20m-cache-\ 2-60-ghz-8-00-gt-s-intel-qpi.html 2https://www.nvidia.com/es-la/geforce/products/10series/geforce-gtx-1080/ 3https://ark.intel.com/content/www/es/es/ark/products/120476/intel-xeon-gold-6138-processor-27-5m-\ cache-2-00-ghz.html 4https://www.nvidia.com/es-es/geforce/graphics-cards/30-series/rtx-3090/ 5https://www.nvidia.com/es-es/data-center/tesla-v100/ 6https://ark.intel.com/content/www/es/es/ark/products/186605/intel-core-i9-9900k-processor-16m-cache-\ up-to-5-00-ghz.html 35
36 CAP´ ITULO 3. IMPLEMENTACI ´ ON 3.2. Descripci´on de los modelos Evaluaremos el rendimiento de los procesos de entrenamiento e inferencia de aprendizaje por refuerzo usando para ello la biblioteca RLlib de Ray, de la que hemos mostrado algunas de las caracter´ısticas y utilidades que ofrece en el cap´ıtulo anterior. Emplearemos el algoritmo PPO para el proceso de entrenamiento de nuestros modelos y el agente interaccionar´a con un entorno propio de RLlib que describimos a continuaci´on. 3.2.1. Descripci´on del entorno del agente RLlib permite la integraci´on de entornos de otras bibliotecas como Gym dentro de su funcionalidad. Para la realizaci´on de los experimentos, el entorno Gym del que partimos es el denominado Pong-v07, inspirado en el videojuego de Atari Inc. que simulaba una partida de tenis de mesa entre dos jugadores por medio de im´agenes bidimensionales y que se lanz´o originalmente en 1972 [17]. En este juego, un jugador marca un punto cuando la pelota sobrepasa la l´ınea vertical en la que se mueve la pala del otro jugador y cada episodio concluye cuando uno de los dos jugadores alcanza los 21 puntos. Gym se basa en la idea de este videojuego para modelar su entorno, en el que las observaciones las constituyen im´agenes RGB de 210×160 p´ıxeles (lo que da lugar a observaciones de tama˜no (210,160,3)) y que est´an formadas por una representaci´on esquem´atica de la partida, en la que se aprecian dos rect´angulos simulando las palas de los jugadores (el jugador que controla el agente que entrenamos aparece a la izquierda de las im´agenes) y la puntuaci´on de cada uno de ellos en la parte superior. Existen seis acciones posibles que puede tomar el agente en cada momento para la interacci´on con el entorno, sin embargo a efectos pr´acticos s´olo hay tres acciones distintas. Podemos consultar las acciones disponibles para este entorno de la siguiente manera: 1import gym 2 3env = gym. make ( 'Pong - v0 ') 4print ( env . unwrapped . get_action_meanings () ) 5 6>> ['NOOP','FIRE','RIGHT ','LEFT','RIGHTFIRE ','LEFTFIRE '] Las acciones NOOP yFIRE mantienen en la misma posici´on la pala del agente, LEFT yLEFTFIRE la mueven a la izquierda (hacia arriba en la imagen) y RIGHT yRIGHTFIRE hacen lo propio hacia la derecha (hacia abajo en la imagen). Cada acci´on vendr´a representada por un entero entre 0 y 5, y podemos indicar al agente que realice una de ellas con el m´etodo step. Esto har´a que se realice sobre el entorno la acci´on indicada kveces consecutivas, siendo kun n´umero seleccionado al azar del conjunto {2,3,4}. Las recompensas obtenidas por cada acci´on individual pueden tomar tres valores distintos: 0.0 si ninguno de los jugadores suma un punto tras esa acci´on, 1.0 si el jugador controlado por el agente suma punto y −1.0 si el el punto lo suma el jugador controlado por la “m´aquina”. En cada episodio la recompensa total obtenida es la suma de las recompensas de cada una de las acciones que lo constituyen y por tanto es un valor entre −21.0 y 21.0 que representa la diferencia de puntos con la que acaba la partida, siendo este valor positivo si el jugador que gana es el que controla el agente y negativo en caso contrario. Partiendo de este entorno como base, el que realmente usa RLlib para modelar el problema de aprendizaje cuando indicamos Pong-v0 como entorno en la inicializaci´on de los agentes es una modificaci´on del mismo. En este entorno modificado las observaciones tienen la forma (dim, dim, 4), donde dim es un par´ametro de configuraci´on que podemos especificar al agente en su creaci´on y que por defecto toma el valor 84. Este entorno es el resultado de aplicar una serie de envolturas (wrappers) al 7https://gym.openai.com/envs/Pong-v0/
3.2. DESCRIPCI ´ ON DE LOS MODELOS 37 Figura 3.1: Comparaci´on entre las im´agenes del entorno Gym original y las del entorno de RLlib equivalente creado con la funci´on wrap deepmind(). entorno original de Gym. Podemos crear este entorno con la funci´on wrap deepmind()8indicando el entorno Gym sobre el que aplicar las envolturas y la dimensi´on de las im´agenes de salida. Esta serie de envoltorios act´uan realmente como un preprocesador de las im´agenes del entorno original de Gym. Las im´agenes en formato RGB del entorno original de Gym de transforman en esa misma imagen pero en escala de grises (haciendo uso de las utilidades de la biblioteca cv29), pasando de tener tres a s´olo un canal de color. Posteriormente, las im´agenes se redimensionan para darles forma de cuadrado con la dimensi´on especificada como n´umero de p´ıxeles por lado, y se guarda una cola con las cuatro ´ultimas im´agenes procesadas en este formato (de ah´ı el 4 de la tercera dimensi´on de las im´agenes), que se ir´a actualizando tras cada observaci´on (ver figura 3.1). As´ı, las observaciones que recibir´a nuestro algoritmo para aprender ser´an conjuntos de cuatro im´agenes en escala de grises, correspondientes con las cuatro ´ultimas observaciones obtenidas del entorno original de Gym correctamente redimensionadas. Usaremos una red neuronal de convoluci´on con la que procesaremos estos datos no s´olo para capturar las dependencias espaciales entre los elementos de la imagen sino tambi´en las temporales entre estados sucesivos del entorno. 3.2.2. Modelo de Tensorflow Keras Los agentes que creemos durante la etapa de entrenamiento e inferencia tendr´an asociado un modelo de Tensorflow Keras. Para nuestro problema, y al ser las observaciones im´agenes, usaremos una red neuronal de convoluci´on en nuestro modelo. Estas redes en RLib se implementan como objetos de la clase VisionNetwork10. En la configuraci´on del agente podemos dar valor a par´ametros espec´ıficos de este modelo como la dimensi´on de la entrada (que como acabamos de ver se usaba tambi´en para crear el entorno con el que interaccionar´a el agente) y la configuraci´on de las capas de convoluci´on que procesar´an las im´agenes de entrada. De esta forma, se crea una red neuronal con dos salidas correspondientes a las salidas de la pol´ıtica (policy network) y la funci´on de valor (value network). La salida de la pol´ıtica vendr´a dada por seis valores que se corresponden con cada una de las seis acciones que podemos realizar en el entorno Pong-v0. Cada salida toma un valor en punto flotante, 8https://github.com/ray-project/ray/blob/master/rllib/env/wrappers/atari_wrappers.py#L288 9https://pypi.org/project/opencv-python/ 10https://github.com/ray-project/ray/blob/master/rllib/models/tf/visionnet.py
38 CAP´ ITULO 3. IMPLEMENTACI ´ ON al que se le aplica la funci´on softmax, dada por: σ:R6→[0,1]6 σ(z)j=ezj P6 i=1 ezi y que mueve los valores obtenidos al intervalo [0,1] para que realmente representen la probabilidad de que elegir cada una de las acciones sea la que maximice la recompensa final. Podemos modificar la configuraci´on del modelo de los agentes que vayamos a crear de manera sencilla. Cada agente que creemos en RLlib recibir´a un diccionario config con los par´ametros necesarios para su configuraci´on. Una de las claves de este diccionario es model, que contiene como valor otro diccionario con la configuraci´on del modelo que entrenar´a nuestro agente. En este diccionario model indicamos con los valores asociados a las claves dim yconv filters la dimensi´on que queremos que tengan los datos de entrada y la configuraci´on de las capas de convoluci´on que aplicaremos a las im´agenes, respectivamente. El primero de estos valores, la dimensi´on, la indicamos con un entero y la red resultante recibir´a datos de entrada de tama˜no (dim, dim, 4). La configuraci´on de las capas de convoluci´on la indicamos mediante una lista, en la que cada uno de sus elementos representa a una capa. A su vez, vamos a especificar los par´ametros para cada capa como una lista con tres elementos: out size: entero que indica el n´umero de filtros con las mismas caracter´ısticas que aplicaremos en esa capa (y que determina la ´ultima dimensi´on de la salida de esa capa). kernel: lista de dos elementos con la que indicamos las dimensiones del filtro de convoluci´on que aplicaremos. stride: entero que indica el desplazamiento en p´ıxeles de cada filtro de convoluci´on. Cuando configuremos manualmente los filtros de convoluci´on hemos de tener en cuenta que la concatenaci´on de los mismos debe producir una salida de tama˜no (B, 1,1, X), donde Xes el n´umero de filtros de convoluci´on de la ´ultima capa. El tama˜no de entrada por defecto de 84 ×84 de Ray trae ya asociada una configuraci´on de capas de convoluci´on. Para el resto de tama˜nos de entrada tendremos que especificar de manera manual la configuraci´on de los mismos. Rllib crea para el agente un modelo de keras, con una serie de capas conv2D correspondientes a los filtros especificados. Todas las capas, a excepci´on de la ´ultima, son creadas con el par´ametro padding=‘same’ mientras que la ´ultima de ellas se configura con padding=‘valid’. El ancho y el alto de cada capa obedecen a la f´ormula dim salida = ceil(dim entrada/stride), salvo para la ´ultima, cuyos valores vienen dados por dim salida = (dim entrada−kernel)/stride+1. Para que la dimensi´on de esta ´ultima capa sea 1 tal y como requiere RLlib, tendremos que hacer que el kernel del ´ultimo filtro de convoluci´on sea de igual tama˜no que el ancho y alto de las entradas que llegan a esta ´ultima capa. A continuaci´on mostramos un ejemplo de c´odigo en el que se refleja c´omo crear un agente PPO que recibe im´agenes de 168 ×168 p´ıxeles y para el que indicamos tambi´en el par´ametro asociado a los filtros de convoluci´on. 1import ray 2import ray . rllib . agents . ppo as ppo 3 4ray. init () 5config = ppo . DEFAULT_CON FIG . copy () 6 7config['model '][ 'dim '] = 168 8config['model '][ 'conv_filters'] = \ 9[16 ,[8 ,8] ,4] ,[32 ,[4 ,4] ,2] ,[32 ,[4 ,4] ,2] ,[256 ,[11 ,11] ,1] 10 11 agent = ppo . PPOTrainer ( config , env ='Pong - v0 ')
3.2. DESCRIPCI ´ ON DE LOS MODELOS 39 (a) Visualizaci´on de la red neuronal con la aplicaci´on Netron. (b) Recompensa media por iteraci´on para los modelos 1, 3 y 4 tras 11000 iteraciones de entrenamiento Figura 3.2: Representaci´on de la red neuronal de keras que se crea para procesar im´agenes de tama˜no 168. Se crea as´ı un modelo de keras con 4 capas de convoluci´on que se corresponden con la configuraci´on especificada. La figura 3.2 muestra la red neuronal que se crea (3.2a) como modelo de keras y que podemos visualizar con la herramienta Netron11. Adem´as, mostramos tambi´en un resumen de la estructura de capas de esta red neuronal, que podemos visualizar invocando la funcion summary() sobre el modelo de keras (si tenemos un agente de RLlib podemos hacer esto con agent.get policy().model.base model.summary()). Adem´as, en (3.2b) podemos ver tambi´en el n´umero de par´ametros entrenables (pesos) que hay en cada capa y el total del modelo. 3.2.3. Modelos propuestos Para poder evaluar el rendimiento en diferentes modelos, uno de los par´ametros que tendremos en cuenta ser´a el tama˜no de las im´agenes que se toman del entorno, pues queremos ver como se gestionan los recursos y c´omo var´ıan los tiempos de inferencia y entrenamiento si los modelos procesan im´agenes m´as o menos grandes. Proponemos as´ı un modelo que recibe im´agenes de 84 ×84 p´ıxeles, ya que este es el valor por defecto en RLib. Adem´as, consideraremos otros cinco modelos m´as en los que el tama˜no de entrada es el doble o el triple del que ofrece RLlib por defecto (168 y 252, respectivamente). Respecto a las capas de convoluci´on, no tenemos manera de decidir que configuraci´on se ajusta mejor a las im´agenes de nuestro modelo, por lo que propondremos varias opciones para cada tama˜no de entrada y al final seleccionaremos aquella configuraci´on con la que obtengamos mejores resultados. 11https://netron.app/
40 CAP´ ITULO 3. IMPLEMENTACI ´ ON Para el modelo con entradas de dimensi´on 84 directamente tomamos la configuraci´on de las capas de convoluci´on que nos da RLlib por defecto. La tabla 3.1 muestra las caracter´ısticas de los seis modelos escogidos, que difieren entre ellos en el tama˜no de los datos de entrada y en la estructura de sus capas de convoluci´on. Modelo Tama˜no de la entrada Filtros de convoluci´on Par´ametros entrenables 1 84 ×84 [16,[8,8],4],[32,[4,4],2],[256,[11,11],1] 2.009.447 2 168 ×168 [16,[16,16],8],[32,[4,4],2],[256,[11,11],1] 2.034.023 3 252 ×262 [16,[8,8],4],[16,[8,8],4],[32,[4,4],2],[256,[8,8],1] 1.108.359 4 168 ×168 [16,[8,8],4],[32,[4,4],2],[32,[4,4],2],[256,[11,11],1] 2.042.279 5 252 ×252 [16,[8,8],4],[32,[4,4],2],[32,[4,4],2],[256,[16,16],1] 4.254.119 6 168 ×168 [16,[8,8],4],[32,[4,4],2],[256,[21,21],1] 7.252.327 Cuadro 3.1: Modelos propuestos, con el tama˜no de las im´agenes que recibe como entrada, la configuraci´on de las capas de convoluci´on que indicamos mediante el par´ametro conv filters y el n´umero de par´ametros entrenables (pesos) de la red neuronal resultante. 3.2.4. Modelos seleccionados De los seis modelos propuestos anteriormente, vamos a seleccionar tres de ellos para continuar con el an´alisis en el que se va a centrar este trabajo. Para ello, vamos a seleccionar un modelo por cada tama˜no de entradas, as´ı podremos realizar el an´alisis de tiempo y rendimiento teniendo datos para modelos distintos que trabajan con im´agenes de diferente tama˜no. Para realizar esta selecci´on, ejecutamos 2000 iteraciones de entrenamiento sobre cada uno de los modelos para observar las recompensas que se obtienen al cabo de este tiempo y tener un criterio m´as que nos ayude a determinar qu´e tres modelos elegir. Al fin y al cabo, hemos elegido la configuraci´on de las capas de convoluci´on sin guiarnos por ning´un criterio, por lo que haciendo esto intentamos determinar de manera emp´ırica qu´e configuraci´on de las propuestas comienza a mejorar sus recompensas m´as r´apidamente. Estas iteraciones de entrenamiento se llevan a cabo en el servidor esfinge con 8 rollout workers y haciendo uso de la GPU GTX. Como podemos observar en la gr´afica 3.3a s´olo hay tres de los seis modelos que mejoran la recompensa media tras 2000 iteraciones de entrenamiento. Adem´as, cada uno de ellos recibe como datos de entrada im´agenes de un tama˜no distinto, por lo que elegimos los modelos 1, 3 y 4 como aquellos que emplearemos para obtener el resto de resultados y conclusiones de este trabajo. El estado de las recompensas medias por iteraci´on tras 11000 iteraciones de entrenamiento ya s´olo para estos tres modelos seleccionados puede verse en la gr´afica de la imagen 3.3b. Tras 2000 iteraciones de entrenamiento vemos como el modelo 1 es el que m´as r´apido empieza a mejorar sus recompensas y el que transcurridas estas iteraciones obtiene mayor valor para la recompensa media, mientras que los modelos 2 y 3 necesitan m´as iteraciones para comenzar a ofrecer valores mayores en las recompensas. Observando los resultados tras 11000 iteraciones vemos que tras una primera fase de crecimiento m´as r´apido el valor de las recompensas se estanca y crece m´as lentamente. De ahora en adelante, no analizaremos m´as la calidad del aprendizaje de los distintos modelos. El hecho de variar los recursos del sistema sobre el que entrenamos los modelos no deber´ıa influir en la capacidad de aprendizaje de los mismos, pues el algoritmo que se est´a ejecutando no var´ıa, ´unicamente lo realizamos sobre soportes distintos. As´ı, la calidad del aprendizaje depende ´unicamente de la estructura del modelo en s´ı (estructura de sus capas), por lo que este an´alisis que hemos realizado para seleccionar los tres modelos sobre los que analizar el rendimiento de los procesos cubrir´ıa esta otra parte de evaluaci´on de la capacidad de aprendizaje y de las recompensas obtenidas.
3.3. AN ´ ALISIS DEL RENDIMIENTO 41 (a) Recompensa media por iteraci´on para los modelos 1,2,3,4,5 y 6 tras 2000 iteraciones de entrenamiento (b) Recompensa media por iteraci´on para los modelos 1, 3 y 4 tras 11000 iteraciones de entrenamiento Figura 3.3: Evoluci´on de las recompensas medias para los modelos propuestos y para los seleccionados para los experimentos. Obs´ervese que, dado que este entrenamiento se realiz´o en varias etapas de 1000 iteraciones que continuaban desde el estado de la anterior, en los valores inmediatos a las iteraciones m´ultiplo de 1000 se observan oscilaciones importantes en el valor de las recompensas y que se corresponden con las iteraciones de calentamiento de cada tanda de iteraciones de entrenamiento. 3.3. An´alisis del rendimiento Detallaremos aqu´ı qu´e aspectos tendremos en cuenta para analizar el rendimiento de los modelos y c´omo obtendremos los datos que nos servir´an para obtener diversas conclusiones, tanto para la fase de entrenamiento como la de inferencia. Propondremos una serie de experimentos de entrenamiento e inferencia, cada uno de los cuales contar´a con una configuraci´on espec´ıfica y tras ejecutarlos, compararemos y analizaremos los resultados obtenidos. 3.3.1. Implementaci´on de los experimentos de entrenamiento Entrenamos los modelos haciendo uso exclusivamente de las funcionalidades que nos ofrece RLlib para ello. Se dise˜nan unos scripts que nos van a permitir llevar a cabo un n´umero espec´ıfico de iteraciones de entrenamiento, guardando un checkpoint con el estado del modelo tras cada una de ellas. Esto proceso parte de la base expuesta en [7]. Para el entrenamiento nos ayudaremos del script train ppo.py, que nos permite ajustar diversos par´ametros para configurar cada uno de los experimentos de entrenamiento (modelo a entrenar, recursos a utilizar, n´umero de iteraciones de entrenamiento, direcci´on de la que cargar los datos si ya hab´ıamos entrenado antes...). Para ver m´as detalles consultar el ap´endice A.1. Realizaremos varios experimentos con cada uno de los tres modelos, en los que variaremos los recursos empleados para el proceso de entrenamiento. Estos experimentos se realizan en el servidor volta1 en el que disponemos de 40 CPUs y 2 GPUs. Los par´ametros de la configuraci´on de recursos que vamos a variar ser´an fundamentalmente tres: GPUs a utilizar: consideraremos cuatro opciones respecto al uso de las GPUs del sistema. As´ı, entrenaremos sin hacer uso de las GPUs (configuraci´on none), usando s´olo la GPU Nvidia Ge-Force RTX (configuraci´on gpu0, usando s´olo la GPU Nvidia Tesla v100-PCIE (configuraci´on gpu1) y usando ambas (configuraci´on both).
48 CAP´ ITULO 3. IMPLEMENTACI ´ ON dataset creator.py21. Generaremos de esta manera un dataset con im´agenes de tama˜no dim para uno de los modelos, que se almacenar´a en el fichero dataset name.npy. 1env = wrappers . wrap_deepmind ( gym . make ('Pong - v0 '), dim = dim ) 2obs = env . reset () 3with open ( dataset_name + '. npy ','wb ') as f: 4for _in range (500) : 5np . save (f, obs) 6action = env . action_space . sample () 7obs , _ , _ , _ = env . step ( action ) Una vez somos capaces de generar el dataset creamos el modelo cuantizado. Para ello, comenzamos leyendo los datos del dataset anteriormente generado: 1images = [] 2with open (dataset_dir , 'rb ') as f: 3for _in range (500) : 4images . append ( np . load (f )) A continuaci´on, definimos la funci´on representative data gen(), que devuelve un generador con una muestra de 100 de los datos del conjunto: 1def representative_data_gen (): 2for data in tf . data . Dataset . from_tensor_slices (( images )). batch (1) . take (100) : 3yield [ tf . dtypes . cast (data , tf . float32 ) ] Ahora cargamos el modelo de keras que previamente hab´ıamos guardado en un fichero con extensi´on .h5 y lo convertimos a uno de Tensorflow Lite pero cuantiz´andolo. Para ello: 1model = tf. keras . models . load_model ( h5_dir , custom_objects ={ 'tf ': tf }) 2converter = tf. lite . TFLiteConverter . from_keras_model ( model ) 3converter . optimizations = [ tf .lite . Optimize . DEFAULT ] 4converter . representative_dataset = representative_data_gen 5converter . target_spec . supported_ops = [ tf . lite . OpsSet . TFLITE_BUILTINS_INT8 ] 6converter . inference_in put _typ e = tf . uint8 7converter . infe renc e_output_typ e = tf . uint8 8tflite_model_quant = converter . convert () 9open( tflite_dir , " wb"). write ( tflite _mo del _quant ) Todo este proceso lo llevamos acabo con el script quantizer.py22. A continuaci´on, y para poder ejecutar el modelo en la TPU debemos compilarlo haciendo uso de la herramienta Edge TPU Compiler23, que crear´a a partir del modelo .tflite cuantizado un modelo compatible con la TPU Google Coral. Podemos realizar esta tarea con la interfaz de l´ınea de comandos edegtpu compiler, por ejemplo con edgetpu compiler model quant.tflite, que generar´a un archivo model quant edgetpu.tflite que ya s´ı que reunir´a todos los requisitos para poder ser ejecutado en la TPU. Como consecuencia de todo este proceso generamos varias versiones de cada modelo: modelX.h5: modelo de Tensorflow keras modelX.tflite: modelo de Tensorflow Lite equivalente, con tensores con valores float32. modelX quant.tflite: modelo de Tensorflow Lite cuantizado, con tensores con valores int8. modelX quant edgetpu.tflite: modelo de Tensorflow Lite cuantizado y preparado para poder ejecutar inferencias sobre ´el en la TPU. La figura 3.5 muestra todos los scripts que ser´an necesarios para poder llevar a cabo los experimentos de inferencia sobre la TPU. Los ficheros que contienen los modelos se encuentran en el directorio exported models24. 21https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/dataset_creator.py 22https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/quantizer.py 23https://coral.ai/docs/edgetpu/compiler/ 24https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/tree/main/exported_models
3.3. AN ´ ALISIS DEL RENDIMIENTO 49 checkpoints/.../ checkpoint-11999 model1.h5 model saver.py model1.tflite tflite converter.py dataset model1.npy model1 quant.tflite model1 quant edgetpu.tflite quantizer.py train ppo.py dataset creator.py edgetpu compiler int8 float32 scripts de Python Figura 3.5: Relaci´on entre los distintos archivos que contienen modelos guardados y los scripts de Python que realizan las conversiones y guardan el modelo resultante. Una vez tenemos los tres modelos de TFLite podemos realizar inferencias sobre ellos y recolectar m´etricas sobre el tiempo empleado para su posterior an´alisis. Siguiendo el ejemplo de clasificaci´on de im´agenes25 accesible en el repositorio de Google Coral, creamos el script rollout coral.py26, que cargar´a un modelo de TFLite cuantizado y compilado para poder ser ejecutado sobre la TPU y realizar´a tantos pasos de inferencia como indiquemos, reportando el tiempo empleado en realizarlo. Ser´a necesario crear un int´erprete de TFLite27 en el que habilitamos la ejecuci´on sobre la TPU por medio de un delegado de TensorflowLite28. Creamos este int´erprete con la funci´on que muestra el siguiente fragmento de c´odigo, d´onde previamente hemos indicado las bibliotecas necesarias para la inferencia sobre la TPU en funci´on del sistema operativo sobre el que estemos ejecutando: 1EDGETPU_SHARED_LIB = { 2'Linux ':'libedgetpu . so .1 ', 3'Darwin':'libedgetpu .1. dylib ', 4'Windows':'edgetpu . dll ' 5}[ platform . system () ] 6 7 8 25https://github.com/google-coral/pycoral/blob/master/examples/classify_image.py 26https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/rollout_coral.py 27https://www.tensorflow.org/api_docs/python/tf/lite/Interpreter 28https://www.tensorflow.org/lite/performance/delegates
50 CAP´ ITULO 3. IMPLEMENTACI ´ ON 9def make_interpreter ( model_file ): 10 model_file , * device = model_file . split ( '@') 11 return tflite . Interpreter ( 12 model_path = model_file , 13 experimental_delegates =[ 14 tflite . load_delegate ( EDGETPU_SHARED_LIB , 15 {'device': device [0]} if device else {}) 16 ]) La estructura general del script para realizar las inferencias seguir´a la idea del que nos proporciona RLlib para la misma tarea (rollout.py). Partiremos de un modelo y especificaremos o bien el n´umero de pasos de inferencia o bien el n´umero de episodios completos (partidas de tenis de mesa finalizadas) que queremos ejecutar sobre ese modelo. En cada paso de inferencia tendremos una imagen obtenida del entorno con el que estamos interaccionando, la colocaremos como tensor de entrada al modelo y lo invocaremos. De las dos salidas que produce el modelo nos quedaremos con la primera de ellas, que se corresponde con la salida de la red de la pol´ıtica y que cuenta con seis valores, cada uno de los cuales (tras aplicar la funci´on softmax) representa la probabilidad de que tomando esa acci´on en ese estado mejoremos la recompensa global. Por ello, y tal y como se hace en RLlib, seleccionaremos como siguiente acci´on a tomar el m´aximo de estos valores. Una vez obtenida la acci´on, la ejecutamos sobre el entorno, obteniendo la siguiente observaci´on a procesar (que representa el estado del entorno tras realizarse esa acci´on), la recompensa asociada a esa acci´on y si hemos concluido o no el episodio. En todo momento debemos asegurar que los valores de las im´agenes deben ser del tipo aceptado por el modelo para sus entradas, por lo que antes de colocar el tensor como entrada del modelo hacemos la conversi´on de tipos si es necesario. Cada paso de inferencia ejecuta la siguiente secuencia de instrucciones: 1start = time . perf_counter () 2interpreter . invoke () 3inference_time = time . perf_counter () - start 4episode_times . append ( inference_time ) 5 6output_data = interpreter . get_tensor ( output_details [0][ 'index ']) 7 8action = np. argmax ( output_data ) 9 10 # Step environment and get reward and done information 11 image , reward , done , _ = env . step ( action ) 12 13 # Place new image as the new model 's input 14 image = image [np. newaxis , ...] 15 if input_details [0][ 'dtype '] == np . float32 : 16 image = np . float32 ( image ) 17 if input_details [0][ 'dtype '] == np . uint8 : 18 image = np . uint8 ( image ) 19 20 interpreter . set_tensor ( input_details [0][ 'index '], image ) Mediremos los tiempos de inferencia sobre cada uno de los 3 modelos cuantizados sobre la TPU. Adem´as, y para ver la ganancia al usar este acelerador, elaboraremos un script similar para ejecutar inferencias sobre los modelos de TFLite en la CPU. Este script,rollout tflite.py29 s´olo se diferencia de rollout coral.py en que no hablita la ejecucic´on sobre la TPU por medio de un delegado al crear el int´erprete de TFLite. 29https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/rollout_tflite.py
Cap´ıtulo 4 Resultados El objetivo del trabajo era analizar el rendimiento en diferentes arquitecturas de los procesos de entrenamiento e inferencia de modelos de aprendizaje por refuerzo. En este cap´ıtulo vamos a presentar los resultados obtenidos tras realizar varios experimentos, implementados siguiendo las pautas descritas en el cap´ıtulo anterior, y las conclusiones que de los resultados se derivan. As´ı, trataremos por separado los resultados obtenidos en cada uno de los dos procesos (entrenamiento e inferencia) considerados. 4.1. Resultados del proceso de entrenamiento Analizaremos aqu´ı el valor de varias m´etricas que Ray genera para cada iteraci´on de entrenamiento. Estas m´etricas se referir´an al uso de recursos y a los tiempos empleados, pero no a la capacidad de aprendizaje de los modelos, pues eso es algo que ya se tuvo en cuenta a la hora de seleccionar los modelos representativos. Y es que, aunque variemos las configuraciones de recursos en los distintos experimentos, la capacidad de aprendizaje de cada modelo ser´a la misma, pues el algoritmo no var´ıa (la red neuronal que entrenamos es la misma), y las ´unicas diferencias son el mayor o menor grado de paralelizaci´on de algunas de sus partes y el soporte hardware sobre el que se desarrolla este proceso. Es por ello que nos vamos a centrar en analizar aquellos factores del proceso de entrenamiento que s´ı se ven afectados cuando variamos la configuraci´on de recursos del entrenamiento, esto es, el tiempo de ejecuci´on de sus distintas fases y la utilizaci´on de los recursos de c´omputo y almacenamiento durante el proceso. Presentamos los resultados gr´aficamente para que resulte m´as c´omoda su interpretaci´on y la comparaci´on entre ellos, obtenidos a partir de los que podemos encontrar en formato tabular (archivos csv) en la carpeta ray results1del repositorio de Github de este proyecto. Para cada experimento encontramos la salida que generar´a Ray, entre ellos los ficheros progress.csv, que contienen los valores de las m´etricas por cada iteraci´on. Los valores que se presentan y analizan a continuaci´on son la media de los obtenidos para cada una de las 20 iteraciones de entrenamiento, desechando las 3 o 4 primeras en el caso de los experimentos que usan alguna de las GPUs, que no reportan valores num´ericos (el valor aparece como NaN) y se denominan iteraciones de calentamiento. 4.1.1. Uso de los recursos disponibles Para evaluar el uso de los recursos empleados durante el proceso de entrenamiento, estudiamos los valores de las siguientes m´etricas relativas al uso de las CPUs, las GPUs y la memoria RAM del sistema, obteniendo con todas ellas porcentajes de utilizaci´on: 1https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/tree/main/ray results 51
52 CAP´ ITULO 4. RESULTADOS cpu util percent: porcentaje medio de utilizaci´on del conjunto de las CPUs del sistema durante cada iteraci´on de entrenamiento, por lo que los valores que obtendremos se encontrar´an en el intervalo 0-100. Estos valores se obtienen apoy´andose en la funci´on cpu percent2de la biblioteca de Python psutil. ram util percent: porcentaje medio de utilizaci´on de la memoria RAM total del sistema durante cada iteraci´on de entrenamiento, as´ı que los valores nuevamente se encontrar´an entre 0 y 100. Ray usa la funci´on virtual memory3de la biblioteca psutil para obtener los valores asociados a esta m´etrica. gpu util percentX: porcentaje medio de utilizaci´on de la GPU con identificador X durante cada iteraci´on de entrenamiento. Viene dada por un valor en el intervalo 0-1. vram util percentX: porcentaje medio de utilizaci´on de la memoria de la GPU con identificador X durante cada iteraci´on de entrenamiento (se mide el porcentaje del tiempo en el que se est´an realizando operaciones de lectura o escritura sobre la memoria). Viene dada por un valor en el rango 0-1. Las dos ´ultimas m´etricas hacen uso de la biblioteca GPUtil4(que ser´a necesario que tengamos instalada en nuestro sistema), obteniendo informaci´on de las GPUs disponibles con GPUtil.getGPUs() y para la lista de GPUs devueltas obtiene el porcentaje de uso (con el atributo load) y de utilizaci´on de la memoria de la GPU (con el atributo memoryUtil) de cada una de ellas. Es por eso, que al realizar las pruebas en el servidor volta1 obtendremos resultados separados para cada una de las dos GPUs disponibles en el sistema. Uso de la CPU En la figura 4.1 podemos ver el porcentaje de utilizaci´on de la CPU para los tres modelos y para las distintas configuraciones probadas. Algunas conclusiones que obtenemos del an´alisis de los diagramas son las siguientes: 1. En primer lugar, observamos que, en general y para todas las configuraciones probadas, el porcentaje de uso de la CPU es bajo (el valor m´aximo se alcanza para el modelo 3 entrenando sin GPUs y est´a pr´oximo al 16 %). Hemos de tener en cuenta que el sistema sobre el que estamos realizando los experimentos dispone de una gran capacidad de c´omputo y que puede dar soporte a las tareas necesarias para el entrenamiento de modelos con RLlib sin mayor problema. 2. El primer patr´on que destacamos y que parece repetirse en los tres modelos es que aumentar el n´umero de workers aumenta tambi´en el porcentaje de uso de las CPUs, como queda reflejado en los resultados obtenidos para el entrenamiento con ambas GPUs y 2, 4, 8 y 16 workers. Esto tiene sentido, pues Ray asigna una CPU a cada worker cuando planifica el proceso de entrenamiento. 3. Observamos tambi´en que el uso de la GPU s´olo para el driver (y que los workers interaccionen con el entorno haciendo uso s´olo de la CPU) conlleva un sobrecoste en el porcentaje de uso de la CPU, que por ligero que sea, no deja de repetirse en los tres modelos y para las tres configuraciones de GPUs probadas. 4. Respecto a los entrenamientos realizados haciendo uso de la CPU, los resultados son independientes para cada modelo. Mientras que en el modelo 1 este porcentaje es menor que en muchos 2https://psutil.readthedocs.io/en/latest/#psutil.cpu_percent 3https://psutil.readthedocs.io/en/latest/#psutil.virtual_memory 4https://github.com/anderskm/gputil
4.1. RESULTADOS DEL PROCESO DE ENTRENAMIENTO 53 (a) Uso de la CPU para el modelo 1 (b) Uso de la CPU para el modelo 3 (c) Uso de la CPU para el modelo 4 (d) Comparaci´on del uso de la CPU para los tres modelos Figura 4.1: Porcentaje medio por iteraci´on de entrenamiento de uso de la CPU del servidor volta1 para diferentes configuraciones de los modelos 1, 3 y 4 tras 20 iteraciones de entrenamiento. de los experimentos con GPUs y el mismo n´umero de workers (8), en los modelos 3 y 4 s´ı que este valor es el mayor de los que se reportan. El modelo 3 es en el que la diferencia es m´as significativa, y este hecho puede deberse a que el tama˜no de las im´agenes que se procesan es mayor, y este procesamiento podr´ıa verse acelerado en mayor medida con el uso de las GPUs. 5. Analizando la gr´afica 4.1d, vemos que se sigue un patr´on claro en los valores que obtenemos para cada experimento para cada uno de los modelos. Matizamos dos aspectos: Si observamos el experimento en el que no se usa ninguna de las GPUs, vemos que si ordenamos los modelos de mayor a menor porcentaje de CPU utilizado, tenemos en primer lugar al modelo 3, seguido del 4 y por ´ultimo el 1, orden que se corresponde tambi´en con el del tama˜no de las im´agenes que toma cada modelo como entrada. En el resto de experimentos (en los que usamos una o ambas GPUs) vemos que el patr´on es distinto. As´ı, observamos que los datos para los modelos 1 y 4 van casi a la par en muchos casos (hay m´as experimentos en los que el uso para el modelo 1 es ligeramente mayor) pero el modelo 3 es siempre el que reporta un porcentaje de utilizaci´on m´as bajo. Esta clasificaci´on se corresponde con la que podr´ıamos hacer si miramos el n´umero de par´ametros entrenables
54 CAP´ ITULO 4. RESULTADOS de cada modelo (tabla 3.1), pues los modelos 1 y 4 tienen un n´umero en torno a los 2 millones de pesos entrenables y este dato para el modelo 1 ronda los 1.1 millones. Estas observaciones refuerzan la hecha en el punto anterior, y es que el hecho de introducir la GPU optimiza el procesamiento de estos datos de entrada de mayor tama˜no, pasando el modelo 1 de ser el que peores resultados obten´ıa al que reporta unos porcentajes m´as bajos de uso. Adem´as, una vez integramos el uso de las GPUs, la tendencia es que el porcentaje de utilizaci´on de la CPU sea mayor si el n´umero de pesos que debemos ajustar en el modelo lo es, y este hecho no se ve condicionado por el tama˜no de los datos de entrada. En definitiva, podemos concluir que el uso de la CPU en un servidor como en el que hemos realizado los entrenamientos no supone un problema, pues los valores obtenidos son relativamente bajos. Adem´as, para aquellos modelos que procesen im´agenes de mayor tama˜no, el uso de la CPU se ve reducido si a˜nadimos tambi´en el uso de GPUs durante el proceso. Uso de la memoria RAM La figura 4.2 muestra el porcentaje medio de utilizaci´on de la memoria RAM del sistema durante el proceso de entrenamiento para los distintos modelos y configuraciones que venimos considerando. a continuaci´on detallamos algunas observaciones que se desprenden de estos datos: 1. En este caso, las gr´aficas de los 3 modelos siguen un patr´on id´entico, por lo que el hecho de comparar un experimento con otro no va a depender del modelo concreto. 2. Vemos que los valores m´as bajos en todos los casos se obtienen cuando s´olo usamos las CPUs para el entrenamiento. 3. Adem´as, para cada configuraci´on de GPUs, el hecho de usarlas s´olo para el driver reduce en m´as de la mitad este porcentaje frente a cuando las usamos tambi´en para los workers. 4. Como ocurr´ıa con el uso de la CPU, vemos una tendencia creciente, m´as marcada en este caso, pasando a multiplicar casi por cuatro el valor para 16 workers si lo comparamos con el de 2 workers. Para los 3 modelos el experimento con 16 workers utiliza, de media, m´as de el 50 % de la memoria RAM disponible. 5. Se observan tambi´en ligeras diferencias en los resultados dependiendo de la configuraci´on de GPU empleada en el experimento, observ´andose unos valores menores cuando usamos la GPU 1 (Tesla-v100). 6. Comparando los resultados obtenidos para los tres modelos, que aparecen reflejados en la gr´afica 4.2d, observamos que las diferencias son poco significativas entre los distintos modelos, si bien en todos los experimentos el orden de los modelos de mayor a menor porcentaje de utilizaci´on de la RAM es el mismo: modelo 3, seguido del 4 y el modelo 1 por ´ultimo. Este orden es el mismo que si ordenamos de mayor a menor tama˜no de las entradas de la red neuronal. Agrupando todas las ideas anteriores, podemos afirmar que usar GPUs para el entrenamiento trae consigo un sobrecoste en la capacidad de RAM ocupada, que se acent´ua m´as cuando los workers hacen tambi´en uso de las GPUs y que se va incrementando seg´un a˜nadimos m´as workers que hacen uso de ella. Adem´as, el uso es ligeramente mayor para los modelos que trabajan con observaciones de mayor tama˜no.
4.1. RESULTADOS DEL PROCESO DE ENTRENAMIENTO 55 (a) Uso de la memoria RAM para el modelo 1 (b) Uso de la memoria RAM para el modelo 3 (c) Uso de la memoria RAM para el modelo 4 (d) Comparaci´on del uso de la memoria RAM para los tres modelos Figura 4.2: Porcentaje medio de uso de la memoria RAM del servidor volta1 para diferentes experimentos realizados con los modelos 1, 3 y 4 y diferentes configuraciones durante 20 iteraciones de entrenamiento. Uso de las GPUs y su memoria La figura 4.3 muestra los resultados obtenidos relativos a la utilizaci´on de las GPUs disponibles en el servidor volta1 durante las iteraciones de entrenamiento y tambi´en sobre el porcentaje de tiempo que se accede a la memoria de estas GPUs. Las m´etricas que representamos aqu´ı son gpu util percent0 (utilizaci´on de la GPU RTX), gpu util percent1 (utilizaci´on de la GPU Teslav100), vram util percent0 (accesos a memoria en la GPU RTX) y vram util percent1 (accesos a memoria en la GPU Tesla-v100), que, aunque Ray las reporte con valores en el intervalo [0,1], se representan tomando valores en [0,100] para facilitar su interpretaci´on. Analizando los datos podemos extraer las siguientes conclusiones: 1. El porcentaje de uso de las GPUs es bastante elevado, sobre todo cuando se usa s´olo una de ellas compartida entre el driver y los workers (entre 50 % y 60 %). Adem´as, cuando su uso se reserva s´olo al driver, como es l´ogico, su porcentaje de utilizaci´on baja, pues durante la fase de sampling no se usa la GPU.
56 CAP´ ITULO 4. RESULTADOS (a) Uso de las GPUs para el modelo 1 (b) Porcentaje del tiempo que se accede a la memoria de las GPUs para el modelo 1 (c) Uso de las GPUs para el modelo 3 (d) Porcentaje del tiempo que se accede a la memoria de las GPUs para el modelo 3 (e) Uso de las GPUs para el modelo 4 (f) Porcentaje del tiempo que se accede a la memoria de las GPUs para el modelo 4 Figura 4.3: Porcentaje medio de uso (izquierda) y porcentaje medio del tiempo que se accede a la memoria (derecha) de ambas GPUs del servidor volta1 para diferentes experimentos realizados con los modelos 1, 3 y 4 y diferentes configuraciones durante 20 iteraciones de entrenamiento.
4.1. RESULTADOS DEL PROCESO DE ENTRENAMIENTO 57 2. En los experimentos en los que se usan ambas GPUs tanto para el driver como para los workers el porcentaje de uso de la GPU RTX es bastante mayor que el de la GPU Tesla-v100. Cuando excluimos a los rollout workers del uso de las GPUs, el porcentaje de uso de ambas s´ı que se equilibra m´as. 3. En cuanto al porcentaje de tiempo que se accede a la memoria de las GPUs este disminuye notablemente en todos los casos cuando la GPU se usa s´olo para el driver, lo que nos indica que cuando usamos la GPU para los rollout workers los accesos a memoria de estos son bastante significativos. 4. En general, el porcentaje de acceso a memoria cuando se usa ´unicamente la GPU RTX es mayor que cuando s´olo se usa la GPU Tesla-v100. 5. Al igual que ocurr´ıa con el porcentaje de utilizaci´on de las GPUs, el porcentaje de accesos a memoria es mayor en la GPU RTX que en la Tesla-v100 cuando ambas se usan simult´aneamente. 6. Por ´ultimo observamos que si bien el n´umero de workers que se creen no tiene un efecto claro en el uso de la GPUs (en los modelos 1 y 4 s´ı que se observa cierto incremento si aumentamos el n´umero de workers), en el porcentaje de accesos a memoria s´ı que observamos una correlaci´on clara entre aumentar el n´umero de workers y que aumenten los accesos a memoria. Concluyendo, podemos afirmar que el porcentaje de uso de las GPUs es alto en la mayor´ıa de los casos, que los workers hacen un uso considerable de la misma cuando se les permite usarla, incrementando los accesos a memoria seg´un aumentamos los workers que se crean y que el hecho de usar ambas GPUs simult´aneamente crea una mayor carga de trabajo en la GPU RTX que en la GPU Tesla-v100, teniendo en cuenta el porcentaje total de la capacidad de cada una que est´a en uso durante este proceso. 4.1.2. Tiempos empleados Analizaremos en este apartado los valores obtenidos en los diferentes experimentos realizados para los temporizadores de las distintas fases de cada iteraci´on del algoritmo de entrenamiento PPO. As´ı, consideraremos los valores de estas cuatro m´etricas que reporta Ray y que se corresponden con cada una de las cuatro fases que tiene la implementaci´on que se hace en RLlib del algoritmo PPO (ver imagen 2.8): sample time ms: tiempo (en milisegundos) que emplean los rollout workers en obtener del entorno los datos necesarios para la actualizaci´on de la pol´ıtica. Cuando analizamos la implementaci´on que hacia RLlib del algoritmo ve´ıamos que en total los workers deb´ıan realizar train batch size interacciones con el entorno, tomando cada worker series de rollout fragment length pasos. Esta fase se ejecuta de manera paralela entre los distintos workers, en caso de haberlos. load time ms: tiempo (en milisegundos) que se emplea en cargar y concatenar las experiencias recolectadas por los workers en el driver antes de comenzar la fase de actualizaci´on de la pol´ıtica. learn time ms: tiempo (en milisegundos) que emplea el driver en completar una etapa de actualizaci´on del modelo que estamos entrenando (computar una serie de iteraciones (por defecto 30, vienen dadas por el par´ametro de configuraci´on num sgd iter) del algoritmo de descenso de gradiente) que nos dar´a unos nuevos valores para los pesos de la red neuronal de convoluci´on del modelo. update time ms: tiempo (en milisegundos) que se emplea en actualizar el modelo en los workers (actualizar los pesos en la red neuronal) tras finalizar la etapa de aprendizaje en el driver.
64 CAP´ ITULO 4. RESULTADOS 2. Cada modelo parece seguir su propio patr´on en lo que respecta a estos valores. 3. S´ı que se aprecia que en general la tendencia parece ser ascendente si aumentamos el n´umero de workers, aunque aun en los modelos 1 y 4 el resultado con 4 workers es mejor que con 2. 4. Respecto al uso de la GPU exclusivamente para el driver o la compartici´on de la misma entre workers ydriver lso tiempos var´ıan dependiendo del modelo y de la GPU empleada. 5. Comparando los tres modelos tampoco se observa una correlaci´on clara entre el n´umero de valores a actualizar (pesos) y el tiempo que se tarda en llevar a cabo esta actualizaci´on, siendo en muchos experimentos mucho mayor el tiempo para el modelo 3, que, aunque sea el que trabaja durante todo el proceso con datos de entrada mayores, es el que tiene pesos que actualizar en su red neuronal. As´ı, concluimos que los resultados obtenidos para el tiempo de actualizaci´on de los modelos arrojan que esta fracci´on del tiempo es muy peque˜na comparada con el resto y que los valores no dependen de manera clara de la configuraci´on de recursos o del modelo en cada experimento. Tiempo promedio por iteraci´on La figura 4.9 recoge datos acerca de los tiempos empelados en cada iteraci´on para los distintos experimentos. Las gr´aficas de la izquierda muestran los datos absolutos y su desglose en las cuatro etapas que hemos analizado previamente, donde se ha omitido el experimento en el que no se usan GPUs para que la diferencia entre los datos representados sea apreciable a simple vista. A la dercha, se representa la distribuci´on de los cuatro valores analizados para cada experimento, mostr´andose como porcentajes sobre el total del tiempo empleado, incluy´endose ya aqu´ı el experimento no gpus 8 workers puesto que lo que representamos son porcentajes y no valores absolutos. Analizando detenidamente los datos, podemos remarcar: 1. La fase de aprendizaje es la que ocupa la mayor parte del tiempo de cada iteraci´on de entrenamiento, seguida de la fase de recogida de experiencias del entorno (sampling), que tambi´en ocupa una fracci´on considerable. M´as residuales son los tiempos empleados para la carga de los datos de la fase sampling en el driver y la de actualizaci´on de los modelos, ocupando esta ´ultima una fracci´on despreciable del tiempo total de las iteraciones. 2. Vemos que, en general, usar la GPU s´olo para el driver mejora los tiempos obtenidos. Este hecho era algo que ya ven´ıa produci´endose para cada uno de los tiempos considerados de manera independiente, por lo que, como es normal, se repite cuando consideramos el tiempo total. 3. Nuevamente y como ya hab´ıamos indicado en algunos casos, cuando variamos el n´umero de workers, los mejores resultados se obtienen cuando este n´umero es 4. 4. El uso de una u otra GPU parece no tener influencia en los resultados obtenidos. Sin embargo, para los modelos 1 y 3 vemos que usar ambas simult´aneamente mejora el dato de tiempo que se obtiene con cada una de ellas por separado. 5. Mirando ahora las gr´aficas de porcentajes de distribuci´on del tiempo vemos como cuando no usamos GPU pr´acticamente la totalidad del tiempo se emplea en la fase de aprendizaje. 6. Comparando los valores num´ericos que toman los datos de tiempo para cada modelo, observamos que el que m´as tiempo consume por iteraci´on es el modelo 3, a continuaci´on el 4 y por ´ultimo el 1. Esto es, los modelos que porcesan im´agenes m´as grandes son aquellos que m´as tiempo tardan en entrenarse.
4.1. RESULTADOS DEL PROCESO DE ENTRENAMIENTO 65 (a) Tiempo medio por iteraci´on para el modelo 1 (b) Distribuci´on (en %) del tiempo promedio por iteraci´on por fases para el modelo 1 (c) Tiempo medio por iteraci´on para el modelo 3 (d) Distribuci´on (en %) del tiempo promedio por iteraci´on por fases para el modelo 3 (e) Tiempo medio por iteraci´on para el modelo 4 (f) Distribuci´on (en %) del tiempo promedio por iteraci´on por fases para el modelo 4 Figura 4.9: Tiempo total promedio por iteraci´on de entrenamiento (izquierda) y distribuci´on de ese tiempo (en porcentaje) en cada una de las etapas de las iteraciones de entrenamiento (derecha) para distintas configuraciones probadas sobre los modelos 1, 3 y 4, tras 20 iteraciones de entrenamiento en el servidor volta1.
66 CAP´ ITULO 4. RESULTADOS En definitiva, vemos aqu´ı corroboradas todas las conclusiones que ven´ıamos extrayendo en los an´alisis anteriores. Destacamos as´ı la importancia del uso de las GPUs para reducir abruptamente el tiempo de entrenamiento (pues se reduce el tiempo de la fase de aprendizaje que ocupa la mayor parte del tiempo en todos los casos), el uso de la GPU s´olo para el driver para reducir tambi´en estos tiempos (se reduce notablemente el tiempo de sampling) y que cuando usamos 4 workers los tiempos son en general mejores. 4.1.3. An´alisis del uso que se hace de las distintas CPUs Analizamos en este apartado los resultados obtenidos en los distintos experimentos que present´abamos en la tabla 3.3 y que ten´ıan por objetivo evaluar si ten´ıa alg´un efecto sobre el coste en tiempo y la utilizaci´on de recursos las restricciones que pod´ıamos imponer a Ray en la utilizaci´on de las CPUs del sistema, recogiendo las m´etricas que venimos tratando para experimentos en los que no imponemos restricciones en este sentido (como los que ya hemos analizado), otros en los que indicamos a Ray el n´umero de CPUs con los que debe planificar las tareas que crea para la ejecuci´on del algoritmo y por ´ultimo probamos a restringir las CPUs del sistema que se pueden usar a s´olo unas espec´ıficas de manera externa a Ray. En primer lugar podemos observar en la figura 4.10 como afectan estas configuraciones al porcentaje total de uso de la CPU del sistema que se usa y a la ocupaci´on de la memoria RAM durante el proceso de entrenamiento. (a) Utilizaci´on media de la CPU para los modelos 1, 3, y 4. (b) Ocupaci´on media de la memoria RAM para los modelos 1, 3 y 4. Figura 4.10: Utilizaci´on de los recursos del servidor volta1 para las distintas configuraciones de uso de las CPUs probadas sobre los modelos 1, 3 y 4, tras 20 iteraciones de entrenamiento. Analizando los resultados, vemos a grandes rasgos que las distintas configuraciones, en lo que a la gesti´on del uso de las CPUs respecta, no afectan en el porcentaje de utilizaci´on total de las CPUs y de la memoria RAM de manera significativa. S´ı que es cierto que fij´andonos en la gr´afica 4.10a observamos que el porcentaje de uso de la CPU total del sistema es ligeramente menor en aquellos casos en los que indicamos a Ray que el n´umero de CPUs de las que dispone es de 9 (una para el driver y el resto para los workers), pero la variaci´on es m´ınima (en torno al 1-2 %). En cuanto a la ocupaci´on de memoria RAM las variaciones son pr´acticamente imperceptibles. En las gr´aficas de la figura 4.11 vemos el efecto que tiene esta manera de configurar el uso de las CPUs sobre el tiempo promedio por iteraci´on, desglosado en fases. Como podemos observar, los resultados dependen de cada modelo y configuraci´on de GPUs. As´ı, podemos destacar:
4.1. RESULTADOS DEL PROCESO DE ENTRENAMIENTO 67 (a) Tiempo medio por iteraci´on para el modelo 1 (b) Tiempo medio por iteraci´on para el modelo 3 (c) Tiempo medio por iteraci´on para el modelo 4 (d) Comparaci´on entre el tiempo promedio por iteraci´on para los modelos 1, 3 y 4 Figura 4.11: Tiempo total promedio por iteraci´on de entrenamiento para distintas configuraciones de uso de las CPUs probadas sobre los modelos 1, 3 y 4, tras 20 iteraciones de entrenamiento en el servidor volta1. 1. Para el modelo 1 observamos que las variaciones en los tiempos son pr´acticamente nulas, si bien en algunos casos se ve un ligero sobrecoste si restringimos el uso de las CPUs a nueve de ellas en concreto. 2. Respecto a los resultados obtenidos para el modelo 4 comprobamos que tanto cuando usamos la GPU RTX como cuando usamos ambas, los resultados son ligeramente mejores cuando no establecemos restricci´on en el uso de las CPUs. Sin embargo, para la GPU Tesla-v100 esta mejora se observa cuando indicamos a Ray que tiene que trabajar s´olo con 9 CPUs pero no forzamos a que sean ningunas en concreto. 3. Analizando los resultados para el modelo 3, observamos que en los casos en los que se usa una de las dos GPUs las diferencias son m´ınimas, si bien hay un suave incremento del tiempo en los experimentos m´as restrictivos. Si ponemos la mirada en los experimentos con ambas GPUs vemos que hay una tendencia a la alza en el tiempo total empleado seg´un aumentamos las restricciones al uso de las CPUs (m´as de 10s de diferencia por iteraci´on entre los experimentos both gpus 9 cpus no cpu limit 8 workers yboth gpus 9 cpus set affinity 8 workers). Esta tendencia se puede observar de manera muy ligera en las tres ´ultimas columnas de la gr´afica 4.11a, se acent´ua un poco m´as en 4.11c y la diferencia se hace bastante notable en 4.11b, lo que
68 CAP´ ITULO 4. RESULTADOS nos hace pensar que el tama˜no de los datos con los que trabaja el algoritmo quiz´as sea un factor que influya en esta apreciaci´on, siendo necesaria mayor flexibilidad en el uso de las CPUs si las observaciones que recibimos del entorno son mayores. En definitiva, podemos que concluir que salvo en excepciones, el hecho de restringir el uso de las CPUs a unas en concreto reduce muy ligeramente el porcentaje de uso total de las CPUs del sistema y no supone un sobrecoste elevado en el tiempo de ejecuci´on. Aun as´ı, los mejores resultados de tiempo se obtienen siempre cuando no imponemos restricciones en el uso de las CPUs, pero si por alg´un motivo necesitamos restringir la ejecuci´on de Ray a unas cuantas CPUs el sobrecoste que obtendr´ıamos en la mayor´ıa de los casos ser´ıa asumible y no supondr´ıa mayor problema. 4.1.4. Conclusiones extra´ıdas de los experimentos de entrenamiento Poniendo en com´un las observaciones y conclusiones que hemos ido extrayendo sobre todo el estudio del entrenamiento, podemos afirmar que no hay una configuraci´on que optimice tanto el uso de recursos como el tiempo empleado por iteraci´on respecto a las dem´as. La principal consideraci´on a tener en cuenta es realizar el entrenamiento haciendo uso de una o varias GPUs. Usar las GPUs s´olo para el driver y que los workers no hagan uso de ellas produce mejores resultados que cuando estas son compartidas tanto por el driver y los workers. Esto nos incita a pensar que la interacci´on con el entorno es m´as r´apida cuando no usamos GPU para ella, resultado que m´as adelante veremos corroborado cuando analicemos la inferencia. Respecto al n´umero de workers a usar, fijaremos el n´umero en 4, pues es donde se optimiza el tiempo medio por iteraci´on. Respecto a la localizaci´on de las CPUs, si es posible, no restringimos su uso, pero en caso de tener que forzar la ejecuci´on, tendremos un ligero sobrecoste que en la mayor´ıa de los casos ser´a asumible. As´ı, proponemos realizar los entrenamientos con un agente con 4 workers, con tantas GPUs como tenga el sistema para el driver y ninguna GPU para los workers. 4.2. Resultados de la inferencia de modelos Analizamos aqu´ı los resultados obtenidos en los distintos experimentos realizados para probar la inferencia de modelos previamente entrenados. Dividimos este an´alisis en dos: por un lado la inferencia en RLlib haciendo uso de las funcionalidades que nos ofrece su API, y por otro, la inferencia sobre el acelerador Google Coral. 4.2.1. Inferencia en RLlib La figura 4.12 muestra las mediciones de tiempo realizadas en los experimentos de inferencia de modelos. Para cada modelo, se ejecutan 10 episodios completos de inferencia haciendo uso del script rollout.py ya descrito en el cap´ıtulo anterior. Los resultados que aparecen en la gr´afica se obtienen como la media de los tiempos todos los pasos de inferencia ejecutados durante los 10 episodios. Aunque el script usado mida el tiempo en segundos, por claridad decidimos representarlo en milisegundos. Durante la ejecuci´on de los experimentos observamos que la creaci´on de workers no tiene mucho sentido en este caso, pues el script de rollout no paraleliza las ejecuciones y las inferencias se van ejecutando de manera secuencial. A la vista de las gr´aficas podemos extraer una serie de conclusiones:
4.2. RESULTADOS DE LA INFERENCIA DE MODELOS 69 Figura 4.12: Tiempos en milisegundos que se tarda en ejecutar un paso de inferencia para las distintas configuraciones probadas y los modelos 1, 3 y 4. 1. Como ya hemos anticipado, el hecho de crear workers no modifica el tiempo de ejecutar las inferencias, pues estas se ejecutan de manera secuencial sobre un ´unico worker que se crea en el driver. As´ı, cuando ejecutemos estas inferencias indicaremos en la configuraci´on que el n´umero de workers a crear es 0 para que no se creen hilos y procesos innecesarios. 2. Vemos en todos los casos que el uso de la GPU1 reduce el tiempo respecto al uso de ambas GPUs y el uso de ´unicamente la GPU0. Esto se debe a que durante la ejecuci´on de una serie de episodios de inferencia, siempre que se usa la GPU0, el primer episodio es m´as lento que el resto e introduce esta ligera penalizaci´on. Esta iteraci´on de “calentamiento” no aparece cuando usamos la GPU1. El hecho de realizar varios episodios de inferencia en cada experimento nos ha permitido observar esta peculiaridad, ya que si s´olo hubi´esemos ejecutado un episodio podr´ıamos haber pensado que este era el tiempo real que se tardaba en ejecutar las inferencias con la GPU0 y la diferencia de tiempos ser´ıa mucho mayor. A´un as´ı la peque˜na diferencia que se observa en las gr´aficas nos advierte de este hecho y de la necesidad de descartar los tiempos de la primera ejecuci´on cuando trabajemos con la GPU0. 3. El experimento para el que obtenemos mejores resultados para los tres modelos es aquel en el que la inferencia se realiza sin el uso de GPUs. Esto no es una novedad, pues es algo que ya se hab´ıa observado analizando los tiempos de interacciones con el entorno (figura 4.4), de donde extra´ıamos que los tiempos eran menores cuando los workers (que eran los que realizaban esta interacci´on) no hac´ıan uso de las GPUs. Y es que en ambos casos lo que estamos haciendo es ejecutar inferencias sobre el modelo, por lo que es l´ogico que hagamos la misma apreciaci´on. Este hecho podr´ıa deberse a que dadas las caracter´ısticas y el tama˜no de los modelos no sea conveniente desplazar la realizaci´on de los c´alculos de las inferencias a la GPU. 4. Comparando resultados obtenidos para los distintos modelos, comprobamos que hay una ligera diferencia condicionada por el tama˜no de los datos con los que trabaja cada uno de ellos. As´ı, el tiempo ser´a suavemente mayor si las im´agenes que tomamos del entorno lo son. En conclusi´on, la mejor configuraci´on para ejecutar inferencias sobre modelos previamente entrenados con RLlib implica no crear workers (no desempe˜nan ninguna funci´on) y ejecutarlas s´olo sobre las CPUs del sistema.
70 CAP´ ITULO 4. RESULTADOS 4.2.2. Inferencia sobre el acelerador Google Coral Figura 4.13: Tiempo por cada ejecuci´on de inferencia para distintas configuraciones en el servidor artecslab001 y los modelos 1, 3 y 4. Modelo RLlib TF Lite TF Lite Cuantizado TPU 1 1.01225 0.57362 10.08402 0.41419 3 1.29343 1.63730 70.47366 1.02737 4 1.45621 1.25892 43.15306 0.75465 Cuadro 4.1: Tiempos (en milisegundos) para los distintos experimentos realizados sobre el servidor artecslab001. Analizaremos aqu´ı el resultado de ejecutar inferencias sobre las redes neuronales cuantizadas y compiladas para su correcta ejecuci´on sobre la TPU Google Coral. Adem´as, para poder medir las ventajas del uso de este acelerador, compararemos estos valores con los datos que obtendremos de ejecutar inferencias sobre el modelo de Tensorflow Lite sin cuantizar (valores en punto flotante de 32 bits) y de ejecutar inferencias dentro del framework de RLlib con la configuraci´on para la que mejores resultados obten´ıamos en el apartado anterior (usando s´olo CPUs). Adem´as, probaremos tambi´en a ejecutar el modelo cuantizado sobre las CPUs del sistema, aunque este modelo no est´e pensado para ser ejecutado sobre una arquitectura de este tipo. La figura 4.13 y la tabla 4.1 muestran los resultados obtenidos para los diferentes experimentos probados. Debido a la gran diferencia num´erica que se observa entre los valores resultantes de la inferencia del modelo de Tensorflow Lite cuantizado sobre las CPUs y el resto de resultados, estos se omiten en la gr´afica. Podemos extraer una serie de conclusiones sobre estos experimentos: 1. En primer lugar, observamos como la inferencia sobre la TPU obtiene los mejores valores de tiempo. 2. Salvo para el modelo 3, el tiempo de inferencia en RLlib es mayor que el del modelo de TFLite.
4.2. RESULTADOS DE LA INFERENCIA DE MODELOS 71 3. Los tiempos de inferencia en la TPU tambi´en se van a ver condicionados por el tama˜no de las entradas que toma el modelo, obteni´endose as´ı un tiempo mayor para el modelo 3, seguido del 4 y del 1. 4. Observamos que la CPU en flotante (modelos de TFLite en float32) es much´ısimo m´as r´apida que en enteros (modelos de Tensorflow cuantizados en int8), aunque probablemente esto se deba a que la CPU haga uso de sus unidades vectoriales. Con esto, podemos concluir que hemos conseguido uno de los objetivos fundamentales del trabajo: evaluar el rendimiento de modelos de aprendizaje por refuerzo sobre la TPU Coral y compararlo con la ejecuci´on de esos modelos sobre la CPU y dentro del framework de RLlib. Adem´as, esta comprobaci´on ha sido bastante satisfactoria, pues hemos conseguido ejecutar un modelo entrenado en RLlib sobre un dispositivo de ultrabajo consumo y precio como es la TPU Google Coral donde adem´as los tiempos de inferencia son bastante menores, liberando adem´as el resto de recursos del sistema durante este proceso. El hecho de ejecutar el modelo cuantizado conlleva una peque˜na p´erdida de precisi´on en la representaci´on de los valores obtenidos como salida, como ya anticip´abamos en el primer cap´ıtulo de este trabajo. Mostramos a continuaci´on un ejemplo de salida obtenida tras aplicar el mismo modelo y para los mismos datos de entrada en la CPU con valores en punto flotante de 32 bits y en la TPU con enteros de 8 bits (los valores de la TPU que mostramos son los resultantes de “descuantizar” los que realmente nos devuelve el modelo, haciendo uso de los valores de zero point yscale con los que se ha realizado la cuantizaci´on). 1En TPU con uint8 : 2 3---- output [0] ---- 4INT8 DATA 5[[[[ 7.150586 -4.8753996 12.1884985 10.319595 -0.08125666 13.894889 ]]]] 6---- output [1] ---- 7INT8 DATA 8[[-0.70262796]] 9 10 - En CPU con float32 : 11 12 ---- output [0] ---- 13 FLOAT DATA 14 [[[[ 7.969496 -5.3353543 13.295782 11.171885 -0.17144847 15.070765 ]]]] 15 ---- output [1] ---- 16 FLOAT DATA 17 [[ -0.6038263]] Vemos que pese a las ligeras diferencias que se observan, los valores est´an bastante pr´oximos entre s´ı, y lo que es m´as importante, en ambos casos el orden los mismos en la salida 0 se mantiene, por lo que la acci´on a tomar, que viene determinada por el ´ındice del mayor valor en esta salida, es la misma. Destacar finalmente que la primera de las salidas (output[0]) se corresponde con la salida de la red de pol´ıtica (policy network) e indica la probabilidad de que la acci´on en cada posici´on sea la que a la larga maximice la recompensa del episodio (estos valores podemos llevarlos al intervalo [0,1] para que realmente representen el valor de una probabilidad, mediante la funci´on softmax, pero el orden de los mismos se sigue manteniendo). La segunda de las salidas (output[1]) se corresponde con el resultado de la red de valor (value network) e indica la recompensa esperada para la secuencia completa de acciones hasta concluir el episodio, que se usa ´unicamente en el entrenamiento del modelo para actualizar los par´ametros de la red neuronal.
72 CAP´ ITULO 4. RESULTADOS
Cap´ıtulo 5 Conclusiones Con la realizaci´on de este trabajo de fin de grado se ha completado un estudio exhaustivo del rendimiento de las aplicaciones de aprendizaje por refuerzo en diferentes arquitecturas hardware, analizando tanto el coste en tiempo como la utilizaci´on de recursos y el consumo de potencia. Los resultados obtenidos ser´an bastante ´utiles a la hora de dise˜nar los procesos de inferencia y entrenamiento para resolver problemas de aprendizaje por refuerzo, ya que bas´andonos en ellos podemos configurar estos procesos para acelerar su ejecuci´on o reducir la utilizaci´on de recursos. Adem´as, hemos integrado en el trabajo una serie de bibliotecas que ofrecen recursos espec´ıficos para la paralelizaci´on de los algoritmos o el modelado de los entornos de aprendizaje, proporcionando scripts que permiten llevar a cabo modelizaciones, entrenamientos e inferencias dentro del marco del aprendizaje por refuerzo y que se pueden ejecutar en diferentes arquitecturas hardware. Analizando la lista de objetivos que propon´ıamos en la introducci´on de esta memoria observamos que se ha cumplido en buena medida con todos ellos: 1. Se ha conseguido modelar el escenario de aprendizaje por refuerzo, integrando un entorno Gym dentro de la funcionalidad de RLlib. 2. Se han propuesto varios experimentos de entrenamiento. Para ello se han desarrollado varios scripts que los ejecutaban haciendo uso de RLlib. Esta parte ha involucrado adem´as un an´alisis profundo de la librer´ıa para tratar de explotar al m´aximo su funcionalidad, siendo necesario muchas veces para ello una lectura exhaustiva del c´odigo fuente de la misma y la interacci´on con otros usuarios a trav´es del foro oficial de Ray1para tratar de aclarar algunas cuestiones. 3. Se ha evaluado el entrenamiento para modelos que interaccionaban con el entorno Pong-v0 de Gym, pero que difer´ıan entre s´ı en el tama˜no de las im´agenes que recib´ıan de este entorno. Adem´as, se propusieron varias opciones para cada uno de los valores del tama˜no de entrada que quer´ıamos evaluar, estableci´endose un criterio para elegir los representantes por cada tama˜no. Quiz´as, quede como futuro trabajo evaluar tambi´en este rendimiento en diferentes entornos (en los que los valores de las recompensas y el rango de acciones sea distinto). 4. Una vez realizados los experimentos propuestos y obtenida informaci´on acerca de los mismos (la cual se encuentra tambi´en el repositorio Github de este trabajo), se ha conseguido organizar, estructurar y representar gr´aficamente esta informaci´on, analizando los datos y extrayendo conclusiones de los mismos. 1https://discuss.ray.io/categories 73
80 AP´ ENDICE A. FUNCIONAMIENTO DE LOS SCRIPTS DE PYTHON full train(checkpoint root, agent, n iter, save file, n ini = 0, header = True, restore = False, restore dir = None): ejecuta una serie de iteraciones de entrenamiento sobre un agente dado y devuelve una estructura con sus resultados, adem´as de guardar esta informaci´on en unos ficheros .csv y.json. Recibe como argumentos: •checkpoint root:string con la ruta del directorio en el que queremos que se vayan guardando los checkpoints para cada paso de entrenamiento realizado. •agent: agente de RLlib sobre el que ejecutar las iteraciones de entrenamiento. •n iter: entero indicando el n´umero de iteraciones de entrenamiento del algoritmo concreto del agente (en nuestro caso PPO) a ejecutar. •save file: ruta al archivo en el que queremos que se almacenen la informaci´on del entrenamiento. Mediante un string indicamos la ruta a un archivo sin extensi´on, as´ı se crear´an dos archivos en esa ruta con extensiones .json y.csv. •n ini: entero indicando el n´umero de la ´ultima iteraci´on realizada, su valor por defecto es 0, indicando que aun no hemos comenzado a entrenar ese modelo. •header: booleano indicando si hay que a˜nadir la l´ınea de cabecera con los nombres de las columnas al fichero .csv con los datos del entrenamiento. Su valor por defecto es True indicando que si es la primera vez que estamos entrenando el modelo s´ı hay que a˜nadir esta l´ınea. •restore: booleano indicando si debemos establecer o no el estado del agente desde un checkpoint, cuya ruta indicamos en restore dir. Su valor por defecto es False. •restore dir: ruta del checkpoint desde el que queremos restaurar el estado del agente, si hemos indicado restore=True. La funci´on devuelve una lista con un diccionario por cada iteraci´on de entrenamiento, en el que se incluyen el n´umero de iteraci´on, las recompensas m´ınima, media y m´axima de los episodios, la longitud media de los episodios, el tiempo de la fase de aprendizaje en ms Y el tiempo total en segundos de esa iteraci´on. Estos mismos datos se guardan en los ficheros .json y.csv antes mencionados. As´ı, para ejecutar uno de los experimentos de entrenamiento ejecutamos el script indicando pudiendo indicarle el valor de varios argumentos: -m, --model: entero (1-6) indicando el identificador del modelo a entrenar. -g, --gpu: string con los valores gpu0, gpu1, none, both indicando la configuraci´on de GPUs con las que realizar el entrenamiento. -d, --driver-gpus: n´umero de GPUs que se asignar´an al driver (config[num gpus]), puede ser un n´umero decimal. El resto se repartir´an a partes iguales entre los workers. -w, --workers: n´umero de workers que se crear´an en el algortimo para recoger experiencias del entorno. -s, --save-name: ruta del fichero, sin extensi´on, en el que se guardar´an los datos de entrenamiento en formatos .json y.csv. -i, --iters: n´umero de iteraciones de entrenamiento a ejecutar. -c, --cpus: n´umero de CPUs que indicamos a Ray en su inicializaci´on. Su valor por defecto es None, que indica que Ray usar´a todas las que encuentre disponibles.
A.2. SCRIPT DE INFERENCIA EN RLLIB 81 -a, --set-affinity: conjunto con los identificadores de las CPUs a las que queremos restringir la ejecuci´on con sched setaffinity. Su valor por defecto es el conjunto vac´ıo ({}), que indica que no forzamos a que el programa se ejecute en unas CPUs concretas. -r, --restore dir: direcci´on del chekpoint desde el que queremos resturar el estado del agente. Su valor por defecto es None que indica que no queremos restaurar desde ning´un checkpoint. Adem´as de realizar las iteraciones de entrenamiento indicadas, la ejecuci´on de este script mueve los ficheros con las m´etricas que reporta Ray (y que por defecto se guardan en un directorio dentro de ∼/ray results cuyo nombre viene dado por el timestamp del momento en que se inicia la ejecuci´on) y los almacena en un directorio dentro de la carpeta ray results del proyecto y con el nombre indicado por save name. Adem´as, tambi´en copia el fichero params.pkl de este directorio en el que se guardan los checkpoints, pues luego ser´a necesario que este ah´ı para la ejecuci´on de inferencias. Por ejemplo, podemos ejecutar 1000 iteraciones de enyrenamiento para el modelo 3 usando s´olo la GPU 0 del sistema, con 0.001 GPUs para el driver y 4 workers que se reparten el resto de la GPU con la siguiente instrucci´on: 1$python tra ini ng_ scr ipt s / train_ppo . py -- model =3 -- gpu = gpu0 -- driver - gpus =0.001 \ 2-- workers =4 --save - name = model3_4_workers_gpu0 --iters =1000 Esto generar´a un directorio para cada checkpoint en checkpoints/ppo/model3 4 workers gpu0 y unos ficheros training results/ppo/model3 4 workers gpu0.csv y el mismo pero con extensi´on .json con algunos datos del entrenamiento. Adem´as, tendremos en ray results/model3 4 workers gpu0 los ficheros con las m´etricas que genera Ray. A.2. Script de inferencia en RLlib El script rollout with time.py2ser´a el que utilicemos para realizar los experimentos de inferencia en RLlib. Este script es una modificaci´on del que proporciona ya RLlib (rollout.py3, al que se le a˜nade el c´odigo necesario para medir y guardar datos sobre el tiempo que se toma en cada inferencia y para la gesti´on de los recursos disponibles. As´ı, podemos especificar una serie de par´ametros cuando ejecutemos este script, algunos de los cuales proviene del script original de RLlib: checkpoint: primer argumento, con ´el indicamos la ruta al checkpoint desde el que queremos restablecer el estado del agente para las inferencias. --run: algoritmo con el que hemos entrenado al agente. En nuestro caso siempre tomar´a el valor PPO. --env: entorno Gym sobre el que ejecutar las inferencias. En nuestro caso tomar´a el valor Pong-v0. --time-output: ruta a un fichero .csv en el que se guardar´an los datos de tiempo de las inferencias. --no-render: es necesario a˜nadir este argumento si no queremos que se muestre por pantalla las interacciones con el entorno. Nosotros siempre lo a˜nadiremos. --gpu: configuraci´on de GPUs con las que realizar la inferencia. Puede tomar los valores gpu0, gpu1,none yboth. 2https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/rollout_with_time.py 3https://github.com/ray-project/ray/blob/master/rllib/rollout.py
82 AP´ ENDICE A. FUNCIONAMIENTO DE LOS SCRIPTS DE PYTHON --video-dir: directorio en el que guardaremos videos de las interacciones. No lo utilizamos en este trabajo. --seteps: n´umero de pasos de inferencia a ejecutar. Si especificamos un n´umero de episodios (con --episodes) el valor que le hayamos dado al n´umero de pasos quedar´a sin efecto. --episodes: n´umero de episodios completos a ejecutar. --config: diccionario con la configuraci´on del agente, que sobreescribe a la cargada del fichero params.pkl del directorio del checkpoint. --save-info: guarda informaci´on sobre las observaciones y las acciones de cada paso de inferencia. No lo utilizaremos. --use-shelve: guarda la informaci´on sobre las observaciones y las acciones de cada paso de inferencia con formato shelf. --set-affinity: Conjunto (set) con los identificadores de las CPUs a las que queremos restringir la ejecuci´on. --num-cpus-ray: n´umero de CPUs que indicamos a Ray en su incicializaci´on. Si su valor es 0 (lo es por defecto), le estamos indicando a Ray que puede usar todas las que encuentre disponibles. La configuraci´on de recursos espec´ıfica (n´umero de workers, GPUs para el driver...) podemos especificarla en le par´ametro --config. Un ejemplo de ejecuci´on de inferencia sin GPUs y sin crear workers ser´ıa: 1$python rollout_with_time . py checkpoints / ppo / model1_gpu / checkpoint_11000 / checkpoint -11000 -- run = PPO -- env = Pong - v0 --time - output = rollout_results / volta1 / model1_no_gpus_0_workers .csv --no - render -- gpu =none -- episodes =10 -- config = '{" num_workers ":0 , " num_ gpus_per_worker ":0 , " num_gpus ":0} A.3. Scripts de exportaci´on y cuantizaci´on de modelos para la TPU Detallaremos ahora el contenido y manera de uso de los cuatro scripts que llevan a cabo el proceso completo de creaci´on de modelos de Tensorflow Lite cuantizados que pueden ser ejecutados en la TPU. A.3.1. Script de exportaci´on de modelos El script model saver.py4parte de un modelo entrenado en RLlib y exporta la red neuronal con la que se modela la pol´ıtica y su valor en formato .h5. Para ello, la ejecuci´on del script requiere dos par´ametros en su llamada: Direcci´on a un checkpoint desde el que restableceremos el estado del agente a exportar. Ruta donde queremos guardar el modelo en formato .h5. Se indicar´a la ruta al fichero y su nombre sin extensi´on. El script crear´a un agente PPO restaurando el estado del checkpoint pasado como primer argumento y guardar´a el modelo de keras que contiene la red neuronal de la pol´ıtica y su valor en un fichero con extensi´on .h5 en la direcci´on especificada como segundo argumento. Por ejemplo, podemos obtener un fichero .h5 del modelo 1 ejecutando: 1$python model_saver . py checkpoints / ppo / model1_gpu / checkpoint_1000 / checkpoint -1000 exported_models / model1 4https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/model_saver.py
A.3. SCRIPTS DE EXPORTACI ´ ON Y CUANTIZACI ´ ON DE MODELOS PARA LA TPU 83 A.3.2. Script de creaci´on de modelos de Tensorflow Lite El script tflite converter.py5crea y guarda un modelo de Tensorflow Lite a partir de un modelo de keras previamente exportado en formato .h5. En su ejecuci´on debemos indicarle el valor de dos argumentos: Direcci´on del archivo con extensi´on .h5 donde se encuentra el modelo de keras exportado. Direcci´on del fichero .tflite con extensi´on donde queremos guardar el modelo resultante. El script crear´a un objeto TFLiteConverter que llevar´a a cabo la conversi´on a partir del modelo de keras previamente cargado. Por ejemplo, para crear un modelo de Tensorflow Lite del modelo 1 podemos ejecutar: 1$python tflite_converter . py exported_models / model1 . h5 exported_models / model1 . tflite A.3.3. Script de creaci´on de datasets para la cuantizaci´on El script dataset creator.py6crea y guarda conjuntos de im´agenes del entorno con el que interaccionan los modelos y que toman como entradas y que son necesarias para que durante el proceso de cuantizaci´on se puedan estimar los rangos que toman los tensores de entrada y de salida del modelo (pues sus valores son variables) y el modelo cuantizado pierda la menor precisi´on posible respecto al original. Debemos especificar el valor de dos argumentos en la ejecuci´on del script: Dimensi´on de las im´agenes que guardaremos en el dataset. Ruta en la que se guardar´a el dataset que se cree, sin extensi´on. Una vez ejecutemos el script, se crear´a un entorno como con el que interaccionan los agentes y se tomar´an 500 im´agenes obtenidas como observaciones tras ejecutar una serie de acciones aleatorias sobre este entorno. Estas im´agenes se guardar´an en un fichero con extensi´on .npy (pues son en realidad arrays de Numpy) en la ruta indicada como segundo argumento. Por ejemplo, podemos crear un dataset con im´agenes de dimensi´on (168 ×168 ×4), que podr´ıan ser usado para la cuantizaci´on del modelo 4, ejecutando: 1$python dataset_creator . py 168 datasets / dataset_model4 A.3.4. Script de cuantizaci´on de modelos de Tensorflow Lite El script quantizer.py7lleva a cabo la creaci´on de un modelo de Tensorflow Lite cuantizado, con todos su par´ametros como enteros de 8 bits, a partir de un modelo de keras exportado en un fichero .h5. Para ello requerir´a tres argumentos cuando lo ejecutemos: Direcci´on a un dataset, con extensi´on .npy que contenga al menos 500 im´agenes que podr´ıan ser entrada del modelo que queremos convertir. Direcci´on del modelo de keras con extensi´on .h5 que queremos convertir a Tensorflow Lite y cuantizar. Direcci´on del fichero con extensi´on .tflite donde queremos guardar el modelo convertido a Tensorflow Lite y cuantizado. 5https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/tflite_converter.py 6https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/dataset_creator.py 7https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/quantizer.py
84 AP´ ENDICE A. FUNCIONAMIENTO DE LOS SCRIPTS DE PYTHON El script contiene la funci´on representative data gen() que toma 100 im´agenes del dataset cargado de la ruta especificada como primer par´ametro para poder estimar el rango de las entradas y las salidas del modelo y que la cuantizaci´on de estos valores sea correcta. As´ı, se carga el modelo de keras guardado en la direcci´on del segundo argumento y se convierte a Tensorflow Lite cuantizando los valores de sus par´ametros, guardando el modelo resultante en el fichero especificado como tercer argumento. Por ejemplo, podemos crear una versi´on cuantizada del modelo 3 ejecutando: 1$python quantizer . py datasets / dataset_model3 . py exported_models / model3 . h5 exported_models / model3_quant . tflite A.4. Scripts de inferencia de modelos de Tensorflow Lite Detallaremos aqu´ı como se implementan y el modo de uso de los scripts rollout coral.py8y rollout tflite.py9que ejecutan inferencias sobre modelos de Tensorflow Lite, bien cuantizados o sin cuantizar sobre el acelerador Google Coral (rollout coral.py) o sobre las CPUs del sistema (rollout tflite.py). La estructura de estos dos scripts es la misma, salvo que el primero de ellos al crear el int´erprete del modelo de Tensorflow Lite establece como delegado la TPU. Adem´as, de la funci´on principal de los scripts, estos cuenta con dos funciones auxiliares: make interpreter(model file). Recibe como par´ametro la ruta a un modelo guardado de Tensorflow Lite y devuelve un objeto de la clase Interpreter sobre el que podremos ejecutar inferencias. En el caso del script para la TPU, aqu´ı se indica mediante un delegado que las ejecuciones se realizar´an en este soporte. keep gping(steps, num steps, episodes, num episodes): Funci´on que implementa la condici´on del bucle, indicando cuando debemos parar de ejecutar pasos de inferencia. Se toma directamente del script de inferencia que nos proporciona RLlib (rollout.py). Cuando ejecutemos el script podemos dar valor a una serie de par´ametros que configuran las inferencias a realizar: -m, --model: ruta al archivo .tflite en el que se encuentra el modelo de Tensorflow Lite (cuantizado o no) sobre el que ejecutaremos las inferencias. -s, --steps: pasos de inferencia que queremos ejecutar. Si damos valor a --episodes el n´umero de pasos indicado no tendr´a efecto. -e, --episodes: n´umero de episodios completos de inferencia a ejecutar. Si indicamos su valor, el de --steps queda sin efecto. -o, --output: ruta a un archivo .csv en el que guardaremos los datos relativos a la ejecuci´on de las inferencias (tiempos, pasos por episodio, recompensas...). Cuando ejecutamos cualesquiera de los dos scripts en primer lugar se crea el int´erprete para el modelo de Tensorflow Lite indicado. Seguidamente se crea un entorno con wrap deepmind con Pong-v0 como base, y de aqu´ı ser´a de donde se toman las iteraciones. Ahora, se itera mientras no hayamos completado el n´umero total de episodios (o mientras no hayamos completado el n´umero total de pasos en caso de no haber indicado un n´umero de episodios a ejecutar) y en cada paso de iteraci´on se toma una imagen 8https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/exported_models/rollout_ coral.py 9https://github.com/javigm98/Mejorando-el-Aprendizaje-Automatico/blob/main/exported_models/rollout_ tflite.py
A.4. SCRIPTS DE INFERENCIA DE MODELOS DE TENSORFLOW LITE 85 del entorno, se coloca como tensor de entrada del int´erprete del modelo, se invoca al modelo y se obtiene el valor del tensor de salida. De la salida de la pol´ıtica, se toma el ´ındice con el valor m´as alto y esa ser´a la siguiente acci´on, que se realiza sobre el entorno, obteni´endose as´ı una nueva observaci´on y comenzando nuevamente el proceso (b´asicamente es la misma idea que se sigue en el script rollout.py de RLlib.
86 AP´ ENDICE A. FUNCIONAMIENTO DE LOS SCRIPTS DE PYTHON
Bibliograf´ıa [1] Imad Dabbura. ((Gradient Descent Algorithm and Its Variants)). En: Towards Data Science (2017). url:https://towardsdatascience.com/gradient-descent-algorithm-and-itsvariants-10f652806a3. [2] Vincent Dumoulin y Francesco Visin. ((A guide to convolution arithmetic for deep learning)). En: arXiv (2016). url:https://arxiv.org/pdf/1603.07285v1.pdf. [3] Jonathan Hui. ((RL — Proximal Policy Optimization (PPO) Explained)). En: Medium (2018). url:https : / / jonathan - hui . medium . com / rl - proximal - policy - optimization - ppo - explained-77f014ec3f12. [4] Renu Khandelwal. ((A Basic Introduction to TensorFlow Lite)). En: Towards Data Science (2020). url:https://towardsdatascience.com/abasicintroductiontotensorflowlite59e480c57292. [5] Ue Kiao. ((Calculate output size of Convolution)). En: OpenGenus IQ (2021). url:https://iq. opengenus.org/output-size-of-convolution/. [6] Mehryar Mohri, Afshin Rostaminzadeh y Ameet Talwalkar. Foundations of Machine Learning (second edition). MIT Press, 2018. [7] Paco Nathan. ((Distributed Computing with Ray: Intro to RLlib: Example Environments)). En: Medium (2020). url:https://medium.com/distributed-computing-with-ray/intro-torllib-example-environments-3a113f532c70. [8] Sumit Saha. ((A comprehensive Guide to Convolutional Neural Networks)). En: Towards Data Science (2018). url:https : / / towardsdatascience . com / a - comprehensive - guide - to - convolutional-neural-networks-the-eli5-way-3bd2b1164a53. [9] Manas Sahni. ((8-Bit Quantization and TensorFlow Lite: Speeding up mobile inference with low precision)). En: HeartBeat Fritz AI (2018). url:https : / / heartbeat . fritz . ai / 8 - bitquantizationandtensorflowlitespeedingupmobileinferencewithlowprecision-a882dfcafbbd. [10] Sabyasachi Sahoo. ((Deciding optimal kernel size for CNN)). En: Towards Data Science (2018). url:https : / / towardsdatascience . com / deciding - optimal - filter - size - for - cnns - d6f7b56f9363. [11] Kaz Sato. ((What makes TPUs fine-tuned for deep learning?)) En: Google Cloud Blogs (2018). url:https://cloud.google.com/blog/products/aimachinelearning/whatmakestpus-fine-tuned-for-deep-learning. [12] John Schulman y col. ((Proximal Policy Optimization Algorithms)). En: arXiv (2017). url: https://arxiv.org/pdf/1707.06347.pdf. [13] John Schulman y col. ((Trust Region Policy Optimization)). En: arXiv (2017). url:https : //arxiv.org/pdf/1502.05477.pdf. 87
88 BIBLIOGRAF´ IA [14] Sagar Sharma. ((Policy Networks vs Value Networks in Reinforcement Learning)). En: Towards Data Science (2018). url:https://towardsdatascience.com/policy-networks-vs-valuenetworks-in-reinforcement-learning-da2776056ad2. [15] Abhishek Suran. ((Proximal Policy Optimization (PPO) With TensorFlow 2.x)). En: Towards Data Science (2020). url:https://towardsdatascience.com/proximal-policy-optimizationppo-with-tensorflow-2-x-89c9430ecc26. [16] Richard S. Sutton y Andrew G. Barto. Reinforcement Learning: An Introduction. MIT Press, 2014. [17] Jordi Torres. ((Deep Q-Network (DQN)-I OpenAI Gym Pong and Wrappers)). En: Towards Data Science (2020). url:https://towardsdatascience.com/deepqnetworkdqnibce08bdf2af. [18] Serdar Yegulalp. ((What is TensorFlow? The machine learning library explained)). En: InfoWold (2019). url:https://www.infoworld.com/article/3278008/what-istensorflow-themachine-learning-library-explained.html.