SOSP'17:DeepXplore: Automated Whitebox Testing of Deep Learning Systems

SOSP'17:DeepXplore: Automated Whitebox Testing of Deep Learning Systems

DeepXplore: Automated Whitebox Testing of Deep Learning Systems

Kexin Pei (Columbia U), Yinzhi Cao(Lehigh U), Junfeng Yang(Columbia U), Suman Jana(Columbia U)

本文收录在 Bug Hunting Session中,是首篇关于深度学习系统白盒测试的工作。

背景

作者Kexin Pei是哥伦比亚大学的二年级博士生,文章的boss应该是Junfeng Yang老师(Junfeng Yang, Systems, Computer Science Professor, Columbia University)。他们组虽然每年的文章数量很少,但是质量都很高。本文在五月份就放在arxiv(Automated Whitebox Testing of Deep Learning Systems)上了,而且代码也在github上开源(peikexin9/deepxplore)了,有兴趣的同学可以去玩儿一下。

文章放在Bug Hunting的session,想必可以吸引不少做传统软件测试的读者。类比传统的软件测试方法,文章的思路也会非常好理解。

这篇文章核心是一种对DL System进行测试的测试用例生成方法。(1) 通过优化neuron coverage,方法可以生成覆盖触发不同类型错误的测试用例。(2) 通过针对同一任务的不同DL System,该方法可以实现自动的错误发现,避免繁杂的人工检查。

值得提一下的是,本文针对的主要是分类系统。

解决的问题

深度学习系统在越来越多的任务中实现了很好的预测精度,但是系统本身的复杂性导致了很多问题,比如测试难度大,可解释性不够等问题。这些成为了DL系统在关键应用(e.g., 自动驾驶,医疗应用)中进行部署的阻碍。最近关于生成对抗网络(GAN)的工作也表明对测试数据的微小修改就可能导致DL系统给出错误结果。因此,对DL系统进行测试的重要性不言而喻。作为对这个方向进行探索的第一次尝试,这篇文章主要回答以下问题:

1、如何生成测试用例诱导DL系统产生错误结果?

2、如何生成测试用例来优化测试完备程度?

3、如何生成有意义的测试用例来准确模拟潜在的真实攻击行为?


解决方法和结果

1、 既然是要发现错误,就要通过测试用例诱导DL系统给出错误的预测。本文通过优化下列目标函数,使得生成的测试用例至少使得一个被测试系统得到不同分类结果。这个优化通过梯度上升是很容易求解的。

2、对于软件系统(不仅限于DL系统)测试,测试完备程度是重要评价指标。现有测试方法通过生成测试用例来cover程序运行的不同分支从而发现更多的软件bug,但是DL系统的逻辑并不完全体现在其代码的分支上,因此code/branch coverage并不适用于DL系统。为解决这个问题,本文提出了neuron coverage的指标来度量测试完备程度。对于DL系统来说,最后的结果实际上受到神经网络中每个神经元的影响。简单来看,神经元输出越大则对最终结果影响越大,因此作者提出设置一个threshold,如果神经元输出超过这个threshold则算是激活。因此,测试用例生成的目的则是最大化被激活的neuron总量。

熟悉软件测试的同学可能知道,传统的软件测试会根据程序的分支条件,通过约束求解等方式生成测试用例,从而驱动程序执行到特定分支。这样就可以生成少量测试用例来cover更多的程序分支,从而对branch coverage进行优化。对于DL系统,既然采用neuron coverage的评价指标,作者采用的方法是选择未被激活的neuron,然后优化如下目标函数,使得该neuron被激活。

该优化可以和1中优化联合进行,即

3、并不是随便生成的测试用例都是合理的。比如针对图片分类问题,如果生成的图片根本不是合法的格式。因此作者在优化中还加入了约束条件,要求生成的测试用例是符合应用的。

从实验结果来看,neuron coverage的指标相对于code coverage是针对DL系统测试更加合理的评价指标。作者通过方法发现了常见(主要是图片分类)神经网络的错误,而且基于Tensorflow的实现看起来效率也很不错。

局限性

本文是一个非常扎实而且前沿的工作,但DL系统测试应该还有很远的路要走。

虽然neuron coverage相对于code coverage或者random generation来说是更好的测试完备性评价指标,但是即使测试cover了所有neuron,依然是far from complete。对于传统的白盒软件测试,如果系统本身不大且约束条件不复杂可以求解的话,覆盖所有分支是有可能实现的,而且对于一些spec非常具体的系统,经过简化甚至可能给出形式化验证。但如果我们仔细考察一般软件测试与DL系统测试,其重要区别在于前者的逻辑是code defined,而后者的逻辑其实是training data defined。前者的错误很多情况下是程序员没有写必要的判断条件,后者则往往是因为缺少特定的训练数据或者模型本身能力受限。相应地,code coverage对于前者是去覆盖代码分支所划分出来的程序输入空间,而对于一个pre-trained模型,neuron coverage其实是想要去近似覆盖模型从训练数据进行学习后得到输入空间。

不过这种近似其实是很难的。对于一般程序测试,比如对于条件 if(a > 1),我们只要生成两个a的值就可以确保改分支代码两侧都被执行。但对于neuron coverage,threshold本身就是个近似,因此我们无法通过优化neuron coverage而覆盖所有可能的输入。另一方面,本文方法生成的测试用例都是从seed出发进行小量修改得到的,很多由于缺少相应训练数据导致的bug就很难发现。一个更直观的例子就是,区分猫和狗的方式有很多,大多数猫和狗因为区别显著容易被区分,但可能存在极其相似但是分属两类的猫狗, 但如果一个猫狗分类的深度学习模型没有见过这样的训练数据,那这个模型可能是会在这样的测试数据上犯错误的。如果按照本文的方法,这样的图片是无法从seed中生成过来的,因此该潜在错误也很难被测试到。

图片来源:Breeders have developed a cat that looks like a WEREWOLF

文章被以下专栏收录