Deep API Learning

Xiaodong Gu, Hongyu Zhang, Dongmei Zhang, Sunghun Kim

Introduction

To implement a certain functionality, for example, how to parse XML files, developers often reuse existing class libraries or frameworks by invoking the corresponding APIs. Obtaining which APIs to use, and their usage sequence (the method invocation sequence among the APIs) is very helpful in this regard . For example, to “parse XML files” using JDK library, the desired API usage sequence is as follows:

DocumentBuilderFactory.newDocumentBuilderDocumentBuilderFactory.newDocumentBuilder

Yet learning the APIs of an unfamiliar library or software framework can be a significant obstacle for developers . A large-scale software library such as .NET framework and JDK could contain hundreds or even thousands of APIs. In practice, usage patterns of API methods are often not well documented . In a survey conducted by Microsoft in 2009, 67.6% respondents mentioned that there are obstacles caused by inadequate or absent resources for learning APIs . Another field study found that a major challenge for API users is to discover the subset of the APIs that can help complete a task .

A common place to discover APIs and their usage sequence is from a search engine. Many developers search APIs from general web search engines such as Google and Bing. Developers can also perform a code search over an open source repository such as GitHub and then utilize an API usage pattern miner to obtain the appropriate API sequences.

However, search engines are often inefficient and inaccurate for programming tasks . General web search engines are not designed to specifically support programming tasks. Developers need to manually examine many web pages to learn about the APIs and their usage sequence. Besides, most of search engines are based on keyword matching without considering the semantics of natural language queries . It is often difficult to discover relevant code snippets and associated APIs.

Recently, Raghothaman et al. proposed SWIM, which translates a natural language query to a list of possible APIs using a statistical word alignment model . SWIM then uses the API list to retrieve relevant API sequences. However, the statistical word alignment model it utilizes is based on a bag-of-words assumption without considering the sequence of words and APIs. Therefore, it cannot recognize the deep semantics of a natural language query. For example, as described in their paper , it is difficult to distinguish the query convert int to string from convert string to int.

To address these issues, we propose DeepAPI, a novel, deep-learning based method that generates relevant API usage sequences given a natural language query. We formulate the API learning problem as a machine translation problem: given a natural language query x=x1,...,xNx=x_{1},...,x_{N} where xix_{i} is a keyword, we aim to translate it into an API sequence y=y1,...,yTy=y_{1},...,y_{T} where yjy_{j} is an API. DeepAPI shows a deep understanding of natural language queries in two aspects:

First, instead of matching keywords, DeepAPI learns the semantics of words by embedding them into a vector representation of context, so that semantically related words can be recognized.

Second, instead of word-to-word alignment, DeepAPI learns the sequence of words in a natural language query and the sequence of associated APIs. It can distinguish the semantic differences between queries with different word sequences.

DeepAPI adapts a neural language model named RNN Encoder-Decoder . Given a corpus of annotated API sequences, i.e., ⟨\langleAPI sequence, annotation⟩\rangle pairs, DeepAPI trains the language model that encodes each sequence of words (annotation) into a fixed-length context vector and decodes an API sequence based on the context vector. Then, in response to an API-related user query, it generates API sequences by consulting the neural language model.

To evaluate the effectiveness of DeepAPI, we collect a corpus of 7 million annotated code snippets from GitHub. We select 10 thousand instances for testing and the rest for training the model. After 240 hours of training (1 million iterations), we measure the accuracy of DeepAPI using BLEU score , a widely used accuracy measure for machine translation. Our results show that DeepAPI achieves an average BLEU score of 54.42, outperforming two related approaches, that is, code search with pattern mining (11.97) and SWIM (19.90). We also ask DeepAPI 30 API-related queries collected from real query logs and related work. On average, the rank of the first relevant result is 1.6. 80% of the top 5 returned results and 78% of the top 10 returned results are deemed relevant. Our evaluation results confirm the effectiveness of DeepAPI.

The main contributions of our work are as follows:

To our knowledge, we are the first to adapt a deep learning technique to API learning. Our approach leads to more accurate API usage sequences as compared to the state-of-the-art techniques.

We develop DeepAPIavailable at: https://guxd.github.io/deepapi/, a tool that generates API usage sequences based on natural language queries. We empirically evaluate DeepAPI’s accuracy using a corpus of 7 million annotated Java code snippets.

The rest of this paper is organized as follows. Section 2 describes the background of the deep learning based neural language model. Section 3 describes the application of the RNN Encoder-Decoder, a deep learning based neural language model, to API learning. Section 4 describes the detailed design of our approach. Section 5 presents the evaluation results. Section 6 discusses our work, followed by Section 7 that presents the related work. We conclude the paper in Section 8.

Deep Learning for Sequence Generation

Our work adopts and augments recent advanced techniques from deep learning and neural machine translation . These techniques are on the basis of Sequence-to-Sequence Learning , namely, generating a sequence (usually a natural language sentence) conditioned on another sequence. In this section, we discuss the background of these techniques.

It has been observed that software has naturalness . Statistical language models have been adapted to many software engineering tasks such as learning natural code conventions , code suggestion , and code completion . These techniques regard source code as a special language and analyze it using statistical NLP techniques.

The language model is a probabilistic model of how to generate sentences in a language. It tells how likely a sentence would occur in a language. For a sentence yy, where y=(y1,...,yT)y=(y_{1},...,y_{T}) is a sequence of words, the language model aims to estimate the joint probability of its words Pr(y1,...,yT)Pr(y_{1},...,y_{T}). Since

it is equivalent to estimate the probability of each word in yy given its previous words, namely, what a word might be given its predecessing words.

As Pr(yt∣y1,...,yt−1)Pr(y_{t}|y_{1},...,y_{t-1}) is difficult to estimate, most applications use “n-gram models” to approximate it, that is,

where an n-gram is defined as nn consecutive words. This approximation means that the next word yty_{t} is conditioned only on the previous n−1n-1 words.

2 Neural Language Model

The neural language model is a language model based on neural networks. Unlike the n-gram model which predicts a word based on a fixed number of predecessing words, a neural language model can predict a word by predecessing words with longer distances. It is also powerful to learn distributed representations of words, i.e, word vectors . We adopt RNNLM , a language model based on a deep neural network, that is, Recurrent Neural Network (RNN) . Figure 1a shows the basic structure of an RNN. The neural network includes three layers, that is, an input layer which maps each word to a vector, a recurrent hidden layer which recurrently computes and updates a hidden state after reading each word, and an output layer which estimates the probabilities of the following word given the current hidden state.

Figure 1b shows an example of how RNNLM estimates the probability of a sentence, that is, the probability of each word given predecessing words (Equation 1). To facilitate understanding, we expand the recurrent hidden layer for each individual time step. The RNNLM reads the words in the sentence one by one, and predicts the possible following word at each time step. At step tt, it estimates the probability of the following word p(yt+1∣y1,...,yt)p(y_{t+1}|y_{1},...,y_{t}) by three steps: First, the current word yty_{t} is mapped to a vector yt\bm{y}_{t} by the input layer:

Then, it generates the hidden state (values in the hidden layer) ht\bm{h}_{t} at time tt according to the previous hidden state ht−1\bm{h}_{t-1} and the current input yt\bm{y}_{t}:

Finally, the Pr(yt+1∣y1,...,yt)Pr(y_{t+1}|y_{1},...,y_{t}) is predicted according to the current hidden state ht\bm{h}_{t}:

During training, the network parameters are learned from data to minimize the error rate of the estimated yy (details are in ).

3 RNN Encoder-Decoder Model

The RNN Encoder-Decoder is an extension of the basic neural language model (RNNLM). It assumes that there are two languages, a source language and a target language. It generates a sentence yy of the target language given a sentence xx of the source language. To do so, it first summarizes the sequence of source words x1,...,xTxx_{1},...,x_{T_{x}} into a fixed-length context vector:

where ff is a non-linear function that maps a word of source language xt\bm{x}_{t} into a hidden state ht\bm{h}_{t} at time tt by considering the previous hidden state ht−1\bm{h}_{t-1}. The last hidden state hTx\bm{h}_{T_{x}} is selected as a context vector c\bm{c}.

Then, it generates the target sentence yy by sequentially predicting a word yty_{t} conditioned on the source context c\bm{c} as well as previous words y1,...,yt−1y_{1},...,y_{t-1}:

The above procedures, i.e., ff and pp can be represented using two recurrent neural networks respectively, an encoder RNN which learns to transform a variable length source sequence into a fixed-length context vector, and a decoder RNN which learns a target language model and generates a sequence conditioned on the context vector. The encoder RNN reads the source words one by one. At each time stamp tt, it reads one word, then updates and records a hidden state. When reading a word, it computes the current hidden state ht\bm{h}_{t} using the current word xt\bm{x}_{t} and the previous hidden state ht−1\bm{h}_{t-1}. When it finishes reading the end-of-sequence word , it selects the last hidden state hTx\bm{h}_{T_{x}} as a context vector c\bm{c}. The decoder RNN then sequentially generates the target words by consulting the context vector (Equation 8). It first sets the context vector as an initial hidden state of the decoder RNN. At each time stamp tt, it generates one word based on the current hidden state and the context vector. Then, it updates the hidden state using the generated word (Equation 6). It stops when generating the end-of-sentence word .

The RNN Encoder-Decoder model can then be trained to maximize the conditional log-likelihood , namely, minimize the following objective function:

where NN is the total number of training instances, while TT is the length of each target sequence. costitcost_{it} is the cost function for the tt-th target word in instance ii. It is defined as the negative log likelihood:

where θ\theta denotes model parameters such as weights in the neural network, while pθ(yit∣xi)p_{\theta}(y_{it}|x_{i}) (derived from Equation 6 to 8) denotes the likelihood of generating the tt-th target word given the source sequence xix_{i} in instance ii according to the model parameters θ\theta. Through optimizing the objective function using optimization algorithms such as gradient descendant, the optimum θ\theta value can be estimated.

RNN Encoder-Decoder Model For API Learning

Now we present the idea of applying the RNN Encoder-Decoder model to API learning. We regard user queries as the source language and API sequences as the target language. Figure 2 shows an example of the RNN Encoder-Decoder model for translating a sequence of English words read text file to a sequence of APIs. The encoder RNN reads the source words one by one. When it reads the first word read, it embeds the word into vector x1\bm{x}_{1} and computes the current hidden state h1\bm{h}_{1} using x1\bm{x}_{1}. Then, it reads the second word text, embeds it into x2\bm{x}_{2}, and updates the hidden state h1\bm{h}_{1} to h2\bm{h}_{2} using x2\bm{x}_{2}. The procedure continues until the encoder reads the last word file and gets the final state h3\bm{h}_{3}. The final state h3\bm{h}_{3} is selected as a context vector c\bm{c}.

The decoder RNN tries to generate APIs sequentially using the context vector c\bm{c}. It first generates as the first word y0\bm{y}_{0}. Then, it computes a hidden state h1\bm{h}_{1} based on the context vector c\bm{c} and y0\bm{y}_{0}, and predicts the first API FileReader.new according to h1\bm{h}_{1}. It then computes the next hidden state h2\bm{h}_{2} according to the previous word vector y1\bm{y}_{1}, the context vector c\bm{c}, and predicts the second API BufferedReader.new according to h2\bm{h}_{2}. This procedure continues until it predicts the end-of-sequence word .

Different parts of a query could have different importance to an API in the target sequence. For example, considering the query save file in default encoding and the target API sequence File.new FileOutputStream.new FileOutputStream.write FileOutputStream.close, the word file is more important than default to the target API File.new. In our work, we adopt the attention-based RNN Encoder-Decoder model , which is a recent model that selects the important parts from the input sequence for each target word. Instead of generating target words using the same context vector c\bm{c} (c=hTx\bm{c}=\bm{h}_{T_{x}}), an attention model defines individual cj\bm{c}_{j}’s for each target word yjy_{j} as a weighted sum of all historical hidden states h1,...,hTx\bm{h}_{1},...,\bm{h}_{T_{x}}. That is,

where each αjt\alpha_{jt} is a weight between the hidden state ht\bm{h}_{t} and the target word yjy_{j}, while α\alpha can be modeled using another neural network and learned during training (see details in ).

2 Enhancing RNN Encoder-Decoder Model with API importance

The basic RNN Encoder-Decoder model does not consider the importance of individual words in the target sequence either. In the context of API learning, different APIs have different importance for a programming task . For example, the API Logger.log is widely used in many code snippets. However, it cannot help understand the key procedures of a programming task. Such ubiquitous APIs would be “weakened” during sequence generation.

We augment the RNN Encoder-Decoder model to predict API sequences by considering the individual importance of APIs. We define IDF-based weighting to measure API importance as follows:

where NN is the total number of API sequences in the training set and nytn_{y_{t}} denotes the number of sequences where the API yty_{t} appears in the training set. Using IDF, the APIs that occur ubiquitously have the lower weights while the less common APIs have the higher weights.

We use API weight as a penalty term to the cost function (Equation 10). The new cost function of the RNN Encoder-Decoder model is:

where λ\lambda denotes the penalty of IDF weight and is set empirically.

DeepAPI: Deep Learning for API sequence generation

In this section, we describe DeepAPI, a deep-learning based method that generates relevant API usage sequences given an API-related natural language query. DeepAPI adapts the RNN Encoder-Decoder model for the task of API learning. Figure 3 shows the overall architecture of DeepAPI. It includes an offline training stage and an online translation stage. In the training stage, we prepare a large-scale corpus of annotated API sequences (API sequences with corresponding natural language annotations). The annotated API sequences are used to train a deep learning model, i.e., the RNN Encoder-Decoder language model as described in Section 3. Given an API-related user query, a ranked list of API sequences can be generated by the language model.

In theory our approach could generate APIs written in any programming languages. In this paper we limit our scope to the JDK library. The details of our method are explained in the following sections.

We first construct a large-scale database that contains pairs of API sequences and natural language annotations for training the RNN Encoder-Decoder model. We download Java projects from GitHub created from 2008 to 2014. To remove toy or experimental programs, we only select the projects with at least one star. In total, we collected 442,928 Java projects from GitHub. We use the last snapshot of each project. Having collected the code corpus, we extract ⟨\langleAPI sequence, annotation⟩\rangle pairs as follows:

To extract API usage sequences from the code corpus, we parse source code files into ASTs (Abstract Syntax Trees) using Eclipse’s JDT compiler . The extraction algorithm starts from the dependency analysis of a whole project repository. We analyze all classes, recording field declarations together with their type bindings. We replace all object types with their real class types. Then, we extract API sequence from individual methods by traversing the AST of the method body:

For each constructor invocation new C()new~{}C(), we append the API C.new to the API sequence.

For each method call o.m()o.m() where oo is an instance of a JDK class CC, we append the API C.m to the API sequence.

For a method call passed as a parameter, we append the method before the calling method. For example, o1o_{1}.m1m_{1}(o2o_{2}.m2m_{2}(),o3o_{3}.m3()m_{3}()), we produce a sequence C2C_{2}.m2m_{2}-C3C_{3}.m3m_{3}-C1C_{1}.m1m_{1}, where CiC_{i} is the JDK class of instance oio_{i}.

For a sequence of statements stmt1stmt_{1}; stmt2stmt_{2} ;…; stmttstmt_{t}, we extract the API sequence sis_{i} from each statement stmtistmt_{i}, concatenate them, and produce the API sequence s1s_{1}-s2s_{2}-…-sts_{t}.

For conditional statements such as if (stmt1stmt_{1}) { stmt2stmt_{2}; } else { stmt3stmt_{3}; }, we create a sequence from all possible branches, that is, s1s_{1}-s2s_{2}-s3s_{3}, where sis_{i} is the API sequence extracted from the statement stmtistmt_{i}.

For loop statements such as while(stmt1stmt_{1}){stmt2stmt_{2};}, we produce a sequence s1s_{1}-s2s_{2}, where s1s_{1} and s2s_{2} are API sequences extracted from the statement stmt1stmt_{1} and stmt2stmt_{2}, respectively.

1.2 Extracting Annotations

To annotate the obtained API sequences with natural language descriptions, we extract method-level code summaries, specifically, the first sentence of a documentation commentA documentation comment in JAVA starts with slash-asterisk-asterisk (/**) and ends with asterisk-slash (*/) for a method. According to the Javadoc guidancehttp://www.oracle.com/technetwork/articles/java/index-137868.html, the first sentence is used as a short summary of a method. Figure 4 shows an example of documentation comment for a Java method IOUtils.copyLargeIOUtils.copyLargehttps://github.com/apache/commons-io/blob/trunk/src/main/java/org/apache/commons/io/IOUtils.java in the Apache commons-io library.

We use the Eclipse JDT compiler for the extraction. For each method, we traverse its AST and extract the JavaDoc Comment part. We ignore methods without JavaDoc comments. Then, we select the first sentence of the comment as the annotation. We exclude irregular annotations such as those starting with “TODO: Auto-generated method stub”, “NOTE:”, and “test”. We also filter out non-words and words within brackets in the annotations.

Finally, we obtain a database consisting of 7,519,907 ⟨\langleAPI sequence, annotation⟩\rangle pairs.

2 Training Encoder-Decoder Language Model

As described in Section 3, we adapt the attention-based RNN Encoder-Decoder model for API learning. The RNN has various implementations, we use GRU which is a state-of-the-art RNN and performs well in many tasks . We construct the model as follows: we use two RNNs for the encoder - a forward RNN that directly encodes the source sentence and a backward RNN that encodes the reversed source sentence. Their output context vectors are concatenated to the decoder, which is also an RNN. All RNNs have 1000 hidden units. We set the dimension of word embedding to 120. We discuss the details of parameter tuning in Section 5.4.

All models are trained using the minibatch Adadelta , which automatically adjusts the learning rate. We set the batch size (i.e., number of instances per batch) as 200. For training the neural networks, we limit the source and target vocabulary to the top 10,000 words that are most frequently used in API sequences and annotations.

For implementation, we use GroundHog , an open-source deep learning framework. We train our models in a server with one Nvidia K20 GPU. The training lasts ∼\sim240 hours with 1 million iterations.

3 Translation

So far we have discussed the training of a neural language model, which outputs the most likely API sequence given a natural language query. However, an API could have multiple usages. To obtain a ranked list of possible API sequences for user selection, we need to generate more API sequences according to their probability at each step.

DeepAPI uses Beam Search , a heuristic search strategy, to find API sequences that have the least cost value(computed using Equation 13) given by the language model. Beam search searches APIs produced at each step one by one. At each time step, it selects nn APIs from all branches with the least cost values, where nn is the beam-width. It then prunes off the remaining branches and continues selecting the possible APIs that follow on until it meets the end-of-sequence symbol. Figure 5 shows an example of a beam search (beam-width=2) for generating an API sequence for the query “read text file”. First, ‘START’ is selected as the first API in the generated sequence. Then, it estimates the probabilities of all possible APIs that follow on according to the language model. It computes their cost values according to Equation 13, and selects File.new and FileInputStream.new which have the least cost values of 6 and 8, respectively. Then, it ignores branches of other APIs and continue estimating possible APIs after File.new and FileInputStream.new. Once it selects an end-of-sequence symbol as the next API, it stops that branch and the branch is selected as a generated sequence.

Finally, DeepAPI produces nn API sequences for each query where nn is the beam-width. We rank the generated API sequences according to their average cost values during the beam search procedure.

Evaluation

We evaluate the effectiveness of DeepAPI by measuring its accuracy on API sequence generation. Specifically, our evaluation addresses the following research questions:

RQ1: How accurate is DeepAPI for generating API usage sequences?

RQ2: How accurate is DeepAPI under different parameter settings?

RQ3: Do the enhanced RNN Encoder-Decoder models improve the accuracy of DeepAPI?

We use the BLEU score to measure the accuracy of generated API sequences. The BLEU score measures how close a candidate sequence is to a reference sequence (usually a human written sequence). It is a widely used accuracy measure for machine translation in the machine learning and natural language processing literature . In our API learning context, we regard a generated API sequence given a query as a candidate, and a human-written API sequence (extracted from code) for the same query as a reference. We use BLEU to measure how close the generated API sequence is to a human-written API sequence.

Generally, BLEU measures the hits of n-grams of a candidate sequence to the reference. It is computed as:

where each pnp_{n} is the precision of n-grams, that is, the ratio of length nn subsequences in the candidate that are also in the reference:

where NN is the maximum number of grams we consider. We set NN to 4, which is a common practice in the Machine Learning literature . Each wnw_{n} is the weight of each pnp_{n}. A common practice is to set wn=1Nw_{n}=\frac{1}{N}. BPBP is a brevity penalty which penalties overly short candidates (that may have a higher n-gram precision).

where rr is the length of the reference sequence, and cc is the length of the candidate sequence.

We now give an example of BLEU calculation. For a candidate API sequence {a-c-d-b} and a reference API sequence {a-b-c-d-e}, their 1-grams are {a,b,c,d} and {a,b,c,d,e}. All four 1-grams of the candidate are hit in the reference. Then, p1=4+14+1=1p_{1}=\frac{4+1}{4+1}=1. Their 2-grams are {ac,cd,db} and {ab,bc,cd,de}, respectively. Then, p2=1+13+1=12p_{2}=\frac{1+1}{3+1}=\frac{1}{2} as only cdcd is matched. p3=0+12+1=13p_{3}=\frac{0+1}{2+1}=\frac{1}{3} and p4=0+11+1=12p_{4}=\frac{0+1}{1+1}=\frac{1}{2} as no 3-gram nor 4-gram is matched. As their lengths are 4 and 5 respectively, BP=e(1−5/4)=0.78BP=e^{(1-5/4)}=0.78. The final BLEU is 0.78×exp(14×log1+14×log12+14×log13+14×log12)=41.91%0.78\times exp(\frac{1}{4}\times log1+\frac{1}{4}\times log\frac{1}{2}+\frac{1}{4}\times log\frac{1}{3}+\frac{1}{4}\times log\frac{1}{2})=41.91\%

BLEU is usually expressed as a percentage value between 0 and 100. The higher the BLEU, the closer the candidate sequence is to the reference. If the candidate sequence is completely equal to the reference, the BLEU becomes 100%.

1.2 Extrinsic Measures - FRank and Relevancy Ratio

We also use two measures for human evaluation. They are FRank and relevancy ratio . FRank is the rank of the first relevant result in the result list . It is important as most users scan the results from top to bottom.

The relevancy ratio is defined as the precision of relevant results in a number of results .

The value of both measures ranges from 0 to 100. The higher the better.

2 Comparison Methods

We compare the accuracy of our approach with that of two state-of-the-art API learning approaches, namely Code Search with Pattern Mining and SWIM .

To obtain relevant API sequences for a given a query, one can perform code search over the code corpus using information retrieval techniques , and then utilize an API usage pattern miner to identify an appropriate API sequences in the returned code snippets.

We compare DeepAPI with this approach. We use Lucene to perform a code search for a given natural language query and UP-Miner to perform API usage pattern mining. Lucene is an open-source information retrieval engine, which has been integrated into many code search engines . Much the same as these code search engines do, we treat source code as plain text documents and use Lucene to build source code index and perform text retrieval. UP-Miner is a pattern mining tool, which produces API sequence patterns from code snippets. It first clusters API sequences extracted from code snippets, and then identifies frequent patterns from the clustered sequences. Finally, it clusters the frequent patterns to reduce redundancy. We use UP-Miner to mine API usage sequences from the code snippets returned by the Lucene-based code search.

In this experiment, we use the same code corpus as used for evaluating DeepAPI, and compare the BLEU scores with those of DeepAPI.

2.2 SWIM

SWIM is a recently proposed code synthesis tool, which also supports API sequence search based on a natural language query. Given a query, it expands the query keywords to a list of relevant APIs using a statistical word alignment model . With the list of possible APIs, SWIM searches related API sequences using Lucene . Finally, it synthesizes code snippets based on the API sequences. As code synthesis is beyond our scope, we only compare DeepAPI with the API learning component of SWIM, that is, from a natural language query to an API sequence. In their experiments, SWIM uses Bing clickthrough data to build the model. In our experiment, for fair comparison, we evaluate SWIM using the same dataset as we did for evaluating DeepAPI. That is, we train the word alignment model and build API index on the training set, and evaluate the search results on the test set.

3 Accuracy (RQ1)

Evaluation Setup: We first evaluate the accuracy of generated API sequences using the BLEU score. As described in Section 4.1, we collect a database comprising 7,519,907 ⟨\langleAPI sequence, annotation⟩\rangle pairs. We split them into a test set and a training set. The test set comprises 10,000 pairs while the training set consists of the remaining instances. We train all models using the training set and compute the BLEU scores in the test set. We calculate the highest BLEU score for each test instance in the top nn results.

Results: Table 1 shows the BLEU scores of DeepAPI, SWIM, and Code Search (Lucene+UP-Miner). Each column shows the average BLEU score for a method. As the results indicate, DeepAPI produces API sequences with higher accuracy. When only the top 1 result is examined, the BLEU score achieved by DeepAPI is 54.42, which is greater than that of SWIM (BLEU=19.90) and Code Search (BLEU=11.97). The improvement over SWIM is 173% and the improvement over Code Search is 355%. Similar results are obtained when the top 5 and 10 results are examined. The evaluation results confirm the effectiveness of the deep learning method used by DeepAPI.

3.2 Extrinsic Evaluation

To further evaluate the relevancy of the results returned by DeepAPI, we selected 17 queries used in . These queries have corresponding Java APIs and are commonly occurring queries in the Bing search log . To demonstrate the advantages of DeepAPI, we also designed 13 longer queries and queries with semantically similar words. In total, 30 queries are used. These queries do not appear in the training set. Table 2 lists the queries.

For each query, the top 10 returned results by DeepAPI and SWIM are manually examined. To reduce labeling bias, two developers separately label the relevancy of each resulting sequence and combine their labels. For inconsistent labels, they discuss and relabel them until a settlement is reached. The FRank and the relevancy ratios for the top 5 and top 10 returned results are then computed. To test the statistical significance, we apply the Wilcoxon signed-rank test (pp<<0.05) for all results of both approaches. A resulting p-value less than 0.05 indicates that the differences between DeepAPI and SWIM are statistically significant.

Table 2 shows the accuracy of both DeepAPI and SWIM. The symbol ‘-’ means no relevant result has been returned within the top 10 results. The results show that DeepAPI is able to produce mostly relevant results. It achieves an average FRank of 1.6, an average top 5 accuracy of 80%, and an average top 10 accuracy of 78%. Furthermore, DeepAPI produces more relevant API sequences than SWIM, whose average top 5 and top 10 accuracy is 44% and 47%, respectively. For some queries, SWIM failed to obtain relevant results in the top 10 returned results. We conservatively treat the FRank as 11 for these unsuccessful queries. Then, the FRank achieved by SWIM is greater than 4.0, which is much higher than what DeepAPI achieved (1.60). The p-values for the three comparisons are 0.01, 0.02 and 0.01, respectively, indicating statistical significance of the improvement of DeepAPI over SWIM. In summary, the evaluation results confirm the effectiveness of DeepAPI.

Table 2 also shows examples of generated sequences by DeepAPI. We can see that DeepAPI is able to distinguish word sequences. For example, DeepAPI successfully distinguishes the query convert int to string from convert string to int. Another successful example is the query expansion. For example, the query save an image to a file and write an image to a file return similar results. DeepAPI also performs well in longer queries such as copy a file and save it to your destination path and play the audio clip at the specified absolute URL. Such queries comprise many keywords, and DeepAPI can successfully recognize the semantics.

We also manually check the results returned by SWIM. We find SWIM may return partially matched sequences. For example, for the query generate md5 hash code, SWIM returns many results containing only Object.hashCode, which simply returns a hash code. SWIM also returns project specific results without fully understanding the query. For example, for the query “test file exists”, SWIM returns “File.new, File.exists, File.getName, File.new, File.delete, FileInputStream.new, FileInputStream.read, …”, which is not only related to file existence test, but also to other project-specific tasks. Such project specific results can also be seen for the query create file. Compared with DeepAPI, SWIM performs worse in long queries. For example, SWIM performs worse in the query copy a file and save it to your destination path than in the query copy file. This is because long queries often have multiple objectives, which cannot be understood by SWIM.

Still, DeepAPI could return inaccurate or partial results. For example, for the query parse xml, it returns related APIs InputSource.new, DocumentBuilder.parse. But it misses the APIs about how DocumentBuilder is created (DocumentBuilderFactory.newDocumentBuilder). The reason could be that an API sequence may be called in an inter-procedural manner. When preparing the training set, we only consider API sequences within one method. The API DocumentBuilderFactory.newDocumentBuilder could be called in another method and is passed as a parameter. This causes incomplete sequences in the training set. In the future, we will perform more accurate program analysis and create a better training set.

4 Accuracy Under Different Parameter Settings (RQ2)

We also qualitatively compare the accuracy of DeepAPI in different parameter settings. We analyze two parameters, that is, the dimension of word embedding and the number of hidden units. We vary the values of these two parameters and evaluate their impact on the BLEU scores.

Figure 6 shows the influence of different parameter settings on the test set. The dimension of word embedding makes little difference to the accuracy. The accuracy of DeepAPI greately depends on the number of hidden units in the hidden layer. The optimum number of hidden units is around 1000.

5 Performance of the Enhanced RNN Encoder-Decoder Models (RQ3)

In Section 3, we describe two enhancements to the original RNN Encoder-Decoder model, for the task of API learning: an attention-based RNN Encoder-Decoder proposed by (Section 3.1) and an enhanced RNN Encoder-Decoder with a new cost function (Section 3.2) proposed by us. We now evaluate if the enhanced models improve the accuracy of DeepAPI when constructed using the original RNN Encoder-Decoder model.

Table 3 shows the BLEU scores of the three models. The attention-based RNN Encoder-Decoder outperforms the basic RNN Encoder-Decoder model on API learning. The relative improvement in the top 1, 5, and 10 results (in terms of BLEU score) is 8%, 5% and 4%, respectively. This result confirms the effectiveness of the attention-based RNN Encoder-Decoder used in our approach.

Table 3 also shows that the enhanced model with the new cost function leads to better results as compared to the attention-based RNN Encoder-Decoder model. The improvement in the top 1, 5, and 10 results (in terms of BLEU score) is 4%, 2% and 1%, respectively. Figure 7 shows that the performance of the enhanced model are slightly different under different parameter settings, with an optimum λ\lambda of around 0.035. The results confirm the usefulness of the proposed cost function for enhancing the RNN Encoder-Decoder model.

Discussion

A major challenge for API learning is the semantic gap between code and natural language descriptions. Existing information retrieval based approaches usually have a bag-of-words assumption and lack a deep understanding of the high-level semantics of natural language and code. We have identified three advantages of DeepAPI that address this problem.

Word embedding and query expansion A significant difference between DeepAPI and bag-of-words methods is, DeepAPI embeds words into a continuous semantic space where the semantically similar words are placed close to each other. When reading words in a query, the model maps them to semantic vectors. Words with similar semantics have similar vector representations and have a similar impact on the hidden states of the RNN encoder. Therefore, queries with semantically similar words can lead to similar results. Figure 8 shows a 2-D projection of the encoded vectors of queries. These queries are selected from the 10,000 annotations in the test set. For ease of demonstration, we select queries with a keyword “file” and exclude those longer than eight words. As shown in the graph, DeepAPI can successfully embed similar queries into a nearby place. There are three clear clusters of queries, corresponding to “read/load files”,“write/save files”, and “remove/delete files”. Queries with semantically related words are close to each other. For example, queries starting with save, write, and output are in the same “cluster” though they contain different words. Learning sequence instead of bag-of-words The hidden layer of the encoder has the memory capacity. It considers not only the individual words, but also their relative positions. Even for the same word set, different sequences will be encoded to different vectors, resulting in different API sequences. In that sense, DeepAPI learns not only the words, but also phrases. While traditional models simply consider individual words or word-level alignments. A typical example is that, queries with different word sequences such as convert int to string and convert string to int can be distinguished well by DeepAPI. Generating common patterns instead of searching specific samples Another advantage of our approach is that, it can learn common patterns of API sequences. The decoder itself is a language model and remembers the likelihoods of different sequences. Those common sequences will have high probabilities according to the model. Therefore, it tends to generate common API sequences rather than project-specific ones. On the other hand, the information retrieval based approaches simply consider searching individual instances and could return project-specific API sequences.

Though several techniques such as query expansion and frequent pattern mining can partially solve some of the above problems, their effectiveness remains to be improved. For example, it has been observed that expanding a code search query with inappropriate English synonyms can return even worse results as compared to the original query . Furthermore, few techniques can exhibit all the above advantages.

2 Threats to Validity

We have identified the following threats to validity: All APIs studied are Java APIs All APIs and related projects investigated in this paper are JDK APIs. Hence, they might not be representative of APIs for other libraries and programming languages. In the future, we will extend the model to other libraries and programming languages. Quality of annotations We collected annotations of API sequences from the first sentence of documentation comments. Other sentences in the comments may also be informative. In addition, the first sentences may have noise. In the future, we will investigate a better NLP technique to extract annotations for code. Training dataset In the original SWIM paper , the clickthrough data from Bing.com is used for evaluation. Such data is not easy accessible for most researchers. For fair and easy comparison, we evaluate SWIM on the dataset collected from GitHub and Java documentations (the same for evaluating DeepAPI). We train the models using annotations of API sequences collected from the documentation comments. In the future, we will evaluate both SWIM and DeepAPI on a variety of datasets including the Bing clickthrough data. In the future, we will perform more accurate program analysis and create a better training set.

Related Work

There is a large amount of work on code search . For example, McMillan et al. proposed a code search tool called Portfolio that retrieves and visualizes relevant functions and their usages. Chan and Cheng designed an approach to help users find usages of APIs given only simple text phrases. Lv et al. proposed CodeHow, a code search tool that incorporates an extended Boolean model and API matching. They first find relevant APIs to a query by matching the query to API documentation. Then, they improve code search performance by considering the APIs which are relevant to the query in code retrieval. As described in Section 6, DeepAPI differs from code search techniques in that it does not rely on information retrieval techniques and can understand word sequences and query semantics.

2 Mining API Usage Patterns

Instead of generating API sequences from natural language queries, there is a number of techniques focusing on mining API usage patterns . API usage patterns are frequent API method call sequences. Xie et al. proposed MAPO, which is one of the first works on mining API patterns from code corpus. MAPO represents source code as call sequences and clusters them according to similarity heuristics such as method names. It finally generates patterns by mining and ranking frequent sequences in each cluster. UP-Miner is an improvement of MAPO, which removes the redundancy among patterns by two rounds of clustering of the method call sequences. By applying API usage pattern mining on large-scale code search results, these techniques can also return API usage sequences in response to user’s natural language queries.

While the above techniques are useful for understanding the usage of an API, they are insufficient for answering the question of which APIs to use, which is the aim of DeepAPI. Furthermore, different from a frequent pattern mining approach, DeepAPI constructs a neural language model to learn usage patterns.

3 From Natural Language to Code

A number of related techniques have been proposed to generate code snippets from natural language queries. For example, Raghothaman et al. proposed SWIM, a code synthesis technique that translates user queries into the APIs of interest using Bing search logs and then synthesizes idiomatic code describing the use of these APIs. SWIM has a component that produces API sequences given user’s natural language query. Our approach and SWIM differ in many aspects. First, SWIM generates bags of APIs using statistical word alignment . The word alignment model does not consider word embeddings and word sequences of natural language queries, and has limitations in query understanding. Second, to produce API sequences, SWIM searches API sequences from the code repository using a bag of APIs. It does not consider the relative position of different APIs. Fowkes and Sutton build probabilistic models that jointly model short natural language utterances and source code snippets. The main differences between our approach and theirs are two-fold. First, they use a bag-of-words model to represent natural language sentences which will not recognize word sequences. Second, they use a traditional probabilistic model which is unable to recognize semantically related words.

4 Deep Learning for Source Code

Recently, some researchers have explored the possibility of applying deep learning techniques to source code . A typical application that leverages deep learning is to extract source code features . For example, Mou et al. proposed to learn vector representations of source code for deep learning tasks. Mou et al. also proposed convolutional neural networks over tree structures for programming language processing. Deep learning has also been applied to code generation . For example, Mou et al. proposed to generate code from natural language user intentions using an RNN Encoder-Decoder model. Their results show the feasibility of applying deep learning techniques to code generation from a highly homogeneous dataset (simple programming assignments). Deep Learning has also been applied to code completion . For example, White et al. applied the RNN language model to source code files and showed its effectiveness in predicting software tokens. Raychev et al. proposed to apply the RNN language model to complete partial programs with holes. In our work, we explore the application of deep learning techniques to API learning.

Conclusion

In this paper, we apply a deep learning approach, RNN Encoder-Decoder, for generating API usage sequences for a given API-related natural language query. Our empirical study has shown that the proposed approach is effective in API sequence generation. Although deep learning has shown promise in other areas, we are the first to observe its effectiveness in API learning.

The RNN Encoder-Decoder based neural language model described in this paper may benefit other software engineering problems such as code search and bug localization. In the future, we will explore the applications of this model to these problems. We will also investigate the synthesis of sample code from the generated API sequences.

An online demo of DeepAPI can be found on our website at: https://guxd.github.io/deepapi/.

References