预训练代码模型的自然性攻击

互联不一般哥 2024-07-12 18:39:25

Natural Attack for Pre-trained Models of Code

Zhou Yang, Jieke Shi, Junda He and David Lo

School of Computing and Information Systems

Singapore Management University

{zyang,jiekeshi,jundahe,davidlo}@smu.edu.sg

引用

Yang Z, Shi J, He J, et al. Natural attack for pre-trained models of code[C]//Proceedings of the 44th International Conference on Software Engineering. 2022: 1482-1493.

论文:Natural attack for pre-trained models of code | Proceedings of the 44th International Conference on Software Engineering (acm.org)

摘要

本文提出了黑盒攻击方法ALERT,通过反向转换输入,使被攻击模型产生错误的输出。ALERT在保留原始输入的操作语义的同时,也关注生成样例的自然语义,在三个下游任务上都取得了较好的结果。

1 引言

最近,研究人员发现,code2vec和code2seq等代码模型可以为操作语义相同的两个代码片段输出不同的结果,其中一个代码片段是通过重命名另一个代码片段中的一些变量而生成的。修改后的代码片段称为对抗样本,受攻击的模型称为受害者模型。

我们认为,自然性要求对于攻击代码模型而言是必不可少的。自然性是对抗样例生成的一个基本要求,违反编码约定或变量命名较差的代码,即时在机器的角度可以接受,但在人工任何时也大多会被拒绝。现有的攻击代码模型的工作有效性有余,但它们专注于操作语义,而很少关注对抗样本对人工判断的角度而言是否自然。例如,最先进的黑盒方法MHM 从一组固定的变量名中随机选择替换变量,而不考虑原始变量及其替换变量之间的语义关系。在本文中,我们认为代码模型的对抗性例子应该考虑在两个级别上保留语义:操作语义(机器角度)和自然语义(人类角度)。

因此,我们提出了ALERT,一种在生成对抗样例代码时考虑自然语义的黑盒攻击方法。与MHM 类似,ALERT通过重命名变量生成对抗样例。我们的方法有三个主要部分:一个自然扰动生成器,一种快速生成对抗样本的启发式方法,以及一种基于遗传算法的方法,用于在启发式方法失败时更全面地搜索对抗样本。

本文研究了在最先进的预训练模型CodeBERT 和GraphCodeBERT 上进行微调的受害者模型。ALERT首先通过掩码预测方法生成变量替换的候选,然后使用预训练模型对这些候选进行上下文嵌入,并计算其余弦相似度。最终,我们只选择top-k个可能的替换作为候选。确定候选后,ALERT通过预先定义的度量对候选进行贪婪攻击搜索,并在失败时换用遗传攻击搜索,以确定最终生成的对抗样例。

我们首先进行了一项用户研究,以检验ALERT生成的替换对人工判断而言是否具有足够的自然性。研究的参与人员会给每个对抗样例一个自然得分(1-5分),结果显示,ALERT(均分3.95)总是能比MHM(均分2.18)生成得分更高的对抗样例。然后,我们在两个预训练模型(CodeBERT,GraphCodeBERT)×三个任务(漏洞预测、克隆检测和代码作者归属)上对ALERT和MHM进行了对比,具体来说,我们同时使用ALERT和MHM搜索ALERT生成的对抗样例。结果表明,在CodeBERT上,ALERT可以在三个下游任务中分别达到53.62%、27.79%和35.78%的攻击成功率。MHM分别仅达到35.66%、20.05%和19.27%,说明ALERT比MHM的攻击成功率分别提高了17.96%、7.74%和16.51%。在GraphCodeBERT上,我们的方法在相同的三个任务上分别获得了76.95%、7.96%和61.47%的成功率,比MHM高出21.78%、4.54%和29.36%。

总的来说,本文的贡献如下:

我们是第一个在为代码模型生成对抗样例时强调自然性的研究。我们还提出了在生成对抗性变量替换时能够感知到自然语义的ALERT方法。一项用户研究证实,使用这些替换可以生成对人工判断来说更为自然的对抗样例。ALERT也可以获得比以往方法更高的攻击成功率。我们是第一个对CodeBERT和GraphCodeBERT开发对抗性攻击的研究,并表明在最先进的预训练模型上进行微调的模型很容易受到此类攻击。我们展示了ALERT生成的样例的价值:使用这些对抗样例对受害者模型进行反向微调,可以将CodeBERT和GraphCodeBERT对ALERT的鲁棒性分别提高87.59%和92.32%

2 技术介绍

本文提出了ALERT(自然感知攻击),这是一种黑盒攻击方法,利用受害者模型用于微调的预训练模型,生成可以感知自然语义的替换。这一过程在本文中被称为自然感知替换。ALERT需要两个步骤来寻找对人工判断而言更为自然的对抗样例。第一步是贪婪攻击,它能够尽快搜索到满足条件的对抗样例。第二步是遗传攻击,它将在贪婪攻击失效时提供更为全面的搜索。

2.1 自然感知替换

ALERT利用预训练模型的掩码预测和上下文嵌入方法进行自然感知替换的生成与选择。为单个变量(例如index2dict)生成自然替换主要分为三个操作步骤:

步骤一:我们将代码片段转换为CodeBERT或GraphCodeBERT可以接受的输入格式。源码通常包含许多特定领域的缩写、术语及其组合,这些组合通常不包含在词汇表中。CodeBERT和GraphCodeBERT都使用字节对编码(BPE),通过对单个单词进行分词,以子标记列表的形式来应对超出词汇表外的问题。例如,变量index2dict可以转换为三个子词(index、2、dict),然后输入到模型中。

步骤二:然后,我们为每个子标记生成潜在的替换。举一个简单的例子,假设输入为单一变量,我们使用来表示BPE该变量名中产生的子标记序列。对于序列中的每个子标记,我们使用CodeBERT或GraphCodeBERT的掩码预测方法来生成一个潜在的替换子令牌的有序列表。我们不是仅仅选择单个输出,而是选择top-k的替换。直观地说,这些替换是预训练模型认为更适合上下文的东西。不过,并不是所有的标记在语义上都与原始的子标记相似。

步骤三:我们假设是一个变量名(例如index,2和dict)的子标记序列。我们将原始序列中的子标记替换为在步骤2中生成的子标记。然后,使用预训练模型计算T`中每个子标记的上下文嵌入。我们将这些新的嵌入连接起来,并计算其与T中子标记进行上下文嵌入并连接的余弦相似度,用以度量新生成的替换与原变量之间的相似度。我们根据余弦相似度的值对替换项按降序排列,并选择相似性值较高的top-k个替换子标记序列转换为具体的变量名。

事实上,一个代码片段中往往会出现多个变量,我们会使用一个解析器提取输入中的所有变量名,并枚举代码中的所有变量及其出现的情况。对于变量的所有出现位置,我们都将应用上述的方法对其进行自然感知替换,将得到的替换集合取并集,并过滤掉重复和无意义的单词(例如命名不符合规范)。对于每个变量,我们将结果以键值对的形式存入字典中,并最终返回所有变量的自然感知替换结果。

2.2 贪婪攻击

2.2.1 总体重要性得分

为了决定进行变换的变量,我们将会评估代码片段中每个变量对帮助模型做出正确预测的贡献。我们引入了一个称为重要性分数的度量标准来量化这种贡献。代码片段c中的第i个标记的重要性分数定义如下:

其中,y为ground truth标签,M(c)[y]指模型生成的结果与y对应的置信度,指将c中的第i个token替换为<unk>的代码片段(第i个标记必须为变量)。直观来说,表示c中的第i个标记帮助模型预测理想结果的能力。如果> 0则表示第i个标记可以帮助模型对c做出正确的预测。由于一个代码片段通常包含出现在多个位置上的多个变量,当执行对抗性攻击时,所有变量的每次出现都应该相应地更新,因此我们将单个标记的重要性分数的定义扩展为一个变量的总体重要性评分(OIS)。

其中,var是c中的一个变量,var[pos]表示c中出现的所有var。我们注意,与自然语言模型相比,OIS的定义可以更好地反映编程语言攻击模型的独特特性。即使在一个位置上的一个变量是微不足道的,但在对抗性攻击中,更频繁的出现可以使它成为一个重要的变量。总体重要性得分可以看作是与白盒攻击中的梯度信息的类比。例如,如果在输入的某些位置上的梯度更大,那么如果我们干扰这些位置,就更容易改变模型输出。

基于多语言解析器生成器工具tree-sitter,我们实现了一个名称提取器,它可以从用C、Python或Java编写的语法有效的代码片段中检索所有变量名。为了避免更改操作语义,我们只提取在代码片段范围内定义和初始化的本地变量,并将它们与代码中从未出现过的有效变量名进行交换。为了提高准确性,还排除与字段名冲突的变量名。提取后,我们计算每个变量的OIS,然后继续进行下一步。

2.2.2 词汇替换

我们设计了一个基于OIS的贪婪算法来搜索能够生成对抗性示例的替代品。算法2展示了贪婪攻击的过程。首先,我们根据它们的OIS(第2行到3行)将原始代码片段中提取的变量按降序排列。我们从它们中选择第一个变量,并找到它自然感知替换生成的候选替换(第4行到6行)。我们用这些替换代替原始输入中的变量,以创建变体列表,然后用这些变体查询受害者模型。我们收集返回的结果,查看是否至少有一个变体使受害模型做出错误的预测(第9行到12行)。如果有这样一个变体,Greedy-Attack将其作为成功的对抗性示例返回。否则,我们用那个能够最大程度降低受害模型对结果置信度的变体替换原始输入,并选择下一个变量重复上述过程(第15行)。Greedy-Attack在找到成功的对抗性示例时终止(第11行),或者在列举完所有提取的变量时终止(第17行)。

算法2 贪婪攻击算法

考虑OIS信息对贪婪攻击有两个方面的好处。首先,如第3.2.1节所述,如果一个变量有更高的OIS,它表明修改代码片段中的这个变量有显著影响。给予具有较大OIS的变量更高优先级可以帮助更快地找到成功的对抗性示例,这意味着需要对受害模型进行较少的查询。由于远程部署的黑盒模型通常限制查询频率,这增加了我们在实践中攻击的可用性。其次,尽早找到成功的对抗性示例也意味着在原始代码片段中修改的变量较少,使得生成的对抗性示例对人类评判者来说更自然。

2.3 遗传攻击

找到合适的替代品以生成对抗性示例本质上是一个组合优化问题,其目标是找到最优的变量组合及其对应的替代品,以最小化受害模型对真实标签的置信度。Greedy-Attack可以更快地运行,但可能会陷入单一的局部最优,导致攻击成功率较低。我们还设计了一种基于遗传算法(GA)的攻击,称为遗传攻击(GA-Attack)。如果贪婪攻击未能找到成功的对抗性示例,我们将应用遗传攻击进行更全面的搜索。

算法3展示了遗传攻击的工作原理。它首先初始化种群(第1行),然后执行遗传算子以生成新的解决方案(第2行到11行)。接着,遗传攻击计算适应度函数(第3.3.4节),并保留适应度值较大的解决方案(第13行)。最后,算法返回适应度最高的解决方案(第15行到16行)。

算法3 遗传攻击算法

染色体表示法 在遗传算法(GA)中,染色体代表了解决目标问题的解决方案,而染色体由一组基因组成。在本文中,每个基因是原始变量与替换对。遗传攻击将染色体表示为这样的对列表。

种群初始化 在遗传算法的运行中,一个种群(染色体的集合)会演化以解决目标问题。GA-Attack维护的种群大小是可替换提取变量的数量。由于遗传攻击只有在贪婪攻击失败后才会被触发,它可以利用前一步发现的信息。对于每个提取的变量,贪婪攻击找到可以最大程度降低受害模型对真实标签置信度的替代品。给定一个变量和贪婪攻击找到的替换,遗传攻击创建一个只将这个变量更改为替换并保持其他变量不变的染色体。这个过程对于代码片段中的每个变量重复进行,以获得一个种群。

算子 贪婪攻击在多次迭代中运行。在每次迭代中,使用两种遗传算子(变异和交叉)来产生新的染色体(即子代)。我们以概率Γ进行交叉,进行变异(第8行)。交叉操作(第7行)在染色体和上的工作方式如下:我们首先随机选择一个截止位置ℎ,并将在位置ℎ之后的基因替换为相应位置的基因。对于变异操作(第9行),我们随机选择一个基因,并对其进行随机替换。

适应度函数 遗传算法使用适应度函数来衡量和比较种群中染色体的质量。更高的适应度值表明染色体(变量替换)更接近这个问题的目标。我们计算受害模型对原始输入和变体的真实标签的置信度值。置信度值之间的差异被用作适应度值。假设T是原始输入,T′是对应染色体的变体,这个染色体的适应度值通过以下方式计算:

在一次迭代中生成子代之后,我们将它们合并到当前种群中,并执行一个选择操作符(第15行)。遗传攻击会将种群大小保持恒定(即变量的数量),并丢弃那些适应度值较低的染色体。

3 实验评估

3.1 数据集与任务

我们介绍了在我们的实验中使用的三个下游任务及其相应的数据集。数据集的统计数据如表1所示。

表1:数据集和受害者模型的统计数据

漏洞预测 这项任务的目标是预测给定的代码片段是否包含漏洞。我们使用了由周等人准备的数据集。该数据集从两个流行的开源C项目FFmpeg3和Qemu4中提取。在周等人的数据集中,有27,318个函数被标记为包含是否包含漏洞。这个数据集被包含在CodeXGLUE基准测试中,用于研究CodeBERT在漏洞预测方面的有效性。CodeXGLUE将数据集划分为训练集、开发集和测试集,我们在本研究中重用了这些数据集。

克隆检测 克隆检测任务的目标是检查两个给定的代码片段是否是克隆的,即在操作语义上等价。BigCloneBench是一个广泛认可的克隆检测基准,包含来自各种Java项目的超过六百万个实际克隆对和二十六万个假克隆对。该数据集涵盖了十个常用的功能,其中的每个数据点是一个Java方法。按照之前工作的设置,我们过滤了没有标签的数据,然后平衡了数据集,使真实对和虚假对的比例达到1:1。为了保持实验在计算上的友好性,我们随机选取了90102个例子用于训练,4000个用于验证和测试。

作者归属 作者归属任务室识别给定代码片段的作者。我们使用Google Code Jam(GCJ)数据集进行了实验,该数据集源自Google Code Jam 挑战赛,这是Google 每年举办的全球编码竞赛。GCJ数据集包含700个Python 文件(70位作者,每位作者10个文件),但我们注意到一些Python文件时C++代码。在丢弃这些C++源代码文件后,我们总共得到了660个Python文件。20%的文件用于测试,80%的文件用于训练。

3.2 目标模型

本文研究了最先进的预训练模型:CodeBERT和GraphCodeBert的鲁棒性。为了获得受害者模型,我们对第3.1节中提到的三个任务分别对CodeBERT和GrapCodeBERT进行了微调。

这些模型的性能如表1所示。我们获得的结果与他们的原始论文和最近的另一篇论文中的报告的结果接近,说明我们实验中使用的受害者模型经过了充分的微调。

3.3 攻击设置

ALERT有多个需要设置的超参数,包括为每个变量生成的自然替代品的数量以及算法3中GA-Attack的参数。我们的实验设置允许ALERT为每个变量出现生成60个候选替代品,并根据与原始嵌入的余弦相似度选择排名前30的替代品。对于遗传攻击,我们将child_size设置为64,并为最大迭代次数max_iter设置了一个动态值:变量数量的5倍或10中的较大者。交叉率Γ设置为0.7。

我们将MHM作为我们的基线,它有两个超参数:迭代的最大次数和每次迭代中采样的变量数量。MHM论文建议将后者设置为30,但没有为最大迭代次数提供标准设置。在每次迭代中,MHM需要多次查询受害模型,这是很耗时的。我们从漏洞预测任务中抽取了5%的测试数据,并发现超过95%的成功对抗性示例在100次迭代之前就已经找到。为了使MHM实验在计算上更为友好,我们将MHM的最大迭代次数设置为100。原始的MHM只能扰动C程序,因此我们将其扩展到扰动Python和Java代码。

3.4 实验结果

RQ1: ALERT 生成的对抗样例的自然性如何?

在为代码中的变量生成替换时,ALERT考虑了对抗样例的自然语义。这个研究问题探讨了自然感知替换方法是否有助于产生对人类评判者来说更自然的对抗样例。为了回答这个问题,我们进行了一个用户研究,以分析由MHM、MHM-NS和我们提出的ALERT方法生成的例子的自然性。与忽略自然性的原始MHM不同,MHM-NS从与ALERT相同的自然感知替换池中选择替换。

由于原始的MHM仅适用于C语言编写的代码片段,我们随机抽取了一些能够被ALERT、MHM和MHM-NS成功攻击的代码片段,这些片段来自漏洞检测任务的数据集。我们在抽取代码片段时引入了一些额外的约束:(1)为了节省参与者阅读长代码片段的时间,我们有意抽取了长度限制在200个标记以内的简洁和短代码段;(2)攻击方法可能在同一个代码片段中选择替换不同的变量;我们只选择所有三种方法至少修改了一个变量的示例,以使比较公平。有196个C代码片段满足上述约束条件。我们使用流行的样本量计算器计算了一个统计上有代表性的样本大小,置信水平为99%,置信区间为10。我们抽取了100个代码片段进行用户研究,这在统计上是有代表性的。

对于每个选定的代码片段,我们可以构建3对,每对包含原始代码片段和由ALERT、MHM或MHM-NS生成的对抗样例。我们高亮每对中更改的变量,并将它们呈现给用户,要求用户评估替换在源代码上下文中自然适应的程度。为了评估“新的变量名看起来很自然,并保留了原始含义”,我们采取与Jin等人相同的设置,参与者使用经典的李克特量表进行打分,其中1表示强烈反对,5表示强烈同意。参与者不知道哪种攻击方法产生了每对中的哪个对抗样例。用户研究涉及四名非作者参与者,他们拥有计算机科学学士/硕士学位,并且至少有四年的编程经验。每位参与者单独评估100对。我们计算每位参与者给出的对抗性示例的平均评分,并在图2中展示结果。x轴区分每个参与者,y轴显示平均评分。结果表明,使用ALERT生成的替代可以帮助生成更自然的对抗性示例。四位参与者给ALERT生成的对抗性示例的平均评分接近4,对MHM-NS生成的对抗性示例给出稍低的平均评分;这表明参与者认为这两种方法生成的替代是自然的。参与者一直给出较低的分数(平均1.86分)给MHM生成的示例,表明他们认为变量替代是不自然的。

图2:对抗性例子的自然性用户评估结果

RQ2:生成对抗样例的成功性、大小和可伸缩性如何?

为回答RQ2,我们从三个维度评估ALERT和MHM-NS在攻击CodeBERT和GraphCodeBERT时的有效性。具体来说,我们采用三个指标来衡量对抗性示例生成方法的性能,每个指标分别捕捉一个质量维度。这些指标是基于数据集X定义的,其中X中的每个元素x都是至少包含一个局部变量的代码片段,并且受害者模型M能够正确预测X中的所有示例。三个指标的定义如下所述:

攻击成功率(ASR) 对抗样例生成方法的ASR定义为,其中x'是生成的示例。ASR越高,表明攻击方法的性能越好。

变量更改率(VCR) 假设输入代码片段有个局部变量,攻击者重命名了个变量,我们定义攻击在X上的变量更改率为。VCR越低越好,因为这意味着找到成功的对抗性示例所需的编辑越少。

查询次数(NoQ) 在对抗性攻击中,尤其是黑盒攻击,需要尽可能减少对受害者模型的查询次数。在实践中,受害者模型通常远程部署,频繁查询模型不仅成本高昂,而且可能引起怀疑。我们统计了在数据集X上生成对抗性示例时,每次攻击对受害者模型的查询次数。NoQ越低的攻击方法越具有可扩展性。

表2显示了MHM-NS和ALERT在六个受害者模型上的比较结果。我们还报告了仅使用贪婪攻击的结果,以强调遗传攻击带来的改进。结果表明,贪婪攻击在CodeBERT的三个下游任务上的攻击成功率分别为49.42%、23.20%和30.28%,相较于MHM-NS分别提高了13.76%、15.71%和11.01%。通过在ALERT中采用遗传攻击,我们可以进一步提升性能:与MHM-NS相比,ASR分别提高了17.96%、7.74%和16.51%。在GraphCodeBERT上,贪婪攻击在三个任务上的成功率分别比MHM-NS高出16.81%、3.33%和14.68%;当采用遗传攻击时,这些数字分别提升到21.78%、4.54%和29.36%。此外,ALERT对原始示例的修改更少,比基线更可扩展。图3比较了VCR和NoQ方面的结果。x轴对应每个下游任务,y轴代表两个评估指标的归一化值。在所有受害者模型上,ALERT生成对抗样例需要修改的变量更少。这表明ALERT可以对输入代码片段进行最小的更改,并产生更自然、更不显眼的对抗性示例。此外,ALERT查询受害者模型的次数也少于MHM-NS。仅使用贪婪攻击时的NoQ比MHM-NS少了82.57%。当采用遗传攻击时,NoQ有所增加,但仍比MHM-NS少了49.62%,这表明ALERT更实用。因为受害者模型通常远程部署,查询可能成本高昂且可能限制频繁查询。查询受害者模型是实验中最耗时的部分,因此较少的NoQ也表明ALERT具有更低的运行时间。

表2:ASR比较结果

图3:VCR与NoQ比较结果

RQ3:我们能用对抗样例来强化受害者的模型吗?

为了检验对抗性示例在强化模型方面的作用,我们研究了使用对抗性微调作为一种防御手段的有效性。我们利用ALERT为每个受害者模型在它们对应的训练集上生成对抗样例。如果一个受害者模型对原始输入预测错误,或者无法从中提取局部变量名,我们将跳过这个样例。对于训练集中的其他输入,我们为每一个输入选择最多一个对抗样例。如果ALERT攻击成功,我们选择首先找到的对抗样例。如果ALERT未能成功攻击,我们选择能够最大程度降低受害者模型对真实标签信心的样例。然后,这些生成的对抗样例被加入到原始训练集中,形成对抗性训练集。我们在对抗性训练集上对受害者模型进行微调。经过对抗性微调后,我们得到了两个模型:CodeBERTAdv和GraphCodeBERT-Adv,并在RQ2中生成的对抗性示例上对它们进行评估。表3显示了新模型在先前生成的对抗性示例上的预测准确率。值得注意的是,未经对抗性重训练强化的原始受害者模型预测所有这些示例都是错误的(即,它们的准确率为0%)。从表3中,我们可以观察到,所有经过对抗性微调的模型的性能都比原始模型要好得多。仅使用Greedy-Attack和使用GA-Attack生成的例子的平均改进幅度相近。CodeBERT-Adv在对抗Greedy-Attack和ALERT时的准确率分别提高了87.76%和87.59%。GraphCodeBERTAdv在对抗Greedy-Attack和ALERT时的准确率分别提高了92.16%和93.31%。对抗由MHM-NS生成的对抗性示例的准确率提高相对较小(分别为CodeBERT和GraphCodeBERT的67.89%和75.93%)。

综上所述,ALERT生成的对抗性示例在提高受害模型的鲁棒性方面具有重要价值。通过使用ALERT生成的对抗性示例进行对抗性微调,可以分别将CodeBERT和GraphCodeBERT的准确率提高87.59%和92.32%。

表3:反向微调的受害者模型的鲁棒性分析

转述:高晨宇

0 阅读:0

互联不一般哥

简介:感谢大家的关注