机器学习测试综述

Posted on
Software Analyze and Testing Machine Learning Testing

论文《Machine Learning Testing: Survey, Landscapes and Horizons》的笔记,作者是Jie M Zhang老师,主要了解近年来对机器学习系统测试方面的研究重点。这篇文章可以作为目录,浏览机器学习测试方面的一些经典文章。

概述

机器学习应用在各个行业扮演着越来越重要的角色。这些应用在自动驾驶、医疗等关乎人身安全的行业中也有着比较广泛的使用场景,这很自然地引起人们对其准确性、鲁棒性、隐私性、效率和公平性等因素的思考,于是越来越多的学者开始针对机器学习系统或应用的测试开展研究。目前比较著名的机器学习测试工具有:

  • DeepXplore:深度学习的白盒测试工具
  • Themis:检测机器学习系统的公平性

实际上,机器学习应用的测试有许多和传统软件测试类似的特性,这些特性在传统软件测试中已经得到充分的研究,但机器学习应用的统计学属性(包含不确定性)给相应的测试工作带来了额外的挑战:

  • 数据敏感:机器学习系统是一个数据驱动的系统,它的表现会随着新数据的输入、训练而不断变化,然而传统软件并不会因为数据输入而改变它的行为。
  • Oracle问题:机器学习系统的测试总是缺少期望的输出(如测试集的label)来判断系统是否存在问题。
  • 涌现特性(Emergent Properties):当系统中的各个组件单独存在时,它们很难发挥作用。只有将它视为一个整体时,在各个组件协同工作的情况下才能有好的效果,这让我们无法通过拆解系统的手段来定位问题,增加了测试的难度。
  • 错误传播:当系统的一个部分出现问题时,它可能会将这个问题扩散到系统的其它部分,从而难以发现问题所在。

本文对机器学习测试问题进行了全面的阐述,主要包括四个角度:

  1. 测试属性(testing properties):如准确率、鲁棒性、公平性等
  2. 测试组件(testing components):如数据、程序和框架
  3. 测试工作流(testing workflow):如测试用例生成、测试执行和测试评估
  4. 应用场景(application scenarios):如自动驾驶、机器翻译等

另外,本文还统计了各种研究问题的分布情况,并就传统软件测试与机器学习应用测试技术的交叉展开了定位及未来研究方向分析。

机器学习测试介绍

本节对机器学习测试做出定义和分析,介绍测试工作流(how to test)、测试目标(what to test)和测试组件(where to test)的概念。

定义

软件中存在的问题(bug)指的是软件当前的状况和所期待的状况有所偏差,本文对机器学习的bug和机器学习测试做出以下定义:

  • ML Bug:指在机器学习应用中,任何使当前状态与期待中状态(required conditions)有所不一致的影响因素(machine learning items)。
  • ML Testing:指任何找到ML bugs的方法(testing activities)。

定义中包含三个重要的概念:

  • required conditions:包括准确率、鲁棒性和隐私性等,在机器学习系统中可能以不同的形式存在,这些是我们测试的目标,本文定义为测试属性(testing properties)
  • machine learning items:包括输入数据、程序和框架,这些是我们测试时可能出现问题的地方,本文定义为测试组件(testing components)
  • testing activities:包括测试用例生成、oracle的鉴定、测试充分性的评估和bug的分类诊断,这些是我们的测试手段,本文定义为测试工作流(testing workflow)

和传统软件测试不同,机器学习系统测试除了要对程序本身进行检查,还需要对输入数据进行检查,这也增加了测试工作的多样性。

机器学习测试工作流

下图展示了部署机器学习系统的生命周期:

https://tva1.sinaimg.cn/large/e6c9d24ely1h5btjcgutcj21km0o6tbz.jpg

  1. 基于历史数据构建原型模型
  2. 开展离线测试(如交叉验证)来检查模型是否达到预期状态
  3. 部署模型
  4. 用户使用,产生新的数据
  5. 在线测试,检验模型在真实环境下的能力
  6. 优化模型,将新数据保存至历史数据库

其中,在线测试扮演者十分重要的角色,因为离线测试无法考虑到真实场景下数据缺失、数据延迟,同时也无法获取一些如点击率、阅读时长之类的商业数据。

https://tva1.sinaimg.cn/large/e6c9d24ely1h5c6qotle1j21dg0nu0wv.jpg

离线测试

在系统部署前,开发者需要使用测试数据来验证模型是否满足用户的特定需求。开发者将数据划分为训练集、验证集和测试集,并按以下步骤操作:

  1. 需求分析
  2. 测试用例生成
  3. 在训练阶段使用训练集训练模型
  4. 训练时利用验证集实时观测模型训练状态
  5. 使用测试集(test oracle)验证模型的效果
  6. 生成bug report用于复现、定位和解决bug
  7. 执行回归测试,观察修复bug后是否会产生新的问题
  8. 部署模型

在线测试

在线测试基于用户的行为不断收集真实环境的数据,它可以找到离线测试环境下无法发现的问题。在线测试在模型运行时动态地检测模型是否达到预期效果,并且可以比较新旧模型之间的差异。

A/B测试是一个常用的在线测试方法,它将用户分成两个部分,每个部分运行的是不同版本的机器学习系统,从而比较新旧系统之间的性能。

MAB(Multi-Armed Bandit)也是一种中常用的在线测试方式,它在A/B测试执行一段时间后找到更好的模型,并把资源逐渐倾斜至更好的模型中。

机器学习测试组件

机器学习开发者在构建机器学习模型的过程中需要和搜集数据、标注数据、设计算法架构、实现模型。在这个过程中涉及到许多相互协作的组件,包括数据、程序、框架,这些组件都有可能包含bug。

其中,数据通过各种手段进行收集和预处理,程序是解决机器学习问题所编写的代码,而框架是一些高度集成机器学习功能的库(如sklearn、TensorFlow)。这些组件因为存在高度相关性,导致错误传播(error propagation)问题十分严重。为了分别测试这些组件,本文总结了以下的方法:

  • 测试数据Bug:机器学习作为数据驱动的算法,其效果很大程度受到数据质量的影响。数据Bug的测试主要关注数据是充足性(是否能够支撑模型的训练和测试)、代表性(是否能够代表未来可能出现的数据)和质量(是否存在噪声、数据偏度或有害数据)。
  • 测试框架Bug:机器学习框架为开发者们提供了便利、统一的开发接口,他们在机器学习系统中扮演着很重要的角色。如果框架中存在问题,则会导致许多机器学习应用出现问题,所以检测框架的Bug也是十分重要的工作。
  • 测试程序Bug:机器学习程序可以分为算法设计和模型实现部署,这两个部分都可能出现bug,均有测试的必要。

机器学习测试属性

本节列举了机器学习中经常考虑到的性能需求,包括功能性需求(如准确率和模型相关性)和非功能性需求(如效率、鲁棒性、公平性和可解释性),这些属性并不一定是相互独立的,在测试时还需要充分考虑他们之间的关系。

准确性

准确性表示模型输出与真实数据对比的偏差情况。具体来说,假设$D$是未来数据的分布,$x$是$D$的样本,$h$为机器学习模型,$h(x)$表示模型预测结果,$c(x)$表示样本真实结果,$E(h)$表示$h(x)$和$c(x)$相同的概率:

$$ E(h)=Pr_{x\sim D}[h(x)=c(x)] $$

达到一个令人接受的准确率是机器学习系统的基本需求,而最能反映模型准确率的还是模型在未知数据上的表现,但一般来说模型在离线测试阶段使用测试集作为test oracle计算经验准确度来检验模型。

我们将经验准确度做如下定义,假设$X=(x_1,\dots,x_m)$是$D$中采样的测试数据,$Y’=(h(x_1),\dots,h(x_m))$是模型的测试结果,$Y=(y_1,\dots,y_m)$是真实的标签,于是经验准确度$\hat{E}(h)$定义为:

$$ \hat{E}=\frac{1}{m}\sum_{i=1}^m \mathbb{I}(h(x_i)=y_i) $$

模型相关性

模型相关性(Model Relevance)指模型对数据的拟合程度,反映了模型的可学习性(capacity,即模型的上限),即模型的函数空间范围。这个能力通常可以用VC维或Radmacher复杂度来表示,它只与数据量和模型的复杂程度相关。

当模型复杂度增加时,在训练数据集上的误差会逐渐减小,在测试数据集上的误差会先见小后增大,而这个拐点就是模型较为理想的复杂程度。

本文使用机器学习算法的可学习性(capacity)和数据分布的关联作为模型相关性,具体定义为:假设$R(D,A)$是数据集$D$上任意算法$A$的最简单的可学习性,$R’(D,A’)$是算法$A’$在测试时的可学习性,则模型相关性被定义为:

$$ f=|(R(D,A)-R’(D,A’)| $$

当这个值较小时,说明模型出现过拟合现象,模型过于复杂。

鲁棒性

鲁棒性表明了系统在非法输入或是较为严格的环境下各功能正常运行的能力。具体来说,令$S$为机器学习系统,$E(S)$为系统$S$的准确性,$\delta(S)$为在任意扰动下的机器学习系统,则鲁棒性可以定义为:

$$ r=E(S)-E(\delta(S)) $$

鲁棒性问题的一个分支是对抗鲁棒性,它所设计的扰动比一般的扰动更加难以检测。

另外,鲁棒性还可以分为局部鲁棒性和全局鲁棒性。局部鲁棒性表示对于任意的含扰动输入$x’$,对于某个数据$x$有当他们之间差异的$p$范数小于$\delta$时,他们的预测结果应该相等,其反映的是单个变量的鲁棒性:

$$ \forall x’: ||x-x’||_p\le \delta \rightarrow h(x)=h(x’) $$

而全局鲁棒性表示,数据集中所有的$x$与扰动输入$x’$的差异$p$范数小于$\delta$时,预测结果的差异应该小于$\epsilon$,其反映了整体的鲁棒性:

$$ \forall x,x’: ||x-x’||_p\le \delta \rightarrow h(x)-h(x’)\le \epsilon $$

安全性

安全性反映了系统对可能产生危害的非法访问的抵抗能力。它与鲁棒性有这一定的相关性,低鲁棒性的系统可能是不安全的,它可能会因为非法数据的大量输入而导致模型崩塌。另外,安全性还反映在对模型窃取和提取的对抗能力等。

数据隐私

数据隐私性反映了系统对用户隐私的保密程度,一般用差分隐私来量化系统的数据隐私性。从概念上来说,差分隐私的定义为:对于两个只存在一个样本差异的数据集,模型的输出应该是大概率相等的。这样攻击者就无法通过用户输入前后模型的差异来判断用户输入的内容。

令$A$是一个随机算法,$D_1$和$D_2$是只有一个样本差异的两个数据集,$S$是模型$A$的部分输出,$\epsilon$阶差分隐私表示为:

$$ Pr[A(D_1)\in S]\le \exp({\epsilon})*Pr[A(D_2)\in S] $$

效率

效率问题主要针对大规模数据下模型的处理速度,对于移动端等计算资源较少或需要实时计算的场景尤为重要。

公平性

机器学习通常基于概率做出一些决策。输入的特征可能包含一些敏感的信息(如肤色、种族、宗教信仰、性别等),模型是否能够公平对待这些特征,是一个很重要的考量因素,这也涉及到了机器学习系统的伦理问题。

可解释性

机器学习常被用在医疗、收入预测、个人信用评估等问题,所以模型的可解释性能够建立人们对机器学习系统的信任。可解释性主要包括透明性(模型如何运作)和事后解释性(从模型中可以获得一些什么信息)

与传统软件测试相比

https://tva1.sinaimg.cn/large/e6c9d24ely1h5d2d0cyj6j211o09ewgn.jpg

机器学习系统测试与软件测试相比,主要有7个方面的差异:

  • 测试对象:传统软件主要测试程序代码;而ML测试需要测试数据和代码(包括程序和框架)。
  • 测试环境的变化:对于传统软件来说,当需求确定后,测试的行为也相对固定;而对于ML测试来说,当数据发生变化,模型也可能发生调整和改变,所以测试行为是在不断变化的。
  • 测试输入:传统软件测试时只有一些固定的输入数据;而ML测试的输入包含着多种形式,包括单个样本、多个样本或程序代码本身。
  • oracle:传统软件测试通常假设test oracle是存在的,且可以由开发者提前确定;而ML测试并不能保证线上测试的test oracle存在,且这些真实标签可能包含开发者的主观判断,有的标签甚至需要特定的领域知识或商业机构进行标注,增加了测试的难度。
  • 测试完整性标准:传统软件已经有一些成熟的、常用的标准来衡量测试的完整性,比如代码行覆盖率、分支覆盖率、数据流覆盖率等。但目前还没有针对机器学习特性而设计的测试完整性标准。
  • 假阳性检测:由于难以获取可靠的oracle,ML测试通常会产生大量的假阳性样本,而传统软件测试的假阳性样本较少。
  • 测试者的角色:传统软件测试主要由开发者进行;而ML测试需要结合数据科学家、算法设计者和开发者的经验进行测试。

ML 测试工作流

测试输入数据生成(Test Input Generation)

特定领域的测试输入生成

测试输入可以分为:包含扰乱因素的对抗输入数据(adversarial inputs)和正常输入数据(natrual inputs)。对抗输入数据虽然不属于正常数的分布,但它可以检验系统的鲁棒性和安全性;正常输入数据是符合真实场景数据分布的。本节介绍一下在真实场景下生成正常输入数据的方法。

  • DeepXplore提出了一种白盒差分测试技术生成机器学习系统的测试输入,它基于传统软件测试的覆盖率理论提出了“神经元覆盖率(neuron coverage)”的概念,生成具有高神经元覆盖率的测试输入。另外,这些输入在不同的模型下也有不同的表现,尽可能地贴近真实世界的数据。为了达到这些目的,它设计了一个联合优化算法,迭代地进行梯度搜索,从而生成满足这些条件的样本。
  • DeepTest是一个为自动驾驶系统生成数据的工具,它使用了九种不同的图像变换,包括:光照变化,对比度变化,位移、缩放、水平裁剪、旋转、透明、雾化和雨化。
  • DeepRoad利用生成对抗网络在不同天气场景下自动驾驶系统的生成测试输入。作者从Udacity Challenge数据集和YouTube雨天与雪天的视频中采样数据并训练,用于生成经过一些变换后的数据图片,作为测试输入。
  • DeepBillboard通过生成包含广告牌的路况图像,找到自动驾驶系统中潜在的转向错误问题。
  • Deepcruiser为音频测试输入设计了一系列的数据变换,考虑到背景噪音和音量变化。作者基于RNN网络抽象并提取出一个概率转移模型,并利用模型定义状态测试准则,来指导测试数据的生成。
  • Ding等人提出一个熙生物细胞分类器的测试框架,基于蜕变关系(metamorphic relations)迭代生成新的图片。如通过在细胞图片中增加人工线粒体的数量或形状生成新的突破,这些新增的特征很容易能被分类器发现。
  • Rabin等人为了测试code2vec算法,提出一种保留代码语义的变换方式生成新的代码数据。
  • Sun等人为了测试机器翻译系统,提出一种通过修改输入词汇而自动生成样本的方法。为了能够保证生成的样本对能够得到相同的翻译,作者基于词嵌入相似度进行单词替换。

基于模糊(Fuzz)和搜索(Search-based)的测试数据生成

模糊测试是传统的自动测试技术,它可以随机生成测试输入来检测缺陷。基于搜索的测试样本生产技术通常使用启发式的搜索算法来引导模糊测试的样本生成过程,具有更高的效率。这些技术在ML测试中也得到了一定的应用:

  • TensorFuzz使用了简单的最近邻爬山算法,为Tensorflow的数据流图找到输入数据空间中可能被覆盖到的合法输入,用于发现潜在的数值错误、神经网络与量化之间存在的不一致错误以及RNN中的预想外错误。
  • DLFuzz基于DeepXplore实现了生成对抗样本的方法,在对输入样本进行微量修改生成变种测试输入,并且找到那些可以增加神经元覆盖率,且能够产生与原样本不一致的预测结果的变种数据。由于不需要交叉引用验证(cross referencing check)这种方法具有很高的效率,相比DeppXpore减少了20%左右的时间。
  • DeepHunter结合了神经元覆盖率和DeepGauge提出的覆盖率准则,提出一种蜕变变化方法,它使用了一种更细粒度的蜕变变种策略(metamorphic mutation startegy)生成假阳率更低、覆盖率更高的的测试样本,具有更强的bug检测能力。
  • Wicker等人提出了一种基于特征的测试数据生成方法,作者基于高斯混合模型,利用SIFT对图像进行编码,并将寻找对抗样本问题转化为两人回合制的随机游戏,基于门特卡洛树搜索算法来找到图像中最容易遭受攻击的元素,作为生成对抗样本的突破口。
  • Uesato等人提出了用于强化学习的对抗样本生成方法,用于降低找到灾难性失败(catastrophic failures)的代价。作者提出一个失败概率预测器来预测agent失败的概率,提升了样本生产的效率。
  • Zhou等人结合了模糊测试和蜕变测试的方法,生成用于自动驾驶的障碍物感知模块的测试数据。
  • Jha等人通过将错误注入建模为贝叶斯网络,来生成那些能够打破安全状态的测试数据。
  • Udeshi和Chattopadhyay针对文本分类任务,提出了一种基于成对输入测试数据的距离与语法的模糊测试方法。
  • Nie等人Wang等人在自然语言推理任务中生成数据,并进行鲁棒性的测试。
  • Chan等人为可微分神经计算机(DNC)生成对抗样本,从而验证其鲁棒性。
  • Udeshi等人针对公平性生成测试输入,用于检验模型中是否包含歧视性的判断。
  • Tuncali等人提出了一种自动驾驶的测试框架,并且比较了三种测试数据生成策略:随机模糊测试生成、覆盖数组(covering array)+模糊测试生成、覆盖数组+基于搜索的测试生成(基于模拟退火算法)。

基于符号执行(Symbolic Execution)的测试数据生成

符号执行是软件测试中一个常用的程序分析技术,它用符号来代替程序的输入,得出每个条件语句分支的输出结果,用于检测程序中的漏洞。动态符号执行(DSE)能够生成高代码覆盖率的测试样本,它利用随机生成的样本,并行地在各个分支中收集符号约束。

在ML测试中,模型的表现不仅取决于代码,也取决于数据,所以符号执行方法在两者上都有着一定研究。

  • Ramanathan和Pullum基于符号分析来生产更有效的测试数据,作者将符号和概率相结合,基于距离理论,使用符号进行数据抽象,来帮助找到那些因为微小变动就会引起算法错误的测试输入。该方法对K-means的一些实现进行测试,找到了一些会因为微小变动(如比特反转)引起的错误。

  • DeepCheck中,作者列举了三点为神经网络中实施符号执行的困难:

    1. 神经网络中没有显式的代码分枝
    2. 神经网络是高度非线性的,很难找到覆盖所有条件的解
    3. 由于神经网络的结构过于复杂,可能会出现可扩展性的问题,超出目前的符号推理工具的能力

    为了解决这些问题,作者将深度神经网络转化为一个程序,使得符号执行可以找到像素攻击(pixel attack),这些像素攻击生成的图片能够在神经网络中产生与原始图片相同的激活值。神经网络中的激活函数与代码中的IF-ELSE语句有着相似的分支结构,可以被符号执行认定为一个路径。DeepCheck能够找到最难被神经网络识别出来的1-pixel attack和2-pixel attack。

  • Agarwal基于局部解释工具LIME将模型近似表示为一个线性模型、决策树或是Falling rule lists模型来找到符号执行中的路径。作者在8个开源的公平性标准中生成THEMIS3.72倍的可用测试用例。

  • DeepConcolic是一个深度神经网络上进行动态符号执行的测试方法,利用具体执行(Concrete execution,使用具体的值来覆盖路径)来指导特定的MC/DC准则下的符号分析,用于评估ML模型中具体的属性。DeepConcolic显式地将覆盖率要求作为输入。作者指出它比DeepXplore高出10%的神经元覆盖率。

用合成数据测试程序

  • Murphy等人生成包含重复数值、缺数据和分类数据的测试数据,来测试两个ML排序应用。
  • Breck等人使用符合模式约束的合成训练数据,来触发代码中不符合约束的隐藏假设。
  • Zhang等人使用未知分布的合成数据来测试过拟合现象。
  • Nakajima和Bui利用一些可预测的特征合成简单的数据集来作为oracle。

Test Oracle

Test oracle的识别是ML测试中一个关键的研究问题,它是判断bug是否存在的重要因素。由于ML算法大多都是基于概率的,所以test oracle问题在ML测试中面临更多的挑战。

将蜕变关系(Metamorphic Relations)作为Test Oracles

蜕变关系指的是:基于一定规则对测试样本进行修改后,得到的变体样本(mutanted samples)与原样本具有相同输出的,则两者满足蜕变关系。如被测程序为:

def sin(x):
    pass # some implementation

根据sin函数的定义,输入$3$与$\pi-3$的返回结果应该一致,若返回不一致,则表明该函数实现存在问题。

在ML测试中,许多蜕变关系方法通过对训练数据和测试数据的变换生成变体样本,其中分为两个类别:粗粒度变化和细粒度变化。

粗粒度变化主要包括扩大数据集、交换数据顺序而不改变单个样本:

  • Murphy等人提出了六种数据变化,包括加值(additive)、乘值(multiplicative)、置换(permutative)、倒序(invertive)、扩充(inclusive)、删减(exclusive)。该方法在MartiRank、SVM-Light和PAYL上的测试都取得了一定的效果。
  • Ding等人提出了11种用于深度学习系统的蜕变关系,作者在生物细胞分类图片的训练集和数据集上进行变化,这些变化并不会影响分类的准确率。如为每个类别的训练姐增加10%的数据量,或从数据中减少一个类别。
  • Murphy等人提出了功能层级(function-level)的蜕变关系,利用功能级别的属性(functional-level properties)在9个ML应用上取得了应用级别属性(application-level properties)170%的效果。

细粒度的变化主要对每一个样本进行一个微小的变化,如修改属性、标签、图片像素等:

  • Xie等人提出了一种测试特定的监督学习分类器的蜕变关系,文中提出了五种类型的蜕变关系。基于这些蜕变关系,可以在输入数据产生特定变化时,获取到输出的相应变化,从而生成新的oracle。作者在KNN和朴素贝叶斯算法上的测试发现不是所有的蜕变关系都是有必要的。
  • Nakajima讨论了SVM和神经网络中蜕变关系的区别。
  • Dwarakanath等人在基于SVM和深度学习的图像分类问题中应用了蜕变关系,其中图像的变换方式包括改变特征或样本的顺序、对测试数据特征的线性缩放、对测试数据进行归一化或者放大(scale up)、改变卷积操作的顺序。作者提出的方法能够找到71%的注入bug。
  • Whrheim通过改变特征名称和特征值名称来验证模型的公平性。
  • Zhang等人提出了PMV方法,将蜕变关系和数据修改结合,用于检测模型的过拟合现象。PMV在数据集中注入噪声,并根据噪声的注入程度,检查训练准确率的下降速度。若训练准确率下降的越快,则模型的过拟合程度就越轻。
  • Al-Azani和Hassine研究了朴素贝叶斯、k近邻算法及两者结合的分类器中蜕变关系的应用。研究发现蜕变关系对于两者的测试都能发挥作用,但对于两者的结合没有产生很好的效果。
  • Tian等人Zhang等人研究自动驾驶系统的测试,主张在不同天气条件下转化后的路况图像不应该有很大的变化,或是完全没有变化。
  • Ramanagopal等人使用相似图片的分类一致性位自动驾驶测试生成test oracles。该方法在未标记数据中的准确率达94%。
  • Xie等人提出了METTLE,用于进行无监督学习中的蜕变测试。METTLE为无监督学习设计了六种不同粒度的蜕变关系,包括修改样本顺序、样本区分度、密度、属性,或是注入离群点。
  • Nakajima等人研究了不同粒度的蜕变关系在SVM和神经网络中的应用,如交换样本顺序、交换属性顺序、翻转标签、变化属性值或修改图片的像素点。

有一些研究针对在不同数据集之间构建蜕变关系:

  • Kim等人Breck等人研究训练集与新数据的蜕变关系,若训练集与新数据有着不同的分布,说明训练集不够充分。Breck同时提出,对于时间上接近的数据集应该拥有一些类似的特征。

一些学者还构建了基于蜕变关系的框架:

  • Murphy等人实现了Amsterdam框架,用于自动使用蜕变关系测试ML bugs。
  • Murphy等人实现了Corduroy框架,通过扩展Java Modelling Language让开发者能够制定蜕变属性并生成ML测试数据。

将交叉引用(Cross-Referencing)作为Test Oracles

交叉引用方法是ML测试中的另一个解决oracle问题的方法。它分为差异测试(differential testing)和N版本程序设计(N-version Programming)。

在传统的软件测试中,差异测试将相同的输入数据输入至相似的应用中,观察结构是否存在差异,一般用于寻找编译bug。类似的,N版本程序设计生成并结合多个功能类似的程序,使得输出具有更高的容错性和鲁棒性。

  • Davis和Weyuker研究“不可测试”的程序中使用差异测试的可能性。其主要思想是:对于算法的多个实现程序,如果相同的输入存在不同的输出,那么至少有一个程序存在缺陷。Alebiosu等人在机器学习任务中评估了这个方法,从7个朴素贝叶斯程序和19个k近邻程序中找到了16个bugs。
  • Pham等人研究在深度学习框架实现中使用交叉引用。作者实现了CRADLE,是首个可以用来寻找和定位深度学习软件框架爱的工具。CRADLE对3个流行的框架(TensorFlow、CNTK和Theano),使用了11个数据集(包括ImageNet、MNIST和KGS Go game等)和30个与训练的模型进行验证,找到了104个不一致问题和12个bugs。
  • DeepXploreDLFuzz使用差异测试的方法作为test oracles,作者偏向于选择那些在不同算法或模型中有着不同的表现的数据。
  • 大多数差异测试方法需要利用算法的多种实现或是多个版本,Qin等人利用训练数据集生成“镜程序(mirror program)”,并检验它在训练集和测试集上的表现是否有差异。所以为镜程序指的是基于训练集生成的程序,并且程序的行为可以反映训练集的特性。
  • Sun等人将交叉引用应用于机器翻译系统的修复,提出TransRepair工具。TransRepair对比多个变种输入的输出,选择与其它输出保持相似度最大的输出,作为翻译输出的候选。

设计Test Oracles的评价标准

一些研究工作中提出了对ML系统非功能性测试的评价标准,如鲁棒性、公平性和可解释性。这些评价标准可以帮助测试者理解测试过程中所看重的属性,观察oracles测试的结果是否达到预期。

  • Kang等人提出了两种车辆视频检测问题中的评价标准:

    1. 闪烁测试(flickering assertion):检测视频中车辆目标框(bounding box)的闪烁情况。
    2. 多框测试(multi-box assertion):检测视频中车辆目标框的个数和重叠情况。

    比如一个车辆被多个目标框框住,则它没有通过多框测试(正常来说一个车辆被一个框框住,效果会比较好)。本文还提出了自动修复这些问题的方法。

  • Li等人提出了测试长期机器学习(lifelong machine learning)的标准,作者认为,对于那些不断能在新任务中学习到知识并且提升表现的模型,则通过了长期机器学习测试。

测试的充分性(Test Adequacy)

测试的充分性检验旨在发现目前的测试是否具有良好的错误发现能力,同时它还能够知道测试数据的生成。在传统的软件测试中,代码覆盖率检测和异变测试(mutation testing)是比较常用的技术,这些技术在ML测试中也可以被使用。

测试覆盖率

在传统软件测试中,代码覆盖率指测试所用的输入数据所执行到的代码相对于总代码量的占比,所以经常需要设计一个测试数据套件(test suites)使覆盖的代码量更高。

与传统软件测试不同的是,ML模型的决策逻辑并不是人为编写的,而是通过模型训练学习而来的。Pei等人提出,我们很容易在ML测试中达到100%的代码覆盖率,于是学者们又提出了许多种覆盖率来体现ML测试的程度。

  • 神经元覆盖率(neuron coverage)
    • 最早由Pei等人提出,指的是在测试数据下,深度神经网络模型中被激活的神经元个数占总神经元个数的比例,这里的激活指的是神经元的值高于某个特定阈值(如用relu函数就是大于零)。
    • Ma等人进一步扩充了神经元覆盖率的概念,提出了更细粒度的神经元覆盖率,主要包括k节覆盖率(k-multisection coverage),神经元边界覆盖率(neuron boundary coverage)和强激活神经元覆盖率(strong neuron activation coverage),反映了神经网络的主要功能表现和边界表现。
  • MC/DC覆盖率(及变体):满足MC/DC条件的测试用例覆盖了所有的分支和条件,并且每个变量可以单独影响条件的执行。Sun等人基于MC/DC的概念,针对深度神经网络不同的特征提出了四个测试覆盖准则。
  • 层级别的覆盖率(Layer-level coverage):
    • Ma等人提出了层级别的覆盖率准则,考虑了最高激活的神经元以及他们的联合(或者序列)来表述深度神经网络的行为。在MNIST和IMageNet上,层级别的覆盖率和神经元覆盖率相结合,能够达到更好的测试效果。
    • Ma等人提出的DeepCT进一步提出了组合式测试覆盖率,基于每一层内神经元的k-way组合,计算每个组合的激活完整性和数量,从而定义相应的覆盖率。
    • Sekhon和Fleming定义了一种准则,用于寻找满足以下条件的的测试用例:
      • 在同一层中,所有神经元对包含所有可能的输出值的组合
      • 在相邻的层中,所有神经元对包含所有可能的输出值的组合
  • 状态级别的覆盖(State-level coverage):上述的准则主要适用于前馈型神经网络。对于基于状态的神经网络(如RNN),Du等人率先提出针对RNN的覆盖准则,作者首先将深度学习系统抽象为一个概率转移系统,并基于状态和概率转移系统来获取动态的状态转移行为。
  • 覆盖率准则的局限性:虽然研究者们设计了多种类型的覆盖准则,但是它们大多专注于深度神经网络。
    • Sekhon和Fleming分析了现有的神经网络测试方法并讨论了这些准则的局限性。
    • Li等人指出由于神经网络和人为编写的代码存在基本的差异,结构化的覆盖率准则存在一定局限性。在他们的实验中,测试集中的被误分类输入数据与结构化覆盖率之间并没有强相关性。由于机器学习天然的黑盒特性,无法明确结构化准则与系统的决策逻辑有什么关联。

异变测试(Mutation Testing)

传统软件测试中,异变测试通过注入错误代码来检验软件发现错误的能力,被检测到的错误占注入错误的比例成为异变得分(Mutation Score)。在ML测试中,异变测试不仅要考虑代码,还要考虑数据和模型结构:

  • Ma等人提出了DeepMutation,从源代码或模型的角度修改深度神经网络,从而对模型的判断边界(decision boundary)带来微小的扰动。此时异变得分被定义使判断结果变化的异变数占异变总数的比例。
  • Shen等人为深度神经网络提出了5种异变操作,并在MNIST数据集上对异变的属性进行验证。作者指出特定领域的异变操作可以更好地进行异变分析。

相对于结构化的覆盖率准则来说,基于异变测试的准则和深度神经网络的判断边界更加相关。举例来说,利用在判断边界附近的输入数据可以很好的检测原始模型和修改后的模型的不一致,从而判断模型实现的有效性。

Surprise Adequacy(意外充分性)

Kim等人提出了surpise adequacy的概念,用于衡量机器学习系统中那些“意外输入”的离散覆盖范围。作者认为测试数据的多样性比训练数据更为重要。相对于训练数据来说,一个好的测试输入应该“充足、但不太意外”。作者对“意外”的概念提出了两个衡量标准:

  1. 基于核密度估计(Kerneral Density Estimation,KDE)来逼近输入训练集中包含相似样本的似然(likelihood)。
  2. 输出特定的输入数据与训练数据的特征向量(由神经元的激活值构成),并计算它们之间的距离。

这个标准还可以检测对抗样本。在未来的工作中,我们可以研究这样的准则是否可以使用“意外”的概念探究ML模型在边界决策问题上的行为。另外,对于对抗样本、正常的错误样本和“意外”准则之间的关系,也是一个值得研究的课题。

给予规则检查测试的充分性

一些研究为检测ML系统的功能性,提出了一些基于常规规则的检测方法。Breck等人提出了基于28个层面的检测规则和得分系统,这些规则主要来衡量一个ML系统被测试的有多完备。这28个层面的规则可以分为四个类型:

  1. 对模型自身的测试
  2. 对构建模型所用的框架的测试
  3. 对构建模型所用数据的测试
  4. 对模型长时间使用情况的测试

这些规则还可以被应用于指导测试数据生成,如训练过程需要能被复现、所有特征应该都是对模型有益的、不存在更简单且效果更好的模型。文章指出,虽然ML测试十分复杂,但是在设计一些基本的测试用例时,还是有很多共同的属性可以利用。

测试数据的优先考虑和缩减

ML测试的输入数据空间十分的庞大,如果对于每一个测试数据都进行标注的话是十分困难的,于是部分学者专注于探究如何在保证测试完整性的前提下,对数据的优先考虑和数据的缩减,从而减少数据生成的负担:

  • Byun等人使用了深度神经网络中的一些标准(如交叉熵、意外度、贝叶斯不确定性)来优先选择一些样本。这些样本可以很好地暴露出模型一些不被预期的行为,并且能够在重新训练(retraining)中发挥重要的作用。
  • 生成数据同样需要耗费巨大的计算资源,Zhang等人针对这个问题,试图寻找拥有更多有效的对抗样本的测试用例。具体来说,作者通过检验测试用例对噪声的敏感程度,并从高到低排序,优先筛选那些更敏感的测试用例。
  • Li等人专注于在深度神经网络测试中缩减测试数据集。作者使用神经网络最后一层输出,提出一种基于最小化交叉熵原则的数据分布近似表示技术。作者在MNIST、Udacity challenge和ImageNet上与随机采样进行对比,仅用了一半的数据量就达到了相似的效果。
  • Ma等人基于模型对数据的置信度提出了一种测试集选择的标准。该标准更倾向选择那些模型不太确定的数据,这些数据包含着更多的信息,能够在重新训练中发挥较好的作用。实验评估发现该方法比随机选择高出了80%的收益。

Bug报告分析

  • Thung等人首次对ML系统的bug报告分析开展研究,具体包括bug的频率、分类、严重性、修复时间、涉及文件数等。研究表明22.6%的问题来自于算法的实现错误,15.6%的bug是非功能性错误,5.6%的bug是数据问题。
  • Zhang等人基于Github和Stackoverflow的Bug报告,研究了Tensorflow的175个bug的症状和根本原因。作者将这些bug分为崩溃错误、底准确度、低效率和位置问题。主要引起bug的原因是API的误使用(18.9%)、张量没有对齐(13.7%)和模型参数或结构的错误(21.7%)。
  • Banerjee等人分析了12个汽车制造商的自动驾驶系统中的bug报告。作者基于NLP技术将引发自动驾驶失效的问题分为10个类别,其中机器学习系统和决策控制错误引发的问题占64%。

Debug和修复

数据重采样(Data Resampling)

在经过上述的数据生成工作后,利用生成的数据对模型进行重新训练,可以有效提升模型的准确度。DeepXplore和DeepTest在重新训练后均有一定的提升。

Ma等人将引起训练错误的神经元称为错误神经元(faulty neurons),他们将引起这些神经元错误的样本进行重新采样,用于增加模型的效果。

Debug框架的开发(Debugging Framework Development)

  • Dutta等人提出了程序转移框架Storm,用于生成能够支持ML测试debug的小程序。通常来说,开发者为了修复一个bug需要详细分析源代码、写好错误报告、执行debug和回归测试。Storm通过程序分析和概率推理来简化概率程序,减少问题定位的难度。

  • Cai等人提出了tfdbg,对基于Tensorflow实现的ML模型进行测试。它主要包括三个部分:

    1. 分析器(Analyzer):对运行时的张量图(tensor graph)结构进行可视化。
    2. 节点计步器(NodeStepper):使客户端可以对图中的节点执行暂停、检测和修改的操作。
    3. 运行计步器(RunStepper):使客户端可以在程序迭代训练过车各种执行高层级的操作。
  • Vartak等人提出了MISTIQUE系统,用于捕获、存储和查询模型的实时状态。

  • Krishnan和Wu提出了PALM,使用两部分的代理模型(two-part surrogate model)来解释复杂模型:

    1. 一个元模型(meta-model):将数据集进行区分
    2. 多个子模型(sub-models):对每个区分的模式(patterns)进行拟合

    PALM可以找出最能影响模型预测能力的训练集,从而找到那些引起预测错误的数据子集,来帮助debug的执行。

对修复的理解(Fix Understanding):施工中

机器学习系统中,bug修复是一件比较困难的工作,因为它可能出现在不同组件中的多个地方。Nushi等人 proposed a human-in-the-loop approach that simulates potential fixes in different components through human computation tasks: humans were asked to simulate improved component states. The improvements of the system are recorded and compared, to provide guidance to designers about how they can best improve the system.

程序修复

Albarghouthi等人提出了一种基于分布引导的感应式合成方法,用于修复ML在内的决策程序问题。其主要思想是生成一个与源程序保持相似语义,且有着相同输出的新程序,具体来说,它将程序编码为SMT结果,并利用采样数据和预测结果来驱动新程序的生成。

通用测试框架和工具

  • Yang等人针对安全性测试提出了合法测试输入生成的框架。
  • Dreossi等人提出了包含三个模块的CNN测试框架,包括图像生成器、采样方法合集和可视化工具套件。
  • Tramer等人提出了一种全面的测试工具,基于简单的、具有可解释性的bug报告来帮助开发者进行公平性问题的测试和debug。
  • Nishi等人提出了一个包含不同评估层面的测试框架,包括可用度、鲁棒度、可避免度和可改进度等,他们同事讨论了不同级别上的ML测试,如系统级、软件级、组件级和数据测试。
  • Thomas等人提出了ML算法涉及框架,简化了对预期外行为的监管。该框架适用于对回归问题、分类问题和强化学习的监管。它能大概率保证模型可以从可能存在偏差的数据中学习后,能够对位置数据的预测保持较小的偏差。对于输入的算法和数据,框架能够返回训练后的高可靠模型,或是返回一个无法训练出高可靠模型的警告。

ML属性测试

ML的属性是ML测试中一个关键的考虑因素,它通常和训练后的模型表现有着很强的关联性。若模型在某些属性上表现不佳,则很可能模型存在问题。

准确性(Correctness)

准确性是衡量ML系统一个基本的属性,在ML任务中几个典型的做法是利用不同类型交叉验证,对模型的准确性进行评估,如K-fold、leave-one-out等。准确性的衡量标准有许多,包括准确率、精准率、召回率、AUC等。

  • Japkowicz研究了这些标准在不同场景下的问题,如准确率不能区分错误的类型(假阳性和假阴性)、精准率和召回率在类别不平衡问题中不能反映模型的能力。所以在选择模型的评价标准时需要考虑到很多因素。
  • Chen 等人在评估 ML 分类器的正确性时,研究了训练数据和测试数据的可变性。作者推导了用于估计性能方差的解析表达式,并提供了一个用高效计算算法实现的开源软件。Chen同时研究了不同统计学方法的AUC表现,并发现F-test指标有着最好的性能。
  • Qin等人提出的镜程序(mirror progam)中利用镜程序的表现充当准确性的oracle,这些镜程序理应在测试数据上有着相似的表现。
  • Zhang等人研究了175个Tensorflow的bug,发现有40个与低准确性有关。

模型相关性(拟合程度)

模型相关性评估主要探究模型和数据之间的差异,模型相关性较差的模型通常有过拟合或者欠拟合的现象。

交叉验证是一个常用的检测过拟合的手段,但它并不能很好地量化过拟合程度,或者当测试数据不能代表没有出现过的数据时,交叉验证并不能起到作用。

  • Zhang等人提出了扰乱模型验证(Perturbed Model Validation,PMV)的方法,在训练集中注入噪声,并使用注入后的数据重新训练,观察准确率的下降情况来检测过拟合或欠拟合的现象。过拟合的模型会拟合噪声数据,而欠拟合的数据无论是否使用存在扰动的数据进行训练,都不会有很高的准确率。所以这两种情况对噪声数据都不太敏感,并且在扰动数据集的准确率都较低。经过测试,该方法相对10折交叉验证能够更好的发现过拟合和欠拟合现象。
  • ML系统在部署后往往会获得新的数据用于重新训练,但测试集并不能保证包含了未来可能出现的数据。Werpachowski等人从测试数据中生成对抗样本进行过拟合检测,如果在对抗样本上重新赋权的误差与原本的测试数据集的误差有很大的不同,则模型出现了过拟合现象。
  • Gossmann等人研究了医学领域中测试数据重复使用的问题,作者发现如果在所有的模拟配置下都适用同样的测试数据,则会引发意外的过拟合问题。
  • Krik认为训练时间可以作为ML模型复杂度的代表,当准确率相近的情况下应该选择耗时更少的模型。
  • Ma等人尝试通过对数据集重采样的方法减少过拟合问题。

鲁棒性和安全性

鲁棒性评价准则

鲁棒性是描述深度学习系统对噪声数据处理能力的一个指标,一个鲁棒的系统应当在存在噪声数据时也保持良好的性能。

  • Moosavi-Dezfooli等人提出了DeepFool,通过在训练时增加噪声来量化神经网络的鲁棒性。
  • Bastani等人提出了三种衡量鲁棒性的指标:
    1. 点级鲁棒性(pointwise robustness):表示引发鲁棒性错误的最小输入变化。
    2. 对抗频率(adversarial frequency):表示能够引发分类器输出改变的数据变化频率。
    3. 对抗严重性(adversarial severity):表示输入数据与其最近邻的对抗数据的距离。
  • Carlini和Wagner构建了一系列攻击方式,用于衡量神经网络鲁棒性的上界和下界。
  • Tjeng等人将输入数据与其最近邻的对抗数据的距离作为鲁棒性的度量标准。
  • Ruan等人基于测试数据构建了全局的鲁棒性上下界。
  • Gopinath等人提出DeepSafe,以数据驱动的方式衡量深度神经网络的鲁棒性,作者认为被聚类在同一个簇的样本应该有相同的标签。
  • Mangal等人提出了概率鲁棒性的概念,将神经网络的行为进行抽象,并基于模型出现的不鲁棒行为计算那些“过度估计”的输入数据。
  • Banerjee等人基于贝叶斯深度学习,基于神经网络内部传播的误差,对硬件误差的敏感性进行建模,摈弃了之前利用额外注入的方式。

扰乱测试集

扰乱测试集的主要做法是生成对抗样本,这些对抗样本能够检测那些需要具备高安全性的机器学习系统是否会产生严重的安全问题。

  • Carlini和Wagner研究了一种基于距离的对抗样本生成方法,并成功地对防御性蒸馏网络的输入图片数据生成对抗样本。
  • Papernot等人开发了对抗样本生成库cleverhans v1.0.0cleverhans v2.0.0。作者认为对抗样本生成的标准化是很有必要的,没有实现对抗生成标准化的方法之间是不可比较的,因为无法知道是两者鲁棒性存在差异,还是两者所生成的对抗样本存在差异。

扰乱整个系统

  • Jha等人提出了三个系统级别的扰乱工具,AVFI使用应用级别的错误注入来模拟自动驾驶系统中传感器、处理器和内存的硬件错误,从而检测系统的鲁棒性;Kayotee基于闭环的模拟环境,进一步描绘了错误的传播和掩蔽(masking),能够在GPU和CPU中注入bit位翻转;DriveFI能够挖掘最大程度影响自动交通工具安全性的状态和错误。
  • Tuncali等人研究了自动驾驶系统中的闭环行为,为对抗样本的生成提供支持。这些生成的样本不仅考虑了图像的数据空间,还考虑了配置的数据空间。

效率(Efficiency)

  • Zhang等人所研究的TensorFlow中出现的175个bug里,有9个(5.1%)属于效率问题。
  • Spieker和Gotlieb研究了三个缩减训练集的方法,试图在训练中过寻找训练集的一个子集,使得模型在加速训练的同时能够在测试集中有着相近的表现。

公平性(Fairness)

公平性是近年来受到重视的非功能性特征,Barocas和Selbst提出5个可能引发公平性问题的原因:

  1. 样本偏差:一旦训练样本在开始时有所偏差,那么这种偏差的情况会一直影响模型的表现。
  2. 污染样本:被误标记的样本会使得模型产生偏差。
  3. 特征限制:样本的特征可能没有包含足够的信息,或是从不可靠的来源收集而来。
  4. 样本数量差异:对于不同类型的样本,若数量存在巨大差异(如有宗教信仰的人数远小于没有宗教信仰的人数),则模型可能会倾向于数量多的样本。
  5. 代理(Proxies):一些特征可能一定程度上代表了敏感属性(如一个人居住的社区),即使敏感属性被排除了,模型也可能存在偏差。

公平性的定义和评价标准

假设$X$为数据集,$Y$为对应的标签集合,$h$为模型,$A$为敏感特征集合,$Z$为非敏感特征集合我们可以定义以下几种公平性标准:

  1. 无意识公平性(Fairness Through Unawareness):在模型进行判断时,不以敏感的特征集合作为判断依据,这是最基础的公平性原则。
  2. 组公平性:数据可以根据一些敏感特征分成不同的两组,原则上每一组对特定预测结果的概率应该保持相同。目前的研究中提出了几种组公平性原则:
    1. 人口学平等(Demographic Parity):$A$中的某个特征$a$的值将数据集划分为$G_1$和$G_2$,若两个数据集的数据对特定预测结果的概率保持相同,则认为它们满足人口学平等,即$P{h(x_i)=1|x_i\in G_1}=P{h(x_j)=1|x_j\in G_2}$。
    2. 均等赔率(Equalised Odds):进一步规定了对于拥有特定标签$y_i$的变量,不同分组的预测概率要保持一致,即$P{h(x_i)=1|x_i\in G_1,Y=y_i}=P{h(x_j)=1|x_j\in G_2,Y=y_i}$。
    3. 机会均等(Equal Opportunity):是均等赔率的变体,它把标签限制在正样本上,即$P{h(x_i)=1|x_i\in G_1,Y=1}=P{h(x_j)=1|x_j\in G_2,Y=1}$。
  3. 反事实公平性:当敏感属性被翻转为一个违反事实的数值,而其它属性固定时,两者预测结果的概率应该保持一致,即$P{h(x_i)a=y_i|x_i\in X}=P{h(x_i’){a’}=y_i|x_j\in X}$。
  4. 个体公平性:对于距离小于一定阈值的样本,它们预测结果的概率应该保持一致,即$P{h(x_i)=y_i|x_i\in X}=P{h(x_j)=y_i|x_j\in X}\ if\ d(x_i,x_j)<\epsilon$ 。

现有的这些公平性定义有各自的优点和缺点,许多学者对这些定义进行了研究和评估:

  • Gajane和Pechenizkiy调查了公平性是如何定义与表示的。
  • Corbett-Davies和Goel研究了三种公平性定义:反分类(anti-classification)、分类平等(classification parity)和校准(calibration)。作者举例说明了各种类型的深层统计局限。
  • Verma和Rubin基于一个通用的数据集解释和分析了目前最主要的公平性定义。

另外,有一些研究致力于提升模型的公平性:

  • Metevier等人提出RobinHood,在offline contextual bandits问题上支持多个用户可定义的公平性定义。RobinHood使用集中不等式来计算高概率边界,用于找到满足公平性的解决方案。
  • Albarghouthi和Vinisky提出了公平性感知编程的概念,基于Python设计了一个能够监控代码是否违背公平性的语言。
  • Agarwal等人将考虑公平性的二分类问题简化为代价敏感分类问题,在准确率和公平性约束之间进行权衡。
  • Albarghouthi等人提出了一种基于分布引导的归纳合成方法对决策程序进行修复。

用于公平性测试的测试集生成

  • Galhotra等人提出了Themis工具,基于原因分析(causal analysis)研究组公平性。作者定义了公平性得分,并使用随机生成的测试数据来评估模型的歧视程度,Themis在包含较多歧视性问题的系统中效果较好。
  • Udeshi等人提出了Aequitas工具,基于数据生成技术发现那些包含歧视性的输入和能反映个人特性的输入。具体来说,Aequitas首先在输入空间中进行随机采样,在找到包含歧视性的输入后,不断寻找与其距离最近的样本点,从而寻找到更多的歧视性输入。
  • Agarwal等人结合了符号执行和局部可解释性来生成测试输入,其主要思想是使用Local Interpretable Model-agnostic Explanations来判定模型的判断结果是否与隐私属性有关。
  • Tramer等人率先提出了“公平性bug”的概念,作者认为如果模型输出与隐私属性有着过于高的关联,则存在公平性bug,并将其命名为“Unwarranted Associations”。作则提出了一个全面的测试工具,来帮助开发者测试公平性bug,并生成简洁明了的bug报告。
  • Sharma和Wehrheim通过检查待测算法是否对数据集的变化敏感,来寻找引起不公平现象的原因。

可解释性(Interpretability)

人为评估的可解释性(Manual Assessment of Interpretability)

在现有的根据经验的可解释性评估方法中,大多有人类参与。人为评估可解释性目前依然是目前的主流方式:

  • Doshi-Velez和Kim对可解释性评估做出几种分类:

    • 基于应用的评估:指在真实应用场景下进行人类实验的结果。
    • 基于人类认知的评估:指人类在简单任务中对其可解释性的理解。
    • 基于功能性的评估:使用一个代表性的量化指标,融合人类的理解来评估可解释性,如决策树的深度。
  • Friedler等人介绍了两种可解释性:

    • 全局可解释性:对所训练模型的整体理解
    • 局部可解释性:针对模型特定的输入与输出进行理解

    作者对1000个用户进行调查,在模型的输入不断的修改的情况下记录用户对每次修改所期待的输出。

自动化解释性评估(Automatic Assessment of Interpretability)

  • Cheng等人提出了一种理解ML模型行为的指标,该指标衡量了模型是否通过遮挡其它物体来帮助自己判断物体。
  • Christoph等人认为,达到模型可解释性最简单的办法是只使用那些创建可解释性模型的算法,这些模型包括线性回归、逻辑回归和决策树。
  • Zhou等人定义了蜕变关系模式(Metamorphic Relation Patterns,MRPs)和蜕变关系输入模式(Metamorphic Relation Input Patterns,MRIPs)。

增加可解释性的方法

在许多医疗应用中,ML分类器有着广泛的应用,但是它所预测结果的临床意义尚不明确。Chen等人将分类得分转化为患病概率,并指出任何一个尺度都能被校准至概率尺度,并且不会降低模型判断的性能。

保密性(Privacy)

  • Ding等人通过统计测试对违背差分隐私的数据进行检测,对于监测到的数据,作者生成反例来描述这些违背行为,帮助开发者们理解和修复bug。
  • Bichsel等人对差分隐私中的$\epsilon$参数及性能估计,为了找到一个能够最大程度上违背差分隐私的三元组$(x,x’,\Phi)$,其中$x$和$x’$为两个不同的输入,$\Phi$是可能的输出集合。

机器学习组件(ML Testing Components)

数据Bug检测

数据是决定ML系统性能的最重要一环。通常在ML系统中,数据会被不断地用于训练并生成新的数据,从而不断改进模型的表现,若能够在系统构建早期就能发现数据中的问题,则可以大大提升模型的可持续性。

但是数据测试是一件十分困难的任务,在ML流程中,数据生成的逻辑通常缺少可视化,我们所使用的数据通常以原始值的形式保存在如csv格式的文件之中,使得我们无法通过一些语义信息来定位数据的问题。

训练集Bug检测

  • 基于规则的数据Bug检测:Hynes等人收到code linters的启发,提出了data linter,用于自动检测ML数据集。坐着考虑了数据集中存在的三类问题:

    1. 误编码数据,如将数字或日期误编码为字符串。
    2. 离群点和尺度:如不正常的列表长度。
    3. 打包错误:如重复数值、空样例等数据组织问题。

    Cheng等人提出了一系列评估标准,用于检测训练集是否覆盖了所有重要的场景。

  • 基于表现的数据Bug检测:Ma等人提出了MODE,用于检测神经网络中引发分类错误的的错误神经元,并且通过冲采样的方式找到引发神经元错误的训练数据。MODE在MNIST、Fashion MINIST和CIFAR-10上平均提升了75%到93%的效果。

测试集Bug检测

  • Metzen等人设计了一个子网络来增强深度神经网络,这个子网络用于从包含对抗性扰乱的数据集中找到正常的样本。
  • Wang等人通过对模型进行修改来检测对抗样本,作者发现这些对抗样本对数据的扰动更为敏感。作者在MNIST和CIFAR10上进行74.1和86.1的修改,分别检测了96.4%和90.6%的对抗样本。
  • 对抗样本的存在增加了数据集的安全风险,因此对抗样本的检测也是一种bug检测工作。Carlini和Wagner对10篇文章进行综合研究后,发现对抗样本的检测很大程度上依赖于损失函数。
  • 测试数据的不足可能导致过拟合问题无法被检测到,这也被视为一种测试集Bug。

训练集与测试集的偏度(skew)

  • Kim等人提出了两种衡量训练集于测试集偏度的标准:
    1. 利用KDE估计训练过程中见到相似输入数据的似然。
    2. 基于神经元输出的向量间的距离。
  • Breck比较了训练数据和线上数据之间的偏度,使用关键特征连接的方式检测特征的偏度,并指出KL散度或余弦相似度并不能充分表示数据分布间的偏度,提出使用概率变化的最大值作为两个分部之间的距离度量。

检测数据Bug的框架

  • Breck提出了一个数据验证系统,系统在每个单独的batch(包括训练数据和新数据)上应用一些约束(如类型、域等),对训练数据和新数据之间的距离进行量化。这个系统被集成在了谷歌TFX平台中。该系统可以在训练初期检测到数据中存在的bug,减少了模型训练中遇到的风险。作者还总结了三种最常见的bug,包括新特征列、预料之外的字符串值和特征列丢失问题。
  • Krishnan等人提出了ActiveClean框架,在训练过程中不断对数据进行清洗的同时保持那些让训练得以收敛的属性。ActiveClean通过数据相对于模型的值以及数据是“脏”数据的似然来判读啊那数据是否是干净的数据。ActiveClean可以执行转换和过滤操作来对脏数据进行清洗。
  • Krishnan等人随后又提出了BoostClean来检测域值问题(domian value violations),即检测某个属性的值是否超出了允许的范围。该工具使用了一些清洗资源(cleaning resources)如Isolation Forest来提升模型的表现。在域值问题被找出来后,模型最高可以提升9%的效果。
  • Schelter等人专注于在大型数据集上进行自动化单元测试,作者提出的系统在测试集上提供了一个可以用户自定义的质量约束接口。
  • Krishnan和Wu也提出了自动化数据清洗工具AlphaClean,坐着使用了贪心树搜索算法,自动对数据清洗流程中的参数进行调优。用户可以专注于定义数据清洗的需求,把剩下的寻优过程交给工具来处理,AlphaClean在三个数据集上的表现是当时最优方法的9倍。

程序Bug检测

程序bug检测指的是在算法设计较好的情况下,程序对其的实现是否存在问题。

单元测试

  • McClure基于TensorFlow自带的功能实现了ML的单元测试,用于确认程序的功能是否可以达到预期的结果。
  • Schaul等人开发了用于随机优化方法的单元测试集合,这些测试具有规模小、相互独立、易于理解的特点,可以在训练初期使用,从而尽早地发现程序里存在的bug。

算法配置检查

  • Sun等人在3个机器学习框架爱中研究了329个真实bug,发现超过22%的原因是操作系统、语言版本和硬件冲突的问题。
  • Guo等人对比了各ML框架在MNIST和CIFAR-10上进行分类时的准确率、模型大小和鲁棒性。
  • Zhang等人发现程序中最常见的bug是并没有完全跟进TensorFlow API的变化。

算法选择检查

开发者通常在解决问题时有很多算法可以选择。

  • Fu和Menzies在StackOverflow问题连接的问题中对比了传统算法和深度学习算法的效果,发现如SVM的传统方法可以达到和深度学习差不多的效果,同时花费更低的代价。
  • Liu等人发现KNN算法在commit消息生成问题上能够达到于深度学习算法类似的效果。

用变体程序模拟故障(Mutant Simulations of Learning Program Faults

  • Murphy等人通过对程序进行微小改变生成变体程序,来发掘蜕变关系是否有助于寻找bug,这些改变主要包括切换比较运算符、数学运算符和改动循环变量。
  • Dolby等人在WALA的基础上扩展功能,定义和跟踪机器学习张量的类型,并生成数据流图来抽象程序的行为。

框架Bug检测

框架bug研究

  • Xiao等人专注于深度学习框架的安全性。在研究了Caffe,TensorFlow和Torch后,作者发现了这些框架实现中容易遭受攻击的部分,其引发最常见的问题包括崩溃、程序无法终止或使用过多内存。
  • Guo等人测试了多个深度学习框架的运行时表现、训练准确率和鲁棒性,发现它们在准确率上的表现近似,但在训练时有着不同的行为。
  • Sun等人发现有10%的bug反馈中涉及深度学习框架效率低下的问题,这些问题往往需要开发者耗费更多时间解决。

对框架内部算法的测试

ML框架中往往集成了许多经典算法,若这些算法存在bug则会对引用它们的程序产生影响。Thung等人发现在500个ML系统的bug中,有22.6%的bug都是由于算法的错误实现引起的。Cheng等人在机器学习框架Weka中注入一些实现错误来观察各模型的表现,作者发现8%-40%的逻辑上与原程序不同的变体程序的表现于原程序差异不大。

Bug探测的实现

一些工作致力于通过算法的多种实现或差异测试的方式检测bug:

  • Alebiosu等人从10个朴素贝叶斯实现中找到5个错误,从20个KNN实现中找到4个错误。
  • Pham等人在3个框架、11个数据集和30个预训练模型中找到了12个错误。

但不是所有的算法都有多个实现的,Murphy等人在ML测试中率先使用了蜕变关系,将数据进行一系列的变化从而生成mutants,如数据乘上一个常数、打乱顺序、增加数据,这些mutants理论上不会对模型的输出造成影响。

Xie等人在监督学习算法中构建了5中蜕变关系,并在之后的工作中进一步对代码进行修改生成蜕变关系,在Weka注入的43个错误中找到了其中的39个。

Dwarakanath等人在图像分类问题中设计蜕变关系来找到bug,对于传统算法,作者进行了交换特征顺序或进行线性变化;对于深度学习算法,作者对测试集进行归一化和缩放,或是改变卷机操作的顺序。这些操作理论上不会改变模型的预测结果。作者通过这种方式发现了71%的bug。

应用场景

自动驾驶

自动驾驶方面的研究有着很长的历史,早在2004年,Wegener和Buhler就针对自动停车系统设计了不同的拟合函数,Woehrle等人也提出自动驾驶测试领域有这许多亟待解决的问题。

近年来,在自动驾驶测试领域,基于搜索的测试样例生成(search-based test generation)方法有这广泛的研究:

  • Abdessalem等人专注于为辅助驾驶系统的搜索测试提升效率和准确性,作者使用了分类模型来提升极端场景下的样本生成效果,同时还利用搜索算法对分类模型的准确率带来改进。
  • Abdessalem等人提出了多目标搜索算法FITEST,用于搜索造成系统错误的特征交互行为。

目前大多数方法专注于半自动驾驶测试,当系统出现问题时需要人类接管驾驶,触发这种问题的情况称为脱离(disengagement)现象。

  • Banerjee等人研究了12个制造商的144个自动驾驶工具sulfas了5328个脱离现象,其中42个导致了交通事故。作者发现64%的脱离现象是由于ML系统错误引起的,其中占比最大的是图像分类问题(如交通灯、道路标记、洞或颠簸的检测错误),占比达到44%。剩余的20%主要是控制和决策框架的错误,例如一些不合适的行为规划决策。
  • Pei等人使用基于梯度的差异测试方法生成测试数据,利用神经元覆盖率作为参考来检测深度神经网络中潜在的bug。
  • Tian等人使用一系列图像变化生成包含噪声的图片,用于模拟真实世界的环境。
  • Zhang等人提出了DeepRoad,使用GAN来生成真实世界的驾驶场景。作者提出的方法支持雪天和雨天的场景。
  • Zhou等人提出了DeepBillboard方法,用于生成包含广告牌的真实世界图像,模拟一些出现遮挡的场景。
  • Wicker等人基于特征的引导,使用Monte Carlo树搜索的方法来找到最能影响自动驾驶系统的图像。
  • Jha等人通过将错误注入行为建模成贝叶斯网络的方法来加速寻找安全隐患的过程。
  • Uesato等人在基于强化学习的自动驾驶系统中寻找严重的错误,作者阐述了传统随机测试方法的弊端,并提出一个预测对抗样本生成的方法,用于预测错误并评估可靠性的风险。
  • Dreossi等人针对卷积神经网络,构建了一个能够生成自然图像,且可以可视化所收集的信息的工具,用于检测自动驾驶场景中的盲点。
  • Tuncali等人提出了一个支持系统级别测试和属性级别测试的框架,支持模糊测试和基于搜索的测试(如模拟退火和交叉熵优化)
  • Zhou等人将模糊测试和蜕变测试结合,用于测试自动驾驶场景中的激光雷达图像。
  • Jha等人提出了AVFIKayotee,可以全面地在自动驾驶系统中注入错误来评估它们的可靠性。
  • O’Kelly等人提出了一种基于风险的框架,基于交通行为的数据分布,测试自动驾驶系统产生事故的概率。作者认为在自动驾驶系统中验证准确性是很难执行的,这是由于对“准确性”的正式定义存在一定难度。在真实环境下,传统的自动驾驶测试技术需要耗费很多的时间,为了解决这个问题,作者将自动驾驶测试转化为稀少事件模拟问题(rare-event simulation problem),并评估事件发生的概率来加速自动驾驶测试过程。

机器翻译

机器翻译将文本自动化地转换从一种语言转化至另一种语言。BLEU(BiLingual Evaluation Uderstudy)得分是机器翻译中应用最广泛的评价标准,它评估了机器的输出和人类理解之间的相似程度。

  • Zhou等人提出了工具MT4MT,使用自定义的蜕变关系来检测机器翻译系统中的翻译不一致问题。其主要思想是,对于输入进行某些改变后,翻译结果的总体结构不应该收到影响。作者的研究发现在长文本翻译中Google翻译比Microsoft翻译的效果好,而段文本则是相反。作者认为评估机器翻译时应该考虑多种维度和多种类型的输入。

  • Sun等人结合了异变测试和蜕变测试的思想,对机器翻译系统的一致性进行检测和修复,提出了TransRepair工具,用于自动地进行测试输入生成、oracle生成和翻译修复。具体来说,TransRepair首先对输入句子进行修改,来发现翻译不一致的问你题,之后使用修改后句子,以黑盒或是灰盒的形式优化翻译结果。TransRepair有四点优势:

    1. 比数据增强更有效果
    2. 和源代码独立
    3. 计算资源消耗少,不需要进行大量的数据收集和重训练工作
    4. 灵活性强,不需要使用其它较好的翻译进行修复工作
  • Zheng等人提出了两个方法来检测两种特定的机器翻译问题:

    1. under-translation:指原文本中存在单词或短语的缺失
    2. over-translation:指原文本存在多余的单词或短语

    该算法对原始文本的翻译文本进行统计分析,来检查单词或者短语是否存在一对一的翻译错误。

自然语言推理

自然语言推理(Nature Language Inference, NLI)用于推断两个自然语言句子之间的关系,如“A girl is in the room”可以推断出“A person is in the room”。

一些研究针对NLI模型的鲁棒性,Nie等人通过生成句子的变体来测试NIL是否拥有语句理解的能力,实验证明7个最好的NLI模型都无法识别句子中一些简单的语义改动。

Wang等人简单地将推理目标对进行交换,作者认为一个好的模型应该在交换矛盾数据对和中立数据对时,交换前后的推理准确率应该保持相似;而对于矛盾的数据对,交换后的准确率应该较低(因为直觉来说矛盾数据对是无法反推的)。

未来展望

ML测试中的挑战

测试输入生成

由于ML模型的行为空间十分巨大,测试数据的生成依然是一个很大的挑战。基于搜索的软件测试生成(Search-based Software Test generation,SBST)使用启发式优化技术(如基因算法)来生成测试数据,改发那个发在传统软件测试中得到广泛应用,并经常用于测试ML程序的准确性和公平性等因素,同时在自动驾驶系统中也有很多应用。在其它ML系统的场景中,SBST技术也有一些应用前景,值得人们去研究。

许多现有的方法尝试生成对抗样本来测试模型的鲁棒性,但由于对抗样本并不能反映真实的场景,一直以来收到质疑的声音。因此,一些比较有意思的研究方向是如何生成自然的测试数据,或是如何自动的衡量生成数据的自然性(naturalness)。

尽管一些研究尝试在自动驾驶场景下生成自然的图像,这些图像仍然无法满足实际的需求,有时人类本身也无法辨别这些图像的场景。

测试评估标准

许多学者在研究如何评估测试集的质量和充分性,但是目前还没有一个系统的研究来对不同评估标准之间的关系进行评估,以及如何衡量这些标准发现错误的能力,这些问题在传统的软件测试中已经得到广泛的研究

目前,各测试评估准则和测试充分性之间的关系仍然比较不清晰,另外评估准则应该可以为未解释和理解模型提供一个方向,这也是值得我们去研究的。

Oracle Problem

尽管蜕变关系是一个很好的解决方案,但是大多数情况下这些蜕变关系需要人类发挥作用,于是如何自动化地识别和构建可靠的oracles是一个很好的研究方向。

Murphy等人认为只要涉及到浮点数运算,蜕变测试中就会出现flaky tests,这些测试用例有时可以通过,有时无法通过。flaky test检测在传统软件测试中是一个很大的挑战,在ML测试中可能这个挑战会更加严峻。

即使不村子啊flask test问题,pseudo oracles也不一定准确,这也造成了学多假阳性结果。因此,提高oracle的准确率、减少假阳性的出现也是一个值得研究的方向,我们可以在测试算法$a$的时候用另一个算法$b$来检测假阳性样本。

减少测试开销

在传统的软件测试中,开销问题一直是一个大麻烦,这也促生了许多减少测试开销的技术产生,例如测试样例的选择、测试样例优先级设置、预测测试执行结果等。在ML测试中,这个问题更加的严重,因为ML组件测试通常需要模型的重训练或者多次预测。同时还需要花费大量时间进行数据生成。

一个可能的研究方向是将ML模型表示为一个中间状态(intermediate state)来使它更好的被测试。

我们同样可以在ML测试中运用传统方法中的开销缩减技术,比如测试集优先级或测试集最小化,在减少测试集的同时不影响测试效果。

随着ML模型在更多的设备上被部署(如移动设备、IoT边缘计算设备),ML测试可能被应用在这些缺少计算资源的设备上,如何更好地执行测试也是一个需要被研究的方向。

ML测试的研究方向

更多的应用场景

目前的大多方法都专注于监督学习,特别是分类问题,而对于无监督学习和度量学习的测试方法还较少。另外,大多监督学习测试还主要关注图像分类,而像语音识别、自然语言处理和强化学习中智能体的游戏、迁移学习等方面还有很多可以研究的课题。

测试其它的属性

现有的大多数方法都在测试模型的鲁棒性和准确性,而很少的论文(小于3%)研究了模型的效率、相关性和可解释性。

模型相关性的挑战性在于未来的数据分布是很难获取的,且模许多模型的上限也是很难去描述的。另外,可以利用经验研究方法分析各种模型中模型相关性差的原因,以及低模型相关性和高风险之间有何关联。

对于效率来说,可以研究模型在不同的平台、框架和硬件之间切换时效率的差异。

对于可解释性来说,目前的方法大多基于人为评估,通过确认人类是否能够理解模型的逻辑和预测结果,从而判断模型的恶可解释性。于是如何自动化地评估模型的可解释性,以及模型违背可解释性的程度,也是值得研究的课题。

对于公平性和可解释性的定义,目前还没有一个很权威的说法。因此,对于它们的定义、表达形式和不同环境下的经验性研究,也有很大的研究价值。

目前已经有许多研究针对传统软件测试和机器学习软件测试的中,对各属性的重视程度的差异进行讨论,因此,我们可以研究什么因素在机器学习测试中占比较大的地位。

提出更多benchmarks

目前所用到的数据集大多是在构建机器学习系统中使用的,而很少有像CleverHans这样专门为机器学习测试构建的数据集。这样的数据集内应该包含真实世界的bugs,用于激发各个bug修复技术的提出。

覆盖更多的测试活动

在ML测试中,需求分析还是一个尚未被挖掘的研究方向,如果能够有好的需求分析,可以解决许多包括公平性在内的非功能性属性。

现有的ML测试大多专注于离线测试,对在线测试的研究较少。

Amershi认为,数据测试时ML测试中非常重要的一环,所以值得更多研究。另外,回归测试、bug报告分析和bug分类诊断也有许多可研究的问题。

由于机器学习的测试结果相对传统软件测试更难理解,所以对机器学习测试结果的可视化也是一个非常重要的研究课题,可以帮助开发者对bug进行理解和定位。

机器学习系统的变体构建(mutating)

许多学者在机器学习系统变体构建方面做出了一些研究,但是目前没有一个很好的研究,可以针对机器学习代码进行合适的修改操作,来模拟真实世界的bug。