首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

AI赋能DevOps:基于TensorFlow的端到端软件缺陷预测

1. 什么是软件缺陷预测?

软件缺陷预测(software bug/defect prediction),致力于根据源代码(source code)的静态特征来自动地预测某个软件模块是否有缺陷,从而帮助我们识别软件系统中有质量风险的部分。

据统计,在软件系统中,通常来说,大部分的缺陷是由小部分的模块造成的。通过软件缺陷预测,可以及时甄别此类模块,有助于更好地分配软件测试资源和提高软件测试效率。另外,在甄别的基础之上,通过重新设计和实现系统中的高风险模块,可以促使系统得到进一步完善。

随着敏捷和DevOps的深入实践,软件工程的自动化程度越来越高。AI技术的快速普及,则为软件工程从自动化向智能化演进提供了机遇。软件缺陷预测,是有潜力在软件工程领域落地的AI应用之一。

那么,如何进行软件缺陷预测呢?答案是使用机器学习技术。本质上,软件缺陷预测是一个二元分类(binary classification)问题。有许多的机器学习算法可以用于解决这类问题。例如逻辑回归、朴素贝叶斯、KNN、K-means、决策树、支持向量机、随机森林、神经网络等。

为了进行软件缺陷预测,我们既可以基于数学原理从头开始实现这些算法,也可以调取第三方库来使用这些算法。在本文中,我们采用另一种端到端的、自顶向下的方法。

2. 什么是自顶向下的方法?

自顶向下(Top-down)的机器学习方法是由Jason Brownlee博士在https://machinelearningmastery.com/machine-learning-mastery-method/中提出的。传统的机器学习方法是自底向上(down-top)的:先学习数学和理论,然后掌握算法的实现方式,最后着手用算法解决实际问题。

自顶向下的方法则反其道而行之:首先选择流程(Process)和工具(Tool),然后在数据集上练习使用流程和工具,最后展示机器学习的结果。

自顶向下的方法目的性很强,关注的是如何以最短路径获得机器学习的收益,而不是过早陷入机器学习的算法细节和数学原理。在这种方法论中,作者还提出了一种使用机器学习技术解决复杂实际问题的标准化的、端到端(end-to-end)的流程模板

这个模板的内容是:

定义问题(Problem)

准备数据

选择、训练、评价、改进算法

展示结果(Results)

这里,我们采用上述方法和流程,并使用开源工具TensorFlow,来解决我们面临的软件缺陷预测问题。

3. 什么是TensorFlow?

TensoFlow是由Google Brain团队开发的、基于数据流图进行数值计算的开源机器学习框架。这个框架具有强大的功能和广泛的适用性。一个经常为人称道的优点是,其分布式执行引擎抽象了硬件的异构性,使得基于TensorFlow的机器学习程序既能部署在移动和边缘计算设备上,也能部署在大规模的计算机集群上。

无论在学术界,还是在工业界,这几年来TensorFlow都获得了极大的关注,其流行程度和影响力与日俱增。今年的TensorFlow全球开发者峰会最近刚刚召开。峰会上发布了一些激动人心的TensorFlow最新功能,例如面向Javascript和Web应用的TensorFlow.js等。想了解更多的朋友,可以在这里观看峰会上的精彩视频:https://www.tensorflow.org/dev-summit/。

4. 准备数据集

数据集是机器学习中非常重要的内容,其相当程度上决定了机器学习结果的好坏。我们使用的数据集来自于著名的美国NASA。作为航空航天领域的顶尖机构,NASA对软件质量的要求是非常苛刻的。

NASA提供了一个面向学术界开放的软件缺陷数据集仓库。这个仓库中有一些数据集,每个数据集是对NASA某个软件项目的统计。具体来说,每个数据集包含一系列的样本(记录)。每条记录表示的是软件项目中某个软件模块的一些静态特征(feature),以及这个模块是否有缺陷的标签(label)。

这里的静态特征是对模块源代码进行静态分析和综合的结果。具体来说,他们从软件模块的源代码中提取了以下几类特征:

McCabe特征集:这类特征是以软件工程专家T.J. McCabe的名字命名的。其核心思想是具有复杂路径的代码更倾向于有缺陷。在获取McCabe特征之前,首先需要构造代码模块的“流图”(flowgraph)。在这个“流图”的基础上,可以衍生出一系列的特征,反映软件模块的Cyclomatic复杂度、Essential复杂度和Design复杂度等。

Halstead特征集:这类特征以另一位软件工程专家M.H. Halstead的名字命名。其核心思想是可读性差的代码更倾向于有缺陷。那么,如何衡量代码的可读性呢?Halstead采用的办法是统计代码中的若干概念(例如操作数、唯一操作符等)的数量。

其他特征集:除了上述两种类型,还有其他一些常见的特征也被提取,例如代码的行数、注释的行数、空白的行数等。

我们以这个数据集仓库中的KC2数据集为例(下图是数据量的一部分展示)。(http://promise.site.uottawa.ca/SERepository/datasets)。

这份数据集来自于NASA的一个C++项目,共有522个样本。每个样本由3个McCabe特征,12个Halstead特征,6个其他特征,一共21个特征和1个标记(用于表述样本模块是否有缺陷,0代表没有缺陷,1代码有缺陷)组成。在522个样本中,没有缺陷的样本有415个,占比79%;有缺陷的样本有107个,占比21%。

5. 使用TensorFlow进行软件缺陷预测

TensorFlow为不同需求的开发者提供不同级别的机器学习接口(API)。估计器(estimator)是其高级别的接口。估计器为我们屏蔽了TensorFlow的底层细节,使我们以更快的速度完成机器学习程序的编程。

在这里,我们使用估计器来编写软件缺陷预测的机器学习程序。那么,估计器是什么呢?估计器将机器学习中经常性的操作,例如训练、评价、预测等进行了实现和封装。我们只需要直接调用这些方法(辅以若干入参)就可以完成构造流图、初始化变量、开始执行、处理异常等大量具体工作。

使用估计器进行开发有两种选择。一种是直接使用TensorFlow内置的估计器,另一种是定义和实现自己的估计器。我们采用前一种方法。

在TensorFlow中,使用估计器开发机器学习程序的一般过程如下:

创建一个或者多个输入函数(Input Function)。

定义模型的特征列(Feature Column)。

用特征列和超参数(Hyper-parameter)等作为入参,实例化估计器,得到估计器对象(object)。

用输入函数作为入参,调用估计器对象的若干方法(训练、评价、预测等),完成机器学习工作。

5.1 创建一个或者多个输入函数

要想调用估计器对象的方法,通常需要传入数据集作为参数。在TensorFlow中,数据集是以“输入函数”的形式作为参数传入的。那么,输入函数是什么样的函数呢?

这个函数最主要的特点就是:其返回值是一个类型为tf.data.Dataset的对象(这种形式的数据集是估计器所期望的)。具体地说,这个对象是一个包含两个元素的元组。其中第一个元素是特征,第二个元素是标签。特征是一个Python字典。在这个字典中,每个key是特征的名字,每个value是由该特征的所有取值构成的数组。标签则是一个由其所有取值构成的数组。

那么,如何从原始的数据集构造出这样的一个对象呢?TensorFlow提供了多种方法。这里,我们使用tf.data.Dataset的API来实现这个目标。在我们的场景中,由于原始数据集是一个类似于CSV格式的文本文件,因此我们采用TensorFlow中的tf.data.TextLineDataset类从文件中读取样本,并且使用其map方法对样本进行逐行处理,然后依次使用shuffle方法对数据集进行打乱、使用repeat方法对数据集进行重复化、使用batch方法将重复后的数据集进行分批。

这样,我们就得到了一个经过处理的数据集(形式上是一个输入函数)。之后,我们将使用这种数据集进行若干机器学习操作。

5.2 定义特征列

特征列用以描述模型应该如何使用来自上述特征字典中的输入数据。在实例化估计器模型时,我们需要传入特征列参数。在我们的场景中,一共有21个特征。观察数据集可知,每个特征的取值都是数字的。因此,我们可以把每个特征表示成一个32位的浮点数。在TensorFlow中,对应的类型是tf.feature_column.numeric_column(dtype=tf.float32)。构造特征列的代码如下:

5.3 实例化估计器

为了实例化估计器,我们需要做两件事情:(1) 选择估计器, (2) 确定实例化估计器需要的入参。

前面提到,TensorFlow中有一些内置的估计器。软件缺陷分类本质上是一个二元分类问题。针对此类问题,有多个内置估计器可供选择。这里,我们使用TensorFlow中的深度神经网络(DNN)分类器,即tf.estimator.DNNClassifier(由tf.estimator派生而来)

确定了估计器类型,那么我们需要传入哪些参数来实例化这个估计器呢?这里,有若干必选和可选的参数需要传入。比如feature_columns参数是必填的特征列,hidden_units参数是必填的神经网络隐含层的结构,n_classes参数是可选的分类的类型数量(默认是2),optimizer参数是可选的用于模型训练的优化器(一个tf.Optimizer实例,默认是Adagrad优化器)等。

以下是构造估计器的代码。注意到,构造估计器时不需要传输数据集。

5.4 训练模型

在5.3中,我们得到了一个估计器对象。要训练模型,我们只需要调用估计器对象的train方法即可。当然,重点仍然是传入适当的参数。这里,我们传入5.1中构造的输入函数(代表用于训练的数据集),并且传入1000作为训练的迭代次数。

5.5 评价模型

在完成模型训练之后,我们可以使用evaluate方法对得到的模型进行评价。

在train不同的是,这里我们不需要传入迭代次数,即只需要得到一次性估计的结果。另外,用于评价的数据集和用于训练的数据集是独立的,即用于评价的数据集不会参与到模型的训练过程中,这可以确保模型的独立性。具体来说,我们需要构造一个用于评价模型的数据集(输入函数,eval_input_fn)。

倘若对评价结果不满意,我们可以尝试对模型进行各种各样的改进。例如更换分类器的神经网络结构、激活函数、损耗函数等,调整学习速度、迭代次数、batch大小等超参数,甚至可以基于TensorFlow低级别API构造自己的估计器。这里有大量的改进工作可以去做。

6. 总结

在本文,我们采用一种自顶向下的方法、并基于Google的TensorFlow框架,探讨了软件缺陷预测这一软件工程实际问题的机器学习解决方案。必须承认的是,无论对问题的分析,还是对方案的设计,本文都是非常粗浅的。

我们注意到,AI在消费者领域已经有了非常广泛的应用,但是在程序员身处的软件工程领域,其应用仍然处于早期阶段,有许多方向值得探索。感兴趣的朋友可以关注这一领域的发展,本公众号也会持续关注相关动向。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180405G19O7P00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券