论文链接:https://arxiv.org/abs/1805.07475v1

一.研究背景

软件程序中的安全漏洞给计算机系统带来了严重的风险。 恶意用户利用这些漏洞可以破坏程序,迫使这些程序崩坏,或是暴露敏感用户信息等。每年都有数以千计的漏洞被公开报告给常见漏洞和披露数据(CVE),还有更多漏洞在内部通过专有代码发现并修补。因为这些漏洞通常是程序员工作的一些疏忽所导致的,所以它们的产生是不可避免的。另一方面,由于开源软件和代码重用的普遍存在,这些漏洞带来的破坏传播速度很快。

概括:
1.安全漏洞——>恶意用户利用——>程序崩坏、暴露敏感用户信息
2.人为的疏忽所致,不可避免
3.开源软件和代码重用的情况,传播速度快

以上的这一现象,使得人们对如何自动修复软件漏洞(即,出错的源代码)这一问题进行研究。NMT(神经机器翻译)系统在文本的翻译和校正上的表现一直相对突出,但训练NMT系统的最常用方法是使用标记的示例对(pair examples)来比较网络输出与期望版本的相似性,这就需要在输入和期望输出数据之间进行一对一映射。在大多数情况下这需要花费大量的时间和精力去人工标记

这说明

最好是在没有匹配的示例对的情况下,也能够完成我们的目标任务。

问题是

虽然在很多的seq2seq应用中,获取不成对的数据相对比较简单,通常只需要将数据标记为源或是目标数据即可。然而,仅能做到判断的效果,不提供对标记集之间的一对一映射(即,没办法进行校正工作)。

为此,引入今天的主题:使用GAN来进行对抗性学习解决这一问题。

这种方法允许在没有配对示例的情况下进行训练,使用传统的NMT模型作为生成器,并用判别器的损失值来替代原本的基于梯度的负似然损失, 判别器通过训练来区分NMT生成的输出和真实样本的期望输出,因此其损失即表示生成的和实际分布之间的差异。

二.要解决的问题

然而,想要真正的实现这一方法,我们需要解决以下三个问题:

  1. 文本的离散性。离散的输出结果不可微分,如何对NMT系统输出结果进行处理?
  2. 因为生成器是被训练来匹配分布而不是对应的样本,因此对抗性训练不能够保证生成的代码(校正后的)将对应于输入的坏代码(有漏洞的)。
  3. 所考虑的对应域并不是双射的,即不是一一对应的,而是一段有漏洞的代码可以有多个修复,或者一个好的代码可以以多种方式被破坏。

三.具体如何解决

3.1 WGAN

在解决上面提到的三个问题之前,先讲一下文中所使用到的WGAN,解决了GAN的不稳定性的问题

原始的GAN的损失函数表达为:
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
当生成的样本和真实的样本不重叠时,这种损失可能是不稳定的。首先,当判别器训练的足够好时,判别器可能会提供0梯度,使得生成器获得不了反馈,进一步的,这样的损失函数会使得模式崩溃。因为生成器得不到反馈去进行学习,最终生成的样本实际上来自于真实数据分布这样一种单一模式

为了缓解这一问题,rjovsky等人[17] 提出了Wasserstein GAN(WGAN)损失,它使用了判别器中生成的和实际数据样本之间的Wasserstein-1或Earth-Movers(EM)距离,EM距离估计相对简单并且导致易于计算的损失函数(其中判别器函数D被约束为1-Lipschitz):
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
具体的如何解决不稳定性的,有兴趣的话可以去读一读WGAN的论文(戳这里),知乎上面也有很多不错的论文的分析,这里贴一个我觉得不错的作为参考:令人拍案叫绝的Wasserstein GAN

3.2 解决问题

好,接下来就具体到论文是如何解决上面的三个问题的。

问题1:

使用离散序列进行对抗训练的主要挑战之一就是从NMT系统的输出中采样以产生离散输出是不可微分的。训练的目标是从未知的真实序列分布PY中生成样本,公式表达为:
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
其中每个条件分布P(yt|y0, …yt-1)使用softmax输出进行估计:
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
其中,f(·)和ht表示生成器网络和RNN在t时刻的隐藏状态。理想情况下,应该从st中取样来生成一个序列并且和真实的数据一起提交给判别器进行训练,但是这个采样的过程是不可微分的,这使得判别器无法返回一个有意义的反馈给生成器继续进行训练。为此,实验中,直接将st提供给判别器,这里的st因为满足上述的条件分布p的one-hot矢量期望,所以可以被视作一种软性的one-hot表示,在某种程度上缓解了不可微分的问题。(ps:这个地方感觉自己的理解有些牵强,有看懂的求指导)

问题2和3(域映射问题):

在源代码或是序列校正问题上,通常会有以下两个基本要求:

(1).正确的序列在通过生成器时应保持不变。
(2).修复后的序列应接近原来相应的错误输入序列。

针对这一要求,论文通过两个自正则化函数来实现
1.除了GAN的训练之外,生成器还起到了作为自动编码器的作用,对从正确序列采样的数据进行训练。这样做相当于满足了条件(1),并间接达到了(2)的要求。(PS:自动编码器的作用是什么?)
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
2.第二个正则化器使得生成的输出中每个标签的频率保持接近输入标签的频率。 这就满足了条件(2)。
论文分享(二)——NIPS 2018 利用GAN进行源代码修复虽然这样可以允许按句子的顺序进行改变,例如,任意重新排序不会增加这种损失。 然而,GAN损失缓解了这个问题,因为任意重新排序产生了与条件分布P(y)差别很大的错误序列.在这个式子中n是句子的长度,freq(x,i)是在x中第i个标记的频率。

四.GAN框架

网络所使用的生成器由一个加入了attnetion机制的标准的NMT系统组成。对于所有的实验,编码器和解码器由使用了LSTM单元的多层RNN组成。由于最近的一些研究表明,使用CNN的判别器更加容易去训练,且相比基于RNN的判别器表现出更好的性能,因此论文使用基于CNN的判别器
两个正则化损失模型如下:
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
在实际操作中,预训练能够给GAN的训练提供一个好的初始参数。因此,实验在期望的数据上将生成器作为一个denoising的自动编码器进行预训练。具体的训练的损失函数为:
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
在这个损失函数中,y^是通过在y中以0.2的概率丢弃标记以及随机插入和删除n个标记而创建的输入的噪声,其中n是序列长度的0.03倍。这些数字是基于超参数调整选择的。使用denoising autoencoder的优点在于,我们不仅能从干净的样本中编解码得到干净的样本,还能从混入噪声的样本中编解码得到干净的样本。因为在训练的过程中就添加了一定的噪声。这样得到的权重初始值更好,因为它具有更好的抗噪声能力,即健壮性好

五.实验

5.1 简介

GAN的方法总是因为缺乏简单的评估准则而被人所诟病。因此,为了能够对实验的结果进行更好的评估,虽然论文所提出的GAN的方法不需要进行对训练,实际的数据集中还是涵盖了paired examples.这样做的另一个好处是,还可以训练seq2seq网络,来将此作为论文方法的实验结果的一个上限的对比

论文基于两个自己策划的数据集,即有序数字序列和上下文无关语法(CFG)来相应的进行实验,这有助于突出提出的GAN方法解决域映射问题的好处。然后研究纠正C / C ++代码中错误的难题。所有的实验结果在表中都已展示了出来。

5.2 排序实验

目的
这个实验是为了体现准确的域映射的重要性

具体的实验任务
在给定错误序列的情况下将序列分类回其原始升序。

实验步骤
首先生成20个随机选择的整数(无替换)的序列,在0到50之间按升序排列。然后通过交换n个彼此相邻的选定标记来添加错误,其中n是一个(舍入的)高斯随机变量,平均值为8,标准偏差为4。这样的一个简单的数据集,既有助于保留正确的和错误的序列对,也能够很好的计算GAN从错误恢复到正确序列的准确性。此准确度称为“序列准确度”。另外,为了评估域映射方法并评估之前定义的自正则化损失函数的有用性,实验还计算了具有有效排序但不一定是有效域映射的序列的百分比,我们称之为“排序准确度”。
论文分享(二)——NIPS 2018 利用GAN进行源代码修复
从表中的结果可以清楚地看出,基本GAN很容易学会产生具有有效排序的序列,但不必注意输入序列。因此在seq的准确率上偏低。 但是,在添加了前面提到的Auto或Freq损失正则化器之后可显著改善Seq的准确率。表明这确实有效地使得了源域和目标域之间的能够精确的进行映射。

5.3 简单语法实验

第二个实验,从简单的Context Free Grammar生成数据,实验的目的和具体的实验任务和上一个实验相类似。

实验步骤
从满足语法且小于20的所有序列的集合中随机选择好的数据。然后我们将错误添加到每个序列中,其中错误的数量n同样使用高斯随机变量(零阈值和舍入)决定,平均值为5,标准差为2。然后随机选择每个错误为删除随机标签,插入随机标签或交换两个随机标签。

相比于第一个实验,这个实验更好地模拟真实数据,因为每个生成的标签必须遵循相应的语法,所以要以所有先前的标签作为条件,即,每个标签之间存在一定的依赖性。

论文分享(二)——NIPS 2018 利用GAN进行源代码修复
除了CFG精度之外,实验还根据添加错误前后的示例对计算BLEU分数(双语评估替换方法,是一种用来评估机器翻译或校正效果的方法,这种方法会将生成的答案和给定的标准答案进行比较)。可以看到,因为错误是随机添加的,因此,会出现一些好多个错误示例仅对应一种正确示例的情况,反过来也一样。也就是前面提到的映射不是双射的情况。而由于BLEU的评估方法的特点,它只会将每个错误输入映射到唯一的单个输出上,因此不难理解在这种评估方法下,准确率略低。事实上,在现实世界中,映射不是双射的情况是普遍存在的,一段正确的代码总是会有很多写错的方式。而从,CFG的准确率的表现可以看出,在面对这种不是双射的情况时,论文所提出的方法是完全可以应对的,是具有普世意义的。

5.4 sate IV数据集实验

SATE IV是一个数据集,其中包含来自116种不同的普通弱点枚举(CWE)类的漏洞的C / C ++合成代码示例(函数),最初设计是用于探索静态和动态分析器的性能。 每个错误的函数都包含一个特定的漏洞,并有相对应的几个修复的方法配对。 论文使用自定义的词法分析器来调用每个函数

根据这些数据,将其分为两个数据集分别来进行两个不同的实验,即配对和非配对数据集。

配对数据集使得GAN方法的性能可以与seq2seq方法在源代码修复问题上的效果进行比较。实验通过获取易受攻击代码的每个示例并随机抽样其中一个修复来创建配对数据。保证实验的公平性。在训练GAN时,仍是不使用配对数据集中的标记对。

对于未配对的数据集,需要做到对给定的源序列在训练数据中的任何位置都没有相应的目标序列。为此,通过将易受攻击的函数或其候选修复以相同的概率放入训练数据集中,将数据划分为两个不相交的集合。需要注意的是,训练数据大小也减半。

论文分享(二)——NIPS 2018 利用GAN进行源代码修复
从实验结果可以看出,在使用不配对的训练数据的情况下,和使用配对训练数据的seq2seq网络的上限准确率相比,并没有很明显的差距,这表现出所提出的实验方法的有效性,在未配对的数据集下,仅使用一半的训练数据就达到了80%以上的校正准确率,与配对的结果相接近,也是令人满意的。

5.5 测试展示

论文分享(二)——NIPS 2018 利用GAN进行源代码修复
上面的函数目的调用sprintf打印出两个字符串,但源代码只提供要打印的第一个字符串。GAN通过提供第二个字符串来修复它。下面的函数在释放后再次使用了变量。 GAN的方法通过删除第一个free来修复它。

六.总结

论文提出了一种基于GAN的方法来训练用于离散域映射应用的NMT系统
主要优点
它可以在没有配对数据的情况下使用,为序列校正任务在数据选择层面上打开了一扇新大门,大量的之前无法用于训练的数据集可以被使用。

方法的关键
增加了两个新的生成器损失函数,这些函数确保了准确的域映射,而不需要多个生成器或域是双射的情况。

后续思考
尽管论文提出的方法是应用于解决源代码校正问题,但它同样适用于其他序列校正问题,例如:语法错误校正或语言情感翻译,将负面评论转换为正面评论等问题。

相关文章: