• No se han encontrado resultados

Completar código mediante aprendizaje automático

N/A
N/A
Protected

Academic year: 2020

Share "Completar código mediante aprendizaje automático"

Copied!
38
0
0

Texto completo

(1)CAMPUS DE EXCELENCIA INTERNACIONAL “Ingeniamos el futuro”. Graduado en Ingenierı́a Informática Universidad Politécnica de Madrid Escuela Técnica Superior de Ingenieros Informáticos. TRABAJO FIN DE GRADO Completar código mediante Aprendizaje Automático. Autor: Pablo Conde de la Mata Director: Damiano Zanardini. MADRID, JULIO DE 2019.

(2)

(3) Quiero dedicar esta memoria y expresar mis agradecimientos en primer lugar a mi familia, especialmente a mis padres, por haberme apoyado no sólo a lo largo de esta carrera, sino durante toda mi vida. También especial mención a mi tutor, Damiano Zanardini, al cual he tenido el placer de conocer, y sin el que no podrı́a haber hecho este trabajo. Y por supuesto a mis amigos y compañeros del capı́tulo de ACM UPM, sin los que este camino hubiese sido mucho más tortuoso.. i.

(4) ii.

(5) Resumen ste Trabajo de Fin de Grado propone la creación de un modelo probabilı́stico capaz de completar las partes de código que faltan en un programa (Code Completion), mediante técnicas de Aprendizaje Automático. El Code Completion consiste en inferir, a partir de un programa incompleto, las partes que faltan por medio del conocimiento adquirido normalmente con un estudio estadı́stico.. E. El objetivo de este trabajo es desarrollar un prototipo que sea capaz de sugerir secciones del código en puntos de programas en los que el código no está disponible. Palabras clave: Aprendizaje Automático, AST, Code Completion, modelo probabilı́stico, Big Code.. iii.

(6) iv.

(7) Abstract his Final Degree Project proposes the creation of a probabilistic model capable of completing the parts of the code that are missing in a program (Code Completion), through techniques of Automatic Learning. The Code Completion consists of inferring, from an incomplete program, the missing parts through the knowledge normally acquired with a statistical study.. T. The objective of this work is to develop a prototype that is capable of suggesting sections of the code in places of the programs in which the code is not available. Keywords: Automatic Learning, AST, Code Completion, probabilistic model, Big Code.. v.

(8) vi.

(9) Índice general. 1. 2. 3. 4. 5. Introducción. 1. 1.1. Descripción del trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . .. 1. 1.2. Investigación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 2. Estado del arte. 5. 2.1. Editores de texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 5. 2.2. Kite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 6. 2.3. TabNine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 6. Desarrollo. 7. 3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 7. 3.2. Conceptos generales . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 8. 3.3. Diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 10. 3.3.1. Módulos del programa . . . . . . . . . . . . . . . . . . . . . . .. 11. 3.3.2. Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 14. Resultados. 19. 4.1. Cambios en la selección de nodos . . . . . . . . . . . . . . . . . . . . .. 19. 4.2. Cambios en los parámetros del modelo . . . . . . . . . . . . . . . . . . .. 20. Conclusiones. 23. 5.1. 23. Prototipo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii.

(10) ÍNDICE GENERAL 5.2. Trabajos futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. Bibliografı́a. 23 25. viii.

(11) Índice de figuras. Ejemplo de lı́nea del fichero JSON del dataset correspondiente a un programa escrito en Python . . . . . . . . . . . . . . . . . . . . . . . . . . .. 2. 3.1. Descripción del flujo del programa . . . . . . . . . . . . . . . . . . . . .. 10. 3.2. Mapa que almacena las etiquetas . . . . . . . . . . . . . . . . . . . . . .. 12. 3.3. Listas de ı́ndices de los nodos ascendientes y hermanos del nodo con ı́ndice 2 12. 3.4. Disposición de las capas en nuestro modelo . . . . . . . . . . . . . . . .. 13. 3.5. Contenido del fichero de configuración con 2 pruebas . . . . . . . . . . .. 15. 3.6. Ejemplo de lı́nea del dataset . . . . . . . . . . . . . . . . . . . . . . . .. 16. 3.7. Ejemplo de fichero con vectores etiquetados habiendo escogido 3 ancestros y 3 hermanos del nodo . . . . . . . . . . . . . . . . . . . . . . . . . . .. 17. Etiquetas de comparación almacenadas con el mismo número . . . . . . .. 20. 1.1. 4.1. ix.

(12) ÍNDICE DE FIGURAS. x.

(13) C A P Í T U L O. 1. Introducción l autocompletado de texto es una caracterı́stica que incorporan gran cantidad de teclados de dispositivos móviles hoy en dı́a. Consiste en predecir el resto de la palabra que está escribiendo el usuario, ası́ como la siguiente palabra que va a escribir, para aumentar la velocidad de escritura. Del mismo modo el autocompletado de código pretende mejorar la productividad a la hora de programar ayudando a corregir errores de escritura y otros fallos comunes.. E 1.1. Descripción del trabajo. El objetivo de este trabajo es poder completar código (Code Completion), es decir, inferir las partes que faltan de un programa incompleto a partir de un modelo probabilı́stico obtenido con técnicas de Aprendizaje Automático. Por ejemplo, si una determinada rutina R es llamada con probabilidad alta en un determinado punto del programa p cuando las lı́neas de código que hay alrededor de p tienen cierta estructura S, el modelo probabilı́stico aprenderá que, en presencia de cierta estructura S, lo más probable es que una llamada a R tenga que ser sugerida para completar el código. Para llevar a cabo esta tarea se ha desarrollado un prototipo que, a partir de unos programas completos, selecciona al azar un fragmento de código de dichos programas. Partiendo ese fragmento, extrae un subconjunto de lı́neas de código cercanas relacionadas con la sentencia en cuestión. Este bloque de código elegido debı́a servir a nuestro modelo para poder inferir el fragmento que habı́a sido elegido al azar al principio. 1.

(14) CAPÍTULO 1. INTRODUCCIÓN Los programas que se han utilizado están escritos en el lenguaje Python1 y se encuentran representados en forma de árbol sintáctico, AST de sus siglas en inglés. En esta representación cada nodo del árbol representa una sentencia del programa original. Para entrenar y evaluar este prototipo se ha utilizado un dataset público, 150K Python Dataset2 , formado por 150.000 programas escritos en Python recogidos de repositorios públicos de GitHub. Este dataset lo forman dos ficheros JSON en los que cada lı́nea corresponde a la representación de un programa en forma de AST. En la Fig. 1.1 se aprecia mejor la conversión que se realiza a un programa y cómo viene representado en el dataset. Lo que aparece en la Fig. 1.1b corresponderı́a a una única lı́nea del fichero, ya que se trata de un único árbol.. x = 9 print x+1. [ {"type":"Module","children":[1,4]}, {"type":"Assign","children":[2,3]}, {"type":"NameStore","value":"x"}, {"type":"Num","value":"9"}, {"type":"Print","children":[5]}, {"type":"BinOpAdd","children":[6,7]}, {"type":"NameLoad","value":"x"}, {"type":"Num","value":"1"} ]. (a) Programa en Python. (b) Representación del programa como AST. Figura 1.1: Ejemplo de lı́nea del fichero JSON del dataset correspondiente a un programa escrito en Python El prototipo realizado es capaz de sugerir partes de código en los puntos del programa en que no se encuentren disponibles.. 1.2. Investigación. La primera parte de este trabajo ha servido para familiarizarme con las ideas esenciales de Análisis de Programas[1], especialmente con la técnica de interpretación abstracta, Abstract Interpretation. Esta técnica permite extraer determinadas propiedades del programa en ciertos puntos, sin que este se esté ejecutando. Se suele utilizar para el análisis estático de programas, es decir, sin que se ejecute el mismo. Esto lo suelen realizar los compiladores, para determinar por ejemplo qué optimizaciones se pueden aplicar, o para la depuración de programas. Al principio empezamos a desarrollar una herramienta que descargaba automáticamen1 2. https://www.python.org https://www.sri.inf.ethz.ch/py150. 2.

(15) 1.2. INVESTIGACIÓN te programas de repositorios públicos de GitHub haciendo peticiones a su API 3 , para posteriormente procesarlos. Pero gracias a [2] sobre el Big Code descrito en la Sec. 3.2, descubrimos los datasets públicos de Python que hemos utilizado finalmente con nuestro prototipo.. 3. https://developer.github.com/v3/. 3.

(16) CAPÍTULO 1. INTRODUCCIÓN. 4.

(17) C A P Í T U L O. 2. Estado del arte ste capı́tulo va a explicar el estado actual de los editores de texto. Después se van a mostrar dos herramientas que utilizan técnicas de Aprendizaje Automático para completar código, una centrada en el lenguaje Python y otra que no está restringida por el lenguaje.. E. 2.1. Editores de texto. Los editores de texto o los entornos de desarrollo integrado, IDE de sus siglas en inglés, generalmente sólo sugieren el final de la palabra que se está escribiendo. Esta sugerencia además la llevan a cabo generalmente en orden alfabético, por lo que no siempre se obtienen las mejores sugerencias, o para seleccionar la deseada es necesario desplazarse por una larga lista de opciones. Pocos editores realizan un análisis del contexto y filtran los resultados que más se aplican, útil cuando se trata de bloques de código parecidos. Pero ninguno utiliza técnicas de Aprendizaje de Automático para mejorar sus resultados. 5.

(18) CAPÍTULO 2. ESTADO DEL ARTE. 2.2 Kite Kite1 es una herramienta que se integra con los editores de texto para completar el código a medida que se escribe con técnicas de Aprendizaje Automático. Todavı́a se encuentra en una fase temprana de desarrollo y en su propia página no se encuentra ninguna explicación acerca de cómo trabaja, ası́ como tampoco en su blog o en su página de GitHub, que está sólo para reportar errores. Cuando se comienza a escribir una lı́nea de código Kite sugiere el resto de la lı́nea, a partir de patrones comunes observados en repositorios de GitHub ordenados por popularidad. Además, desde su última versión todo el cómputo lo lleva a cabo en la máquina del usuario, por lo que proporciona bajas latencias, a pesar de que el usuario no disponga de buena conexión a internet y otorga mayor seguridad al no compartir el código. De momento únicamente está disponible para el lenguaje Python tanto en MacOS como en Windows y en los editores Atom, Sublime, Pycharm, VS Code y Vim.. 2.3 TabNine A diferencia de Kite, TabNine2 , no está restringido al lenguaje, puede completar cualquier lenguaje. Va mejorando sus predicciones conforme aumenta el volumen de código. Esta herramienta también es capaz de completar más de una palabra por lı́nea a la vez, al igual que Kite. Los datos que utiliza TabNine son los archivos fuente del proyecto en el que se esté utilizando, respetando el archivo gitignore si es que existe, para no empobrecer las predicciones con código no deseado. Estos datos los indexa para mostrar las secciones de código que más se ajustan, ordenadas acorde a un modelo de regresión softmax, de modo que sabe con qué frecuencia aparecen sı́mbolos consecutivos. Estas secciones marcadas como más frecuentes y, por tanto candidatas para completar la parte de código que falta, se utilizan en una segunda fase, que basada en patrones similares del código puede sugerir sentencias adicionales para el código.. 1 2. https://www.kite.com https://tabnine.com. 6.

(19) C A P Í T U L O. 3. Desarrollo n este capı́tulo se explicará el modelo que se ha desarrollado para realizar el completado del código, ası́ como una breve introducción de la notación y terminologı́a que será utilizada a lo largo de este documento. Se describirá cómo se han extraı́do los datos de los datasets utilizados y la manera en la que se han seleccionado los datos introducidos en el modelo probabilı́stico, además de la estructura de la propia red neuronal que se ha utilizado.. E 3.1. Introducción. Para la creación de este programa, capaz de completar fragmentos de código, se ha creado un modelo probabilı́stico capaz de predecir, en base al resto del programa, una determinada sentencia. Para entrenar y evaluar dicho modelo probabilı́stico se han creado vectores etiquetados. Cada vector contiene las sentencias cercanas a la que se quiere predecir, codificadas con números según su tipo, y como etiqueta del vector la propia sentencia a predecir. El lenguaje que se ha utilizado para programar este modelo ha sido Scala1 [3][4], un lenguaje que fusiona la programación funcional con el paradigma orientado a objetos, desarrollado por Martin Odersky. Tanto para la creación del modelo probabilı́stico como para el resto de tareas, se han usado diferentes librerı́as. Por ejemplo, en la fase de decodificación de los ASTs en formato JSON 1. https://www.scala-lang.org. 7.

(20) CAPÍTULO 3. DESARROLLO se ha utilizado Circe2 , una librerı́a de Scala especı́fica para el tratamiento de JSON. La librerı́a MXNet3 se ha utilizado para la creación del modelo probabilı́stico que se detallará más adelante. Es una librerı́a de código abierto, utilizada para entrenar y desplegar redes neuronales, que soporta múltiples lenguajes como Scala, Python, R y C++ entre otros.. 3.2. Conceptos generales. Code Completion Consiste en intentar predecir, de manera similar a los predictores de texto, el final de la lı́nea de código que se está escribiendo o, la siguiente lı́nea de código. El autocompletado de código pretende ayudar a la hora de programar reduciendo el número de errores y aumentando la velocidad. Aprendizaje Automático Se trata de una rama de la Inteligencia Artificial, que permite desarrollar técnicas para que las máquinas aprendan. Los principales algoritmos que existen se pueden clasificar entre: • Aprendizaje supervisado: los algoritmos deben de establecer una correspondencia entre las entradas y las salidas deseadas proporcionadas. Este es el que hemos utilizado para nuestro prototipo. • Aprendizaje no supervisado: en este caso no existe un conjunto de salidas deseadas, por lo que los algoritmos deben de ser capaces de extraer caracterı́sticas de los valores de entrada. Modelo probabilı́stico Los modelos probabilı́sticos determinan para cada valor de entrada, la probabilidad de ajustarse acorde a nuestra función de predicción. Abstract Syntax Tree (AST) Es una representación abstracta, en forma de árbol, de código fuente de un lenguaje de programación, utilizada generalmente durante el análisis semántico por el compilador. Cada nodo del árbol representa una sentencia del programa. En esta representación los tipos, ası́ como el orden de cada sentencia, debe conservarse. Esta es la manera en la que se representan en este proyecto los programas seleccionados para su análisis. Big Code Se denomina ası́ a las grandes cantidades de código disponible en internet, como por ejemplo el de sitios como GitHub. Dataset Es un conjunto de datos tabulados, donde generalmente las columnas indican una caracterı́stica o variable particular y cada fila corresponde a un miembro del conjunto. En el dataset que utilizamos nosotros, por ejemplo, cada lı́nea representa un programa. 2 3. https://circe.github.io/circe/ https://mxnet.apache.org/api/scala/index.html. 8.

(21) 3.2. CONCEPTOS GENERALES Matriz de confusión Permiten visualizar con claridad la efectividad del modelo. En la diagonal de la matriz se encuentra el número de veces que una etiqueta ha sido predicha correctamente, y en el resto de la matriz nos sirve para ver con qué valores se confunde. Neurona Las neuronas artificiales son nodos o unidades conectadas entre sı́ para transmitirse señales, de manera similar a lo que ocurre en un sistema biológico. Red neuronal Es un modelo computacional que se compone de numerosas neuronas conectadas entre sı́. Estas neuronas se organizan en capas y cada capa anterior está conectada con la siguiente. Función de activación Las funciones de activación sirven para normalizar los datos dentro de un modelo, evitando ası́ datos muy dispares, para poder determinar cuando una neurona se activa o no. Algunas de las funciones de activación más comunes son: • Función sigmoide: es la función de activación más antigua, aunque en los últimos años no se usa tanto. Esta función se define como: σ(x) = 1+e1−x . • Rectified Linear Units (ReLU): estas funciones tienen una salida 0 si su entrada es menor que 0, y el valor de su entrada si esta es mayor a 0. La función es la siguiente: f (x) = max(x, 0). • Softmax: Los valores de salida de esta función van de 0 a 1, pero además la suma total de los mismos es 1. Hyperparameters Son variables que determinan la estructura de la red neuronal, como por ejemplo la función de activación, el batch size o el número de épocas. Features Es una caracterı́stica o propiedad del fenómeno que se está considerando. En este caso se trata de los nodos del árbol que representan las rutinas del código. Batch size Es el tamaño del conjunto de datos que toma de cada vez el modelo, este se debe especificar cuando se itera sobre los datos que recibe el modelo. Épocas Cada época se refiere a un ciclo completo de entrenamiento. Como no se introducen todos los datos a la vez en el modelo, se hace según el batch size, deben producirse varias iteraciones hasta que se han introducido todos los datos. Cuando esto ocurre se considera una época.. 9.

(22) CAPÍTULO 3. DESARROLLO. 3.3. Diseño. En esta sección se va a explicar detalladamente el funcionamiento del programa y cada una de sus partes. En la Fig. 3.1 se muestra la estructura básica del programa, junto con los módulos que lo componen y los ficheros necesarios para su ejecución.. Nodos de entrenamiento. Nodos de evaluación. Creación de vectores etiquetados. ASTs de entrenamiento. Buscar parientes. Aplicación. Entrenamiento y evaluación. ASTs de evaluación Estadı́sticas. Archivo de configuración. Archivo de salida. Figura 3.1: Descripción del flujo del programa 10.

(23) 3.3. DISEÑO Como se aprecia en la Fig. 3.1, el programa hace uso de seis ficheros. El primero que utiliza es el fichero de configuración, donde se han de seleccionar todos los parámetros a la hora de crear los modelos probabilı́sticos para cada prueba entre otras opciones, como por ejemplo las rutas a los archivos, y cuya descripción completa se encuentra en la Sec. 3.3.2. Luego existen cuatro ficheros, dos de ellos correspondientes al entrenamiento del modelo y los dos restantes a la evaluación. Los ficheros que contienen los ASTs mencionados anteriormente de los programas son del dataset utilizado, y estos han de ser procesados para poder ser utilizados por nuestro modelo, generando dos ficheros nuevos con los vectores etiquetados. El archivo de salida explicado en la Sec. 3.3.2 muestra diferentes estadı́sticas extraı́das del entrenamiento y la evaluación del modelo, como por ejemplo la matriz de confusión.. 3.3.1. Módulos del programa. El programa desarrollado consta de dos módulos principales, un módulo encargado de extraer los datos de los datasets, seleccionar los nodos que se utilizarán para el entrenamiento del modelo probabilı́stico y, crear los vectores etiquetados para el mismo. El segundo módulo se trata del propio aprendizaje automático, la definición de la arquitectura de la red, la creación del modelo probabilı́stico y la extracción de analı́ticas para poder evaluar la efectividad de los modelos y poder afinarlos.. Selección de nodos En la Sec. 3.3.2 se detalla la estructura de los datasets que contienen los ASTs de los programas escritos en Python. Ahı́ se muestra que para cada nodo del árbol, hay un parámetro obligatorio que es su tipo. Este tipo hace referencia a la sentencia de código del programa que representa el árbol, y es lo que vamos a predecir con nuestro modelo. Lo primero que hacemos es decodificar el dataset que está en JSON con la librerı́a Circe, mediante un tipo algebraico de datos, Algebraic Data Type (ADT)4 . Cada nodo del árbol está representado por un objeto JSON y es validado por nuestro ADT, que al igual que el JSON, para cada nodo contiene el tipo, y opcionalmente el valor y sus hijos. Todos estos nodos los almacenamos en una lista, y ası́ cada lista representa un árbol. Se ha hecho una correspondencia de cada tipo distinto con un número natural para crear los vectores etiquetados, debido a que no se pueden utilizar cadenas de texto en el modelo probabilı́stico. Por ello, al principio de la ejecución se recorren los dos datasets y, para cada nodo de cada árbol, si este no se encontraba ya previamente, se almacena en un map como el de la Fig. 3.2. Al terminar la fase de selección de nodos se sustituye cada tipo con 4. https://docs.scala-lang.org/glossary/#algebraic-data-type. 11.

(24) CAPÍTULO 3. DESARROLLO su número correspondiente. En caso de no existir alguno de los nodos requeridos, porque se piden cinco ascendientes del nodo raı́z por ejemplo, estos se declaran como “UNDEF”, por lo que lo añadimos al mapa actual. Map(UNDEF -> 0, CompareGtELtE -> 1, NameStore -> 8, Return -> 23, Global -> 64, Set -> 90, .... Figura 3.2: Mapa que almacena las etiquetas Seguidamente, por cada AST que como se ha indicado antes, decodificado es una lista de ADT, se selecciona un nodo aleatorio y, en base a ese nodo seleccionado, se buscan los parientes conforme al fichero de configuración, por ejemplo 3 ascendientes y 3 hermanos. Como resultado se obtiene una lista con los ı́ndices de los nodos del árbol que tienen el parentesco deseado. En caso de no existir alguno de los nodos, se le pone como ı́ndice -1 como se puede ver en la Fig. 3.3. Ancestors of: 2 -> List(1, 0, -1) Siblings of: 2 -> List(3, -1, -1). Figura 3.3: Listas de ı́ndices de los nodos ascendientes y hermanos del nodo con ı́ndice 2 Finalmente, se sustituyen los tipos de cuyos nodos tenemos los ı́ndices, por los números correspondientes según el map que hemos creado anteriormente con las etiquetas, dando como resultado el vector etiquetado. Un ejemplo de este vector se puede ver en la Fig. 3.7. Todos estos vectores etiquetados extraı́dos de cada árbol se escriben en un fichero, uno por lı́nea, como se explica en la Sec. 3.3.2. Modelo probabilı́stico En este módulo del programa se ha utilizado la librerı́a MXNet para realizar las tareas de Aprendizaje Automático. Se ha elegido esta librerı́a porque es la que tiene mayor integración con el lenguaje Scala. Una vez se han obtenido todos los nodos que se desean procesar, se guardan en dos ficheros descritos en la Sec. 3.3.2, que contienen los vectores etiquetados para el entrenamiento y la evaluación del modelo. Esto se realiza ası́ para poder tener la opción de únicamente entrenar el modelo, siempre que se proporcionen los vectores etiquetados. Por tanto, lo primero que hacemos es extraer los vectores etiquetados de los archivos y guardarlos en N-dimensional array, que son colecciones multidimensionales de elementos 12.

(25) 3.3. DISEÑO del mismo tipo y forma. Se almacenan en cuatro N-dimensional array, uno para los nodos o features de entrenamiento, otro para sus etiquetas y lo mismo para la evaluación. Seguidamente se crean dos iteradores, uno para el entrenamiento y otro para la evaluación, con el batch size seleccionado en el fichero de configuración. A continuación se crea el modelo. La estructura que se ha utilizado en el modelo probabilı́stico es de 4 capas, contando la inicial y la final, como se muestra en la Fig. 3.4. La primera capa es del tamaño del vector etiquetado, sin contar la etiqueta. Las dos capas intermedias tienen el mismo número de nodos, que se especifica en el fichero de configuración. La última capa tiene tantos nodos como categorı́as diferentes haya en los datasets.. Capa de entrada. Capa intermedia. Capa intermedia. Capa de salida. x1. h1 (1). h1 (2). y1. x2. h2 (1). h2 (2). y2. h3 (1). h3 (2). .. .. .. .. hn (1). hn (2). .. . xn. .. . yn. Figura 3.4: Disposición de las capas en nuestro modelo Las capas intermedias son dos capas totalmente interconectadas, y su función de activación es rectified linear unit (ReLU). La función de activación de la última capa es Softmax. El 13.

(26) CAPÍTULO 3. DESARROLLO número de épocas del modelo también es un parámetro que se modifica en el archivo de configuración, para ası́ facilitar la tarea a la hora de realizar varias pruebas seguidas. Al acabar el entrenamiento del modelo se guarda en un fichero, por si más adelante se quiere evaluar sin entrenarlo de nuevo, y se realizan las estadı́sticas sobre los resultados del mismo. Estas estadı́sticas contienen la matriz de confusión, el porcentaje de nodos de relleno, Sec. 3.3.2, el nombre de las etiquetas que más a acertado el modelo y las etiquetas más repetidas a lo largo del dataset.. 3.3.2. Ficheros. Como se indicaba al principio de esta sección, y se mostraba en la Fig. 3.1, el programa utiliza varios ficheros que se detallan a continuación. Fichero de configuración El programa dispone de un fichero de configuración donde se pueden configurar distintos parámetros para facilitar realizar las baterı́as de pruebas. Para cada prueba se ha de especificar obligatoriamente un nombre para identificar la carpeta en la que se dejarán los ficheros de salida. Además se pueden incluir varios parámetros opcionales, como por ejemplo, el número de nodos que se van a utilizar de cada AST para crear el vector etiquetado para entrenar el modelo probabilı́stico, el número de nodos en las capas intermedias del modelo o el número de épocas. Todos estos parámetros son opcionales ya que tienen valores por defecto en la implementación. Todos los parámetros que se pueden especificar en el fichero son los siguientes: • Nombre de la prueba • Fases: hay tres posibles fases, all que realiza todo el proceso, extracción de los nodos, entrenamiento y evaluación del modelo, train: dado unos vectores etiquetados realiza el entrenamiento y la evaluación, eval: dados los vectores etiquetados y un modelo, realiza la evaluación del mismo y extrae estadı́sticas. • Ruta y nombre del archivo con los ASTs para el entrenamiento en formato JSON del dataset. • Ruta y nombre del archivo con los ASTs para la evaluación, también del dataset original. • Ruta y nombre del archivo con los vectores etiquetados para el entrenamiento, ya procesados. 14.

(27) 3.3. DISEÑO • Ruta y nombre del archivo con los vectores etiquetados para la evaluación. • Número de nodos a seleccionar para crear los vectores etiquetados y tipo: ancestros, descendientes o hermanos del nodo seleccionado como etiqueta del vector. • Tamaño del conjunto de datos a tomar por el modelo, o batch size. • Número de épocas para entrenar el modelo. • Número de nodos en las dos capas intermedias del modelo probabilı́stico. • Diferentes niveles de verbosidad para la salida por pantalla (logger). Se ha utilizado la librerı́a PureConfig5 para facilitar a la hora de leer y cargar los datos. El fichero tiene una estructura muy parecida a la de un JSON, como se puede ver en la Fig. 3.5. parameters = [ { name = "Prueba1" epochs = 30 logger-level = "debug" tree-order = { ancestors = 5, siblings = 5 } }, { name = "Prueba2" epochs = 35 train-resources = "data/python100k_train.json" eval-resources = "data/python50k_eval.json" tree-order = { ancestors = 5, descendants = 3 } } ]. Figura 3.5: Contenido del fichero de configuración con 2 pruebas Gracias a esta librerı́a únicamente hay que crear en Scala una clase que valide dicha estructura deseada, donde se debe indicar los tipos que recibe cada parámetro. 5. https://github.com/pureconfig/pureconfig. 15.

(28) CAPÍTULO 3. DESARROLLO case class Model(parameters: List[ModelParameters]) case class ModelParameters(name: String, loggerLevel: Option[String], trainResources: Option[String], .... Para leer los datos del archivo de configuración se utiliza la función loadConfig de la librerı́a, que devuelve una estructura Map con los valores. Ficheros con ASTs El módulo encargado de seleccionar los nodos, descrito en la Sec. 3.3.1, extrae los ASTs de los datasets. Estos ficheros tienen formato JSON, y en cada lı́nea se encuentra una lista de objetos JSON que representa un árbol sintáctico. Un ejemplo de dicha lı́nea se encuentra en la Fig. 3.6. [ {"type":"Module","children":[1,4]}, {"type":"Assign","children":[2,3]}, {"type":"NameStore","value":"x"}, {"type":"Num","value":"9"}, {"type":"Print","children":[5]}, {"type":"BinOpAdd","children":[6,7]}, {"type":"NameLoad","value":"x"}, {"type":"Num","value":"1"} ]. Figura 3.6: Ejemplo de lı́nea del dataset Los campos que puede tener cada objeto son: • Tipo: tipo del nodo. • Valor: cadena de caracteres que contiene el valor del nodo actual. • Hijos: array con el ı́ndice de los nodos hijos. Los ı́ndices de los nodos del árbol comienzan por 0. Tanto el valor como los hijos son parámetros opcionales. Ficheros con vectores etiquetados Los ficheros con los vectores etiquetados se crean en el primer módulo tras la elección de los nodos y son dos, uno para el entrenamiento del modelo probabilı́stico y otro para la evaluación. 16.

(29) 3.3. DISEÑO Estos ficheros, en la primera lı́nea contienen todos posibles valores de las etiquetas con sus números asociados, al igual que en el map de la Fig. 3.2. Las lı́neas siguientes contienen los vectores etiquetados siendo el primer número la etiqueta, y los demás números los parientes de dicho nodo. Map(NameStore -> 5, Num -> 6, Print -> 7, BinOpAdd -> 8, NameLoad -> 9, Assign -> 4, UNDEF -> 0, Module -> 3) 5 4 3 0 6 0 0. Figura 3.7: Ejemplo de fichero con vectores etiquetados habiendo escogido 3 ancestros y 3 hermanos del nodo En la Fig. 3.7 se ve un ejemplo de vector etiquetado para el árbol de la Fig. 3.6. El número 5 corresponderı́a al tipo del nodo escogido, los tres siguientes números a los tipos de sus ancestros y los tres últimos a los tipos de sus hermanos. En este caso, el nodo elegido al azar es un nodo de tipo NameStore, que corresponde con el nodo de ı́ndice 2 de la Fig. 3.6 ya que sólo existe un nodo con ese tipo. Dicho nodo tiene como padres los nodos de ı́ndices 0 y 1. Estos corresponden con un nodo de tipo Module y otro de tipo Assign respectivamente, que sustituido por los valores asignados a dichos tipos son los números 4 y 3. Como se pedı́a 3 ancestros, pero únicamente tenı́a dos ese nodo, el último número se pone a 0, que equivale a la etiqueta “UNDEF”. El nodo seleccionado sólo tiene un hermano, de tipo Num que corresponde con el 6, por lo que los otros dos hermanos que se pedı́an se ponen a 0. Con esto se completa el vector etiquetado de este nodo. Fichero de salida El fichero de salida contiene las estadı́sticas calculadas en cada prueba. Este fichero se almacena en una carpeta con el nombre de la prueba especificado en el archivo de configuración, junto con los archivos de los vectores etiquetados y el modelo entrenado. Por cada prueba se almacena la matriz de confusión, el porcentaje de nodos de “relleno”, las etiquetas más repetidas y las etiquetas que más ha acertado el modelo. Los nodos de “relleno” son aquellos que se han puesto a 0 en los vectores etiquetados debido a que no se disponı́a de dichos nodos, cuanto mayor sea ese porcentaje, peores resultados va a obtener el modelo.. 17.

(30) CAPÍTULO 3. DESARROLLO. 18.

(31) C A P Í T U L O. 4. Resultados n esta sección se van a exponer los resultados obtenidos por el prototipo durante las pruebas. Se explicarán también los ajustes realizados al prototipo, tanto al módulo donde se clasifican los nodos y se crean los vectores etiquetados como al modelo probabilı́stico, y las configuraciones elegidas para obtener los mejores resultados.. E 4.1. Cambios en la selección de nodos. En las primeras ejecuciones del prototipo obtenı́amos unos valores de acierto demasiado bajos independientemente de los “hyperparameters” con los que configurásemos el modelo probabilı́stico. Ası́ que empezamos a observar las estadı́sticas que obtenı́amos tras cada fase de entrenamiento, explicadas en la Sec. 3.2. En ella observamos que habı́a un número de etiquetas que intentaba predecir, pero que apenas aparecı́an en los programas de los datasets, y otro número de etiquetas que eran parecidas y que se equivocaba bastante entre ellas al intentar predecirlas, parecido a un subconjunto de etiquetas. Este subconjunto se trataba de las etiquetas de comparación, como por ejemplo CompareGtELtE, CompareGtGtGt, CompareLtEq, CompareIsNotIsNot o CompareEqGt. El conjunto total de etiquetas que aparecen en los datasets es de algo más de 180, y el subconjunto de etiquetas de comparación son aproximadamente 50, por lo que decidimos aunar todas las referentes a comparación bajo una misma etiqueta. Este cambio no fue difı́cil debido a cómo habı́amos almacenado las etiquetas. Al guardarse 19.

(32) CAPÍTULO 4. RESULTADOS todas las etiquetas en un mapa, en vez de dar un número único a cada etiqueta como se explicó en la Sec. 3.3.1, a todas las etiquetas de comparación les asignamos el mismo, como muestra la Fig. 4.1. Map(UNDEF -> 0, CompareGtELtE -> 1, CompareGtGtGt -> 1, CompareLtEq -> 1, CompareIsNotIsNot -> 1, CompareEqGtNameStore -> 1, .... Figura 4.1: Etiquetas de comparación almacenadas con el mismo número De la misma manera, hicimos lo mismo con las etiquetas que apenas aparecı́an a lo largo del dataset, asignamos esos nodos a una categorı́a miscelánea. Al contrario que con las etiquetas de comparación, como estas no aparecı́an muy frecuentemente, no causó ningún cambio reseñable en los valores de acierto. Por eso decidimos revertir los cambios. El agrupar las etiquetas de comparación bajo una sola aumentó los valores de acierto del modelo sobremanera.. 4.2. Cambios en los parámetros del modelo. Tras aplicar los cambios en la selección de nodos, más concretamente a la reducción del número de etiquetas que el modelo debı́a calcular, nos centramos en buscar la mejor configuración para nuestro modelo. En un primer momento nuestro modelo contaba únicamente con una capa intermedia, y los valores máximos de acierto que obtenı́amos estaban en torno al 50 %. Decidimos añadirle otra capa intermedia más, totalmente conectada a la anterior capa y con el mismo número de nodos, como se muestra en la Sec. 3.3.1. Ahora con las mejores configuraciones, que explicaremos a continuación, llegamos a obtener el 85 % de acierto en el prototipo. Un factor determinante en los resultados es la elección de los nodos que conforman el vector etiquetado, algunos resultados se muestran a continuación: Nodos. Acierto. 3, 5, 8 o más ancestros 5 ancestros y 5 descendientes 5 ancestros y 5 hermanos 5 ancestros, descendientes y hermanos. 45 % 75 % 66 % 85 %. Cuadro 4.1: Relación de elección de nodos para vector etiquetado y porcentaje de acierto En este cuadro se resumen los resultados obtenidos por nuestro prototipo con los siguientes parámetros en el modelo probabilı́stico: 35 épocas, 150 nodos en las capas intermedias y un batch size de 32. Con estos parámetros se obtienen los resultados más óptimos. 20.

(33) 4.2. CAMBIOS EN LOS PARÁMETROS DEL MODELO Si se decide aumentar el número de nodos se empeoran los resultados debido a que es raro que un nodo tenga más de cinco hermanos o ancestros, lo que harı́a que el vector etiquetado tenga gran número de 0 de relleno.. 21.

(34) CAPÍTULO 4. RESULTADOS. 22.

(35) C A P Í T U L O. 5. Conclusiones lo largo de este documento se ha detallado el desarrollo de nuestro prototipo, ası́ como las pruebas que se han llevado a cabo con el modelo, indicando los parámetros necesarios para obtener los mejores resultados. En este capı́tulo se va a hacer una pequeña reflexión de todo lo que se ha elaborado.. A 5.1. Prototipo. Con este trabajo se ha llevado a cabo un prototipo, capaz de completar una sentencia a partir de bloques de código similares. Tras el afinamiento del prototipo, tanto a la hora de seleccionar los nodos como se explica en la Sec. 4.1, como en la parte del Aprendizaje Automático, Sec. 4.2, junto con una correcta selección de “hyperparameters”, los resultados que se obtienen son bastante buenos, llegando hasta un 85 % de acierto, como se indica en la Sec. 4.2.. 5.2. Trabajos futuros. Actualmente, cada prueba que se realiza sobre el modelo no actúa sobre las demás. Serı́a interesante que en el futuro los resultados de cada prueba se comparasen entre sı́ de forma automática para determinar otros impactos en la salida a parte de la eficacia, para poder determinar fácilmente con qué “hyperparameters” se obtienen los mejores resultados. 23.

(36) CAPÍTULO 5. CONCLUSIONES Otro trabajo futuro serı́a añadir más formas de visualizar los datos. Si las pruebas no son estancas, y se comparan sus resultados, se obtendrán muchas más formas de presentar la información. Finalmente, otro trabajo interesante serı́a ver qué tal se comporta el prototipo con otros lenguajes de programación. Para este proyecto únicamente se han utilizado datasets de programas escritos en Python, pero serı́a interesante ver cómo se comporta con otros lenguajes y si es capaz de mantener su eficacia.. 24.

(37) Bibliografı́a [1] F. Nielson, H. R. Nielson y C. Hankin, Principles of Program Analysis. Springer Publishing Company, Incorporated, 2010, ISBN: 3642084745, 9783642084744. [2] M. Allamanis, E. T. Barr, P. Devanbu y C. Sutton, “A survey of machine learning for big code and naturalness”, ACM Comput. Surv., vol. 51, n.o 4, 81:1-81:37, jul. de 2018, ISSN: 0360-0300. DOI: 10.1145/3212695. dirección: http://doi. acm.org/10.1145/3212695. [3] M. Odersky, L. Spoon y B. Venners, Programming in Scala: Updated for Scala 2.12, 3rd. USA: Artima Incorporation, 2016, ISBN: 0981531687, 9780981531687. [4] P. Chiusano y R. Bjarnason, Functional Programming in Scala, 1st. Greenwich, CT, USA: Manning Publications Co., 2014, ISBN: 1617290653, 9781617290657.. 25.

(38) Este documento esta firmado por Firmante Fecha/Hora Emisor del Certificado Numero de Serie Metodo. CN=tfgm.fi.upm.es, OU=CCFI, O=Facultad de Informatica - UPM, C=ES Tue Jul 02 23:07:33 CEST 2019 [email protected], CN=CA Facultad de Informatica, O=Facultad de Informatica - UPM, C=ES 630 urn:adobe.com:Adobe.PPKLite:adbe.pkcs7.sha1 (Adobe Signature).

(39)

Referencias

Documento similar

¿Cómo se traduce la incorporación de ésta en la idea de museo?; ¿Es útil un museo si no puede concebirse como un proyecto cultural colectivo?; ¿Cómo puede ayudar el procomún

Primeros ecos de la Revolución griega en España: Alberto Lista y el filohelenismo liberal conservador español 369 Dimitris Miguel Morfakidis Motos.. Palabras de clausura

[r]

[r]

SECUNDARIA COMPRENDE LOS

- Un curso formativo para los técnicos de laboratorio de la UPV sobre la prevención de los residuos en los laboratorios, que se llevará a cabo los días 23, 24, 25, 26 y 27

Como él, todos los estudiantes del Tecnológico de Monterrey se preparan para terminar las clases semestrales e iniciar este próximo 14 de mayo el periodo de exámenes finales.. Si

desarrollo del país y cumplir con la misión que se ha propuesto, tiene la intención de impulsar un Plan de Formación en Educación Financiera y Económica para mujeres cabeza de