FGSM代码实践

浅谈FGSM

FGSM是什么?从机器学习到深度学习,第一次听这个名词,后来查阅资料了解到是一种图像的攻击方法。本来有一个模型可以识别出你的图片内容,你把一张小狗的图片喂给模型,模型告诉你是狗,把猫喂给模型,模型告诉你是猫。当你给这张小狗的图片添加上噪声之后(肉眼无法识别有没有加噪声),再次喂给模型,模型告诉你是这是其他东西,从而达到了欺骗模型的目的。

FGSM原理

一种基于梯度生成对抗样本的算法,属于对抗攻击中的无目标攻击(即不要求对抗样本经过model预测指定的类别,只要与原样本预测的不一样即可)

FGSM代码实践_第1张图片

             图1                                                          图二(噪声)                            图三(对抗样本)

把图一喂给模型,模型告诉你57.7%的概率是熊猫,别看概率小,机器总会输出概率值最大的那个结果。随后把图二加入图一中,生成一张新的图三,把图三喂给模型,模型告诉你99.3%是金丝猴,但肉眼看依然是熊猫,这就是FGSM攻击方法

如果要看具体公式什么的跳转这个链接,这些符号对我很不友好,但代码能看懂就行https://blog.csdn.net/qq_35414569

FGSM代码实战

 1、pytorch导入各种包,预先设置一个扰动值e,再把设备选择好,有钱GPU,没钱CPU,此处实现的是无目标攻击。

from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import numpy as np
import matplotlib.pyplot as plt
from torchvision import models
import cv2
from torch.autograd import Variable
e=0.5#扰动值
#获取计算设备 默认是CPU
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

2、图像的加载以及预处理,喂给模型的图片还是挺讲究的,要考虑图片通道数(RGB),尺寸,图片类型,不然模型不吃。

image_path="data/goldfish.jpg"
orig = cv2.imread(image_path)[..., ::-1]
orig = cv2.resize(orig, (224, 224))
img = orig.copy().astype(np.float32)
#使用Imagenet的均值和标准差是一种常见的做法。它们是根据数百万张图像计算得出的。如果要在自己的数据集上从头开始训练,则可以计算新的均值和标准差,这是一种经验值
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
#归一化,为什么进行归一化,因为训练效果好
img /= 255.0
img = (img - mean) / std
#把HWC的图片转为CHW的图片
img = img.transpose(2, 0, 1)

img=np.expand_dims(img, axis=0)

img = Variable(torch.from_numpy(img).to(device).float())
print(img.shape)

3、定义模型,优化器、损失等等参数

#使用预测模式 主要影响droupout和BN层的行为
model = models.alexnet(pretrained=True).to(device).eval()
#取真实标签
label=np.argmax(model(img).data.cpu().numpy())#这里为什么要加cup()?因为np无法直接转为cuda使用,要先转cpu
print("label={}".format(label))
# 图像数据梯度可以获取
img.requires_grad = True

# 设置为不保存梯度值 自然也无法修改
for param in model.parameters():
    param.requires_grad = False

optimizer = torch.optim.Adam([img])#优化器
loss_func = torch.nn.CrossEntropyLoss()#交叉熵计算损失

epochs = 10#训练轮次
target = 31#原始图片的标签
target = Variable(torch.Tensor([float(target)]).to(device).long())#转换数据类型
print(target)

4、定义fgsm攻击函数

def fgsm_attack(image, epsilon, data_grad):
    # 使用sign(符号)函数,将对x求了偏导的梯度进行符号化
    sign_data_grad = data_grad.sign()
    # 通过epsilon生成对抗样本
    perturbed_image = image + epsilon*sign_data_grad
    #噪声越来越大,机器越来越难以识别,但人眼可以看出差别
    # 做一个剪裁的工作,将torch.clamp内部大于1的数值变为1,小于0的数值等于0,防止image越界
    perturbed_image = torch.clamp(perturbed_image, 0, 1)
    # 返回对抗样本
    return perturbed_image

5、简单进行训练就达到了目的,原本标签为31,最后预测的结果不为31,收敛于610,loss也收敛,这里是纯数据对比,没有可视化,可视化代码还在码

for epoch in range(epochs):
    # 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))

    # 梯度清零
    optimizer.zero_grad()
    # 反向传递 计算梯度
    loss.backward()
    img.data = fgsm_attack(img,e,img.data)

总结

代码可直接在pycharm里运行,前提是有pytorch运行环境,如有问题可一起探讨交流,哪里有错误也请指出,虚心接收,AI小白一枚。

2021年10月9日续可视化代码

更新一下可视化代码,可直接运行,报错找我,此处实现的是定向攻击,注释都标的挺清楚了

import matplotlib.pyplot as plt
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
from PIL import Image

#对比展现原始图片和对抗样本图片
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="data/goldfish.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(img.shape)

#使用预测模式 主要影响droupout和BN层的行为
model = models.alexnet(pretrained=True).to(device).eval()
#取真实标签
label=np.argmax(model(img).data.cpu().numpy())#这里为什么要加cup()?因为np无法直接转为cuda使用,要先转cpu
print("label={}".format(label))

# 图像数据梯度可以获取
img.requires_grad = True

# 设置为不保存梯度值 自然也无法修改
for param in model.parameters():
    param.requires_grad = False

optimizer = torch.optim.Adam([img])#优化器
loss_func = torch.nn.CrossEntropyLoss()#交叉熵计算损失

epochs = 1#训练轮次,只需一次,多了就变成I-FGSM了
e = 0.001#扰动值

target = 254#此处是一个定向攻击
target = Variable(torch.Tensor([float(target)]).to(device).long())#转换数据类型

for epoch in range(epochs):

    # 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:
        print("成功")
        break

    # 梯度清零
    optimizer.zero_grad()
    # 反向传递 计算梯度
    loss.backward()
    img.data = img.data - e * torch.sign(img.grad.data)#FGSM最重要的公式
print(model(img).argmax())
adv=img.data.cpu().numpy()[0]
adv = adv.transpose(1, 2, 0)
adv = (adv * std) + mean
adv = adv * 255.0
adv = np.clip(adv, 0, 255).astype(np.uint8)#np数据类型是0-255,转PIL之后进行存储
#对抗样本的保存
# print(adv.shape)
# img = Image.fromarray(adv)
# img.show()
# plt.imshow(img)
# plt.show()
# img.save('one.jpg')#图片存储为什么不用指定路径,存储结果在远程服务器
show_images_diff(orig,388,adv,target.data.cpu().numpy()[0])

运行结果如下:

FGSM代码实践_第2张图片FGSM代码实践_第3张图片

 原始标签为31,定向攻击目标为254

FGSM代码实践_第4张图片

 这里其实我自己有个问题没有搞定,生成的对抗样本保存时为什么不用指定路径,我无法直接保存到pycharm中,我是保存在远程服务器上,但我从服务器上并不能打开这张图片,是黑的,等于保存失败......

你可能感兴趣的:(python,深度学习)