一种软件程序的智能解析方法与流程

文档序号:16428240发布日期:2018-12-28 19:57阅读:665来源:国知局
一种软件程序的智能解析方法与流程

本发明涉及一种解析方法,尤其涉及一种软件程序的智能解析方法。

背景技术

程序理解(programcomprehension)在许多软件工程的应用中起到至关重要的作用。一方面能够帮助开发者提高开发效率和代码质量,另一方面也有助于软件维护。典型的应用包括代码分类,代码克隆检测,缺陷预测以及代码摘要生成等。其中,实现程序理解很重要同时也是较为困难的一部分是如何建立有效的程序表示方法以尽可能地捕捉程序本身蕴含的语义,比如某程序的功能。

虽然我们可以以人工的方式去设计或提取程序的特征来实现诸多相关的应用,这种方法的缺点在于需要耗费大量的劳动力且得出的特征往往局限于特定的任务。因此现有技术往往直接采用传统的信息检索中的一些方法来提取程序中的特征。比如,将程序看作词的集合或者序列来实现代码的克隆检测。此外,主题模型如潜在语义分析(latentsemanticindexing,lsi)或隐含狄利克雷分布(latentdirichletallocation,lda)也被广泛应用于程序分析与处理。然而这些应用的共同问题是把代码当做自然语言进而套用自然语言处理(naturallanguageprocessing,nlp)中的方法。实际上,代码中包含丰富的结构信息,而上述的方法并不能捕捉到这种信息。

最新的技术使用解析工具将程序代码转换成抽象语法树(abstractsyntaxtree,ast)来结构化地表示程序。在此基础上,结合神经网路的方法来自动的获取程序的向量表示,用于不同的任务和场景。比如,在得到某个程序的ast之后,可以直接利用递归神经网络(recursiveneuralnetwork)对整个语法树进行自底向上的递归式的编码,进而得到最终的语义向量。类似地,现有的研究也结合了基于树的卷积神经网络(convolutionalneuralnetwork,cnn)或者长短时记忆网络(longshorttermmemory,lstm)来增强结构信息的表示能力。但是这些方法仍然存在三个主要问题:

1.仅依赖于树结构的神经网络容易导致梯度消失的问题;

2.自底向上或使用滑动窗口遍历整个ast的方法容易丢失原始的程序中所包含的代码块之间的依赖信息;

3.现有的基于树的方法大多将原始的ast转换成二叉树的形式,这破坏了原始的结构信息,并且会导致树的深度大大增加而进一步带来捕捉依赖的困难。



技术实现要素:

针对现有技术存在的问题,本发明提出了一种软件程序的智能解析方法。所述智能解析方法为基于ast的序列化神经网络。首先,对于获取到的ast,将其分割成一系列的代码块也就是子树并保持原始的语句顺序,而不是对整个树结构进行编码计算。每个代码块为池化层为根的子树。其次,在得到这种代码块序列之后,利用循环神经网络(recurrentneuralnetwork,rnn)来捕捉这种上下文依赖关系。最后,对于子树的计算使用批量计算算法来避免破坏原始的结构信息。经过端到端的训练,学习到的向量可以更好地解决不同的软件分析任务。

附图说明

图1为本发明基于ast的序列化神经网络的整体架构图。

图2为本发明中节点迭代更新示意图。

具体实施方式

为了使本发明的目的、技术方案及优点更加清楚明白,以下结合附图及实施例,对本发明进行进一步详细说明。应当理解,此处所描述的具体实施例仅仅用以解释本发明,并不用于限定本发明。此外,下面所描述的本发明各个实施方式中所涉及到的技术特征只要彼此之间未构成冲突就可以相互组合。

如图1所示为本发明基于ast的序列化神经网络的整体架构,本发明的神经网络结构是一种层次化的向量表示方法。该方法的步骤如下:

步骤1:在最底层,将原始程序转化成ast之后进一步分割得到对应的代码块序列。这些代码块之间是有次序之分的,且与原始的语句顺序相吻合;

步骤2:每个代码块由一种递归式的编码器来将这种子树编码成向量e1,e2,...,et,t为正整数;

步骤3:上述得到的向量序列经过双向循环层以提取代码块之间的依赖特征;

步骤4:所有的时间步的多维特征经过池化层采样得到最终的单个向量表示形式,来表达该程序的语义信息。

在将程序转化成ast之后,我们首先以前序遍历的方式得到此树的标识符序列,并利用现有的词嵌入技术(如word2vec)将每个标识符转化成向量的形式,以便于上层的编码。接下来将原始的ast分割成代码块序列。该方法包括两个部分:遍历器(traverser)与构造器(constructor)。

在该方法中,遍历器用于深度优先地遍历原始的ast,而构造器则遵循规则构造代码块的子树。具体步骤为,输入某个程序的ast的根节点,当遍历器访问到的某个元素为复合语句的头部如函数定义或者控制语句等,调用构造器将此头部语句的不包含嵌套的复合语句元素的子树形式构造出来。如果当前访问的元素是复合语句,则对于其中的所有的单个语句递归地调用遍历器或者构造子树。最终,所有的构造出来的子树被存入到了一个全局的有序集合中,也就是所说的代码块序列。

上述得到的每个代码块是一种多叉树的形式,树的每个节点是一个标识符。对于给定的代码块t,n表示其中的非终结点,c表示其子节点的集合,则具体的编码步骤为:

步骤1:根据公式vn=wexn得到该节点的词向量,其中we是经过词向量训练方法word2vec得到的预训练的权重,xn是该节点的one-hot向量表示形式。

步骤2:对节点n的向量表示与其子节点的向量表示组合计算,

其中,wn是权重矩阵,bn是偏置项,hi是子节点的向量表示,σ是激活函数,如tanh函数或者恒等函数。

步骤3:经过递归计算,每个节点都有了其向量表示。对于代码块中的n个节点,利用池化对其进行特征采样,以得到该代码块的向量表示et。其计算方式为:

et=[max(hi1),...,max(hik)],i∈[1,n],这里k表示向量的k个元素,i为n个节点的索引。

一般来说,由于每个代码块是多叉树的形式,其结构是多变的,因此难以进行批量计算。为了提高计算效率,本发明设计了一种动态的批量计算方法。该方法的具体实现如下所描述。在此方法中,批量计算l个代码块,并且以深度优先的方式从每个代码块的根节点开始计算。首先,对于当前的所有节点,利用矩阵相乘的方式根据步骤1批量计算其所有的词向量,然后检查每个节点是否有子节点并且按照节点所属的索引进行迭代更新。更新的方式图2所示,当前批处理的节点为灰色节点,在检查其子节点之后,将子节点分成新的多批节点并记录其索引,以此进行迭代批量计算。这样递归地批量计算其子节点的向量后,按照步骤2中的公式进行向量的隐藏状态更新。所有的以向量表示的节点被置入一个全局的张量中。最后对该张量进行池化,得到每个代码块的最终向量表示。

在循环层中,使用双向门控循环单元(gatedrecurrentunit,gru)来表征代码块之间的依赖信息。假设一个序列中有t个代码块,其表达形式为[e1,...,et],t∈[1,t]。在t时刻,其隐藏状态的向量计算方式为:

rt=σ(wret+urht-1+br)

zt=σ(wzet+uzht-1、+bz)

其中,rt表示重置门,用于控制前一个状态对当前状态的影响;zt表示更新门,用于将历史状态与当前状态结合;是候选状态,用于与历史状态以及当前更新的状态进行线性组合计算,以决定最终的当前状态ht。这里所有的w和u是权重矩阵,b是对应的偏置项。σ是sigmoid激活函数。在进行迭代计算之后,

前向的依赖信息便自动获取到。除此之外,使用了反向的gru,其计算公式与前向的gru一致。不同点在于,计算顺序与之相反,也就是其起始位置为最后一个代码块的向量,结束位置为第一个代码块的向量。并且将这两个方向计算得到的隐藏状态连接到一起组成某时刻的状态。此过程用公式表述如下:

最终,再用最大池化或平均池化来对每一时刻的隐藏状态进行采样,得到整个程序的向量表示r。

本方案中提出的软件程序向量表示方法可用于不同任务的训练与预测,如代码分类任务以及代码克隆检测。

对于代码分类,假设有m个类别,首先根据得到与m个类别的映射,这里wo为权重矩阵。于是损失函数可定义为交叉熵:

这里θ表示模型中所有的待优化的权重参数,y表示真实的类别,i表示样本的索引编号,j表示类别编号。

对于代码克隆检测,用r1和r2分别代表两个程序的向量,则可以用|r1-r2|来度量其向量之间的相似度,然后利用上述同样的方式得到于是损失函数定义为:

训练上述模型即优化其损失函数,使之最小化。我们利用现有的优化器如adamax。

在得到训练后的模型之后,便可用于不同任务的预测。对于代码分类,其预测值p为对应于多个标签的概率。取最大概率对应的类别即可,如

同样地,对于代码克隆检测,p是在[0,1]范围内的实数。根据阈值便可以判断结果,即若p大于δ,则认为是克隆,否则认为不是克隆。一般地,δ取值为0.5。

最后应说明的是:以上实施例仅用以说明本发明的技术方案,而非对其限制;尽管参照前述实施例对本发明进行了详细的说明,本领域的普通技术人员应当理解:其依然可以对前述实施例所记载的技术方案进行修改,或者对其中部分技术特征进行等同替换;而这些修改或者替换,并不使相应技术方案的本质脱离本发明各实施例技术方案的精神和范围。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1