Bagging异构集成的代码异味检测与重构优先级划分

2024-03-03 11:21吴海涛蔡咏琦高建华
计算机工程与应用 2024年3期
关键词:开发人员异味异构

吴海涛,蔡咏琦,高建华

上海师范大学 信息与机电工程学院,上海 200234

代码异味[1]是代码的不良设计和实现的症状,会阻碍开发人员理解代码并增加代码的更改次数以及维护成本[2]。Fowler[1]对22 种代码异味进行了定义,这些代码异味可能源自不良设计模式[3],但更多来自开发人员的编码过程。由于代码异味的存在,重构操作变得至关重要。通过重构,开发人员可以在不改变系统功能的情况下提升软件内部结构质量、提高软件可读性和可理解性、定位软件缺陷并促进软件开发过程。

过去的研究[4-7]已经提出多种代码异味检测技术,其中大部分是基于规则或启发式的方法。这些方法使用一组基于代码度量标准或阈值的预定义规则,但通常存在阈值校准问题,导致发生误报或遗漏[8]。机器学习作为发展中的技术,是解决上述问题的一种方法。机器学习能够自动组合代码度量,并且无需指定任何阈值。尽管大部分机器学习算法在代码异味检测方面取得了较高的性能[9],但单一算法仍然存在适应性限制。

在模型选择方面,没有一种单一模型能够在所有异味上表现良好[10],因为代码异味的识别是具有主观性的[11],不同的异味类型可能存在不同的最佳模型。因此,集成学习被应用于代码异味检测。然而,以往的研究更多关注同构集成,忽视了异构集成。因此,Alazba等人[12]使用Stacking方法,将14个异质基分类器组合成更为强大的Stacking集成模型,展现出更为出色的检测性能。

在模型输出方面,由于开发人员的经验和认知存在差异,对代码异味的判断也会有所不同。因此,代码异味的检测是非常主观的。然而,过去的研究[4-12]只为开发人员提供布尔决策,没有参考开发人员的主观认知并提供代码异味存在的概率。Boutaib 等人[11]引入了可能性分布的概念,旨在模拟开发人员的主观认知,提高代码异味识别的准确率。

在异味重构方面,现有的大多数异味检测方法仅识别出异味实例,缺乏后续的重构操作。Boutaib等人[11]提出将可能性分布应用于代码重构的想法,为开发人员提供重构优先级划分。然而,他们并未对这一想法进行相应的研究来验证其有效性。

为了探究上述问题,本文在Alazba 等人与Boutaib等人研究的基础上,提出一种Bagging 异构集成模型(bagging heterogeneous ensemble model,BHE)。该模型集成多个基分类器,综合考虑不同分类器的预测能力,通过F1 集成策略结合多个基分类器的预测结果,得到类、方法存在代码异味的概率。随后在重构中引入可能性分布,将异味概率通过可能性变换公式[13]转化为可能性分布后,作为重构优先级的指导意见提供给开发人员。

本文的主要贡献如下:

(1)采用F1加权集成策略的Bagging异构集成模型相较于6个基分类器,在代码异味检测的性能上有显著提升。

(2)通过Bagging异构集成模型,证实了最佳分类器因代码异味类型的不同而存在差异。

(3)将Bagging 异构集成模型输出的异味概率通过可能性分布公式进行转化后,得到的重构优先级与6名受访者的排序具有高度一致性,能够为开发人员提供可靠的重构顺序,节省开发人员的工作量。

1 相关术语

1.1 代码异味

代码异味是不良的设计和代码实现的症状,会对软件维护带来一定影响,通常利用代码重构解决。本文主要研究3 种代码异味,其描述与影响如表1。代码异味的选择具有一定标准,田迎晨等人[14]和Azeem等人[10]指出工业界更关注质量、成本和效率之间的平衡,同时表1中的代码异味被工业界关注且广泛认可。通过研究工业界所关注的对象,为开发人员提供重构优先级建议,更贴近开发生产中的实际需要。

表1 代码异味描述Table 1 Description of code smell

1.2 代码度量

源代码的结构数据可以用McCabe 圈复杂度[15]或Chidamber等人[16]研究中的结构度量进行统计。代码的结构度量在许多研究中都被证实适合通过机器学习进行研究[17-18]。Das等人[19]发现,基于代码度量的模型在识别代码异味时,其准确率可以达到90%。实验中,选择了以Chidamber等人[16]研究为基础开发的代码度量挖掘工具CK[20],并以其提供的代码度量指标作为数据。该CK工具在Chidamber等人的基础上进行了扩展,其中类级代码度量47个,方法级代码度量28个,如表2所示。

表2 代码度量及其相关信息Table 2 Code metrics and related information

1.3 代码重构

代码重构是在不改变软件系统外部行为的前提下,改善代码内部结构,即通过调整程序代码改善软件的质量、性能,使程序的设计模式和架构更合理,提高软件的扩展性和维护性。

Tufano等人[2]指出超过50%的代码异味在项目开发的1 000天后仍然“活跃”,并认为如何开始重构,即如何确定重构顺序,严重困扰着开发人员。因此,即使机器学习能够识别出大量的代码异味实例,但对于开发人员来说,确定重构优先级才能真正帮助开发人员进行代码质量维护。

1.4 可能性分布

可能性分布是一种主观的不确定性,与概率分布的随机性不同,后者反映事件发生的规律,具有客观性,而前者代表模糊性,即开发人员由于不明确哪个实例需要优先重构,从而产生主观上的不确定性,这种主观不确定性能够用可能性分布进行表示。概率分布能够和可能性分布进行转化,变换公式如式(1)。可能性分布的有效性在Boutaib 等人[21]的工作中已得到证实,在检测代码异味中取得良好的效果。

其中,π表示可能性分布,ω表示状态空间,在本文中即Smelly、Non-Smelly 两种状态空间,p表示概率分布。π(ωk)=1 表示ωk的实现是完全可能的,而π(ωk)=0 表示ωk是拒绝状态。将p转换为π之前应将p按降序排序,即p(ω1)≥p(ω2)≥…≥p(ωn),其中p的总和为1。可能性分布值具有以下形式之一:[X,Y]或[Y,X],其中X在[0,1]之间,Y等于1。

在可能性分布中,X定义了不可能性,即对于某个存在代码异味的类、方法,X表示Non-Smelly的可能性分布,X越小,该类、方法越倾向于不是Non-Smelly,因此该实例的重构优先级越高。由于可能性分布的存在,且可能性分布的和≥1,开发人员可在已知实例存在或不存在代码异味的情况下,通过可能性分布大小判断该实例是否急迫需要进行重构。

2 Bagging异构集成模型的构建

本文提出的Bagging 异构集成模型构建过程如图1 所示,主要分为3 个步骤:(1)寻找合适的基分类器;(2)通过网格搜索寻找基分类器的最优超参数;(3)根据F1值对基分类器分配在预测中所持有的权重。

图1 Bagging异构集成模型构建流程Fig.1 Building flowchart of Bagging heterogeneous ensemble mode

2.1 基分类器选择

Bagging算法[22]是并行式集成学习中的最具代表性的算法之一,其通常对训练样本采用Bootstrap 抽样策略,并行地训练多个独立的基分类器,通过结合多个分类器降低泛化误差。然而在Bagging 算法中,大多都采用同构分类器训练,因此在基分类器的构成上存在一定的改进空间。

本文使用6种有监督机器学习分类器作为Bagging异构集成模型的基分类器,即RandomForest(RF)、SVM、DecisionTree(DT)、Adaboost(AST)、LogisticRegression(LR)、KNN。选择过程遵循2个标准:

(1)选择的分类器应是公认的分类技术,例如SVM、DT。

(2)分类器应涵盖不同的分类簇,例如基于规则、基于回归和基于投票的。

对于每一种代码异味数据集,将总数据集分为80%的训练集和20%的测试集。在训练集上,分别在6种模型上进行训练。这6种模型之间具有一定差异,能通过使用不同学习策略,从不同角度和空间学习特征,实现模型间的互补,从而提升Bagging 模型的整体性能。在测试集上,通过F1来评估各模型的性能。

2.2 基分类器训练及超参数搜索

为了能够最大程度地观察各个基分类器在不同代码异味检测上的性能,并提高Bagging 异构集成模型的检测能力,同时为重构优先级划分提供可靠基础,本文在训练过程中对所有6种基分类器进行网格搜索,以得到最优参数从而提高自身的预测能力,各个基分类器所选超参数如表3。

表3 分类器超参数Table 3 Hyper parameters of classifiers

选择表3中的超参数主要有2个原因:

(1)所有超参数对于各自的分类器具有较高的重要性。

(2)适当控制超参数数量,减少网格搜索的交叉验证时间。

在训练过程中采用10折交叉验证对基分类器进行网格搜索,以得到模型的最优参数。该策略允许所有观察样本用于训练和测试,且已在机器学习相关领域中被广泛使用[23]。该策略将数据随机划分成10份,其中9份作为训练集,剩余1 份作为测试集,交叉验证后重复10次,允许10 个子集中的每一个恰好成为一次测试集[24]。由于实验中的代码异味数据集处于不平衡状态,因此在实验中的每一折都应用分层抽样,以保证代码异味的分布与原始训练集相同[7]。

2.3 Bagging集成策略

Bagging 算法将基分类器训练完成后,通常将它们的结果以简单投票或模型平均的方法进行结合。然而在集成阶段仅仅以简单的多数投票或整体平均进行综合并不合理,没有考虑不同代码异味具有不同的最适分类器,因此在集成策略上仍然存在改进空间。

本文提出的Bagging异构集成模型需要将6个异构基分类器的预测结果进行加权融合,因此在获得基分类器的最优超参数并完成训练后,对基分类器在测试集上的F1 进行降序排序并赋予相应的权重,权重之和为1,如公式(2):

每个基分类器乘上各自的权重后相加,即得到Bagging异构集成模型,模型架构如图2,其输出为某个类、方法存在代码异味的概率。

图2 Bagging异构集成模型架构Fig.2 Architecture of Bagging heterogeneous ensemble model

通过设定概率的阈值,能够输出某个类、方法存在代码异味的标签。实验中选择0.5 作为概率阈值,异味标签的判断方法如公式(3):

一方面,选择加权是因为实验中不同分类器的性能存在差异,通过加权能够强化对应于代码异味的最适分类器对结果的影响,其性能由基分类器在测试集上的F1来表示。另一方面,选择F1作为赋予权重的依据,是由于F1 是Precision 和Recall 的加权调和平均,能够有效地对极端值进行惩罚,不偏向Precision和Recall的任何一方,使得模型在任何情况下对数据不平衡率都不敏感。

3 实验设计

3.1 实验系统

实验研究包含6个软件系统的32个版本,分析的系统包括:

(1)Apache 基金会的5 个系统:Xerces、Hadoop、Hbase、Hive、Manifoldcf。

(2)Eclipse基金会的1个系统:Eclipse。

表4报告了实验中研究的系统特征,即系统、版本、系统版本数、类的数量、方法的数量。实验将通过6个在软件开发领域应用广泛且一直较为活跃的开源系统来验证。

表4 软件系统描述Table 4 Description of software system

选择这6个系统的原因是:

(1)本次实验挖掘的代码异味及统计代码度量的静态工具只适用于由Java语言编写的系统,所选系统都基于Java程序语言开发的。

(2)上述6 个系统中有一定的代码异味实例数,具备进行研究的条件。

3.2 实验流程

Bagging 异构集成模型的实验流程如图3 所示,主要分为4个步骤,分别是获取数据集、Bagging异构集成模型的构建、模型检测性能评估以及可能性分布下的重构优先级一致性检验。

3.2.1 数据集的获取及数据预处理

实验采用由Palomba等人[25]人工识别的代码异味实例数据集,文献[25]的数据集只有代码异味标签,因此实验中采用CK工具获取6个软件系统的32个版本的代码度量数据,再与文献[25]中的异味实例一一对应并进行归一化后形成实验所需的数据集。其中,类级代码异味Complex Class、Spaghetti Code 数据集由1 个标签、47个特征组成,方法级代码异味Long Method数据集由1个标签、28个特征组成,数据集格式如图4。

实验中每种代码异味实例较少,仅1%左右,如表5所示。但根据先前的研究表明[7,26],将不平衡算法应用于代码异味检测相关的数据会损害模型的性能。因此实验中不应用任何不平衡算法对少数类进行平衡。

3.2.2 Bagging异构集成模型

在确定了实验包含的软件系统、3 种代码异味以及相关的数据集后,首先选择6种有监督机器学习分类器RF、SVM、DT、AST、LR 与KNN 作为Bagging 异构集成模型的基分类器。

随后对3种代码异味数据集依次进行训练,通过网格搜索得到6 种基分类器的最佳超参数。为了减轻因实验数据集不平衡给实验结果带来的影响,实验在基分类器训练过程中进行分层抽样训练,以保证代码异味的分布与原始数据集相同,并采用10折交叉验证策略[23]。

最后将6种基分类器通过F1集成策略赋予权重后,组合为Bagging异构集成模型,并给出类、方法存在代码异味的概率,为重构优先级提供依据。

3.2.3 模型检测性能评估

本次实验通过计算经典的性能指标,即通过Precision、Recall、F1 和AUC[27]以及四分位数来验证6 种基分类器在检测3种代码异味上的稳定性,并评估实验中构建的Bagging异构集成模型的分类性能。

3.2.4 重构优先级与一致性检验

本文提出的Bagging异构集成模型在输出异味概率后,仍然可能拥有额外的价值。通过可能性变换公式,将异味概率转化为可能性后,能够作为重构优先级提供给开发人员。

在重构优先级划分的有效性评估上,采用Cohen’s Kappa[28]对Bagging 异构集成模型转化后的重构优先级与6 名受访者得出的重构优先级进行一致性检验。Kappa 一致性是一种衡量分类精度的指标,Kappa 系数越大表明一致性越高。一般认为0~0.20为极低一致性,0.21~0.40 为一般一致性,0.41~0.60 为中等的一致性,0.61~0.80为高度一致性,0.81~1为几乎完全一致。显著性p值表示出现极端样本的概率,一般认为p值<0.05时具有统计学差异,并拒绝原假设。

在具有先后顺序的一致性检验中,即对于本文给出的重构优先级,优先级差距越大,惩罚力度也应越大,因此选择使用平方加权Kappa(quadratic weighted Kappa)[29]。

4 实验结果与分析

为了验证Bagging异构集成模型检测实验中提到的3 种代码异味的有效性,本文就以下3 个问题展开实验与结果分析:

RQ1:在不同代码异味检测中,表现最好的基分类器是哪个?

RQ2:在代码异味检测中,Bagging异构集成模型在多大程度上提高基分类器的检测性能?

RQ3:将Bagging 异构集成模型输出的异味概率转化为可能性分布是否具有潜在的附加价值?即提供的重构优先级建议是否有效?

4.1 评价指标

本次实验通过计算传统的性能指标,包括Precision(P)、Recall(R)、F1 和AUC[27]来验证Bagging 异构集成模型在检测Complex Class、Long Method、Spaghetti Code上的有效性,其公式如(4)~(6):

其中,TP表示真阳性,FP表示假阳性,TN表示真阴性,FN表示假阴性,Precision 表示所有预测值为阳性的样本中被正确预测的比例,Recall表示在所有实际值为阳性的样本中被正确预测为阳性的比例。

AUC[27]是接受者操作员特征(ROC)曲线下方面积的百分比。AUC的取值范围为[0,1],其中0表示最差分类器,1 表示最优分类器。AUC 值为0.5 的分类器类似于随机猜测分类器。

4.2 RQ1:基分类器检测性能

为了回答RQ1,本节给出6 种基分类器在3 种代码异味上的性能,并通过分析6种基分类器在不同代码异味检测上的稳定性来验证不同代码异味是否存在不同的最适分类器。

基分类器对类级和方法级代码异味的检测性能如表6 所示。在类级代码异味中,所有分类器在Complex Class 上表现出高性能而在Spaghetti Code 上则有所下滑。其中,LR的检测性能最差,F1仅为47.76%和24.24%,而RF与KNN分别是检测Complex Class、Spaghetti Code中性能最高的分类器,F1 达到80.29%和63.64%。与类级代码异味相比,所有分类器在Long Method的检测上都能取得较好的性能,但LR 的表现仍非常一般。上述分析表明,针对不同的代码异味,其最佳分类器可能因代码异味类型而异。

表6 基分类器性能Table 6 Performance of base classifiers 单位:%

由于最适分类器的数量可能不止一个,需继续研究分类器的稳定性。表7 总结了各代码异味类型的最佳和最差分类器。若分类器的F1 在前25%(Q3),则认为该分类器是此异味类型检测中性能最好的分类器之一;反之若低于后25%(Q1),则是性能最差的分类器。表7中,“+”表示该分类器是检测该异味类型的最佳分类器之一,而“-”则相反。此外,N/A表示空白项,不表示最佳或最差以及任何数字。

表7 基于F1值的各代码异味中最佳/差分类器Table 7 Best and worst classifiers for each code smell in terms of F1

通过表7中数据可以观察到RF、SVM、KNN分别是检测Complex Class、Long Method、Spaghetti Code 的最适分类器,其中RF 的性能最为稳定,而LR 则始终是最差的分类器。由于选择的分类器数量有限,不能排除存在性能更好或更差的分类器,就上述结论而言,导致RF 性能最为稳定而LR 性能最差的原因可能有以下四点,从分类器原理角度解释:(1)RF作为Bagging中最具代表性的机器学习算法,引入参数、样本等的随机,能避免单棵决策树的过拟合。(2)RF的结果由许多棵不同的简单决策树投票构成,具有强泛化能力。从代码异味角度分析:(3)代码异味的产生存在诸多原因,表现出复杂的特征(尤其是类级代码异味),LR 不能很好地处理较大的特征空间,易欠拟合。(4)代码异味数据集不一定是线性可分的,对LR的性能影响巨大。

此外,即使分类器在检测某个代码异味时表现良好,但不能保证对其他代码异味的检测仍然保持性能稳定。例如,SVM是检测Long Method的最佳分类器,但对Complex Class的性能却排在倒数。

总结RQ1,分类器的性能因代码异味类型而异。在所有分类器中,RF、SVM、KNN 都存在各自适合检测的代码异味,其中RF 的性能最为稳定。另一方面,LR 始终是最差的分类器。

4.3 RQ2:Bagging异构集成模型的性能

为了回答RQ2,本文将在每一种代码异味实验中分为7组,第一组为Bagging异构集成模型的预测性能,后6 组为基分类器与Bagging 异构集成模型性能的差值。利用从6个系统的32个版本中获取的相关数据集,对本次实验构建的Bagging 异构集成模型进行验证,性能结果如表8 所示。其中BHE 表示Bagging 异构集成模型(下同)。

表8 模型性能对比Table 8 Comparison of model performance

对于Complex Class 和Spaghetti Code,表8 所示结果表明Bagging异构集成模型的性能在F1与AUC上相较于其他6 种基分类器有明显优势,分别提升1.11~40.51个百分点和0.76~28.37个百分点。Bagging异构集成模型优于基分类器的原因如下:(1)从模型优化出发,集成多种模型可以减少单一模型陷入局部最优点而导致模型泛化能力不佳的风险;(2)从集成策略出发,Bagging异构集成模型通过加权结合多种分类器,在充分发挥最适分类器的同时降低性能较差的分类器对结果的影响。

对于Long Method,Bagging 异构集成模型虽然没有在全部指标上取得最好的得分,但与性能最好的基分类器SVM 差距非常小。同时Bagging 异构集成模型在Precision 上提高1.44~7.81 个百分点,说明Bagging 异构集成模型的性能与最优基分类器相差不大,同样具有良好的检测能力。

总结RQ2,本文提出的Bagging 异构集成模型是通过使用6 种有监督机器学习模型,采用F1 集成策略的概率模型,在Complex Class、Long Method、Spaghetti Code上都具有良好的检测能力。

4.4 RQ3:重构优先级划分的有效性

为了回答RQ3,并进一步探究Bagging 异构集成模型中采用的可能性分布是否可以作为有效的重构优先级提供给开发人员,本文利用可能性分布转化公式[13],将模型输出的概率分布转化为可能性分布。为验证可能性分布作为重构优先级的有效性,本文共邀请39 位受访者,以探究转化后的重构优先级与实际应用中的一致程度。最后收到其中6 位的回复:2 位从事后端开发(Dev1、Dev2),1位从事硬件开发(Dev3),3位是软件工程方向研究生(Dev4、Dev5、Dev6)。为了确保受访者的响应率,必须控制每位受访者所需执行的任务数量,因此在每种代码异味中选择5 个已知存在异味的实例作为对象,并让受访者为每个实例分配一个重构优先级,从1(最优先)到5(最滞后)进行排序。虽然受访者的数量很少(占回复率的15.4%),但也符合关于最低回复率的调查研究[30]。

由Bagging异构集成模型转化得到的概率分布与可能性分布如表9所示。为了更直观地判断Bagging异构集成模型提供的重构优先级是否有效,实验采用平方加权Kappa进行一致性检验,结果如表10所示。

表9 三种代码异味的概率分布与可能性分布Table 9 Probability and possibility distributions of three code smells

由于每一种代码异味的异味实例过少,因此将15个异味实例作为整体进行分析。表10 结果表明,Bagging异构集成与Dev1~Dev6 的p值都小于0.05,拒绝原假设,即两者具有一致性。此外,Bagging异构集成模型转化得到的重构优先级与Dev1~Dev6 具有高度一致性,Kappa系数最高可达0.900。

表9、表10 中,对于优先级较高的排名,Bagging 异构集成模型转化得到的重构优先级与Dev1~Dev6 的判断几乎一致,然而有两项异味实例的重构优先级存在较大分歧。对于Complex Class,Eclipse-3.5.2 的Problem-Reporter类,Dev1、Dev3、Dev4的判断与Bagging异构集成模型存在差距。对于Spaghetti Code,Xerces-1.4.1 的RegexParser类,Dev1仍然持不同意见。Dev1认为ProblemReporter与RegexParser在业务实现中必须使用大量if-else与switch-case语句,导致代码冗长,不可避免地产生代码异味。即使需要添加新功能,其代码增量也较少,同时也极少修改原代码,重构所需付出的成本和效率不成正比,因此重构优先级较低。上述案例很好地说明了代码异味是代码的一种症状,任何基于机器学习的代码异味检测与重构算法都应当指出开发人员所关注的设计[14,31]与重构问题,因此研究者应将研究成果真正受用于开发者。

总结RQ3,本文给出的重构优先级与Dev1~Dev6给出的重构优先级整体具有高度一致性,Kappa系数最低也达到0.700,最高为0.900。该结果表明,将Bagging异构集成模型的概率输出转化为可能性分布,在代码异味重构优先级上具有较高有效性。

5 结束语

本文提出Bagging 异构集成模型,在6 个项目的32个版本上进行验证实验并对比了该模型与基分类器的性能,以探究Bagging 异构集成模型的有效性。RQ1、RQ2 结果显示,最佳分类器因不同代码异味类型而异,同时与6 种基分类器相比,Bagging 异构集成模型的F1提升了0.45~14.67个百分点,AUC提升了0.76~21.98个百分点,表明Bagging 异构集成模型在检测Complex Class、Long Method、Spaghetti Code上较基分类器有明显提升。在RQ3 中,Bagging 异构集成与Dev1~Dev6 的Kappa 系数最高达到0.900,表明将Bagging异构集成模型的概率输出转化为可能性分布并作为重构优先级具有较高有效性。

未来的工作包括:(1)设置模型动态概率阈值以获得更合理的结果。(2)扩展检测的代码异味种类,以探究Bagging 异构集成模型在其他代码异味上的适用性。(3)增加过程度量,为重构优先级提供可靠依据。

猜你喜欢
开发人员异味异构
试论同课异构之“同”与“异”
Semtech发布LoRa Basics 以加速物联网应用
基于4G技术的VOCs及异味检测系统
用这些告别异味吧!夏天就要清清爽爽过!
PIC-408系列采用育种技术控制公猪异味
异构醇醚在超浓缩洗衣液中的应用探索
overlay SDN实现异构兼容的关键技术
LTE异构网技术与组网研究
后悔了?教你隐藏开发人员选项
去除鞋柜异味等