一种基于长短期记忆网络的代码推荐方法转让专利
申请号 : CN201710687197.4
文献号 : CN107506414B
文献日 : 2020-01-07
发明人 : 余啸 , 殷晓飞 , 刘进 , 伍蔓 , 姜加明 , 崔晓晖
申请人 : 武汉大学
摘要 :
权利要求 :
1.一种基于长短期记忆网络的代码推荐方法,其特征在于,包括以下步骤:
步骤1,通过网络爬虫从GitHub网站爬取至少一万个Java开源软件代码,且每个Java开源软件代码的更新版本次数均超过1000次,这些开源软件代码构成了源代码库,然后对源代码进行预处理形成API序列事务库,并生成API字典和API向量矩阵,具体包括:步骤1.1,使用网络爬虫从GitHub网站爬取至少一万个更新版本次数已超过至少1000次的Java开源软件代码,形成源代码库;
步骤1.2,以方法为单位,对一个方法中包含的代码提取出该方法的API序列,源代码库中所有方法提取出的所有API序列组成了API序列事务库;对方法中包含的代码提取出API序列的规则为只提取新建对象语句的API和对象调用方法语句的API;新建对象语句提取出的API表示为“类名.new”,这里的类名为新建的对象所属的类的名字;对象调用方法语句提取出的API表示为“类名.方法名”,这里的类名为该对象所属的类的名字;
步骤1.3,从API序列事务库中提取出API字典,并生成API向量矩阵;
API字典定义为:设API序列事务库为D,API字典可以表示为VD={1:API1,w1,2:API2,w2,…,i:APIi,wi,…n:APIn,wn},n为API字典的包含的API的个数,APIi表示VD中第i个API的名称,wi表示集合VD中第i个API的向量;
API字典和API向量矩阵的生成过程为:遍历API序列事务库,判断当前API是否存在于API字典中,如果存在,则忽略当前API,继续遍历下一个API,否则,将当前API加入到API字典中,并赋予其唯一的ID和赋予其一个随机的M维API向量;API字典中包含的n个API的n个M维的API向量组成了API向量矩阵;API向量矩阵是作为长短期记忆网络(Long Short-Term Memory,LSTM)模型的参数,在训练LSTM模型时会对API向量进行学习;
步骤2,构建API推荐模型,即构建长短期记忆网络;定义长短期记忆网络包括输入层、隐藏层、全链接层和输出层;其中,
输入层接受一串数值输入,经过前向传播输入到隐藏层,并与隐藏层上一时刻的输出共同影响隐藏层的当前输出,最后隐藏层产生的输出输入全链接层,全链接层输出数据,输入到输出层,输出层中的Softmax分类器输出最后的分类结果;
隐藏层的神经单元为长短期记忆单元,使用dropout技术防止长短期记忆网络过拟合,神经元激活函数使用ReLu函数;输入层的神经元个数为M,M为步骤1.3中生成的API向量的维度;隐藏层神经元个数为M,全链接层神经元个数为M,输出层的神经元个数为n,n为API字典中包含的API的个数,M、n的取值均为正整数;
步骤3,训练API推荐模型,即训练长短期记忆网络;
API推荐模型的输入为一个Nb×Ns行M列的矩阵Tinput,其中Nb表示批大小,Ns表示序列长度,M表示API向量的维度,矩阵第i行表示输入序列中的第i个API所对应的向量;
API推荐模型的目标矩阵Ttarget是一个Nb行Ns列的矩阵,其中第i行第j列表示输入序列中的第i个API所对应的目标输出API在步骤1.3中生成的API字典中的ID;
API推荐模型的输出为Nb×Ns行n列的输出概率矩阵Tprob,其中n表示API字典中包含的API的个数,第i行第j列的数表示输入序列中的第i个API输入后所预测的下一个API属于API字典中第j个API的概率;
该步骤包括以下步骤:
步骤3.1,将API序列事务库中的所有API序列头尾相连,产生一个API总序列;
步骤3.2,设置一个指针变量point,且point的初始值为1,从API总序列的第point个API开始,每次依次提取Ns个API,一共提取Nb批,对于每一个API,从API字典中读取其对应ID,并利用其ID,从API向量矩阵中提取该API所对应的向量,将其存入输入矩阵Tinput中;例如,第i批第j个API所对应的向量,存入输入矩阵Tinput中的第i×j行;对于目标矩阵,从API总序列的第point个API开始,每次依次提取Ns个API,一共提取Nb批,对于每一个API,从API字典中读取其对应ID,将对应的ID存入目标矩阵;最后,当输入矩阵和目标矩阵填充完毕后,令point变量指向API总序列中最后一个被目标矩阵读取的API;值得说明的是,当提取到API总序列中最后一个API后,继续提取API总序列中的第一个API;
步骤3.3,从输入矩阵中依次提取API向量,作为API推荐模型的输入,对于时刻t,从输入矩阵中依次每一行API的向量作为模型的输入向量,记该API为APIt,将输入标记为xt,则LSTM模型的隐藏层输入门计算结果为it=σ(wixt+uibt+vict-1),遗忘门计算为结果ft=σ(wfxt+ufbt+vfct-1),输出门计算为ot=σ(woxt+uobt+voct),最后隐藏层的输出为bt=ot·tanh(ct),数据从隐藏层传入全连接层,最后输出层使用Softmax分类器;输出层得到:其中|VD
|表示API字典中包含的API个数,θ表示神经网络当前权值,θ1表示输出层第一个输出节点对应的一套权值;最后,将公式转置,并存入输出概率矩阵中;重复此步骤,直到输入矩阵中的API向量全部输入API推荐模型当中;
步骤3.4,利用输出概率矩阵和目标矩阵计算交叉熵损失函数;交叉熵损失函数为其中,l表示指示函数,l(yt=j)表示当yt=j时,l(yt=j)=1,否则l(yt=j)=0,yt表示时刻i的目标输出API的ID; 表示输出概率矩阵中第i行第j列的输出概率;
步骤3.5,根据交叉熵损失函数,将网络中的权值W作为变量,计算网络中所有权值的梯度;并同时基于梯度剪裁,将权值的更新控制在一个设定范围;具体是:先设置一个名字为梯度剪裁的常量,标记为clip_gradient,在进行了反向传播时,将会得到每个参数的梯度,标记为diff,此时,并不选择直接更新权值,而是先求所有权重梯度的平方和,标记为sumsq_diff,如果所有权重梯度的平方和大于clip_gradient,则继续求出缩放因子,标记为scale_factor=clip_gradient/sumsq_diff;这个scale_factor在(0,1)之间;如果权重梯度的平方和sumsq_diff越大,那缩放因子将越小;最后将所有的权重梯度乘以这个缩放因子,这时得到的梯度才是最后的梯度信息;根据公式W=W-η▽J(θ)更新权值,▽J(θ)表示的是对应权值梯度,η表示学习率;
步骤3.6,重复步骤3.2-3.5,直至收敛,即损失J(θ)不再上升或者下降;
步骤4,对开发者正在编辑的代码提取出API序列,然后生成预测子序列集合;
步骤4.1,对开发者正在编辑的代码提取出API序列,并记为P={P1,P2,…,Pi,…,PL},其中Pi表示该API序列P中第i个API,PL表示该API序列P中第L个API,也即该API序列P中包含的API个数为L;提取出API序列的规则与步骤1.2中的规则相同;
步骤4.2,以第L个API为参考位置,向前选择所有长度小于等于阈值γ的子序列,即选取的子序列为Subi={PL-i,…,PL},其中1
步骤5,将步骤4中生成的预测子序列集合VSub中的序列依次输入到步骤3训练好的API推荐模型中,输出一个|VSub|行n列的概率矩阵,其中|VSub|为子序列集合VSub中包含的子序列个数,n为步骤一中产生的API字典中包含的API个数,概率矩阵的第i行第j列表示当先前API序列是预测子序列Subi时,下一个API是API字典中的第j个API的条件概率Pr(wj|Subi);
将产生的预测概率矩阵Tprediction每一列取最大值,得到一个一维概率矩阵t,该一维概率矩阵中值最大的一列为第m列,则优先推荐API字典中第m个API。
说明书 :
一种基于长短期记忆网络的代码推荐方法
技术领域
背景技术
发明内容
附图说明
具体实施方式
BufferedReader.readLine,第八条语句“System.out.println(lineTxt)”为对象调用语句,提取出的API为System.out.println,第九条语句“read.close()”为对象调用语句,提取出的API为InputStreamReader.close。因此,图2中的readTxtFile方法中代码所提取出的API序列为File.new,File.isFile,FileInputStream.new,InputStreamReader.new,BufferedReader.new,BufferedReader.readLine,System.out.println,
InputStreamReader.close。
InputStreamRead.new,w4}。第一个API序列的第五个API BufferedRead.new不存在于API字典中,赋予其唯一的ID为5和赋予其一个随机的100维API向量w5=[0.1,0.6,0.5,
0.6,…,0.5],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:
File.ifFile,w2,3:FileInputStream.new,w3,4:InputStreamRead.new,w4,5:
BufferedRead.new,w5}。第一个API序列的第六个API BufferedRead.readLine不存在于API字典中,赋予其唯一的ID为6和赋予其一个随机的100维API向量w6=[0.5,0.3,0.5,
0.7,…,0.3],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:
File.ifFile,w2,3:FileInputStream.new,w3,4:InputStreamRead.new,w4,5:
BufferedRead.new,w5,6:BufferedRead.readLine,w6}。第一个API序列的第七个API System.out.println不存在于API字典中,赋予其唯一的ID为7和赋予其一个随机的100维API向量w7=[0.1,0.3,0.5,0.5,…,0.5],并将其加入API字典中,当前的API字典为VD={1:
File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:System.out.println,w7}。
第一个API序列的第八个API InputStreamReader.close不存在于API字典中,赋予其唯一的ID为8和赋予其一个随机的100维API向量w8=[0.7,0.2,0.1,0.8,…,0.3],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:File.ifFile,w2,3:
FileInputStream.new,w3,4:InputStreamRead.new,w4,5:BufferedRead.new,w5,6:
BufferedRead.readLine,w6,7:System.out.println,w7,8:InputStreamReader.close,w8}。
InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:
System.out.println,w7,8:InputStreamReader.close,w8,9:Scanner.new,w9}。第二个API序列的第三个APIFile.new存在于API字典中,忽略。第二个API序列的第四个API FileWriter.new不存在于API字典中,赋予其唯一的ID为10和赋予其一个随机的100维API向量w10=[0.4,0.2,0.8,0.7,…,0.3],并将其加入API字典中,当前的API字典为VD={1:
File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:System.out.println,w7,8:
InputStreamReader.close,w8,9:Scanner.new,w9,10:FileWriter.new,w10}。第二个API序列的第五个API Scanner.hasNextLine不存在于API字典中,赋予其唯一的ID为11和赋予其一个随机的100维API向量w11=[0.1,0.4,0.5,0.3,…,0.1],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:
InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:
System.out.println,w7,8:InputStreamReader.close,w8,9:Scanner.new,w9,10:
FileWriter.new,w10,11:Scanner.hasNextLinec,w11}。第二个API序列的第六个API Scanner.nextLine不存在于API字典中,赋予其唯一的ID为12和赋予其一个随机的100维API向量w12=[0.5,0.3,0.5,0.7,…,0.3],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:
InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:
System.out.println,w7,8:InputStreamReader.close,w8,9:Scanner.new,w9,10:
FileWriter.new,w10,11:Scanner.hasNextLinec,w11,12:Scanner.nextLine,w12}。第二个API序列的第七个API FileWriter.append不存在于API字典中,赋予其唯一的ID为13和赋予其一个随机的100维API向量w13=[0.3,0.1,0.7,0.3,…,0.6],并将其加入API字典中,当前的API字典为VD={1:File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:
InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:
System.out.println,w7,8:InputStreamReader.close,w8,9:Scanner.new,w9,10:
FileWriter.new,w10,11:Scanner.hasNextLinec,w11,12:Scanner.nextLine,w12,13:
FileWriter.append,w13}。
BufferedRead.readLine,w6,7:System.out.println,w7,8:InputStreamReader.close,w8,
9:Scanner.new,w9,10:FileWriter.new,w10,11:Scanner.hasNextLinec,w11,12:
Scanner.nextLine,w12,13:FileWriter.append,w13,14:FileWriter.close,w14}。第二个API序列的第九个API Scanner.close不存在于API字典中,赋予其唯一的ID为15和赋予其一个随机的100维API向量w15=[0.5,0.2,0.3,0.1,…,0.2],并将其加入API字典中,最终提取出的API字典为VD={1:File.new,w1,2:File.ifFile,w2,3:FileInputStream.new,w3,4:
InputStreamRead.new,w4,5:BufferedRead.new,w5,6:BufferedRead.readLine,w6,7:
System.out.println,w7,8:InputStreamReader.close,w8,9:Scanner.new,w9,10:
FileWriter.new,w10,11:Scanner.hasNextLinec,w11,12:Scanner.nextLine,w12,13:
FileWriter.append,w13,14:FileWriter.close,w14,15:Scanner.close,w15}。本实施例中,API字典中包含的15个API的15个100维的API向量组成了如图5所示的API向量矩阵。
总序列的第2个API开始,依次提取2个API,一共提取2批,因此提取出的API为File.isFile,FileInputStream.new,InputStreamReader.new,BufferedReader.new,对于每一个API,从API字典中读取其对应ID,存入目标矩阵Ttarget中,因此目标矩阵