现代电子技术
Modern Electronics Technique
Aug.2023Vol.46No.16
2023年8月15日第46卷第16期
0引言
随着软件技术的发展以及人们对软件的应用需求,软件开发的规模越来越大。在软件开发时,人们通常利用应用程序接口(API )来减少一些重复的劳动,但由于API 的复杂性越来越高,开发人员学习和使用API 的成本上也越来越大。虽然有的API 提供了相关文档供开发人员参考使用,但其中很多文档都是以解释与描述为
主,缺乏代码示例,给开发人员学习和使用API 造成了不便[1]。
为此,人们利用大量的开源代码来挖掘出API 的调用模式[2]。研究表明,利用频繁序列模式挖掘算法在挖掘API 调用模式时具有良好的效果[3]。在该算法中,通常有一个支持度阈值,用来筛选出现频率大于该阈值的API 调用序列。但频繁序列模式挖掘算法也会伴随一
个问题:若设置的支持度阈值过低,则会返回大量的结DOI :10.16652/j.issn.1004⁃373x.2023.16.013
引用格式:杨超逸,钟林辉,莫俊杰,等.多源数据驱动的API 调用模式挖掘方法研究[J].现代电子技术,2023,46(16):75⁃80.
多源数据驱动的API 调用模式挖掘方法研究
杨超逸1,钟林辉1,莫俊杰1,卢腾骏2,高荣锦1,阮书鹤1,祝艳霞1
(1.江西师范大学计算机信息工程学院,江西南昌
330022;2.江西财经大学VR 现代产业学院,江西南昌
330032)
要:软件开发人员在编程过程中需要使用大量的应用程序接口(API ),但是API 文档自身可能存在不完整、过时等
情况,导致对其理解和使用出现困难。通常基于序列模式挖掘API 调用模式的方法(例如UP⁃Miner 等)
针对的是单一的数据来源(即用户源程序),在使用过程中若阈值设置较高,则挖掘出的API 调用模式完整性会降低,甚至会丢失一些重要的API 调用模式。为此,文中提出一种多源驱动的API 调用模式挖掘方法,将用户代码和问答网站(如Stack Overflow )上的专家示例代码相结合,采用分类和聚类的方法挖掘出较少的API 调用模式。与UP⁃Miner 等其他工具的对比实验结果表明,所提方法在召回率以及准确率上有较大的提升。
关键词:API 调用模式;序列模式挖掘;多源数据驱动;BE⁃Miner 挖掘系统;分类;聚类;问答网站中图分类号:TN919⁃34
文献标识码:A
文章编号:1004⁃373X (2023)16⁃0075⁃06
Research on API call pattern mining method driven by multisource data
YANG Chaoyi 1,ZHONG Linhui 1,MO Junjie 1,LU Tengjun 2,GAO Rongjin 1,RUAN Shuhe 1,ZHU Yanxia 1
(1.School of Computer and Information Engineering,Jiangxi Normal University,Nanchang 330022,China;2.School of VR Modern Industry,Jiangxi University of Finance and Economics,Nancha
ng 330032,China)
Abstract :Software developers usually need to use a large number of APIs (application program interface)in the programming process,but the API document itself may be incomplete and outdated,which makes it difficult to understand and use the API.The method of mining API call patterns based on sequential patterns (such as UP ⁃Miner)is aimed at using the single data source (i.e.user source program).If the threshold value is set higher in the use process,the integrity of the discovered API call patterns will be reduced,or even some important API call patterns will be lost.A multisource driven API call pattern mining method is proposed,which combines user code with expert example code on Q&A websites (such as Stack Overflow),and can mine fewer API call patterns by means of classification and clustering methods.In comparison with other
tools such as UP ⁃Miner,the experimental results show that the proposed method has a greater improvement in recall and precision than other methods.Keywords :API call pattern;sequential pattern mining;multisource data driven;BE ⁃Miner mining system;classification;
clustering;Q&A website
收稿日期:2022⁃12⁃26修回日期:2023⁃02⁃09
基金项目:国家自然科学基金项目(62062039);国家自然科学基金项目(61966017);江西省自然科学基金项目(20212BAB202017);江西省
自然科学基金项目(20224BAB202013);江西省自然科学基金项目(20212BAB202018);校教改课题(JXSDJG2044)
75
现代电子技术2023年第46卷
果,到的API调用模式完整性相对较高,但其中许多结果是冗余的;若设置的支持度阈值过高,则潜在的
API调用模式可能会被过滤,从而使结果的完整性降低。如果存在一些API调用模式在源程序代码中出现次数极低,或者源程序代码量极大,则很难将这些调用模式挖掘出来,或者即便通过调低支持度阈值挖掘出这些模式,也会产生大量的冗余结果。
为了解决上述问题,本文采用一种多源数据驱动的方法,除了利用已有的用户代码外,还通过问答网站(例如Stack Overflow)提取相关API回答的专家代码示例,并将其转换成API调用序列作为训练集;再将待挖掘的源程序代码作为测试集,用分类算法对源程序进行划分,从而使得在源程序中出现
次数极少的API调用模式也能够被挖掘出来,同时也不会产生大量的冗余结果。本文首先介绍相关研究,然后讨论基于示例的API调用模式挖掘方法的步骤及关键技术,最后通过实验对比和分析验证本文方法的有效性。
1相关研究
API调用模式可分为有序调用模式与无序调用模式两类。
1)无序的API调用模式,描述了在源程序中以一定的频率同时出现的一组API方法集合,该方法集合不区分调用的先后顺序。无序的API调用模式通常采用关联规则挖掘算法,其中较为典型的是A.Michail在1999年首次研究源程序与API调用之间的复用模式,利用关联规则算法和标识方法之间的关系,帮助开发人员理解复用API[4]。另外,也有研究者利用无序的API调用模式解决软件工程中的特定问题,Li I等人设计了PR⁃Miner工具,使用关联规则算法挖掘无序的API调用模式,用于检测源程序中违反规则的代码[5];B.Livshits等人设计了DynaMine工具,针对掘源代码的历史版本,采用关联规则算法挖掘潜在的API的调用模式,并检查源程序中违规代码[6]。
2)有序的调用模式,需要考虑API方法在使用时的时态。Zhong H等人设计并实现了MAPO工具,通过解析Java源程序提取其中的API调用序列;再根据方法名、类名及API名进行相似度计算,将这些调用序列进行聚类;最后使用SPAM算法挖掘出API调用模式。但MAPO返回的结果存在大量冗余,会出
现很多类似的结果[7]。后期Wang J等又提出了一种改进的工具UP⁃Miner,能处理频繁调用序列样本相对较少的情况,同时使用了BIDE闭合频繁序列挖掘算法和两次聚类减少冗余的API调用模式[8]。T.T.Nguyen等人使用带标记的有向图表示API调用依赖关系,提出了一种新的API调用模式挖掘算法[9]。J.Fowkes等提出了一种基于概率分布的API调用模式挖掘方法(PAM),该方法无需提供序列模式挖掘算法所需要的阈值等参数,且挖掘准确率也高于MAPO与UP⁃Miner等经典工具[10]。Zhang Tianyi 等人则利用API调用模式挖掘,验证问答网站上示例代码是否可靠[11]。Nguyen等人提出了基于图的统计语言模型GraLan,其将程序语言用图结构来表示,计算原图产生新的子图的概率和使用隐马尔科夫链,从而预测
API的方法调用和学习API调用模式[12⁃13]。
2多源数据驱动的API调用模式挖掘
本文提出的方法(BE⁃Miner)首先获取问答网站中指定API的专家代码示例,再给定一组使用了该API的源程序代码集合,并通过抽象语法树提取这些代码中的方法调用,将其转化成API调用序列。接着将专家代码示例中的调用序列作为分类算法的训练集,对源程序的调用序列进行分类,并对分类运算形成的每一个类先聚类再进行频繁序列挖掘。为了减少挖掘结果的冗余,在挖掘之后对频繁序列再使用一次层次聚类运算,使相似的序列聚为一类,将每一类视为一个调用模式,并进行排序返回给开发人员参考。图1所示为BE⁃Miner的API
调用模式挖掘过程。
图1BE⁃Miner的API调用模式挖掘过程
2.1用户源程序中API调用序列生成
使用Eclipse JDT语法解析器解析Java源程序代码,将其转换成抽象语法树的形式,并消除一些无关信息,提取其中API相关的方法调用序列,每个方法调用表示成“包名.类名.方法名”的形式。在提取调用
序列时只考虑方法调用和类实例创建,同时,提取的调用序列不枚举条件语句可能的分支。例如在声明方法中,if(f1()){f2();}else{f3();},提取的序列为f1、f2、f3,而不是f1、f2和f1、f3。因为如果f1、f2、f3确实为频繁调用的方法序列,那么返回这个序列要比返回其子序列提供的信息更加完善。
2.2专家示例代码的序列生成和聚类
在问答网站Stack Overflow中,开发人员可以通过
76
第16期
搜索关键字来获取相关问题的解答帖,但是并非所有问
题的回答都包含代码示例,在这里只考虑包含了代码示例的问答帖,并将评分和荣誉值较高的代码认为是专家示例代码。同时,舍弃那些代码结构比较混乱、语法解析器难以识别的专家示例代码。
接着,采用层次聚类算法将相似的专家示例序列聚为一类(cluster ),并视为同一问题下不同专家给出的示例序列。聚类采用的相似性度量公式如下:
Sim ()
S i ,S j =
||G ()S i
∩G ()S j
|
|G ()S i
∪G ()S j
(1)
式中:G (S )表示示例序列S 的n ⁃gram 集合,定义见式(2)。如果两条序列的n ⁃gram 集合重复数越多,则两条序列的相似度越大。
G ()
x ={x 1,x 2,…,x n ,x 1x 2,x 2x 3,…,x n -1x n ,…,
}
x 1x 2…x n -1,x 2x 3…x n ,x 1x 2…x n (2)
式(2)表示任何序列x 能表示为由长度1~n (n 为序列x 的长度)的子序列集合。显然,如果两条序列单个项重复得越多,则相似度越大;同时,如果两条序列的公共子序列重复越多,则相似度越大。2.3
API 调用序列分类
由于源程序中API 方法调用分布不均衡,一些API 调用模式可能非常常见,会得到很大的支持度;而对于一些不常见的API 调用模式,其出现的频率会很低。为了增加挖掘这些出现次数较低的调用模式的概率,将提取到的示例序列作为分类算法的训练集。本文使用KNN 分类算法,使用公式(1)对API 调用序列进行分类,所有不能归类的API 调用序列单独成为一类,结果如表1所示。表1中,e i (i =1,2,3)代表代码示例聚类后得到的3个示例序列类,s i (i =1,2,3…)代表不同的API 方法,即e 1={s 1s 2s 3,s 1s 2s 3s 4},e 2={s 3s 4s 5},e 3={s 5s 6s 7,s 5s 6s 7s 8};待分类的源程序序列分别为{s 1s 2s 3s 5,s 1s 2s 4,s 1s 5s 6s 7,s 7s 8s 9}。表中数值是基于公式(1)计算得到的相似度,在取相似度阈值为0.3、K 值为3的条件下,s 1s 2s 3s 5、s 1s 2s 4被分到e 1类,s 1s 5s 6s 7被分到e 3类,s 7s 8s 9被分到其他类。
通过分类算法将源程序序列根据示例序列划分成多个类,从而能够在大量的源程序中挖掘到一些出现频率较低的调用模式,在一定程度上解决了无法挖掘出现频率较低的调用模式以及挖掘结果召回率不足的问题。2.4
API 调用模式挖掘
采用文献[4]提出的方法对分类后的API 调用序列进行序列模式挖掘。首先对源程序调用序列进行一次层次聚类,用于避免API 调用模式在源程序中分布不均
衡的情况;之后基于每个类使用频繁闭合序列挖掘算法来识别频繁使用的API 方法调用。但是,从不同的聚类中挖掘出的频繁闭合序列可能仍然相似,导致结果仍然存在冗余,因此对挖掘出的频繁闭序列再次进行聚类,得到的每个类将被视为一个调用模式,并根据这些类构造概率图,使得开发人员更好地理解API 的使用。
表1源程序序列分类
源程序
序列
s 1s 2s 3s 5s 1s 2s 4s 1s 5s 6s 7s 7s 8s 9
s 1s 2s 3:e 10.60.3330.0660
s 1s 2s 3s 4:e 10.4280.30.0520
s 3s 4s 5:e 20.1420.0900.0660
s 5s 6s 7:e 3
000.60.090
s 5s 6s 7s 8:e 3
000.4280.230
类别e 1e 1e 3其他
3实验设计和结果分析
为了评估BE⁃Miner 方法的有效性,本节设计两组
实验,通过与MAPO 、UP⁃Miner 方法进行对比,试图回答2个问题:
RQ1:BE⁃Miner 方法能否具有更高的召回率。RQ2:BE ⁃Miner 能否挖掘到出现次数较少且UP ⁃
Miner 无法挖掘到的API 调用模式。3.1
实验数据
本文在GitHub 中下载了3个较为流行的Java 开源
项目(Netty 、Weld 及Spring⁃data⁃neo4j )进行调用模式挖掘。这些项目代码库所调用的API 拥有足够多的人工编写的代码示例,以便本文可以将挖掘到的模式与库开发人员编写的模式进行比较;同时,项目中分别调用了主流的API ,包括Netty 、Weld 、Spring⁃data⁃neo4j 等。此外,通过爬虫技术,从代码问答网站Stack Overflow 上下载了示例代码以及对应的投票数等信息作为BE⁃Miner 方法的输入。由于Stack Overflow 网站的限制,每个框架仅提取了相关回答帖中前40页的回答帖;此外,因为问答帖中代码回答的质量参差不齐,本实验中只选取投票数大于3的代码示例,但由于Stack Overflow 网站的限制,每个框架仅提取了相关回答帖中前40页的回答帖。另外,因为问答帖中代码回答的质量参差不齐,为了使BE⁃Miner 的挖掘效果更佳,本实验中只选取投票数大于3的代码示例。经过上述处理,所得到的示例代码序列和API 调用序列统计结果如表2所示。3.2
评估标准
为了评估BE⁃Miner 挖掘系统的有效性,本文实验
引用了精确率、召回率以及F⁃Measure 值等指标来衡量挖掘系统的性能,同时确定了一个“黄金集”,这个“黄金
杨超逸,等:多源数据驱动的API 调用模式挖掘方法研究77
现代电子技术
2023年第46卷
集”是通过PAM 挖掘工具[10]对实验项目进行调用模式挖掘获取的。PAM 是目前已知的精确率最高的API 调用模式挖掘工具。为了确保“黄金集”的准确性,本文对PAM 的挖掘结果进一步进行人工筛选。召回率为挖掘到的调用模式且能在“黄金集”中到的模式数量与“黄金集”中的调用模式数量的比值;精确率为挖掘到的调用模式且能在“黄金集”中到的模式数量与挖掘到的调用模式数量的比值。精确率(Precision )、召回率(Recall )以及F⁃Measure 定义如下:
Precision =java技术专家
L ∩C
C (3)Recall =
|
|L ∩C |
|L (4)
F⁃Measure =
2·Precision·Recall
Precision +Recall
(5)
式中:L 为“黄金集”中的频繁闭序列集合;C 为挖掘到的
频繁闭序列集合;
||L 、||C 、||L ∩C 为频繁闭序列的数量;L ∩C 为挖掘到的且与“黄金集”相匹配的序列。
由于“黄金集”是以调用序列形式展示,而BE ⁃
Miner 返回的模式是以概率图的形式返回,故为了统一度量,本文将BE⁃Miner 返回的每个簇中的频繁闭序列
与“黄金集”进行对比。此外,若挖掘到的序列为“黄金集”中的子序列,且序列长度误差不超过2,则认为该序
列为挖掘出的且与“黄金集”相匹配的序列。F⁃Measure 能够综合地评估挖掘系统的精确率和召回率,取值范围为0~1,越高越好。
表2示例代码序列和API 调用序列的统计
开源项目名称Netty 的问答示例Weld 的问答示例
Spring⁃data⁃neo4j 的
问答示例
示例代码
序列个数
5812921
开源项目名称Netty 的开源程序Weld 的开源程序
Spring⁃data⁃neo4j 的
开源程序API 调用序列个数12325950
3.3
实验内容
3.3.1
使用不同频繁闭序列阈值的实验对比
在确定了示例序列相似度阈值、分类相似度阈值及
K 值后,将源程序序列根据示例序列类进行划分,并对每个类进行API 调用模式挖掘。示例序列类对
源程序序列分类之后,使得源程序序列在更小的样本下进行频繁闭序列挖掘,从而使一些出现频率不高的API 调用模式也能够被挖掘出来。该方法相对于UP⁃Miner ,可以挖掘到更多的API 调用模式。
表3、表4和表5分别展示了MAPO 、UP⁃Miner 和BE⁃
Miner 挖掘Netty 项目时不同频繁闭序列阈值下的挖掘
结果对比。
表3MAPO 在不同频繁闭序列挖掘阈值下的挖掘结果对比
频繁闭序列阈值0.050.10.15
黄金集序列数116116116
挖掘的序列数3030636580
正确的序列数664738
精确率0.02170.07380.0655
召回率0.56890.40510.3275
F⁃Measure 0.04160.12460.1091
表4UP⁃Miner 在不同频繁闭序列挖掘阈值下的挖掘结果对比
频繁闭序列阈值0.050.10.15
黄金集序列数116116116
挖掘的序列数832914
正确的序列数512111
精确率0.61440.72410.7857
召回率0.43960.1810.0948
F⁃Measure 0.51250.28960.1691
表5BE⁃Miner 在不同频繁闭序列挖掘阈值下的挖掘结果对比
频繁闭序
列阈值0.050.10.15
黄金集序列数116116116
挖掘的序列数977457
正确的序列数685343
精确率0.7010.71620.7543
召回率0.58620.45680.3706
F⁃Measure 0.63840.55780.497
本组实验中,MAPO 、UPMiner 和BE⁃Miner 的源程序序列聚类阈值为0.2,BE⁃Miner 的示例序列聚类阈值为0.6,分类阈值为0.3,K 值为3。使用MAPO 挖掘项目时,尽管其召回率比UP⁃Miner 高,但由于存在较多冗余,故其精确率显著低于UP⁃Miner ,因此其F⁃Measure 值也明
显低于UP⁃Miner ;在使用UP⁃Miner 挖掘Netty 项目时,当阈值达到0.2时便无法挖掘到频繁闭序列;而在使用BE⁃Miner 挖掘时,由于源程序序列先被示例序列划分成了各个类,再对每个类进行频繁闭序
列挖掘,因此可以挖掘出更多的频繁闭序列。根据表3~表5可知,在频繁闭序列阈值不断升高时,三个挖掘工具的精确率均不断上升,召回率均不断下降,但MAPO 的精确率远低于其他两个挖掘工具,因此F⁃Measure 值最低;UP⁃Miner 改进了MAPO ,通过二次聚类及频繁闭序列挖掘去除了大多数的冗余,因此精确率相较于MAPO 大幅度提升,但在频繁闭序列阈值上升时,其召回率大幅度下降;而BE ⁃Miner 在频繁闭序列阈值为0.05时,精确率高于UP ⁃Miner ,而其在频繁闭序列阈值为0.1和0.15时的精确率虽略低于UP⁃Miner ,但其召回率下降幅度并不大,且所有频繁闭序列阈值下的召回率都高于UP⁃Miner ,因此78
第16期
BE⁃Miner 总体的F⁃Measure 值也高于UP⁃Miner 。图2所
示为MAPO 、UP⁃Miner 和BE⁃Miner 在不同频繁闭序列阈
值下的F⁃Measure
值的对比。
图2不同频繁闭序列阈值下的F⁃Measure 值对比
3.3.2
挖掘出现次数较少的调用模式结果分析
为了进一步分析BE⁃Miner 能否挖掘出次数较少的
调用模式,本实验将两个工具挖掘出的频繁闭序列进行对比。本组实验对比中BE⁃Miner 的示例序列聚类阈值为0.6,分类阈值为0.3,K 值为3,同时BE⁃Miner 与UP⁃Miner 的源程序序列聚类均为0.6,频繁闭序列阈值均为0.1。实验中将序列S ={isConnected ,write}设为正确的
API 调用模式,如图3所示。该模式描述了在调用write ()方法前需要先调用isConnected ()方法来判断Channel 是否为连接状态,但该序列及其超序列在整个源程序中只出现了两次(为区分不同位置上的S ,分别记为S 1和S 2)
图3序列在源程序代码中的展示(截图)
BE⁃Miner 首先对示例序列进行聚类,得到了3个示
例序列类,分别记为C 0、C 1和C 2,在示例序列类中,C 0类
拥有31条示例序列,C 1类拥有13条示例序列,C 2类拥有
14条示例序列;由于设置的K 值为3,因此到与源程
序序列S 最相似的有三条示例序列,分别是S ′1=
{isConnected},S ′2={isConnected}以及S ′3={isConnected ,
write},其与序列S 1、S 2、S 3的相似度分别为0.33、0.33、1,而这三条序列均在示例序列类C 0中。BE⁃Miner 利用分类器对源程序序列进行分类时,序列S 1、S 2均被划分进
类C 0中,其中被划分进C 0类中的源程序序列共48条,被划分进C 1类中的源程序序列共11条,被划分进C 2类中的源程序序列共18条,其余源程序序列被划分进其他类中,共44条。接着,BE⁃Miner 分别对C 0、C 1、C 2及其他类中的源程序序列进行聚类,其中C 0类中的源程序序列经过聚类后得到了4个小类,分别记为C 00、C 01、C 02、C 03,每个小类中分别拥有18、17、10、3个源程序序列,其中
序列S 1、S 2位于C 00中。因为C 00中的序列数为18,且频繁
闭序列阈值为0.1,序列S =S 1=S 2,即在C 00中,序列S 出现了两次,2>18×0.1,因此序列S 被作为频繁闭序列返回
出来。
而UP⁃Miner 在挖掘频繁闭序列时,首先对源程序序列进行聚类,得到5个类,记为U 0、U 1、U 2、U 3、U 4,每个类中拥有的源程序序列个数分别为33、9、39、25、17;其中序列S 1、S 2被划分进了U 0类中,由于设置的频繁闭序
列阈值为0.1,而U 0类中的源程序序列个数为33,2<33×
0.1,因此序列S 没有被作为频繁闭序列挖掘出来。
实验中除了对序列S 的挖掘效果进行分析,还统计
了所有BE⁃Miner 与UP⁃Miner 挖掘出的结果。图4(图中展示了部分挖掘到的频繁序列)所示为BE⁃Miner 挖掘出的能在黄金集中到的频繁闭序列,其中sup 表示该频繁闭序列出现的次数,共计53条,有20条与UP⁃Miner 挖掘结果一致,33条为BE⁃Miner 挖掘到而UP⁃Miner 没有挖掘到的频繁闭序列。UP⁃Miner 挖掘出的能在黄金集中到的频繁闭序列共计21条,其中有1条为BE⁃Miner 没有挖掘到的序列。从图4中可以看出:UP ⁃Miner 挖掘出的频繁闭序列,其出现次数普遍较高,即使用UP⁃Miner
进行挖掘时,需要序列的出现次数较高才能够被挖掘出;而使用BE⁃Miner 进行挖掘时,一些出现次数较低且能在黄金集中到的序列也能够被挖掘出来,因此其召回率相较于UP⁃Miner
要高很多。
图4BE⁃Miner 挖掘出的频繁闭序列
4结论
API 向开发人员提供了一组可访问的方法或者服
务,然而缺乏如何准确使用API 的文档或文档的解释,且描述较为笼统,缺少代码示例,给开发人员在学习API 的使用规则造成了一定的障碍。本文在已有研究工作的基础上[14⁃15],提出一种基于示例代码的API 调用
杨超逸,等:多源数据驱动的API 调用模式挖掘方法研究79