Adversarial example是为了混淆神经网络而产生的特殊输入,会导致模型对给定输入进行错误分类。这些输入对人眼来说是无法辨别的,但却导致网络无法识别图像的内容。FGSM(Fast Gradient Signed Method) 是一种白盒攻击,其目标是确保分类错误。
关于攻击的分类有很多种,从攻击环境来说,可以分为黑盒攻击,白盒攻击或者灰盒攻击:
在这里,从熊猫的图像开始,攻击者在原始图像上添加小的扰动,这导致模型将此图像标记为长臂猿,且具有很高的可信度。
FGSM的工作原理是利用神经网络的梯度来创建一个Adversarial example。对于输入图像,该方法使用相对于输入图像的损失的梯度来创建使损失函数最大化的新图像。这个新图像被称为对抗图像。可以使用以下表达式对其进行总结:
在这里,梯度是相对于输入的图像的。这样做是因为其目标是创造一个最大化损失的图像。实现这一点的方法是找出图像中每个像素对损失值的贡献程度,并相应地添加一个扰动(使用链式规则去计算梯度可以很容易地找到每个输入像素的贡献程度)。此外,由于模型不再被训练(因此梯度不针对可训练变量,即模型参数),因此模型参数保持不变。唯一的目的就是使一个已经受过训练的模型发生错误的分类。
在这篇文章中,模型是MobileNetV2模型,在ImageNet上进行了预训练。
import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['figure.figsize'] = (8, 8)
mpl.rcParams['axes.grid'] = False
pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,
weights='imagenet')
pretrained_model.trainable = False
有关迁移学习的内容可以参考:Tensorflow2.0之tf.keras.applacations迁移学习。
def preprocess(image):
image = tf.cast(image, tf.float32)
image = image/255
image = tf.image.resize(image, (224, 224))
image = image[None, ...]
return image
image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
image_raw = tf.io.read_file(image_path)
image = tf.image.decode_image(image_raw)
image = preprocess(image)
# Helper function to extract labels from probability vector
def get_imagenet_label(probs):
return tf.keras.applications.mobilenet_v2.decode_predictions(probs, top=1)[0][0]
image_probs = pretrained_model.predict(image)
plt.figure()
plt.imshow(image[0])
_, image_class, class_confidence = get_imagenet_label(image_probs)
plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))
plt.show()
loss_object = tf.keras.losses.CategoricalCrossentropy()
def create_adversarial_pattern(input_image, input_label):
with tf.GradientTape() as tape:
tape.watch(input_image)
prediction = pretrained_model(input_image)
loss = loss_object(input_label, prediction)
# Get the gradients of the loss w.r.t to the input image.
gradient = tape.gradient(loss, input_image)
# Get the sign of the gradients to create the perturbation
signed_grad = tf.sign(gradient)
return signed_grad
# Get the input label of the image.
labrador_retriever_index = 208
label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])
label = tf.reshape(label, (1, image_probs.shape[-1]))
perturbations = create_adversarial_pattern(image, label)
plt.imshow(perturbations[0])
def display_images(image, description):
_, label, confidence = get_imagenet_label(pretrained_model.predict(image))
plt.figure()
plt.imshow(image[0])
plt.title('{} \n {} : {:.2f}% Confidence'.format(description,
label, confidence*100))
plt.show()
epsilons = [0, 0.01, 0.1, 0.15]
descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')
for eps in epsilons]
for i, eps in enumerate(epsilons):
adv_x = image + eps*perturbations
adv_x = tf.clip_by_value(adv_x, 0, 1)
display_images(adv_x, descriptions[i])
其中epsilons 表示噪声的干扰程度。