对抗样本指的是一个经过微小调整就可以让机器学习算法输出错误结果的输入样本。在图像识别中,可以理解为原来被一个卷积神经网络(CNN)分类为一个类(比如“熊猫”)的图片,经过非常细微甚至人眼无法察觉的改动后,突然被误分成另一个类(比如“长臂猿”)。
为什么这样轻微的扰动可以对神经网络造成如此大的影响,业界目前的共识认为这不是过拟合的结果,而是因为输入特征维度过高和模型的线性性质导致的。
深度模型本实确实是一个非常非线性的模型,但是模型的组成部分都是线性的,全连接网络的矩阵乘法是线性的,卷积网络的卷积计算也是计算点乘,同样也是线性的,哪怕是激活函数,看似是为了造成模型的非线性,但是目前主流的relu激活函数其实在部分区域内也是线性的。
因此,尽管深度模型整体呈非线性,但是其中的线性部分却依旧占了很大一部分,也正是如此,轻微的扰动经过网络的积累后会形成雪崩效应,从而对最终结果造成影响。
更具体的来说,在高维空间中,每个像素值即使改变很小,这些改变会通过线性模型的参数进行点乘累计造成很明显的变化。只要方向正确,即使数据上只是迈出了一小步,在特征空间上就是一大步,极大概率会跨越决策层。
x′=x+η
η =εsign(▽xJ(θ,x,y))
参数解释:
x -输入图象 ,x^′-输出图像, η-扰动
ε-步长,即扰动大小
Sign()-符号函数,J(θ,x,y)损失函数
θ -模型参数,x-模型输入,y-标签
攻击成功的目标是使得模型分类错误,也即是利用加入扰动的样本使得模型的损失增大,而我们沿着梯度方向添加扰动,可以对分类结果产生最大的影响,因为我们一般是沿着梯度下降的方向去减小损失函数。
from __future__ import absolute_import, division, print_function, unicode_literals
import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['figure.figsize'] = (8, 8)
mpl.rcParams['axes.grid'] = False
#MobileNetV2
# Let's load the pretained MobileNetV2 model and the ImageNet class names.
pretrained_model = tf.keras.applications.MobileNetV2(include_top=True, weights='imagenet')
pretrained_model.trainable = False
# ImageNet labels
decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions
# Helper function to preprocess the image so that it can be inputted in MobileNetV2
# 对图像预处理,使得图像可以输入MobileNetV2模型
def preprocess(image):
image = tf.cast(x=image, dtype=tf.float32)
image = image/255
image = tf.image.resize(images=image, size=(224, 224))
image = image[None, ...]
return image
# Helper function to extract labels from probability vector
# 得到图像真实标签 将图像输入模型并得到概率最高的分类结果
def get_imagenet_label(probs):
return decode_predictions(probs, top=1)[0][0]#只获取第一个
#image_path = tf.keras.utils.get_file(fname='YellowLabradorLooking_new.jpg', origin='https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
#image_path=tf.keras.utils.get_file('turtle.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/Green_Sea_Turtle_grazing_seagrass.jpg')
image_path = tf.keras.utils.get_file(fname='grace_hopper.jpg', origin='https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')
image_raw = tf.io.read_file(filename=image_path)
image = tf.image.decode_image(contents=image_raw)#可以自动判定图像格式然后 decode
image = preprocess(image)#预处理
image_probs = pretrained_model.predict(image) #模型预测,输入测试集,输出预测结果---概率
plt.figure()
plt.imshow(image[0])
_, image_class, class_confidence = get_imagenet_label(image_probs)#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(tensor=input_image) #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, image_probs)
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, 0.20]
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[0]
adv_x = tf.clip_by_value(adv_x, 0, 1) # 将值限制在[0,1]范围内
display_images(adv_x, descriptions[i])
参考链接
https://blog.csdn.net/nemoyy/article/details/81052301
https://blog.csdn.net/nemoyy/article/details/81052301
https://download.csdn.net/download/qq_35225463/11646431