BertAttack

Introduction

原先方法

  1. 原先的攻击方式基于特定的规则,这样就不能同时保证生成的对抗样本语义一致并流畅。
  2. 方法较为复杂

本方法

  1. 使用Bert作为语言模型去生成对抗样本。高效

image-20240407161127638

算法

首先找到句子中重要性大的单词:

$o_y$ 是模型预测概率 $S_{/w_i} = [w_0,…,w_{i-1},[MASK],w_{i+1},…]$
$$
I_{wi}=o_y(S) - o_y(S_{/w_i})
$$
所以最后值越大的就代表这个单词最易收到攻击,因为将其mask掉后概率很低。(代码里用的是[UNK]标记)

1
masked_words.append(words[0:i] + ['[UNK]'] + words[i + 1:])

用语义连贯的单词去替换这些单词:

这里并不会将i位置的单词mask掉然后让其预测,因为会产生这样的问题:

  • ”I like the cat.” 将cat位置mask掉很难生成cat,因为生成”I like the dog.”同样是顺畅的。(不过按照代码实现的角度确实应该是mask了)

  • 计算代价大

因此将原始句子$S=[w_0,w_1,…]$分词成sub-word $H=[h_0,h_1,…]$ 将H输入ML model中得到概率P,这里不会直接取最大值而是取前K个值作为每一个位置的K个替换词。也就是最后生成一个$P^{\in n×K}$ 的矩阵,其中第i行代表第i个单词的前k位替换词。

  1. 参数解析

    • sub-word:例如一个句子[‘i’,’like’,’dog’],sub-word就是将’dog’这样的单词进行第一次分词,BERT采取的BPE来进行分词,因此结果可能导致一个单词分词后成两个token。针对这种情况本文分开进行处理:
      • single word:将K个候选词进行第一次过滤去除其中的停顿词。(不同任务清理的也不同)替换后进行测试是否攻击成功。
      • sub-word:如果一个单词被分成$[h_0,h_1,…,h_t]$ ,那么对应P部分该单词的token候选应该是$R\in t*K$ ,可以得到$K^t$个token组合将其转换成正常单词后计算困惑度得到前K个组合,从而找到合适的子词组合。
  2. 代码实现

获得可替换词P:

1
2
3
4
5
6
7
8
sub_words = ['[CLS]'] + sub_words[:max_length - 2] + ['[SEP]']
input_ids_ = torch.tensor([tokenizer.convert_tokens_to_ids(sub_words)])
#这一步就是将sub_words变成[103,1023,..]这样的形式,其实和直接编码结果一样
word_predictions = mlm_model(input_ids_.to('cuda'))[0].squeeze() # seq-len(sub) vocab
#将句子输入到MLM model里,例如input_ids_ = [10,12,312]有三个token,那么最后生成3*30155的矩阵,就是每个位置预测结果
word_pred_scores_all, word_predictions = torch.topk(word_predictions, k, -1) # seq-len k
word_predictions = word_predictions[1:len(sub_words) + 1, :]
word_pred_scores_all = word_pred_scores_all[1:len(sub_words) + 1, :]

总览

image-20240407164028760

实验

image-20240409145849106

  • Perturb:扰动率,越小说明语义一致性越强
  • Original Acc 和 Attacked Acc:原始成功率和攻击后成功率,两者对比
  • Query Number:黑盒模型查询次数
  • Avg Len:句子平均长度,越长理论上需要查询次数越多。

人工评价:

image-20240409151256944

消融和讨论

  • K值选取:
  • 子词级攻击

image-20240409172214060

  • 可转移性,对抗训练