APP下载

CLPDetector:一种基于伪孪生网络的跨语言代码抄袭检测工具

2022-07-06李国繁

小型微型计算机系统 2022年7期
关键词:源代码代码向量

李国繁,张 峰,2,刘 聪

1(山东科技大学 计算机科学与工程学院,山东 青岛 266590) 2(山东省智慧矿山信息技术重点实验室,山东 青岛 266590) 3(山东理工大学 计算机科学与技术学院,山东 淄博 255000)

1 引 言

互联网的发展使得通过网络获取源代码变的越来越容易,也带来了代码抄袭问题.源代码抄袭检测技术被越来越多的学者研究,其研究成果在计算机程序教学[1](通过Online Judge[2]提交代码)和知识产权保护方面有着广泛的应用[3].近年来,一些跨编程语言的代码自动转换工具(1)https://www.tangiblesoftwaresolutions.com的出现,使得计算机程序教学中跨语言的代码抄袭检测成为一个新的挑战.现有的代码抄袭检测方法主要用于检测同种语言代码之间的相似性,而不同编程语言之间的语法差异使得这些方法并不适用于检测不同语言代码之间相似度.在跨语言抄袭检测方面,近年来出现了一些跨语言抄袭检测的方法和工具[4-7].第1类方法是传统的基于中间特征的检测方法.该类方法将两段不同语言编写的源代码转换为统一的中间表示(如,寄存器中间语言(Register Transfer Language,RTL)[8],抽象语法树(Abstract syntax tree,AST)[9]等),进而将跨语言抄袭检测问题转换为同种语言抄袭检测.第2类方法是基于机器学习的跨语言代码抄袭检测方法.该类方法通常将源代码转化为标记(Token)序列,然后为每个标记赋予一个向量表示,进而通过一些机器学习方法.如,潜在语义分析(Latent Semantic Analysis,LSA)[6]、深度学习[10]等,将跨语言代码抄袭的检测问题转换为二分类问题(抄袭或非抄袭).基于机器学习的方法可移植性较强,在训练得到模型的情况下,其判断抄袭的速度更快,是当前跨语言代码抄袭检测的研究热点.但是,目前基于机器学习的大多数方法只是将代码作为文本处理,很少考虑代码的结构特征(例如,AST[9]、控制结构[11]等),受更改语句顺序、等价结构替换等混淆手段的影响,其检测效果较考虑代码结构特征的机器学习方法要差.同时,现有基于机器学习的方法极易受到冗余代码的影响,导致检测准确率的下降.

结构是代码的一种重要特征.例如,AST是源代码语法结构的一种抽象表示,它以树的形式展现编程语言的语法结构.对同一问题的不同编程语言的实现代码,若其实现思路相同,则代码对应的AST的结构也具有一定的相似性[12].针对现有基于机器学习的跨语言代码抄袭检测方法存在的问题,本文结合代码的AST结构特征,提出了一种跨语言代码抄袭检测工具CLPDetector(Cross-Language Plagiarism Detector).该工具将大量具有是否抄袭标签的跨语言代码对作为训练集进行有监督学习,完成训练后得到的代码抄袭检测模型可直接用于跨语言的代码抄袭检测.在训练过程中,对于一段代码,该工具首先将其转换为AST,通过深度遍历该AST,结合AST的节点值和节点类型形成表示代码的token序列,为序列中每个token赋予一个向量表示,从而形成代码的表示矩阵.然后将双向长短期记忆网络(Bi-directional Long Short Term Memory,BiLSTM)[13]、卷积神经网络(Convolutional Neural Networks,CNN)[14]以及自注意力机制(Self-attention Mechanism)[15]3种深度学习方法相结合构建特征提取模型,并将该模型嵌入到伪孪生网络架构[16]中,以构建抄袭检测模型,判断不同语言代码之间是否存在抄袭.为提高检测模型的检测准确率,在模型训练之前,采用了两个预处理策略:1)将预训练训练集中的代码转换为AST,利用skip-gram算法[17]对代码标记(token)进行预训练,以生成一个能准确表示token的词嵌入(embedding);2)基于程序依赖图(Program Dependency Graph,PDG)[11]对训练集中的冗余代码进行删除[18].同时,为提高训练模型的检测效率,在抄袭检测之前,本文提出了一个基于属性计数的过滤器,以降低非抄袭代码对检测效率的影响.本文的主要贡献包括:

1)在代码抄袭检测方法层面,结合代码的AST结构特征,基于伪孪生网络框架,通过融合多种深度学习模型,提出了一个新的跨语言抄袭检测工具CLPDetector;

2)在方法执行效率方面,为了提高方法检测准确率和检测速度,基于PDG对代码中的冗余代码进行删除,并提出了一个用于检测过程的基于属性计数的过滤器;

3)在实验部分,我们以Java和Python语言为例,基于一个开源的数据集(2)https://www.csg.ci.i.u-tokyo.ac.jp/projects/clone/,从精确率、召回率和F1值3个角度对CLPDetector的检测效果行了验证.实验表明,CLPDetector的检测效果要优于目前其他基于机器学习的跨语言代码抄袭检测方法.

2 相关工作

本节中我们将抄袭检测相关工作分为两部分进行讨论,首先讨论了同种语言的代码抄袭检测研究,然后又讨论了跨语言的代码抄袭检测研究.

2.1 同种语言代码抄袭检测

目前,同种语言的抄袭检测主要有基于结构度量和属性计数[19]的两大类方法.其中基于结构度量的方法是当前的主流方法,包括基于token[20,21]、基于树[22,23]、基于图[24,25]的3类方法.

基于token的方法以快速著称,应用场景最为广泛,如CPDP[26],SIM[27]和JPlag[28].但是这类方法忽略了代码的结构特征,致使其精度要逊于其他基于树和图的结构度量方法.基于树或图的方法通过将源代码转换为AST[29]、控制流图(CFG)[30]或程序依赖图(PDG)[11],通过子树匹配或子图匹配实现代码相似度的度量.这两类方法在抄袭检测方面有更高的准确率,但是较高计算代价使这类方法往往不能扩展到大型代码库[31].

2.2 跨语言抄袭检测

现有跨语言代码抄袭检测主要包括传统的基于中间特征的检测方法和基于机器学习的检测方法.

基于中间特征的方法将不同语言编写的代码转换为某一种中间特征,如中间语言或基于树的中间表示[32,33,9],然后基于中间表示直接度量两段代码的相似度.例如,一些检测方法[4,8,34]利用编译器将不同语言代码转换为同一种中间表示语言,通用中间语言(Common Intermediate Language,CIL)[4]、如寄存器中间语言(Register Transfer Language,RTL)[8]以及编译后的二进制文件[34].然后,再将其转换为标记序列或直接在中间语言基础上比较相似度.这类方法忽略了代码的结构特征,且对编译器的依赖性较强.例如,基于CIL的方法只能应用于Microsoft.Net语言,例如C#,ASP.Net,Visual Basic等.基于树的中间表示方法通常将不同语言代码转换为同种树结构,并通过子树匹配或将树转换为对应的矩阵,来度量不同语言代码之间的相似度.例如,eCST(enriched Concrete Syntax Tree)[32]、AST[9]以及CodeDOM(Code Document Object Model)[33].与同种语言基于树的抄袭检测方法类似,这类方法的计算代价仍然较高.

为了在保证精度的条件下降低计算代价,提高检测效率,近年来,一些研究人员将传统方法与机器学习方法相结合来检测跨语言的代码抄袭.基于机器学习的方法通常将不同语言的源代码转换为标记序列,并为每个标记赋予对应向量表示.然后,通过大量的包含是否抄袭标记的代码对训练集对所构建的机器学习模型进行有监督的训练生成一个分类器,最后用训练得到的分类器直接判断两段代码是否存在抄袭.按照是否考虑代码的结构特征,该类方法可分为不考虑代码结构特征[5,6,10,35]和考虑代码结构特征[12,36]的两类方法.首先,第1类方法通常直接将代码视为文本进行处理.例如,DeSoCoRe[5]通过将代码转换为标记序列,用tri-gram模型提取代码特征,并基于归一化对词频附加权重,然后通过余弦距离计算代码相似度.Flores等[6]基于相似单词出现在相似语境中的原则,利用潜在语义分析(Latent Semantic Analysis,LSA)计算不同语言代码文本之间的相似性.类似的,Ullah等[35]利用主成分分析(Principal Component Analysis,PCA)从源代码的标记序列中提取特征,然后通过多元逻辑回归模型对源代码文档进行分类.Nafi等[10]提出了CLCDSA,从AST中提取代码属性特征(变量个数、圈复杂度等)形成代码对应的属性向量,进而结合深度学习的方法训练抄袭检测模型.第2类方法考虑代码的结构特征,通常利用代码的AST对代码标记进行预训练以获取较精确的标记向量,或直接在代码AST上进行模型训练.例如,Perez等[12,36]将代码转换为AST并利用Skip-gram算法[17]从中学习标记的向量表示,然后用BiLSTM构建训练模型判断代码是否抄袭.

不考虑代码结构特征的机器学习方法由于直接将代码作为文本进行处理,所以其转换代码为对应的矩阵表示的过程要更快,但受更改语句顺序、等价结构替换等混淆手段的影响,其检测精度一般要比考虑代码结构特征的方法差;而考虑代码结构特征的机器学习方法与之相反.此外,基于机器学习的跨语言抄袭检测方法的精度还受其他多方面因素的影响.例如,是否进行预训练、训练集的代码质量以及所采用的具体的机器学习方法等.

3 方法主要框架

代码抄袭检测问题相当于一个二分类问题,其结果只有两种情况:抄袭、非抄袭.因此,可以基于文本分方法的思想,通过潜在特征学习判断代码对属于抄袭和非抄袭的概率[37],而属于抄袭的概率即可视为两段代码之间的相似度.与解决文本分类问题的思路类似,本文方法可分为特征工程和分类模型两部分,如图1所示.

图1 方法主要思想Fig.1 Idea of proposed approach

基于文本分类的思想,本文基于伪孪生网络框架提出代码抄袭检测工具CLPDetector,其检测模型的训练框架如图2所示.为提高检测模型的检测精度,加快训练模型的收敛速度,在训练前,首先利用基于AST的skip-gram算法[12]对词向量进行了预训练.具体来说,对N种编程语言分别进行预训练,得到N种编程语言对应的词向量,用于训练过程中的embedding层.检测模型的训练集为带标签(是否抄袭)的代码对集合,为减小冗余代码对检测模型精度的影响,在将代码转换为AST之前,基于PDG将源码中与输出结果无数据依赖的冗余代码删除[18].然后,将代码转换为对应的AST,通过深度优先遍历AST生成代码的标记序列,结合预训练生成的embedding将源代码转换为对应的矩阵表示.接着,利用伪孪生网络架构[16]对数据进行训练,并设定阈值把抄袭检测问题转换为一个有监督的深度特征学习问题.在阈值的选择上,参考已有工作[12,38,39],同时为了在实验对比阶段统一判定标准,本文将判定阈值设为0.5,大于该阈值则判定为抄袭.

图2 抄袭检测模型训练框架Fig.2 Training framework of plagiarism detection model

完成抄袭检测模型训练之后,CLPDetector的抄袭检测过程如图3所示.为提高模型的检测效率,本文基于变量声明数量、参数数量、圈复杂度等9种度量属性(见4.2节)构建了一个过滤器,用于初步排除非抄袭的代码对.具体检测过程为:

图3 抄袭检测流程Fig.3 Process of plagiarism detection

对于两段不同语言编写的源代码,首先基于PDG对代码中的冗余代码进行删除.然后基于SQO-OSS[40]提取代码的变量声明数量、参数数量、圈复杂度等9种度量属性,并利用余弦相似度初步计算两段代码的相似度.若计算结果低于既定阈值,则判定代码为非抄袭并结束检测过程.若计算结果大于既定阈值,则深度优先遍历代码对应的AST并结合预训练生成的词向量将代码转换为对应的矩阵表示,然后利用训练完成的分类器进一步判断代码是否抄袭.

4 抄袭检测模型构建

本节主要介绍词向量的预训练过程、基于代码属性的Metrics过滤器、以及具体的跨语言抄袭检测训练网络模型.

4.1 词向量预训练

代码抄袭检测模型的输入是基于词向量的源代码的矩阵表示.源代码词向量的获取有两种方法:1)通过设置随机向量作为词向量;2)通过预训练获得较为准确的词向量.为了提高检测准确率,本文采用了第2种方法.

词向量的训练需要大量的不同编程语言的代码,这些代码可以从开源网站上获取,如GitHub.本文的词向量预训练模型采用Perez等人[12]提出的基于AST的Skip-gram算法.Skip-gram算法[17]是Word2vec算法的一种,其核心思想是通过上下文来推断中心词.在进行Skip-gram算法训练词向量之前首先要为跨语言的一对代码的编程语言分别建立一个token列表,每个列表对应于某种编程语言.具体过程如下:

1)将预训练数据集中所有代码转换为AST;

2)深度优先遍历每个AST,统计AST中节点出现的次数.其中,每个节点的命名规则为:如果AST中的节点只有节点类型没有节点标识,则以节点类型作为节点的名字;如果既有节点类型又有节点标识,则将节点命名为“节点类型_节点标识”;

3)按照频次从高到低将节点名称进行排序,选取频次最高的若干个节点名字外加“unknow”标记[12]作为token表.以Java代码为例,其对应的token列表如表1所示.

表1 Java代码token索引

接下来通过词汇表和代码转换后的AST集合来构建Skip-gram算法的输入.对于AST中的每一个节点,设置其父节点递归深度为2,子节点递归深度为1,则每个节点将拥有3组数据:<当前节点索引,父节点索引>,<当前节点索引,子节点索引>,<当前节点索引,父节点的父节点索引>.当对所有的AST节点遍历完毕后,会得到一个输入集合,我们将这个集合作为Skip-gram算法的输入.借鉴文献[12],本文中该算法的整体参数设置见表2.

表2 词向量训练参数设置Table 2 Parameter setting of word vector training

4.2 Metrics过滤器

如果两段代码具有相同的属性值,那么它们的功能也是相似的[2].借鉴该思想,为减少进入代码抄袭检测模型的代码对数量,提高模型的执行效率,使模型更易于扩展到大型数据集,本文提出了基于属性度量的过滤器(Metrics Filter),用于在抄袭检测阶段初步排除不可能得抄袭代码对,以提高抄袭检测效率,其属性列表如表3所示.Kawser等[10]通过手工验证,证明了这9种属性在跨语言代码抄袭检测中的适用性.过滤器首先通过SQO-OSS[40]获取代码中各属性的值,利用各属性值构建的向量作为源代码的属性向量表示.通过余弦相似度计算两向量的相似性.在抄袭或抄袭检测中通常将0.5作为判断阈值[12,41].为降低假阳性误报率,我们仅排除在Metrics过滤器中所计算的相似度小于0.3的代码对.

表3 源代码各属性及其描述Table3 Source code attributes and their descriptions

4.3 跨语言代码抄袭检测训练网络模型

在分类器模型设计上,本文选择了BiLSTM[13]、CNN[14]以及Attention[15]3种分类模型作为基础模型.其中,BiLSTM是在Hochreiter等[42]提出的长短期记忆网络(LSTM)的基础上实现的.LSTM解决了循环神经网络(RNN)在长序列文本中的梯度消失和梯度爆炸问题,但只能获取前向或后向文本依赖,而BiLSTM可以更好的捕捉上下文信息.卷积神经网络(CNN)通常用来处理图像识别问题[43-45].如果将文本中的每个词用一个向量表示,那么每个文本就可以形成一个矩阵.由此,就可以通过CNN来提取文本特征[46].Transformer是一种基于自注意力机制的新型神经网络,通过编码组件和解码组件完成文本到文本之间的转换,有效的改善了循环神经网络(RNN)训练时间长的问题.为了使其适用于抄袭检测场景,本文仅仅使用其编码组件部分作为训练模型,由于每次输入模型中的样本集较小,所以设置transform中自注意力层的多头数量为5.

为了提高检测准确率,本文对上述3种基本分类模型及其组合模型进行了对比试验(见5.2节),结果表明同等条件下BiLSTM、CNN以及Attention相结合的模型效果最好.因此,本文将该组合模型作为分类器子模型,并将其嵌入到伪孪生网络架构[16]中.伪孪生网络架构是孪生网络架构[47]的变体,两者在图像验证[48]、视觉跟踪[49]方面有着广泛的应用.孪生网络架构中两个训练模型的权值是共享的,而伪孪生网络架构中两个训练模型的权值是非共享的.考虑到不同编程语言的语法差异,我们采用伪孪生网络架构[16]作为本文方法的主体架构.架构中的具体分类器模型设计如图4所示.

图4 分类器模型Fig.4 Classifier model

分类器模型的输入是源代码对应的矩阵表示(嵌入层),嵌入层的具体生成过程如图2所示,将代码转换成对应的AST之后,通过深度优先遍历AST形成表示代码的标记序列{x1,x2…xn}.然后将标记序列的每个标记都用预训练好的标记向量来替换,形成嵌入矩阵R.其中,xi∈Rn×d,n为标记个数,d为标记的向量维度.接着将矩阵R作为卷积神经网络的输入,经过卷积层、池化层进行局部特征提取.这里的滤波器窗口大小分别3、4、5,激活函数为relu函数.在CNN进行局部特征提取之后,将其输出作为BiLSTM的输入.本文的BiLSTM模型的激活函数为softmax,模型优化使用Adam优化器.BiLSTM之后的注意力模型主要用来表示序列中标记与输出结果的相关性,通过对向量分配不同的权重来区分序列中标记信息的重要性大小,提高分类的准确性.模型的具体训练参数如表4所示.

表4 分类器模型训练参数设置

5 实验与验证

本节以Java和Python语言为例,验证本文提出的跨语言抄袭检测工具CLPDetector的检测效果.

5.1 数据集

实验在一个开源数据集[12]上进行,该数据集包含预训练数据集和跨语言抄袭检测模型训练数据集.预训练数据集中Java代码是Apache开源代码,Python代码是从GitHub上下载的Python项目,这些项目的大小介于100K~100M之间.抄袭检测模型训练数据集规模如表5所示.对于这两个数据集,分别将其中的80%用于训练,将剩余的20%用于测试.在抄袭检测模型训练之前,需要利用预训练数据集按照4.1节所示方法完成对不同语言的词向量的预训练.

表5 实验数据集Table 5 Experimental data set

5.2 多种机器学习方法对比实验

本节在特定试验场境下对多种机器学习模型进行对比,以选出最适合的模型作为抄袭检测训练模型.

通过将4.3节提到的3种分类模型及其组合(BiLSTM+Attention,BA;BiLSTM+CNN+Attention,BCA)嵌入到伪孪生架构中来构建抄袭检测模型,并在实际数据集中验证它们各自的检测效果.为得到每个模型的最优结果,通过交叉验证的方法对各超参数进行调优.评价指标为抄袭检测的准确率.实验结果如图5所示,在非组合情况下BiLSTM的检测效果要优于另外两种方法;在组合模型中BiLSTM结合Attention要优于其结合CNN,而三者结合之后效果达到最优.

图5 各分类器模型效果对比Fig.5 Comparison of different classifier models

5.3 跨语言抄袭检测对比实验

本节与4种现有的跨语言抄袭检测方法进行对比,分别是基于中间特征表示的方法LICCA[32]、不考虑代码结构特征的机器学习方法CLCDSA[10]、DeSoCoRe[5]以及考虑代码结构特征的机器学习方法ASTLearner[12].其中,LICCA主要依赖于SSQSA平台,通过一种通用的基于树的中间表示eCST(丰富的具体语法树)来进行相似度对比.CLCDSA将属性计数和深度学习方法结合起来进行抄袭检测.DeSoCoRe利用tri-gram模型提取代码特征,并基于归一化对词频附加权重,然后用过余弦距离计算相似度.ASTLeaner通过从抽象语树中学习token向量,然后利用BiLSTM模型进行抄袭分类.实验数据集采用5.1节所描述的数据集.实验的评价指标为精确率、召回率和F1值.实验结果如图6所示.

图6 各跨语言抄袭检测工具效果对比Fig.6 Comparison of various cross language plagiarism detection tools

由实验结果可知,DeSoCoRe的检测效果要低于其他几种方法,其原因是DeSoCoRe是基于串的方法,而基于串的方法对编程语言语法的依赖性较强,导致其在跨语言抄袭检测方面效果较差.基于中间表示的LICCA在检测效果上要低于除DeSoCoRe外的其他3种方法,其原因是LICCA要求两个代码片段的大小相似,且拥有相同的控制结构[10].相比之下,CLPDetector的检测效果要优于CLCDSA和ASTLearner,这里的原因是多方面的,一方面得益于模型中融合的自注意力机制,这使得本文的模型在训练阶段能够更好的提取源代码的潜在特征.另一方面得益于基于PDG的预处理方法,因为冗余代码对基于属性计数的CLCDSA是有影响的,因为这会改变源代码的属性数量.同时,ASTLearner对于冗余代码同样没有任何处理措施.

5.4 基于PDG的预处理效果验证

本节针对本文提出的基于PDG的预处理方法的有效性进行验证.实验结果如图7所示,从中可以看出在停用基于

图7 基于PDG的预处理效果验证Fig.7 Validation of pre-process effect based on PDG

PDG 的预处理方法之后本文方法的检测效果仅仅略高于CLCDSA.但仍然明显优于ASTLearner和CLCDSA.

6 总结与未来工作

现有的基于机器学习的跨语言代码抄袭检测方法很少考虑代码的结构特征,导致其检测模型不能准确的提取代码的特征.本文考虑了代码基于AST的结构特征,基于AST对词向量进行预训练,并通过将BiLSTM、CNN、Attention这3种机器学习模型进行融合并将其嵌入到伪孪生神经网络中构建出一种新的抄袭检测模型.同时通过基于PDG 的代码预处理方法减小了冗余代码对整个模型检测效果的影响,并且在利用完成训练的模型检测之前,添加了基于属性统计的过滤器,这排除了一些非抄袭代码对对抄袭检测效率的影响.实验表明此方法在精确率、召回率和F1值上优于现有的其他跨语言抄袭检测方法.

本文的研究工作还可以进一步优化.由于模型融合后复杂度升高,同时由于基于伪孪生网络架构本身收敛较慢,因此本文所提模型的训练周期较长.今后研究中我们拟通过设置更有效快捷的过滤器,精度更高,收敛更快的机器学习模型,以及更加精确的预训练方法,来进一步提高跨语言抄袭检测效率.

猜你喜欢

源代码代码向量
向量的分解
基于TXL的源代码插桩技术研究
神秘的代码
保护好自己的“源代码”
一周机构净增(减)仓股前20名
重要股东二级市场增、减持明细
解密别克安全“源代码”
向量垂直在解析几何中的应用
向量五种“变身” 玩转圆锥曲线
近期连续上涨7天以上的股