DeepAPI#: Синтез цепочки вызовов API CLR/С# по текстовому запросу Александр Чебыкин Михаил Кита Яков Кириленко Санкт-Петербургский Санкт-Петербургский Санкт-Петербургский Государственный Университет Государственный Университет Государственный Университет Email: a.e.chebykin@gmail.com Email: mihail.kita@gmail.com Email: jake.kirilenko@gmail.com Аннотация—Разработчики часто ищут способы реализо- за счёт использования моделей глубокого машинного вать стандартную функциональность с помощью библио- обучения из области обработки естественных языков. течных функций (например, создать кнопку в UI или полу- Глубокое обучение успешно используется для перевода чить данные из формата JSON). Обычно источником такой информации является Интернет. Альтернатива - различ- между натуральными языками, DeepAPI пользуется ные статистические инструменты, которые, обучившись на результатами последних исследований в этой области. большом объеме кода, по текстовому запросу могут предо- Задача генерации кода по тексту здесь рассматрива- ставлять пользователю набор вызовов функций, решающих ется как задача перевода с английского языка на язык задачу. вызовов функций (слова в таком языке — функции язы- Мы рассматриваем один из таких инструментов — DeepAPI. Этот современный алгоритм основывается на глу- ка программирования, предложения — упорядоченные боком обучении, и, согласно его авторам, работает лучше наборы слов). аналогов. Мы пытаемся воспроизвести этот результат, взяв Результаты, представленные в статье об алгоритме целевым языком не Java, как оригинальный DeepAPI, а C#. DeepAPI, привлекли наше внимание, и мы решили В данной статье мы рассказываем о возникающих пробле- повторить этот опыт и расширить его. В результате мах сбора данных для обучения, сложностях в построения и обучения модели, а также обсуждаем возможные моди- работы оригинального алгоритма DeepAPI получается фикации алгоритма. линейная последовательность вызовов функций, код с их применением программисту приходится писать I. Введение самостоятельно. Хотелось бы облегчить ему этот этап, предлагая не список функций, а сниппет кода — то есть Последние несколько десятков лет предпринимаются отрывок кода на языке программирования, в котором попытки анализировать исходный код для разных це- нужно модифицировать минимальное количество де- лей: генерация имени метода по его телу [1], анализ талей для того, чтобы он заработал. Для реализации качества кода [2], генерация кода по текстовому за- этого может оказаться полезным алгоритм T2API [9], просу [3], [4]. Последнее особенно интересно, поскольку который так же является достаточно свежим исследо- позволяет разработчикам избежать долгого изучения ванием возможности генерации кода по текстовому за- программных библиотек в поисках нужной функцио- просу. В первой части алгоритма по текстовому запросу нальности, что является значимой проблемой [5]. выбираются некоторые релевантные элементы API, во Обычно разработчики ищут способы реализовать второй — из них генерируются граф управления и стандартную функциональность с помощью поисковых код. Теоретически, замена первой части алгоритма на движков в сети Интернет, для чего те не оптимизирова- DeepAPI позволит сделать итоговые результаты более ны [6]. Исследователями предлагались разные замены точными, так как модель, используемая на первом им, в том числе инструменты, основывающиеся на шаге T2API, более старая, а также не упорядочивает статистическом анализе большой кодовой базы. Яркие элементы API на выходе. представители: На текущий момент мы пытаемся повторить экс- • MAPO[4] генерирует по имени функции наиболее перимент из статьи DeepAPI и реализовать алгоритм релевантные шаблоны её использования; из неё на базе другого языка программирования. В • UP-Miner [7] — улучшенная версия MAPO, дости- оригинальной статье авторы работают с элементами гающая лучших результатов в генерации благода- API и кодовой базой языка Java и упоминают, что ря более сложному алгоритму; одна из угроз действительности эксперимента — работа • SWIM[3] генерирует код по текстовому запросу. только с одним языком. Мы желаем нивелировать эту Одним из самых свежих алгоритмов в этой области угрозу, а потому работаем с языком C#. является DeepAPI [8] — алгоритм из статьи строит Наш текущий вклад в исследования можно описать последовательность вызовов функций по текстовому следующими тезисами: запросу, достигая лучших результатов, чем SWIM, • cобран набор тренировочных данных, реализован инструментарий для сбора данных; Для увеличения точности работы алгоритма вводит- • проведено исследование использования в качестве ся еще одно улучшения — новая функция потерь, вклю- тренировочных данных данные, отличные от рас- чающая в себя наказание за использование чересчур смотренных в оригинальной статье; частых элементов API. Ведь если функция часто ис- • построена аппроксимация алгоритма DeepAPI; пользуется, то скорее всего, она не связана с реализаци- • поставлен частично успешный первичный экспери- ей конкретной функциональности, а потому считается мент. шумом. Оценка важности элемента API основывается на tf–idf [11], и выглядит так: II. DeepAPI widf (yt ) = log(N/nyt ) Модель DeepAPI основывается на современных тех- никах глубокого обучения и машинного перевода. Клю- где N — общее число экземпляров в тренировочном на- чевая техника — обучение Sequence-to-Sequence [10], боре, nyt — число экземпляров, в которых встречается суть которой — по входной строке генерировать вы- yt . ходную. При этом обычно первая строка принадлежит Так как DeepAPI должен переводить запросы на одному естественному языку, вторая — другому. В слу- английском языке в цепочки вызовов функций, она чае DeepAPI исходный язык — английский, выходной — должна тренироваться на примерах того, какие англий- язык элементов API. ские предложения каким цепочкам вызовов функций Техника Sequence-to-Sequence включает в себя две соответствуют. Такие примеры авторы оригинальной рекуррентные нейронные сети (рекуррентная нейрон- статьи извлекают из открытого исходного кода, а имен- ная сеть (RNN) — один из подвидов моделей глубокого но из методов с комментариями. Так как целевым обучения). Кодер — первая из этих сетей — поэле- языком программирования выбрана Java, имеющая за- ментно считывает вход и обновляет свои параметры в фиксированную структуру комментария для докумен- текущем скрытом состоянии. После окончания считы- тации Javadoc, авторы пользуются этим, и берут первое вания последнее состояние — предполагается, что в нём предложение комментария в качестве описания метода заключена суть входного предложения — берётся в ка- на английском языке (по определению Javadoc, именно честве вектора контекста, и передаётся в качестве входа это написано в первом предложении). Чтобы получить второй RNN — декодеру. Декодер на каждом шаге, список вызовов функций, код метода анализируется сверяясь с вектором контекста, генерирует очередной компилятором Eclipse JDT. Выводятся классы каж- элемент выходной последовательности, и обновляет дой переменной, вызовы методов записываются вместе своё состояние. Работа декодера прекращается, когда с именем класса, которому этот метод принадлежит. он генерирует слово, означающее окончание строки — Пример вычленения информации из кода представлен . на рисунке 1. Пример работы модели представлен на рисунке 2. Входной поток — “generate random number”, выход- ной — “Random.new Random.nextInt”. На рисунке для простоты понимания состояния рас- ширены по времени: поскольку нейронная сеть рекур- рентная, переход из состояния должен вести в него же. То есть здесь h1, h2, h3 — одно и то же состояние в моменты времени 1, 2, 3. В модели DeepAPI также используется улучше- ние техники Sequence-to-Sequence: механизм внимания. Вместо использования лишь последнего состояния ко- дера в качестве вектора контекста для генерации це- лого предложения, на каждом шаге работы декодера используется свой вектор контекста, получаемый как взвешенная сумма всех состояний кодера T X Рис. 1: Пример получения данных из кода cj = ajt ht t=1 После обучения, во время генерации результата по где cj — вектор контекста для шага j, ht — историче- запросу, авторами используется лучевой поиск — вме- ские состояния кодера, ajt — веса этих состояний для сто того, чтобы генерировать самое вероятное слово шага j (моделируются при помощи отдельной заранее на каждом шаге, поддерживается список из n текущих натренированной нейронной сети). самых вероятных слов, и генерация продолжается для Рис. 2: Пример работы RNN Кодер-Декодера каждого из них. Другие результаты отсекаются. Цель рые в большом количестве доступны в репозитории подобной практики — предотвратить случаи, когда NuGet [13]. Для улучшения качества данных пакеты наиболее точная цепочка отбрасывается из-за того, что рассматривались в порядке убывания популярности: её первое слово оказалось не самым подходящим. В ито- мы исходили из предположения, что наиболее популяр- ге для каждого запроса получаем список из нескольких ные пакеты содержат более качественный код. Паке- возможных ответов. ты скачивались в автоматическом режиме, после чего Для человеконезависимой оценки качества работы содержащиеся в них библиотеки собирались в одну алгоритма используется метрика BLEU. BLEU часто папку, чтобы обеспечить разрешение зависимостей. В применяется для оценки точности машинного перево- случае возникновения коллизии имён предпочтение от- да, она измеряет, насколько сгенерированная после- давалось более поздней версии библиотеки. Помимо довательность близка к той, что была написана че- самих библиотек также собирались и все доступные ловеком и использовалась во время обучения. Шкала xml-файлы, содержащие комментарии к методам. оценок идёт от 0 до 100. Далее следовал процесс обработки собранных фай- По представленным в статье оценкам, DeepAPI пре- лов: для каждого извлечённого метода генерировалось восходит предыдущие техники. Поскольку не у всех из синтаксическое дерево, а затем производился его раз- них была подсчитана оценка BLEU, авторы DeepAPI бор. Описанные действия позволили получить исчер- реализовали и оценили прошлые алгоритмы самосто- пывающую информацию о каждом вызове метода в ятельно. В итоге UP-Miner получил оценку 11.97, коде, включая тип объекта, имя метода и параметры. SWIM — 19.90, DeepAPI — 54.42. Эта информация использовалась для построения по- Имеется также рабочий прототип [12], относительно следовательности вызовов, которая затем сохранялась правдоподобно отвечающий на запросы. на диск для дальнейшего использования. III. Получение данных Помимо последовательностей вызовов необходимо Отдельное внимание стоит уделить процессу сбора было также собрать комментарии к методам. На этом данных для исследования и тренировки модели. Для этапе мы столкнулись с проблемой: количество методов получения большего количества данных, мы использо- с комментариями в скачанных нами пакетах составляет вали не только скачивание кода открытых проектов лишь 11.21% от числа всех методов. Были собраны все с Github, но и обработку скомпилированных сборок доступные комментарии из xml-файлов, а для остав- проектов, полученных из репозитория NuGet. В сле- шихся методов мы генерировали описания, используя дующих двух секциях мы рассматриваем эти способы имя данного метода и имя содержащего его класса. подробнее. Как показала практика, описанный метод сбора дан- ных оказался достаточно эффективным. Было скачано A. NuGet 4,100 пакетов, содержащих в общей сложности 4,278 Первый подход заключался в извлечении нуж- библиотек. Итоговое количество собранных методов ных данных из скомпилированных библиотек, кото- составило 611,945, из них 68,585 — с комментариями. B. Github наказывающую генерацию слишком частых функций. GitHub [14] — популярный хостинг проектов с откры- Это изменение не было приоритетным, т.к. влияет на тым исходным кодом. Именно отсюда авторы ориги- оценку BLEU не больше, чем на 2 очка, увеличивая её нальной статьи скачали 442,928 проектов на Java. Для с 52.49 до 54.42, а для нас более интересно получить фильтрации проектов авторы выставили условие, что первичный результат примерно того же порядка, что и у каждого из них должна быть хотя бы одна звезда. 52.49. По подобному запросу для языка C# выдаётся A. Первый эксперимент 121,672 репозитория (https://github.com/search?utf8= %E2%9C%93&q=language%3AC%23+stars%3A%3E0& В качестве функции потерь в модели DeepAPI бе- type=Repositories&ref=searchresults). рётся условная логарифмическая функция правдоподо- Таким образом, по сравнению с Java, репозиториев, бия. Модель тренируется максимизировать её с помо- с которыми можно потенциально работать, примерно в щью стохастического градиентного спуска [15] в ком- 4 раза меньше. Однако судя по имеющимся у нас дан- бинации с оптимизатором AdaDelta [16]. Авторы реа- ным, кажется возможным собрать количество данных лизуют модель на базе библиотеки GroundHog. Однако того же порядка. Авторы в итоге получили 7,519,907 он устарел и более не поддерживается, поэтому мы пар “описание на естественном языке — список вызовов реализуем нашу первую модель на основе TensorFlow — API”. После обработки 48,789 репозиториев мы получа- популярной библиотеки для машинного обучения с ем 870,008 пар. открытым кодом. Помимо количества репозиториев, возникает пробле- Надо упомянуть, что обучение рекуррентных ней- ма из-за выбранного целевого языка. В Java из ис- ронных сетей очень ресурсоёмко, а потому ведётся на ходного кода можно легко получить тип (или надтип) видеокартах. В то время как авторы для тренировки переменной из её объявления. Однако в языке C#, пользовались видеокартой Nvidia K20, у нас имеется начиная с версии 3.0, переменные могут иметь неявный гораздо менее мощная Nvidia GTX 660. В связи с этим тип var, поэтому для выведения явного типа таких нам пришлось уменьшить параметры тренировки до переменных требуется компиляция всего проекта. Это менее оптимальных. Например, авторы обнаружили, ограничивает количество репозиториев, которые мы что оптимальное количество нейронов в скрытом слое можем обработать. Кроме этого, мы сталкиваемся и кодера и декодера — 1000. Мы выставляем их коли- с другими сложностями со стороны языка, например, чество в 700. Авторы не обсуждают влияние размера динамический тип данных dynamic, о котором во время пакета (batch) на модель, выставляя его значение в 200. компиляции мы ничего не знаем. Но такие проблемы по Мы делаем его размер равным 32. сравнению с проблемой неявных типов несущественны: Мы вводим дополнительное улучшение в виде мы провели небольшой эксперимент, запустив поиск в bucketing — разделения исходных предложений на исходном коде 470 случайно выбранных репозиториев классы по длине, и тренировки внутри этих классов. слова "dynamic"и "var и получив всего 4,856 результа- Дело в том, что для быстрого обучения нам нужны тов для первого, и 176,561 результатов для второго. вектора фиксированной длины — а предложения имеют Поэтому пока что мы не исследуем способы решения длину разную. Можно их все дополнять до максималь- подобных неприоритетных проблем, оставляя это на ной длины (с помощью специального слова), а можно будущее. разделить на категории вида длина < 10, длина < 20, В качестве компилятора мы используем Roslyn — и т.д., и дополнять до максимума в этой категории, компилятор C# с открытым исходным кодом, разра- тренируя категории отдельно. Чем меньше дополняем ботанный Microsoft. Чтобы собирать проекты в авто- пустыми словами, тем меньше шума, и лучше (теоре- матическом режиме, необходимо, чтобы: тически) результат. 1) не надо было предпринимать никаких дополни- Мы тренируем модель на 605,146 парах данных в тельных действий (например, запускать индиви- течение 40,000 итераций, ограничив словарь самыми дуальные для каждого проекта скрипты); популярными 10,000 слов в исходном и выходном язы- 2) проект содержал файл с расширением .sln — ках. именно с ним может работать Roslyn. Итоговая модель умеет верно отвечать на некото- Из 100 самых популярных результатов поиска таким рые запросы, например ’generate random number — ограничениям удовлетворяет 51 проект, в среднем по System.Random.new System.Random.Next’ или ’replace всем результатам поиска — 11.2%. part of string with other string — System.String.Replace’, Код из подходящих репозиториев мы обрабатываем но на большинстве входов выдаёт нерелевантный ре- аналогично оригинальной статье. зультат. IV. Эксперимент B. Второй эксперимент На текущий момент мы не занимались реализаци- Так как хороших результатов получить на модели ей упомянутой ранее модификации функции потерь, с урезанными параметрами не удалось, мы создаём 1) собрать набор данных, сопоставимый по размеру с набором данных в оригинальной статье; 2) поэкспериментировать с параметрами обучения, и найти оптимальные; 3) исследовать возможность тренировки на не ком- ментированных методах, используя имена и клас- сы параметров, имя метода; 4) найти способы решения специфичных для целе- вого языка проблем; 5) реализовать алгоритм T2API; 6) объединить алгоритмы DeepAPI и T2API в один, проанализировать получившийся результат. VI. Заключение В этой статье мы описали нашу частично успеш- Рис. 3: График изменения функции потерь во время ную попытку повторить результат статьи DeepAPI. обучения Используя современные методы глубокого обучения и машинного перевода, мы достигли небольших успехов в задаче генерации списка вызовов функций по тексто- модель на основе нового фрэймворка от Google - tf- вому запросу. seq2seq, специализирующегося на Sequence-to-Sequence моделях. С его помощью мы обучаем модель на тех же Мы считаем, что модель DeepAPI действительно мо- значениях параметров, что и в оригинальной статье, жет быть реализована на базе другого языка, а также при этом для этого хватает производительности нашей улучшена, что мы и собираемся продемонстрировать по не мощной видеокарты. Также во время декодирования результатам дальнейшей работы. мы используем лучевой поиск - улучшение, предло- Список литературы женное авторами оригинальной статьи и описанное в секции II; в предыдущей модели мы этот механизм не [1] M. Allamanis, H. Peng, and C. Sutton, “A convolutional attention network for extreme summarization of source code.” реализовывали. [2] V. Barstad, M. Goodwin, and T. Gjøsæter, “Predicting source Модель мы тренируем на 924,593 парах данных в те- code quality with static analysis and machine learning.” in NIK, чение 390,000 итераций, ограничивая словарь в исход- 2014. [3] M. Raghothaman, Y. Wei, and Y. Hamadi, “Swim: synthesizing ном и выходном языках самыми популярными 10,000 what i mean: code search and idiomatic snippet synthesis,” in слов. Proceedings of the 38th International Conference on Software Итоговая модель может не только отвечать на оди- Engineering. ACM, 2016, pp. 357–367. [4] T. Xie and J. Pei, “Mapo: Mining api usages from open source ночные запросы, как старая, но и показывает нену- repositories,” in Proceedings of the 2006 international workshop левые результаты на тестовом множестве из 14,000 on Mining software repositories. ACM, 2006, pp. 54–57. пар данных. Надо упомянуть, что пары из тестового [5] M. P. Robillard and R. Deline, “A field study of api learning obstacles,” Empirical Software Engineering, vol. 16, no. 6, pp. множества не используются во время тренировки, по- 703–732, 2011. этому положительные результаты на них означают, что [6] J. Stylos and B. A. Myers, “Mica: A web-search tool for модель не просто запомнила данные ей в тренировоч- finding api components and examples,” in Visual Languages and Human-Centric Computing, 2006. VL/HCC 2006. IEEE ном множестве примеры, а действительно научилась Symposium on. IEEE, 2006, pp. 195–202. обобщать знания. Оценка модели по метрике BLEU [7] J. Wang, Y. Dang, H. Zhang, K. Chen, T. Xie, and D. Zhang, растёт от 0.48 после 158,000 итераций до 1.95 после “Mining succinct and high-coverage api usage patterns from source code,” in Proceedings of the 10th Working Conference 390,000 итераций. Помимо этого, искусственная функ- on Mining Software Repositories. IEEE Press, 2013, pp. 319– ция потерь, которую модель минимизирует, стабильно 328. уменьшается (см. рисунок 3). [8] X. Gu, H. Zhang, D. Zhang, and S. Kim, “Deep api learning,” in Proceedings of the 2016 24th ACM SIGSOFT International Таким образом, из общей положительной динамики Symposium on Foundations of Software Engineering. ACM, мы можем заключить, что техника имеет право на су- 2016, pp. 631–642. ществование. Мы считаем, что тренировка на большем [9] T. Nguyen, P. C. Rigby, A. T. Nguyen, M. Karanfil, and T. N. Nguyen, “T2api: synthesizing api code usage templates from количестве данных позволит получить сравнимый с english texts with statistical translation,” in Proceedings of оригинальным результат. the 2016 24th ACM SIGSOFT International Symposium on Foundations of Software Engineering. ACM, 2016, pp. 1013– V. Дальнейшая работа 1017. [10] I. Sutskever, O. Vinyals, and Q. V. Le, “Sequence to Мы планируем продолжать исследование данного sequence learning with neural networks,” in Advances in neural information processing systems, 2014, pp. 3104–3112. подхода. В ближайшей перспективе перед нами стоят [11] G. Salton and M. J. McGill, “Introduction to modern следующие цели: information retrieval,” 1986. [12] H. K. U. of Science and Technology. (2017) Deepapi. [Online]. Available: http://www.cse.ust.hk/~xguaa/deepapi/ [13] Nuget gallery. [Online]. Available: http://www.nuget.org [14] Github. [Online]. Available: https://github.com [15] L. Bottou, “Large-scale machine learning with stochastic gradient descent,” in Proceedings of COMPSTAT’2010. Springer, 2010, pp. 177–186. [16] M. D. Zeiler, “Adadelta: an adaptive learning rate method,” arXiv preprint arXiv:1212.5701, 2012. DeepAPI#: CLR/C# call sequence synthesis from text query Alexander Chebykin, Mikhail Kita, Iakov Kirilenko Developers often search for an implementation of typical features via libraries (for example, how to create a UI button control, extract data from a JSON-formatted file, etc.). The Internet is the usual source of the information on the topic. However, various statistical tools provide an alternative: after processing large amounts of source code and learning common patterns, they can convert a user request to a set of relevant function calls. We examine one of those tools — DeepAPI. This fresh deep learning based algorithm outperforms all others (according to its authors). We attempt to reproduce this result using different target programming language — C# — instead of Java used in the original DeepAPI. In this paper we report arising problems in the data gathering for training, difficulties in the model construction and training, and finally discuss possible modifications of the algorithm.