参考书籍:AI安全之对抗样本
入门
深度学习在训练过程中,通过计算样本的预测值与真实值之间的损失函数,之后在反向传递的过程中通过链式法则调整模型的参数,不断减小损失函数的值,迭代计算出模型的各层参数。
生成对抗样本的基本过程也可以参考这一过程,不同的是在迭代训练过程中,将网络参数固定下来,把对抗样本当做唯一需要训练的参数,通过反向传递过程调整对抗样本
import torch
import torchvision
from torchvision import datasets, transforms
from torch.autograd import Variable
import torch.utils.data.dataloader as Data
import torch.nn as nn
from torchvision import models
import numpy as np
import cv2
#对比展现原始图片和对抗样本图片
def show_images_diff(original_img,original_label,adversarial_img,adversarial_label):
import matplotlib.pyplot as plt
plt.figure()
#归一化
if original_img.any() > 1.0:
original_img=original_img/255.0
if adversarial_img.any() > 1.0:
adversarial_img=adversarial_img/255.0
plt.subplot(131)
plt.title('Original')
plt.imshow(original_img)
plt.axis('off')
plt.subplot(132)
plt.title('Adversarial')
plt.imshow(adversarial_img)
plt.axis('off')
plt.subplot(133)
plt.title('Adversarial-Original')
difference = adversarial_img - original_img
#(-1,1) -> (0,1)
difference=difference / abs(difference).max()/2.0+0.5
plt.imshow(difference,cmap=plt.cm.gray)
plt.axis('off')
plt.tight_layout()
plt.show()
#获取计算设备 默认是CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
#图像加载以及预处理
#从这里修改图片的路径
image_path="../picture/cropped_panda.jpg"
orig = cv2.imread(image_path)[..., ::-1]
orig = cv2.resize(orig, (224, 224))
img = orig.copy().astype(np.float32)
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
img /= 255.0
img = (img - mean) / std
img = img.transpose(2, 0, 1)
img=np.expand_dims(img, axis=0)
img = Variable(torch.from_numpy(img).to(device).float())
print("shape=一张图片,三个通道,1代表一张图片,3,224,244,代表3*224*224")
print("0维一元,1维三元,二维224元,三维224元")
print(img.shape)
#使用预测模式 主要影响droupout和BN层的行为
print("——————————————")
print("使用Alexnet模型,进行预测,pretrained=True是预测模式,device是设备信息")
#使用model.eval()时,框架会自动把BN(BatchNorm)和DropOut固定住,不会取平均,而是用训练好的值
model = models.alexnet(pretrained=True).to(device).eval()
label=np.argmax(model(img).data.cpu().numpy())
print("label=+++{}".format(label))
#对抗样本生成核心代码
#图像数据梯度可以获取
img.requires_grad = True
#设置为不保存梯度值 自然也无法修改
for param in model.parameters():
param.requires_grad = False
#torch.optim.Adam是toch的一个优化工具,通过优化器可以进行参数的优化
optimizer = torch.optim.Adam([img])
#采用交叉熵进行梯度计算
loss_func = torch.nn.CrossEntropyLoss()
epochs=100
#定向攻击的目标
target=288
target=Variable(torch.Tensor([float(target)]).to(device).long())
for epoch in range(epochs):
# 梯度清零
optimizer.zero_grad()
# forward + backward
output = model(img)
#计算交叉熵
loss = loss_func(output, target)
label=np.argmax(output.data.cpu().numpy())
print("epoch={} loss={} label={}".format(epoch,loss,label))
#如果定向攻击成功
if label == target:
break
#optimizer.zero_grad() 清空过往梯度;
#loss.backward() 反向传播,计算当前梯度;
#optimizer.step() 根据梯度更新网络参数
loss.backward()
optimizer.step()
adv=img.data.cpu().numpy()[0]
print(adv.shape)
adv = adv.transpose(1, 2, 0)
adv = (adv * std) + mean
adv = adv * 255.0
#adv = adv[..., ::-1] # RGB to BGR
adv = np.clip(adv, 0, 255).astype(np.uint8)
show_images_diff(orig,388,adv,target.data.cpu().numpy()[0])