Set-level Guidance Attack:一种针对VLP模型的高迁移性对抗样本攻击方法

论文:Set-level Guidance Attack: Boosting Adversarial Transferability of Vision-Language Pre-training Models

代码:GitHub - Zoky-2020/SGA: Set-level Guidance Attack: Boosting Adversarial Transferability of Vision-Language Pre-training Models. [ICCV 2023 Oral]

参考笔记:[论文总结] Set-level Guidance Attack - 知乎 (zhihu.com)

1.摘要,介绍,相关工作

1.1 摘要

研究背景:文章指出,视觉-语言预训练模型(Vision-Language Pre-training, VLP)在多模态任务中表现出对对抗性示例(adversarial examples)的脆弱性。这些模型尽管在多种任务上取得了显著性能,但仍然容易受到攻击

研究动机:尽管现有的研究主要集中在白盒攻击(white-box attacks)上,即在攻击者可以完全访问目标模型的情况下,但跨模型的对抗性示例的迁移性(adversarial transferability)尚未得到充分研究。这种迁移性在实际应用中是一个更实际的设置,因为攻击者可能无

研究贡献:文章提出了一种新的攻击方法,称为集级指导攻击(Set-level Guidance Attack, SGA),该方法充分利用模态间交互(modality interactions)和跨模态指导(cross-modal guidance),以增强对抗性示例在不同VLP模型之间的迁移性法完全访问目标模型

实验结果:通过在多个下游视觉-语言任务上的实验,研究者展示了SGA能够生成在不同VLP模型之间强烈迁移的对抗性示例。特别是在图像-文本检索任务中,SGA显著提高了从ALBEF模型到TCL模型的迁移攻击成功率,与现有最先进的方法相比,提高了至少9.78%到30.21%

研究意义:这项研究不仅首次系统地评估了VLP模型的对抗性迁移性,而且还提供了一种新的测试VLP模型鲁棒性的方法

图中的柱状图显示了每种攻击方法在图像-文本检索任务上的攻击成功率。这些成功率可以用来评估不同攻击方法的有效性,尤其是在跨模型迁移攻击的场景中。根据摘要中的描述,SGA在跨不同VLP模型的攻击迁移性方面表现出色,与现有的最先进方法相比有显著提升。这表明SGA能够更有效地生成能够在不同模型之间迁移的对抗性示例,其中BA指BERT Attack指是一种针对文本模态的攻击,它使用BERT模型来生成对抗性文本,SA指Sep-Attack,这是一种针对图像模态的攻击,它使用图像的梯度信息来生成对抗性图像.

1.2 介绍

文章首先指出了视觉-语言预训练(VLP)模型在多模态任务中的显著性能,同时也强调了这些模型对对抗性示例的脆弱性。尽管VLP模型在多种任务上取得了成功,但现有的研究主要关注于白盒攻击,即在攻击者可以完全访问目标模型的情况下的攻击。然而,对抗性示例在不同VLP模型之间的迁移性(即黑盒设置下的攻击)尚未得到充分研究,这在实际应用中是一个更为关键的问题。

文章进一步讨论了多模态学习中存在的挑战,特别是VLP模型依赖于跨模态交互和多模态对齐,这些对齐是多对多的关系(many-to-many),例如,一张图片可以用多种自然语言描述。为了解决现有方法在迁移性方面的局限性,文章提出了一种新的攻击方法,称为集级指导攻击(SGA),该方法充分利用了模态间的交互,并引入了保持对齐的增强方法,以及精心设计的跨模态指导。

最后,文章总结了其贡献,包括首次尝试探索VLP模型上的对抗性示例的迁移性,提供了SGA这一新的可迁移的多模态攻击方法,并通过广泛的实验表明SGA在不同VLP模型上的对抗性迁移性方面超越了现有的最先进方法。

图2指出三种针对VLP模型的攻击思路,V表示输入图像,t是配对的标题。V '和t '分别表示对应的对抗性示例。U(v)和U(t)表示尺度不变的图像集和最匹配的标题集。箭头表示生成对抗性示例的指导

图三指出Image和text协同攻击(co-attack)效果更佳,图四指出co-attack 的迁移性比较差,结论:

(1)对抗多模态数据(即对抗图像和文本)对源模型具有很强的攻击性能,但在迁移到目标模型时很难保持相同的能力

(2)应该专门设计具有更高迁移性的攻击,而不是直接利用现有的白盒攻击方法。

作者认为对抗样本的迁移性下降主要是由于现有攻击方法的局限性:

  1. Sep-Attack的一个主要限制是它没有考虑不同模态之间的相互作用。作为每种模态的组合独立攻击方法,它无法对模态间对应关系进行建模,而模态间对应关系对于多模态学习中的成功攻击至关重要。这在图像文本检索等多模态任务中尤其明显,其中基本事实不是离散标签(例如图像分类),而是与输入模态相对应的另一种模态数据。Sep-Attack 中完全缺乏跨模态交互,严重限制了对抗样本的泛化,并降低了它们在不同 VLP 模型之间的迁移性。
  2. 虽然 Co-Attack 旨在利用模态之间的协作来生成对抗样本,但它仍然存在一个关键缺点,阻碍了其向其他 VLP 模型的迁移。与单模态学习不同,多模态学习涉及具有多对多跨模态对齐的多种互补模态,这对实现足够的对抗迁移性提出了独特的挑战。然而,联合攻击仅使用单个图像-文本对来生成对抗数据,限制了其他模态中多个标签的指导多样性。跨模态指导缺乏多样性使得对抗样本与白盒模型的对齐模态高度相关。 因此,对抗样本的通用性受到限制,并且其迁移到其他模型的有效性下降

1.3论文前置基础

 Co-Attack Co-Attack:针对VLP模型的对抗样本攻击

2.攻击方法

2.1 Alignment-preserving Augmentation

为了提高多模态攻击的迁移性,我们首先对黑盒设置中现有方法的缺点进行调查。通过对失败案例的系统分析,我们发现大约一半的案例是由于图像存在多个匹配的标题而引起的,更具体地说,研究结果表明,虽然生成的对抗图像可能与源模型中的单个监督文本相距甚远,但它很容易接近目标模型中的其他匹配文本,其中可以对对齐进行建模并最终导致攻击失败。因此,为了在迁移到其他模型时保持对抗图像的攻击能力,至关重要的是考虑多个配对文本并将对抗图像推离所有配对文本,从而在迁移到其他黑盒模型时保留攻击能力。制作具有高迁移性的对抗文本遵循类似的方法,这也可以从更多配对图像中受益。

表6和表7分别指出两种不同的SGA迁移性攻击思攻击i将对抗图像推离所有文本(包括白盒和黑盒模型),对抗性和迁移性都更高。

2.2 Cross-modal Guidance

思路

当我们针对某一模态进行攻击时,使用来自另一种模态的配对信息作为监督来指导优化对抗数据的方向。这是一种迭代指导方法,该指导反复推开多模态信息并破坏跨模态交互,以获得更好的和谐扰动,值得注意的是,由此产生的对抗样本可以感知源自多个指导的梯度。

具体做法

首先,我们为文本集t中的所有标题生成相应的对抗性标题,形成一个对抗性标题集t ' = {t ' 1, t ' 2…该过程可表述为,该过程可表述为

在嵌入空间中,对抗性标题t 'i被约束为与原始图像v不同。然后,通过求解生成对抗图像v '

其中g(v',si)表示以图像v'和比例系数si作为输入的调整大小函数,鼓励v'导出的所有缩放图像原理嵌入空间中的所有对抗样本t'i,最后生成的对抗文本t'如下

2.3 Set-level Guidance Attack 总体流程

3.实验

3.1 实验设置

  1. 数据集:实验使用了两个广泛使用的多模态数据集,Flickr30K和MSCOCO。Flickr30K包含31,783张图像,每张图像有五个对应的标题。MSCOCO包含123,287张图像,每张图像大约有五个标题。这些数据集被用于评估不同VLP模型在图像-文本检索任务上的性能。

  2. VLP模型:作者评估了两种流行的VLP模型,包括融合型(fused)和对齐型(aligned)VLP模型。对于融合型VLP,考虑了ALBEF和TCL模型。对于对齐型VLP模型,选择了CLIP模型,并考虑了两种不同的图像编码器选择:CLIPViT和CLIPCNN,分别使用ViT和ResNet-101作为基础架构。

  3. 对抗性攻击设置:为了生成对抗性图像,作者采用了PGD(Projected Gradient Descent)方法,并为文本模态的攻击采用了BERT-Attack。此外,为了增加图像集的多样性,作者通过调整原始图像的尺寸来构建多尺度图像集,并为标题集选择了与每张图像最匹配的标题对。

  4. 评估指标:实验使用攻击成功率(Attack Success Rate, ASR)作为评估对抗性鲁棒性和迁移性的主要指标。ASR衡量的是产生成功对抗性示例的攻击百分比,更高的ASR表示更好的对抗性迁移性。

  5. 实验配置:作者详细描述了实验的具体配置,包括用于生成对抗性数据的模型、数据集的预处理、以及评估过程中使用的参数设置

3.2对比实验

在论文的5.3节 "Experimental Results" 中,作者展示了SGA(Set-level Guidance Attack)在不同VLP(Vision-Language Pre-training)模型上的实验结果。

  1. 多模态融合模块:作者首先比较了具有不同融合模块的VLP模型,包括融合型(如ALBEF和TCL)和对齐型(如CLIP)模型。实验结果表明,SGA在所有黑盒设置中都优于现有的多模态攻击方法,特别是在源模型和目标模型类型相同的情况下,SGA在攻击成功率(ASR)上有显著提升。

  2. 模型架构:接着,作者探讨了不同模型架构对对抗性迁移性的影响。实验结果显示,ViT(Vision Transformer)基础的CLIP模型(CLIPViT)在白盒攻击下比CNN(Convolutional Neural Networks)基础的CLIP模型(CLIPCNN)表现出更好的鲁棒性。然而,从CLIPViT到CLIPCNN的对抗性迁移性比反向迁移性更高。SGA在这两种模型上都实现了更高的对抗性迁移性。

  3. 跨任务迁移性:作者还研究了SGA在图像标题生成(Image Captioning)和视觉定位(Visual Grounding)这两个额外的视觉-语言任务上的有效性。实验结果表明,SGA在这些任务上也表现出优越的对抗性迁移性,与现有的Co-Attack方法相比,SGA在图像标题生成任务上提高了BLEU和CIDEr评分,在视觉定位任务上也显示出更好的性能

  4. IR (Image Retrieval):图像检索任务的目标是从一个图像数据库中检索与给定文本查询相关的图像。在多模态学习中,这通常涉及到理解文本查询的内容,并在图像数据库中找到与之语义匹配的图像。

  5. TR (Text Retrieval):文本检索任务则相反,它的目标是从一个文本数据库中检索与给定图像查询相关的文本。这要求模型能够理解图像的内容,并找到与之语义匹配的文本描述

3.2.1 攻击和通用迁移方法直接结合

将Sep-Attack,Co-Attack直接和通用迁移攻击结合(比如MI-FGSM,DIM-FGSM,[10]PNA PO),不考虑跨模态交互和多模态学习中独特的多对多对齐是有问题的。Sep-Attack和MI,DIM直接结合,白盒攻击性能,迁移攻击性能都有所下降。Co-Attack和Mi,DIM直接结合,白盒攻击有所下降,迁移攻击有所上升,但上升得不多。

3.2.2 攻击的模型迁移性比较

使用ALBEF模型作为白盒模型生成对抗性数据去攻击TCL,Clip,表展示了SGA在图像-文本检索任务上与现有方法的比较。结果显示,SGA在所有黑盒设置中都取得了更高的ASR,尤其是在源模型和目标模型类型相同的情况下。例如,从ALBEF到TCL的攻击,SGA比Co-Attack提高了约30%的ASR

3.2.3 攻击的任务迁移性比较

  1. ITR (Image-Text Retrieval):这是多模态学习中的一个任务,目标是从一个图像数据库中检索与给定文本查询语义相关的图像。在这项任务中,模型需要理解文本查询的内容,并在图像数据库中找到与之相匹配的图像。

  2. IC (Image Captioning):这是另一个多模态任务,目标是为给定的图像生成描述性文本。这通常涉及到图像理解以及自然语言生成的能力。

在Table 4中,作者报告了在ITR任务中生成的对抗性数据对IC任务的影响。具体来说,作者使用ALBEF模型作为源模型来生成对抗性图像,然后攻击BLIP模型(用于IC任务)。表中展示了在不同攻击方法下,IC任务的性能变化,包括BLEU、METEOR、ROUGE、CIDEr和SPICE等评估指标。

  • Baseline:这代表了在没有对抗性攻击的情况下,BLIP模型在干净数据上的性能。
  • Co-Attack:这是现有的多模态攻击方法,用于比较。
  • SGA:这是作者提出的集级指导攻击方法。

实验结果通常以指标的降低来表示对抗性攻击的效果,因为对抗性攻击的目的是降低模型的性能。例如,如果BLEU分数从Baseline的39.7降低到Co-Attack的37.4,这表明Co-Attack成功地降低了模型的标题生成质量。如果SGA进一步降低了这些指标,这表明SGA在跨任务迁移性方面更为有效

评估指标:为了衡量跨任务迁移性,作者使用了多种评估指标,包括BLEU、METEOR、ROUGE、CIDEr和SPICE等,这些都是用于评估图像标题生成任务质量的常用指标。SGA在这些指标上的表现优于现有的Co-Attack方法,进一步证明了其在跨任务迁移性方面的优势

3.3 消融实验

在论文的5.5节 "Ablation Study" 中,作者进行了消融实验(Ablation Study),以系统地研究SGA(Set-level Guidance Attack)中不同组件的影响。消融实验通过移除或修改模型的某些部分,来观察这些变化对模型性能的影响。以下是这一部分的详细总结:

  1. 实验目的:消融实验的目的是理解SGA中各个组成部分的作用,特别是集级对齐保持增强(set-level alignment-preserving augmentations)和跨模态指导(cross-modal guidance)对对抗性迁移性的影响。

  2. 实验设置:作者在Flickr30K数据集上进行了消融实验,使用ALBEF作为源模型,CLIPViT作为目标模型。实验中,作者调整了增强图像集(multi-scale image set)的规模和增强标题集(multi-pair caption set)的数量。

  3. 多尺度图像集:作者研究了不同尺度的图像集对对抗性迁移性的影响。通过改变图像的尺度范围和步长,作者观察到随着尺度范围的增加,对抗性迁移性显著提高,尤其是在尺度范围设置为[0.50, 1.50]时,迁移性达到最高。

  4. 多对标题集:作者还研究了不同数量的标题集对对抗性迁移性的影响。结果显示,当标题集的数量增加时,黑盒性能显著提高,但最终会趋于稳定。这表明使用多个保持对齐的跨模态信息可以有效地提高对抗性迁移性。

  5. 实验结果:消融实验的结果表明,SGA的性能随着增强图像集和标题集的规模增加而提高。这证实了SGA中集级对齐保持增强和跨模态指导策略的有效性。

  6. 结论:通过消融实验,作者验证了SGA中关键组件的重要性,并展示了这些组件对于提高对抗性迁移性的贡献。这些发现有助于理解SGA的工作原理,并为未来的研究提供了改进方向

符号讲解:

在信息检索和推荐系统领域,R@1、R@5和R@10是常用的评估指标,用于衡量检索或推荐任务的性能。这些指标表示在给定的检索结果列表中,正确或相关结果出现在前N个位置的比例。具体来说:

  1. R@1 (Recall@1):这表示在所有查询中,至少有一个查询的前1个检索结果包含了正确的答案或最相关的项。R@1的值越高,表示系统在至少返回一个相关结果方面表现得越好。这个指标对于用户来说非常重要,因为它直接关系到用户能否在第一次尝试中找到他们想要的信息。

  2. R@5 (Recall@5):这表示在所有查询中,至少有一个查询的前5个检索结果包含了正确的答案或最相关的项。R@5提供了一个更宽松的评估标准,允许系统有更多的机会返回正确的结果。这个指标有助于了解系统在提供多个候选结果时的整体性能。

  3. R@10 (Recall@10):这表示在所有查询中,至少有一个查询的前10个检索结果包含了正确的答案或最相关的项。R@10进一步扩展了检索结果的范围,允许系统有更多的空间来展示相关结果。这个指标有助于评估系统在处理更复杂或更多样化的查询时的性能。

  1. 0.75~1.25, 0.25:这表示在生成对抗性数据时,原始图像被缩放到0.75倍至1.25倍的范围内,步长为0.25。这意味着图像会被调整为原始尺寸的75%、100%(即原始尺寸)、125%的大小。

  2. 0.50~1.50, 0.25:这表示图像被缩放到0.50倍至1.50倍的范围内,步长同样为0.25。这包括了50%、75%、100%、125%、150%的尺寸。

  3. 0.25~1.75, 0.25:这表示图像被缩放到0.25倍至1.75倍的范围内,步长为0.25。这包括了25%、50%、75%、100%、125%、150%、175%的尺寸

左侧的条形图展示了在图像-文本检索(Image-Text Retrieval, ITR)任务中,使用不同数量的增强图像集(multi-scale image set)对攻击成功率(Attack Success Rate, ASR)的影响

两张图都证明通过使用更大的增强图像和catition set,SGA的性能不断提高

4.核心实现代码

很多地方没看懂,后面再debug看看,以白盒攻击vit clip,然后迁移攻击cnn clip

4.1 retrieval_eval

# model:VIT-B/16版本的CLip模型   t_model:ResNet50版本的Clip模型
# ref_model,t_ref_model是对应用于基于掩码的语言建模任务
# tokenizer,t_tokenizer是对应的token,将text转为模型认识的token
def retrieval_eval(model, ref_model, t_model, t_ref_model, t_test_transform, data_loader, tokenizer, t_tokenizer, device, config):
....
    for batch_idx, (images, texts_group, images_ids, text_ids_groups) in enumerate(data_loader):
        print(f'--------------------> batch:{batch_idx}/{len(data_loader)}')
        texts_ids = []
        txt2img = []
        texts = []
        for i in range(len(texts_group)):
            texts += texts_group[i]
            texts_ids += text_ids_groups[i]
            #images和texts的是不等长的,txt2img[i]指定图片i对应哪几句文本,一般一张图片对应五个文本
            txt2img += [i]*len(text_ids_groups[i])

        images = images.to(device)                                                                  
        adv_images, adv_texts = attacker.attack(images, texts, txt2img, device=device,
                                                max_lemgth=77, scales=scales) 

4.2 SGAttack

class SGAttacker():
    def __init__(self, model, img_attacker, txt_attacker):
        self.model=model
        self.img_attacker = img_attacker
        self.txt_attacker = txt_attacker

    
    def attack(self, imgs, txts, txt2img, device='cpu', max_length=30, scales=None, **kwargs):
    
        # original state
        with torch.no_grad():
            #imgs通过vit Clip模型生成image_embed=origin_img_output
            #img_supervisions:是进行了一次F.normalize的image_embed
            origin_img_output = self.model.inference_image(self.img_attacker.normalization(imgs))
            img_supervisions = origin_img_output['image_feat'][txt2img] 
        adv_txts = self.txt_attacker.img_guided_attack(self.model, txts, img_embeds=img_supervisions)


        with torch.no_grad():
            txts_input = self.txt_attacker.tokenizer(adv_txts, padding='max_length', truncation=True, max_length=max_length, return_tensors="pt").to(device)
            txts_output = self.model.inference_text(txts_input)
            txt_supervisions = txts_output['text_feat']
        adv_imgs = self.img_attacker.txt_guided_attack(self.model, imgs, txt2img, device, 
                                                       scales=scales, txt_embeds = txt_supervisions)
        
        with torch.no_grad():
            adv_imgs_outputs = self.model.inference_image(self.img_attacker.normalization(adv_imgs))
            img_supervisions = adv_imgs_outputs['image_feat'][txt2img]
        adv_txts = self.txt_attacker.img_guided_attack(self.model, txts, img_embeds=img_supervisions)
                            
        return adv_imgs, adv_txts

4.3 ImageAttacker

class ImageAttacker():
    def __init__(self, normalization, eps=2/255, steps=10, step_size=0.5/255):
        self.normalization = normalization
        self.eps = eps
        self.steps = steps 
        self.step_size = step_size 
    
    """论文中的损失函数,目的是让adv_imgs_embeds远离所有的txts_embeds"""
    def loss_func(self, adv_imgs_embeds, txts_embeds, txt2img):  
        device = adv_imgs_embeds.device    

        it_sim_matrix = adv_imgs_embeds @ txts_embeds.T
        it_labels = torch.zeros(it_sim_matrix.shape).to(device)
        #选取仅和当前adv_imgs_embeds对应的txts_embeds
        for i in range(len(txt2img)):
            it_labels[txt2img[i], i]=1
        
        loss_IaTcpos = -(it_sim_matrix * it_labels).sum(-1).mean()
        loss = loss_IaTcpos
        
        return loss
    
   #model:要被攻击的Clip模型,imgs:图片集  txt2img:图像和它匹配文本的映射关系,一般一张图片匹配五条文本
   #scales='0.5,0.75,1.25,1.5' 用于将对抗图像进行放大或缩小
    def txt_guided_attack(self, model, imgs, txt2img, device, scales=None, txt_embeds=None):
        
        model.eval()
        #图像集批次大小
        b, _, _, _ = imgs.shape
        
        if scales is None:
            scales_num = 1
        else:
            scales_num = len(scales) +1
        "创建对抗图像,通过从均匀分布中随机采样添加噪声到原始图像中。self.eps 控制了添加的噪声大小,并且通过 imgs.detach() 确保不会修改原始图像"
        "这段代码即PGD的随机添加初始噪声"
        adv_imgs = imgs.detach() + torch.from_numpy(np.random.uniform(-self.eps, self.eps, imgs.shape)).float().to(device)
        adv_imgs = torch.clamp(adv_imgs, 0.0, 1.0)
        #steps=10
        for i in range(self.steps):
            adv_imgs.requires_grad_()
            #将图片进行缩小再放大+随机噪声 并上 原始图片
            scaled_imgs = self.get_scaled_imgs(adv_imgs, scales, device)        
            #adv_imgs_output为对抗样本的embedding
            #normalization = transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))
            #这里使用了一组均值和标准差来进行归一化。
            if self.normalization is not None:
                adv_imgs_output = model.inference_image(self.normalization(scaled_imgs))
            else:
                adv_imgs_output = model.inference_image(scaled_imgs)
                
            adv_imgs_embeds = adv_imgs_output['image_feat']
            model.zero_grad()
            with torch.enable_grad():
                loss_list = []
                loss = torch.tensor(0.0, dtype=torch.float32).to(device)
                #将每一张经过缩放+随机噪声的图片都放进去求论文中的损失,求得的损失全部相加为整体损失
                for i in range(scales_num):
                    loss_item = self.loss_func(adv_imgs_embeds[i*b:i*b+b], txt_embeds, txt2img)
                    loss_list.append(loss_item.item())
                    loss += loss_item
            loss.backward()
            #PGD攻击进行更新
            grad = adv_imgs.grad 
            grad = grad / torch.mean(torch.abs(grad), dim=(1,2,3), keepdim=True)           
            
            perturbation = self.step_size * grad.sign()
            adv_imgs = adv_imgs.detach() + perturbation
            adv_imgs = torch.min(torch.max(adv_imgs, imgs - self.eps), imgs + self.eps)
            adv_imgs = torch.clamp(adv_imgs, 0.0, 1.0)
        
        return adv_imgs



    def get_scaled_imgs(self, imgs, scales=None, device='cuda'):
        if scales is None:
            return imgs
        #获取原始图像的尺寸
        ori_shape = (imgs.shape[-2], imgs.shape[-1])
        #创建一个反向变换,用于将放缩后的图像还原为原始尺寸
        reverse_transform = transforms.Resize(ori_shape,
                                interpolation=transforms.InterpolationMode.BICUBIC)
        result = []
        # #scales='0.5,0.75,1.25,1.5'
        for ratio in scales:
            scale_shape = (int(ratio*ori_shape[0]), 
                                  int(ratio*ori_shape[1]))
            #将放缩后的图像还原为原始尺寸
            scale_transform = transforms.Resize(scale_shape,
                                  interpolation=transforms.InterpolationMode.BICUBIC)
            #给图片添加一些随机噪声,添加随机性
            scaled_imgs = imgs + torch.from_numpy(np.random.normal(0.0, 0.05, imgs.shape)).float().to(device)
            scaled_imgs = scale_transform(scaled_imgs)
            scaled_imgs = torch.clamp(scaled_imgs, 0.0, 1.0)
            
            reversed_imgs = reverse_transform(scaled_imgs)
            
            result.append(reversed_imgs)
        #将原始图像与放缩后的图像拼接起来,并返回拼接后的张量
        return torch.cat([imgs,]+result, 0)

4.4 TextAttacker

#图片指导针对一组文本进行的Bert Attack
class TextAttacker():
    def __init__(self, ref_net, tokenizer, cls=True, max_length=30, number_perturbation=1, topk=10, threshold_pred_score=0.3, batch_size=32):
        self.ref_net = ref_net
        #
        self.tokenizer = tokenizer
        self.max_length = max_length
        # epsilon_txt
        self.num_perturbation = number_perturbation
        self.threshold_pred_score = threshold_pred_score
        self.topk = topk
        self.batch_size = batch_size
        self.cls = cls
    #图片指导的Bert Attack,net是被白盒攻击的模型vit_Clip,img_embeds是图片的embedding信息
    #texts是一张图片对应的多个标题信息
    def img_guided_attack(self, net, texts, img_embeds = None):
        device = self.ref_net.device
        #tokenizer来自BertTokenizer.from_pretrained(text_encoder),text转为embedding信息text_inputs
        text_inputs = self.tokenizer(texts, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt').to(device)

        # substitutes
        mlm_logits = self.ref_net(text_inputs.input_ids, attention_mask=text_inputs.attention_mask).logits
        word_pred_scores_all, word_predictions = torch.topk(mlm_logits, self.topk, -1)  # seq-len k

        # original state
        origin_output = net.inference_text(text_inputs)
        if self.cls:
            origin_embeds = origin_output['text_feat'][:, 0, :].detach()
        else:
            origin_embeds = origin_output['text_feat'].flatten(1).detach()

        final_adverse = []
        for i, text in enumerate(texts):
            # word importance eval
            important_scores = self.get_important_scores(text, net, origin_embeds[i], self.batch_size, self.max_length)

            list_of_index = sorted(enumerate(important_scores), key=lambda x: x[1], reverse=True)

            words, sub_words, keys = self._tokenize(text)
            final_words = copy.deepcopy(words)
            change = 0

            for top_index in list_of_index:
                if change >= self.num_perturbation:
                    break

                tgt_word = words[top_index[0]]
                if tgt_word in filter_words:
                    continue
                if keys[top_index[0]][0] > self.max_length - 2:
                    continue

                substitutes = word_predictions[i, keys[top_index[0]][0]:keys[top_index[0]][1]]  # L, k
                word_pred_scores = word_pred_scores_all[i, keys[top_index[0]][0]:keys[top_index[0]][1]]

                substitutes = get_substitues(substitutes, self.tokenizer, self.ref_net, 1, word_pred_scores,
                                             self.threshold_pred_score)


                replace_texts = [' '.join(final_words)]
                available_substitutes = [tgt_word]
                for substitute_ in substitutes:
                    substitute = substitute_

                    if substitute == tgt_word:
                        continue  # filter out original word
                    if '##' in substitute:
                        continue  # filter out sub-word

                    if substitute in filter_words:
                        continue
                    '''
                    # filter out atonyms
                    if substitute in w2i and tgt_word in w2i:
                        if cos_mat[w2i[substitute]][w2i[tgt_word]] < 0.4:
                            continue
                    '''
                    temp_replace = copy.deepcopy(final_words)
                    temp_replace[top_index[0]] = substitute
                    available_substitutes.append(substitute)
                    replace_texts.append(' '.join(temp_replace))
                replace_text_input = self.tokenizer(replace_texts, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt').to(device)
                replace_output = net.inference_text(replace_text_input)
                if self.cls:
                    replace_embeds = replace_output['text_feat'][:, 0, :]
                else:
                    replace_embeds = replace_output['text_feat'].flatten(1)

                loss = self.loss_func(replace_embeds, img_embeds, i)
                candidate_idx = loss.argmax()

                final_words[top_index[0]] = available_substitutes[candidate_idx]

                if available_substitutes[candidate_idx] != tgt_word:
                    change += 1

            final_adverse.append(' '.join(final_words))

        return final_adverse
    """论文中的损失,目的是让txt_embeds远离所有的img_embeds"""
    def loss_func(self, txt_embeds, img_embeds, label):
        loss_TaIcpos = -txt_embeds.mul(img_embeds[label].repeat(len(txt_embeds), 1)).sum(-1) 
        loss = loss_TaIcpos
        return loss

    # net:VIT-B/16版本的CLip模型
    # texts:要被攻击的文本
    def attack(self, net, texts):
        device = self.ref_net.device

        text_inputs = self.tokenizer(texts, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt').to(device)

        # substitutes
        mlm_logits = self.ref_net(text_inputs.input_ids, attention_mask=text_inputs.attention_mask).logits
        word_pred_scores_all, word_predictions = torch.topk(mlm_logits, self.topk, -1)  # seq-len k

        # original state
        origin_output = net.inference_text(text_inputs)
        if self.cls:
            origin_embeds = origin_output['text_embed'][:, 0, :].detach()
        else:
            origin_embeds = origin_output['text_embed'].flatten(1).detach()



        criterion = torch.nn.KLDivLoss(reduction='none')
        final_adverse = []
        for i, text in enumerate(texts):
            # word importance eval
            important_scores = self.get_important_scores(text, net, origin_embeds[i], self.batch_size, self.max_length)

            list_of_index = sorted(enumerate(important_scores), key=lambda x: x[1], reverse=True)

            words, sub_words, keys = self._tokenize(text)
            final_words = copy.deepcopy(words)
            change = 0

            for top_index in list_of_index:
                if change >= self.num_perturbation:
                    break

                tgt_word = words[top_index[0]]
                if tgt_word in filter_words:
                    continue
                if keys[top_index[0]][0] > self.max_length - 2:
                    continue

                substitutes = word_predictions[i, keys[top_index[0]][0]:keys[top_index[0]][1]]  # L, k
                word_pred_scores = word_pred_scores_all[i, keys[top_index[0]][0]:keys[top_index[0]][1]]

                substitutes = get_substitues(substitutes, self.tokenizer, self.ref_net, 1, word_pred_scores,
                                             self.threshold_pred_score)


                replace_texts = [' '.join(final_words)]
                available_substitutes = [tgt_word]
                for substitute_ in substitutes:
                    substitute = substitute_

                    if substitute == tgt_word:
                        continue  # filter out original word
                    if '##' in substitute:
                        continue  # filter out sub-word

                    if substitute in filter_words:
                        continue
                    '''
                    # filter out atonyms
                    if substitute in w2i and tgt_word in w2i:
                        if cos_mat[w2i[substitute]][w2i[tgt_word]] < 0.4:
                            continue
                    '''
                    temp_replace = copy.deepcopy(final_words)
                    temp_replace[top_index[0]] = substitute
                    available_substitutes.append(substitute)
                    replace_texts.append(' '.join(temp_replace))
                replace_text_input = self.tokenizer(replace_texts, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt').to(device)
                replace_output = net.inference_text(replace_text_input)
                if self.cls:
                    replace_embeds = replace_output['text_embed'][:, 0, :]
                else:
                    replace_embeds = replace_output['text_embed'].flatten(1)

                loss = criterion(replace_embeds.log_softmax(dim=-1), origin_embeds[i].softmax(dim=-1).repeat(len(replace_embeds), 1))
                
                loss = loss.sum(dim=-1)
                candidate_idx = loss.argmax()

                final_words[top_index[0]] = available_substitutes[candidate_idx]

                if available_substitutes[candidate_idx] != tgt_word:
                    change += 1

            final_adverse.append(' '.join(final_words))

        return final_adverse

[10]Wei et al. Towards transferable adversarial attacks on vision transformers. In AAAI, 2022

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值