Invisible Backdoor Attack with Sample-Specific Triggers

1. BaseLine简介

现有的后门攻击方法添加的触发器是与样本无关的,即不同的被污染样本中包含相同的触发器,这会导致其很容易被现有的后门防御方法减轻攻击效果。基于上述问题,本文提出了一种新的后门攻击方法:通过生成样本特定(sample-specific)的触发器实现攻击,只需要修改某些训练样本中看不见的扰动,而无需像许多现有攻击那样操纵其它训练组件。具体而言,本文所提出的模型通过编码器-解码器网络将攻击者指定的字符串编码到良性图像中,生成样本特定的看不见的附加噪声作为后门触发器,当 DNNs 在被污染的数据集上进行训练时,将会生成从字符串到目标标签的映射。对基准数据集的大量实验验证表明,本文所提出的方法能够攻破现有的后门防御方法。

[Code]

2. 代码结构

2.1. 目录结构

Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
.
├─ checkpoint
│ ├─ args.json # 输入参数
│ ├─ imagenet.txt # 日志文件
│ └─ imagenet_checkpoint.pth.tar # 最佳模型结果
├─ ckpt
│ ├─ datasets # 测试数据集
│ │ ├─ sub-imagenet-200
│ │ │ ├─ test
│ │ │ ├─ train
│ │ │ └─ val
│ │ └─ sub-imagenet-200-bd
│ │ ├─ inject_a
│ │ │ ├─ train
│ │ │ └─ val
│ │ ├─ inject_train.py
│ │ └─ inject_val.py
│ ├─ encoder_imagenet
│ └─ res18_imagenet
├─ data
│ └─ imagenet
│ ├─ bd # 原始样例图片与对应sample-specific触发器
│ └─ org # 原始样例图片
├─datasets
│ ├─ sub-imagenet-200
│ │ ├─ test
│ │ ├─ train
│ │ └─ val
│ └─ sub-imagenet-200-bd
│ └─ inject_a
│ ├─ train
│ └─ val
├─ utils # 辅助函数
├─ class_index.py # 数据集类别索引
├─ encode_image.py # 生成sample-specific触发器
├─ models.py # 加载ResNet-18模型
├─ requirements.txt
├─ test.py # 模型测试文件
├─ train.py # 模型训练文件
└─ train.sh # Shell脚本

2.2. 代码模块说明

2.2.1. encode_image.py

此文件采用 StegaStamp 模型对图像进行隐写操作,将特定的触发器嵌入到给定的图像中,并保存隐写图像和残差图像文件。文件中以 n01770393_12386.JPEG(蝎子图片) 为例进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import bchlib  # 执行 BCH 编码和解码
import os
from PIL import Image
import numpy as np
import tensorflow as tf
from tensorflow.python.saved_model import tag_constants
from tensorflow.python.saved_model import signature_constants
import argparse # 解析命令行参数

# 解析命令行参数
parser = argparse.ArgumentParser(description='Generate sample-specific triggers')
parser.add_argument('--model_path', type=str, default='ckpt/encoder_imagenet')
parser.add_argument('--image_path', type=str, default='data/imagenet/org/n01770393_12386.JPEG')
parser.add_argument('--out_dir', type=str, default='data/imagenet/bd/')
parser.add_argument('--secret', type=str, default='a')
parser.add_argument('--secret_size', type=int, default=100)
args = parser.parse_args()

# 从命令行参数中获取值
model_path = args.model_path
image_path = args.image_path
out_dir = args.out_dir
secret = args.secret # Length of secret less than 7
secret_size = args.secret_size

# 加载 TensorFlow 模型
sess = tf.InteractiveSession(graph=tf.Graph())
model = tf.saved_model.loader.load(sess, [tag_constants.SERVING], model_path)

# 获取输入和输出节点的名称
input_secret_name = model.signature_def[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY].inputs['secret'].name
input_image_name = model.signature_def[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY].inputs['image'].name
output_stegastamp_name = model.signature_def[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY].outputs['stegastamp'].name
output_residual_name = model.signature_def[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY].outputs['residual'].name

# 获取输入和输出节点
input_secret = tf.get_default_graph().get_tensor_by_name(input_secret_name)
input_image = tf.get_default_graph().get_tensor_by_name(input_image_name)
output_stegastamp = tf.get_default_graph().get_tensor_by_name(output_stegastamp_name)
output_residual = tf.get_default_graph().get_tensor_by_name(output_residual_name)

# 设置图像的宽和高
width = 224
height = 224

# 初始化 BCH 编码器
BCH_POLYNOMIAL = 137
BCH_BITS = 5
bch = bchlib.BCH(BCH_POLYNOMIAL, BCH_BITS)

# 对 secret 消息进行编码
data = bytearray(secret + ' ' * (7 - len(secret)), 'utf-8')
ecc = bch.encode(data)
packet = data + ecc
packet_binary = ''.join(format(x, '08b') for x in packet)
secret = [int(x) for x in packet_binary]
secret.extend([0, 0, 0, 0])

# 加载输入图像,并将其归一化到 [0, 1] 范围内
image = Image.open(image_path)
image = np.array(image, dtype=np.float32) / 255.

# 构建 TensorFlow 的 feed_dict,将输入 secret 消息和图像提供给模型
feed_dict = {input_secret: [secret], input_image: [image]}

# 运行 TensorFlow 会话,获取隐写图像和残差
hidden_img, residual = sess.run([output_stegastamp, output_residual], feed_dict=feed_dict)

# 后处理隐写图像和残差,将其转换为整数数组并保存为图像文件
hidden_img = (hidden_img[0] * 255).astype(np.uint8)
residual = residual[0] + .5
residual = (residual * 255).astype(np.uint8)

# 获取文件名称
name = os.path.basename(image_path).split('.')[0]

# 保存隐写图像和残差的图像文件
im = Image.fromarray(np.array(hidden_img))
im.save(out_dir + '/' + name + '_hidden.png')
im = Image.fromarray(np.squeeze(residual))
im.save(out_dir + '/' + name + '_residual.png')

2.2.2. train.py

此代码用于训练具有后门攻击能力的 DNN 模型,主体思路如下:

Part 1 定义辅助函数

  • save_checkpoint(state, is_best, checkpoint='checkpoint', filename='imagenet_checkpoint.pth.tar')

保存模型检查点。

  • adjust_learning_rate(lr, optimizer, epoch, args)

根据指定的学习率调整策略调整学习率。

Part 2 定义数据集类

  • bd_data:加载带有后门攻击标签的训练数据集;

  • bd_data_val:加载带有后门攻击标签的验证数据集。

Part 3 定义训练和验证函数

  • train:训练模型,其中包括数据加载、前向传播、计算损失和梯度更新等步骤;

  • test:验证模型,用于评估模型在验证集上的性能。

Part 4 主函数 main

首先解析命令行参数,准备训练和验证数据集,接着初始化模型、损失函数和优化器。不断迭代训练模型,每轮包括训练和验证两个阶段,最后保存最佳模型参数。

Part 5 命令行参数解析 & 调用主函数

  • 解析命令行参数,包括模型选择、优化器设置和数据路径等;

  • 调用 main 函数开始训练。

2.2.3. test.py

此脚本用于测试已经训练好的 DNN 模型在原始图像和带有后门攻击的图像上的性能。通过加载模型和图像数据,进行模型推理,并输出预测结果。

3. 实验设置

3.1. 数据集

ImageNet

人脸识别数据集 MS-Celeb-1M(仅在论文中使用)

3.2. 模型选择

ResNet-18

3.3. 训练参数

  • 训练轮次 epochs = 50

  • train_batch = 32

  • test_batch = 32

  • 初始学习率 lr = 0.001

  • gamma = 0.1

  • momentum = 0.9

  • weight_decay = 0.0001

  • 后门标签 bd_label = 0

  • 后门中毒率 bd_ratio = 0.1

4. 实验结果

4.1. 论文结果

4.1.1. 攻击实验

  • 中毒样本:对于每一个数据集,本文设置投毒率γ=10%\gamma=10\%,目标标签yt=0y_t=0
  • 生成触发器:BadNet 和 Blended Attack 的触发器位于图像的右下角,触发器尺寸为 20×20,由两条相互垂直的线将触发器平均分为 4 块:Blended Attack 的触发器和BadNet的不同点在于 BadNet 是直接将触发器添加到图像的右下角, 而 Blended Attack 的触发器是按照一个透明系数(实验中设置为 10%10\% )和图像加权平均得到最后的带触发器的图片;本文提出的方法 SSBA 的触发器是肉眼看不清的,它是以干净的样本和目标类标签字符串拼接构成训练集,以训练生成触发器的编码器,代码中作者直接使用了 StegaStamp 图像隐写的模型;

  • 训练植入后门的模型的参数设置:优化器选用 SGD;初始化学习率lr=0.001,batchSize=128lr=0.001, batchSize=128,总迭代次数 maxEpoch=30maxEpoch=30,学习率在第 15 轮以及第 20 轮后,以衰减因子为 0.1 的方式衰减。

攻击实验的可视化对比结果如下图所示。

4.1.2. 主要结果

下图是不同方法在 ImageNet 和 MS-Celeb-1M 数据集上对无防御的 DNNs 的比较,其中最好的结果以粗体表示,第二好的结果使用下划线表示。

【注】良性准确率(BA)越高越好;攻击成功率(ASR)越高越好;峰值信噪比(PSNR)越高越好;无穷范数ll_∞越小越好。

下图是不同攻击对基于剪枝的防御的良性攻击率(BA)和攻击成功率(ASR)比较,根据图像可得,本文所提出的攻击方法(SSBA)更能抵抗基于剪枝的防御。

Neural Cleanse 首先计算触发候选项以将所有良性图像转换为每个标签,之后其采用一个异常检测器来验证是否有人显著小于其他人作为后门指标。异常指数的值越小,Neural-Cleanse 防御攻击就越困难。如下图所示,本文的攻击方法对 Neural-Cleanse 的抵抗性更强。

如下图所示,本文对不同攻击的合成触发器(即所有候选者中异常指数最小的一个)进行了可视化。BadNets 和 Blended Attack 合成的触发器包含与攻击者使用的类似的模式(即右下角的白色方块),而本文所提出攻击方法的触发器无意义,即本文所合成的触发器更难被理解和识别,这也使得本文的攻击方法更难被检测到。

下图是 STRIP 生成的不同攻击的熵,反映了攻击方法对 STRIP 的抵抗性。熵越高,代表 STRIP 防御起来越难。

【注】STRIP 是一种防御手段,它基于对可疑图像施加各种图像模式生成的样本的预测随机性来过滤掉被篡改的样本,这种随机性是通过那些样本的平均预测的熵来衡量的。因此熵越高,对 STRIP 的攻击防御就越困难。

SentiNet 根据不同样本的 Grad-CAM 的相似性识别触发区域。如下图所示,Grad-CAM 成功区分了由 BadNets 和 Blended Attack 生成的触发区域,而无法检测到由本文的攻击生成的触发区域。因此本文的攻击方法对 SentiNet 的抵抗性更强。

DF-TND 通过观察每个标签在手工制作的通用对抗攻击前后的 logit 增值,来检测一个可疑的 DNN 是否包含隐藏的后门。如果目标标签的 logit 增值唯一地处于峰值,那么这种方法就会成功。为了公平的演示,本文对其超参数进行了微调,以寻找对本文的攻击最佳的防御设置。如下图所示,目标类别的 logit 增值(图中的红色条)在两个数据集上都不是最大的,这表明本文的攻击方法也可以绕过 DF-TND。

Spectral Signatures 发现后门攻击可以在特征表示的协方差的谱中留下可检测的痕迹。下图为 Spectral Signature 所生成的样本的异常分数。异常分数越大,表示样本越可能异常。如果清洁样本的值小,中毒样本的值大,那么 Spectral Signature 就会成功。本文测试了 100 个样本,其中 0 - 49 是干净样本,50 - 100是中毒样本。本文的攻击显著扰乱了此方法,使得干净样本有较高得分。

4.1.3. 讨论

之前的实验中,本文设置的中毒样本的目标标签都是 0。在这一部分,本文首先用不同的目标标签(yt=1,2,3)(y_t=1,2,3)测试其攻击方法,并给出攻击的 BA/ASR,展现这种攻击方法在使用不同目标标签时的有效性。

下图展示了投毒率 γ\gamma 对本文攻击方法的 BA/ASR 影响效果。只毒化 2% 的训练样本,本文的攻击方法在两个数据集上都达到了高 ASR (> 95%)。随着 γ\gamma 的增加,ASR 会增加,而 BA 几乎保持不变。

下图展示的是两种不同的触发器情况下的 ASR,Ours 代表的是使用一致的触发器进行攻击的情况,Ours (inconsistent) 代表的是使用不一致的触发器进行攻击的情况。前者指的是触发器基于同一张图像生成,而后者指的是触发器基于不同的图像生成,即对于每一张测试图像 x\boldsymbol{x},随机选择另一张测试图像 x\boldsymbol{x}',然后使用 x\boldsymbol{x}'生成的触发器来进行攻击测试。

下图展示的是在攻击阶段本文方法的数据集外泛化性情况,即一个在另一个数据集上训练的编码器进行攻击的有效性是否与在同一数据集上训练的编码器相当。结果表明,如果图像大小相同,攻击者可以重用已经训练过的编码器来生成被污染的样本,此特性将显著降低攻击的计算成本。

下图是本文攻击方法被数据集外测试样本攻击的 ASR,本文的攻击可以根据数据集外的图像生成的毒化样本也可以达到近乎 100% 的 ASR。即攻击者可以使用数据集外的图像(不必使用测试图像)激活被攻击的 DNNs 中的隐藏后门。

4.2. 实验结果

训练 50 轮后,得到的攻击效果如下图所示,生成的日志文件 imagenet.txt 详见附录。其中最佳 Valid ACC 为 83.34%,对应的 ASR 值为 99.50753768844221%。

【注】utils 文件夹 eval.py 中需要将correct_k = correct[:k].view(-1).float().sum(0)修改为correct_k = correct[:k].reshape(-1).float().sum(0)

运行测试脚本的结果如下图所示。对于给出的示例而言,模型在攻击前均能准确进行分类,同时在原始图像上添加触发器后模型均能够将分类结果预测为目标标签(yt=0)(y_t=0),说明模型的攻击效果好。


Invisible Backdoor Attack with Sample-Specific Triggers
https://shoutaoml.top/2024/05/27/Invisible-Backdoor-Attack-with-Sample-Specific-Triggers/
作者
Shoutao Sun
发布于
2024年5月27日
许可协议