【新智元导读】众所周知,神经网络难以debug。谷歌大脑的Augustus Odena和Ian Goodfellow提出了一种新方法,能够自动Debug神经网络。Goodfellow表示,希望这将成为涉及ML的复杂软件回归测试的基础,例如,在推出新版本的网络之前,使用fuzz来搜索新旧版本之间的差异。
论文地址:
https://arxiv.org/pdf/1807.10875.pdf
众所周知,由于各种原因,机器学习模型难以调试(debug)或解释。这造成了最近机器学习的“可重复性危机”(reproducibility crisis)——对难以调试的技术做出可靠的实验结论是很棘手的。
神经网络又特别难以debug,因为即使是相对直接的关于神经网络的形式问题,解决的计算成本也很高,而且神经网络的软件实现可能与理论模型有很大的差异。
在这项工作中,我们利用传统软件工程中的一种技术——覆盖引导模糊测试(coverage guided fuzzing,CGF),并将其应用于神经网络的测试。
具体来说,这项工作有以下贡献:
我们对神经网络引入了CGF的概念,并描述了如何用快速近似最近邻算法( fast approximate nearest neighbors algorithms)以通用的方式检查覆盖率。
我们开源了一个名为TensorFuzz的CGF软件库。
我们使用TensorFuzz在已训练的神经网络中查找数值问题,在神经网络及其量化版本之间查找分歧,以及在字符级语言模型中查找不良行为。
图1:fuzzing主循环的简略描述。左:模糊测试程序图,表示数据的flow。右:用算法的形式描述了模糊测试过程的主循环。
覆盖引导模糊测试(Coverage-guided fuzzing)
在实际的软件测试中,覆盖引导模糊测试(Coverage-guided fuzzing)被用来查找许多严重的bug。最常用的两种coverage-guided模糊测试器是AFL和libFuzzer。这些模糊测试器已经以各种方式被扩展,以使它们更快、或增加代码中特定部分可以被定位的范围。
在CGF的过程中,模糊测试过程维护一个输入语料库,其中包含正在考虑的程序的输入。根据一些突变程序对这些输入进行随机变化,并且当它们行使新的“覆盖”时,突变输入( mutated inputs)被保存在语料库中。
“覆盖率”(coverage)是什么呢?这取决于模糊器的类型和当前的目标。一种常见的衡量标准是已经执行的代码部分的集合。在这种度量下,如果一个新的输入导致代码在if语句中以不同于先前的方式分支,那么覆盖率就会增加。
CGF在识别传统软件中的缺陷方面非常成功,因此我们很自然地会问,CGF是否可以应用于神经网络?
传统的覆盖率度量标准要跟踪哪些代码行已经执行。在最基本的形式中,神经网络被实现为一系列的矩阵乘法,然后是元素运算。这些操作的底层软件实现可能包含许多分支语句,但其中大多都是基于矩阵的大小,或基于神经网络的架构。因此,分支行为大多独立于神经网络输入的特定值。在几个不同的输入上运行的神经网络通常会执行相同的代码行,并使用相同的分支,但是由于输入和输出值的变化,会产生一些有趣的行为变化。因此,使用现有的CGF工具(如AFL)可能不会发现神经网络的这些行为。
在这项工作中,我们选择使用快速近似最近邻算法来确定两组神经网络的“激活”是否有意义上的不同。这提供了一个覆盖率的度量(coverage metric),即使神经网络的底层软件实现没有使用很多依赖于数据的分支,也能为神经网络生成有用的结果。
TensorFuzz库
从前面描述的模糊测试器中获得灵感,我们做了一个工具,称之为TensorFuzz。它的工作方式与其他模糊测试器类似,但它更适合神经网络的测试。
TensorFuzz不是用C或C++编写的,而是向任意的TensorFlow graph提供输入。TensorFuzz也不是通过查看基本的blocks或控制流中的变化来测量覆盖率,而是通过查看计算图的“激活”。我们在论文中详细讨论了模糊测试器的总体架构,包括数据流和基本构建块,以及语料库如何抽样,如何执行突变,如何评估覆盖率和目标函数等,具体请阅读原论文。
实验结果
我们简要介绍了CGF技术的各种应用,证明它在一般设置中是有用的。
CGF可以有效地发现训练好的神经网络中的数值误差
由于神经网络使用浮点数学,因此无论是在训练期间还是在评估期间,它们都容易受到数值问题的影响。众所周知,这些问题很难debug,部分原因是它们可能只由一小部分很少遇到的输入触发。这是CGF可以提供帮助的一个例子。我们专注于查找导致非数(NaN)值的输入。
CGF可以快速地找到数值误差(numerical errors):使用CGF,我们应该能够简单地将检查数值运算添加到元数据并运行模糊测试器(fuzzer)。为了验证这一假设,我们训练了一个完全连接的神经网络来对MNIST数据集里的数字进行分类。我们故意用了一个很糟糕的交叉熵损失,这样就有可能出现数值误差。我们对模型进行了35000步的训练, mini-batch size为100,验证精度为98%。然后检查MNIST数据集中是否有导致数值误差的元素。
如图2所示,TensorFuzz在多个随机初始化过程中快速发现了NaN。
图2:我们使用一些不安全的数值运算训练了一个MNIST分类器。然后,对来自MNIST数据集的随机种子运行10次fuzzer。fuzzer每次运行都发现了一个non-finite元素,而随机搜索从未发现过non-finite元素。左:fuzzer运行时的累计语料库大小,运行10次。右:fuzzer找到一个满意的图像。
其他发现:
基于梯度的搜索技术可能无助于查找数值误差
随机搜索对于查找数值误差来说效率极低
CGF反映了模型与其量化版本之间的分歧
量化(Quantization)是一个存储神经网络权重的过程,并使用由较少内存位组成的数值表示来执行神经网络计算。量化是降低神经网络计算成本或减小网络尺寸的流行方法,并广泛用于在手机上运行神经网络推理,例如 Android Neural Networks API或TFLite,以及在自定义机器学习硬件中运行推理,例如谷歌的TPU或NVIDIA的TensorRT。
找到量化产生的误差很重要:当然,如果量化显著地降低了模型的准确性,那么量化就没有多大用处。给定一个量化模型,检查量化在多大程度上降低了精度是有用的。
通过检查现有数据几乎找不到错误:作为基线实验,我们使用32位浮点数训练了一个MNIST分类器(这次没有故意引入数值问题)。 然后,将所有权重和激活截断为16-bits,我们在MNIST测试集上比较了32-bit和16-bit模型的预测精度,没有发现不一致。
但是,CGF可以快速地在数据周围的小区域找到许多错误,如图3所示。
图3:我们训练了一个32-bit浮点数的MNIST分类器,然后将相关的TensorFlow graph截为16-bit 浮点数。左:fuzzer运行时的累计语料库大小,运行10次。右:fuzzer找到了16-bit 和32-bit 的神经网络分类不同的图像
结果显示,fuzzer在我们尝试的70%的示例中产生了分歧。也就是说,CGF可以找到在测试时可能发生的真正错误。
在给定与CGF相同数量的突变的情况下,随机搜索未能找到新的错误。
结论
我们提出了神经网络的覆盖引导模糊测试的概念,并描述了如何在这种情况下构建一个有用的覆盖率检查器。我们已经通过使用TensorFuzz查找数值误差、发现神经网络和它们的量化版本之间的分歧、以及在RNN中找到不良行为等试验,证明了TensorFuzz的实用性。最后,我们将同时发布TensorFuzz的实现,以便其他研究人员既可以在我们的工作基础上进行研究,也可以使用fuzzer来查找实际的问题。
论文地址:
https://arxiv.org/pdf/1807.10875.pdf
原文发布时间为:2018-08-01
本文来自云栖社区合作伙伴新智元,了解相关信息可以关注“AI_era”。
原文链接:谷歌大脑开源TensorFuzz,自动Debug神经网络!