对抗样本生成算法复现代码解析:FGSM和DeepFool

参考链接:
https://www.jianshu.com/p/72c072551e10
论文地址:
FGSM:地址
DeepFool:地址

导入包

import numpy as np
import torch   # pytorch机器学习开源框架
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import torchvision
import torch.optim as optim
from torchvision import transforms
from tqdm import *
import matplotlib.pyplot as plt
import copy
from torch.autograd.gradcheck import zero_gradients

建立目标模型

class Net(nn.Module):
    # 定义Net的初始化函数,这个函数定义了该神经网络的基本结构
    def __init__(self):
        # 复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
        super(Net, self).__init__()
        # 定义fc1(fullconnect)全连接函数1为线性函数:y = Wx + b,并将28*28个节点连接到300个节点上。
        self.fc1 = nn.Linear(28*28, 300)
        # 定义fc2(fullconnect)全连接函数2为线性函数:y = Wx + b,并将300个节点连接到100个节点上。
        self.fc2 = nn.Linear(300, 100)
        # 定义fc3(fullconnect)全连接函数3为线性函数:y = Wx + b,并将100个节点连接到10个节点上。
        self.fc3 = nn.Linear(100, 10)

    #定义该神经网络的向前传播函数,该函数必须定义,一旦定义成功,向后传播函数也会自动生成(autograd)
    def forward(self, x):
        # 输入x经过全连接1,再经过ReLU激活函数,然后更新x
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        # 输入x经过全连接3,然后更新x
        x = self.fc3(x)
        return x

加载数据集

# 定义数据转换格式
mnist_transform = transforms.Compose([transforms.ToTensor(), transforms.Lambda(lambda x : x.resize_(28*28))])

# 导入数据,定义数据接口
# 1.root,表示mnist数据的加载的相对目录
# 2.train,表示是否加载数据库的训练集,false的时候加载测试集
# 3.download,表示是否自动下载mnist数据集
# 4.transform,表示是否需要对数据进行预处理,none为不进行预处理
traindata = torchvision.datasets.MNIST(root="./drive/My Drive/fgsm/mnist", train=True, download=True, transform=mnist_transform)
testdata  = torchvision.datasets.MNIST(root="./drive/My Drive/fgsm/mnist", train=False, download=True, transform=mnist_transform)

# 将训练集的*张图片划分成*份,每份256(batch_size)张图,用于mini-batch输入
# shffule=True在表示不同批次的数据遍历时,打乱顺序
# num_workers=n表示使用n个子进程来加载数据
trainloader = torch.utils.data.DataLoader(traindata, batch_size=256, shuffle=True, num_workers=0)
testloader = torch.utils.data.DataLoader(testdata, batch_size=256, shuffle=True, num_workers=0)

# 展示图片
index = 100
image = testdata[index][0]
label = testdata[index][1]
image.resize_(28,28) # 调整图片大小
img = transforms.ToPILImage()(image)
plt.imshow(img)

运行显示
在这里插入图片描述
展示图片(每次刷新显示不一样)

index = 100
batch = iter(testloader).next() # 将testloader转换为迭代器
# 例如:如果batch_size为4,则取出来的images是4×c×h×w的向量,labels是1×4的向量
image = batch[0][index]
label = batch[1][index]
image.resize_(28,28)
img = transforms.ToPILImage()(image)
plt.imshow(img)

初始化模型、选择代价函数及优化器

net = Net()
loss_function = nn.CrossEntropyLoss() # 交叉熵损失函数
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-04) # 随机梯度下降优化

训练分类模型

num_epoch = 50  
for epoch in tqdm(range(num_epoch)): # python进度条,num_epoch=50,所以每2%显示一次
    losses = 0.0
    for data in trainloader:
        inputs, labels = data # 获取输入
        # inputs, labels = Variable(inputs), Variable(labels)
        optimizer.zero_grad() # 参数梯度置零
        # 前向+ 反向 + 优化
        outputs = net(inputs)
        loss = loss_function(outputs, labels) # 计算loss
        loss.backward() # 传回反向梯度
        optimizer.step() # 梯度传回,利用优化器将参数更新
        losses += loss.data.item() # 输出统计 
    print("*****************当前平均损失为{}*****************".format(losses/2000.0))

运行显示
在这里插入图片描述
测试模型拟合效果

correct = 0 # 定义预测正确的图片数,初始化为0
total = 0 # 总共参与测试的图片数,也初始化为0
for data in testloader:
    images, labels = data 
    outputs = net(Variable(images))# 输入网络进行测试 # 因为神经网络只能输入Variable
    _, predicted = torch.max(outputs.data, 1)#返回了最大的索引,即预测出来的类别。
      # 这个_,predicted是python的一种常用的写法,表示后面的函数其实会返回两个值
      # 但是我们对第一个值不感兴趣,就写个_在那里,把它赋值给_就好,我们只关心第二个值predicted
      # torch.max(outputs.data,1) ,返回一个tuple(元组)。第二个元素是label
    total += labels.size(0) # 更新测试图片的数量
    correct += (predicted == labels).sum() # 更新正确分类的图片的数量
print("预测准确率为:{}/{}".format(correct, total))

运行显示
在这里插入图片描述
保存和加载模型

# 保存整个网络 #...It won't be checked...是保存模型时的输出
PATH1="./drive/My Drive/fgsm/mnist_net_all.pkl"
torch.save(net,PATH1) 
# 保存网络中的参数,速度快,占空间少,PATH1是保存路径和文件名
PATH2="./drive/My Drive/fgsm/mnist_net_param.pkl"
torch.save(net.state_dict(),PATH2)
#针对上面一般的保存方法,加载的方法分别是:
#model_dict=torch.load(PATH)
#model_dict=model.load_state_dict(torch.load(PATH))
net = torch.load(PATH1) # 加载模型

index = 100 # 选择测试样本
image = testdata[index][0]
label = testdata[index][1]

outputs = net(Variable(image)) # 因为神经网络只能输入Variable
predicted = torch.max(outputs.data,0)[1]
print('预测值为:{}'.format(predicted))

image.resize_(28,28) # 显示一下测试的图片,和上文代码相同
img = transforms.ToPILImage()(image)
plt.imshow(img)

运行显示
在这里插入图片描述
FGSM对抗样本

index = 100 # 选择测试样本
image = Variable(testdata[index][0].resize_(1,784), requires_grad=True) # requires_grad存储梯度值
label = torch.tensor([testdata[index][1]])
outputs = net(image)
loss = loss_function(outputs, label)
loss.backward()
# 计算结束后,其梯度值存储在image.grad.data中
# FGSM添加扰动
epsilon = 0.1 # 扰动程度
x_grad = torch.sign(image.grad.data) # 快速梯度符号法
x_adversarial = torch.clamp(image.data + epsilon * x_grad, 0, 1) # 0和1表示限制范围的下限和上限

# 使用之前建立的模型对扰动后的图片数据进行分类
outputs = net(x_adversarial) # net = torch.load(PATH1)。
predicted = torch.max(outputs.data,1)[1] #outputs含有梯度值,其处理方式与之前有所不同
print('预测值为:{}'.format(predicted[0]))

# 展示扰动之后的图片
x_adversarial.resize_(28,28)
img = transforms.ToPILImage()(x_adversarial)
plt.imshow(img)

运行显示
在这里插入图片描述
Deepfool对抗样本

PATH1="./drive/My Drive/fgsm/mnist_net_all.pkl"
net = torch.load(PATH1) # 加载模型
index = 100 # 选择测试样本
image = Variable(testdata[index][0].resize_(1,784), requires_grad=True)
label = torch.tensor([testdata[index][1]])
f_image = net.forward(image).data.numpy().flatten() # flatten()函数默认是按行的方向降维
I = (np.array(f_image)).flatten().argsort()[::-1] # argsort()[::-1]表示降维排序后返回索引值
label = I[0] # 样本标签

input_shape = image.data.numpy().shape # 获取原始样本的维度,返回(第一维长度,第二维长度,...)
pert_image = copy.deepcopy(image) # 深度复制原始样本,复制出来后就独立了
w = np.zeros(input_shape) # 返回来一个给定形状和类型的用0填充的数组
r_tot = np.zeros(input_shape)

loop_i = 0 # 循环
max_iter = 50 # 最多迭代次数
overshoot = 0.0

x = Variable(pert_image, requires_grad=True) # 因为神经网络只能输入Variable
fs = net.forward(x) # 调用forward函数
fs_list = [fs[0][I[k]] for k in range(len(I))] # 每个类别的取值情况,及其对应的梯度值
k_i = label

参考论文算法2理解,通过内部的for循环可以获得x到各分类边界的距离;在外部的while循环中,我们利用内部循环获得的所有边界距离中的最小值对x进行更新。重复这一过程,直到的分类标签发生变化。
在这里插入图片描述

#参考论文算法2理解
while k_i == label and loop_i < max_iter: # 分类标签变化时结束循环
    pert = np.inf # np.inf表示一个足够大的数
    fs[0][I[0]].backward(retain_graph=True) # 反向传播,计算当前梯度;连续执行两次backward,参数表明保留backward后的中间参数。
    orig_grad = x.grad.data.numpy().copy() # 原始梯度
    
    for k in range(len(I)): # 获得x到各分类边界的距离
        zero_gradients(x)
        fs[0][I[k]].backward(retain_graph=True)
        cur_grad = x.grad.data.numpy().copy() # 现在梯度
        
        w_k = cur_grad - orig_grad
        f_k = (fs[0][I[k]] - fs[0][I[0]]).data.numpy()
        
        pert_k = abs(f_k) / np.linalg.norm(w_k.flatten())
        if pert_k < pert:  # 获得最小的分类边界距离向量
            pert = pert_k # 更新perk,pert为最小距离
            w = w_k
    r_i = (pert + 1e-4) * w / np.linalg.norm(w)
    r_tot = np.float32(r_tot + r_i) # 累积扰动
    
    pert_image = image + (1+overshoot)*torch.from_numpy(r_tot) # 添加扰动
    x = Variable(pert_image, requires_grad=True)
    fs = net.forward(x)
    k_i = np.argmax(fs.data.numpy().flatten()) # 扰动后的分类标签
    loop_i += 1
r_tot = (1+overshoot)*r_tot # 最终累积的扰动
outputs = net(pert_image.data.resize_(1,784))
predicted = torch.max(outputs.data,1)[1] #outputs含有梯度值,其处理方式与之前有所不同
print('预测值为:{}'.format(predicted[0]))

pert_image = pert_image.reshape(28,28)
img = transforms.ToPILImage()(pert_image)
plt.imshow(img)

运行显示
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200412162445787.png可见预测值是2是错误的,但是想要显示添加扰动后的图片总是报错RuntimeError: cannot resize variables that require grad。没有找到解决办法,如果有同学知道的话,麻烦告诉一下,谢谢!

  • 10
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
以下为使用Python代码实现的攻击代码生成并保存mnist数据集的对抗样本: ```python import tensorflow as tf import numpy as np import matplotlib.pyplot as plt from tensorflow.examples.tutorials.mnist import input_data sess = tf.compat.v1.Session() # 加载mnist数据集 mnist = input_data.read_data_sets('MNIST_data', one_hot=True) # 定义输入和输出的placeholder x_input = tf.compat.v1.placeholder(tf.float32, shape=[None, 784]) y_label = tf.compat.v1.placeholder(tf.float32, shape=[None, 10]) # 定义模型 x_image = tf.reshape(x_input, [-1,28,28,1]) conv1 = tf.compat.v1.layers.conv2d(inputs=x_image, filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool1 = tf.compat.v1.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2) conv2 = tf.compat.v1.layers.conv2d(inputs=pool1, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool2 = tf.compat.v1.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2) flatten = tf.reshape(pool2, [-1, 7 * 7 * 64]) dense = tf.compat.v1.layers.dense(inputs=flatten, units=1024, activation=tf.nn.relu) logits = tf.compat.v1.layers.dense(inputs=dense, units=10) # 定义损失函数 cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=y_label)) # 定义fgsm算法 epsilon = 0.1 gradients, = tf.gradients(cross_entropy, x_input) adv_x = tf.stop_gradient(x_input + epsilon * tf.sign(gradients)) # 定义评估模型的accuracy correct_pred = tf.equal(tf.argmax(logits,1), tf.argmax(y_label,1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 初始化变量 sess.run(tf.compat.v1.global_variables_initializer()) # 训练模型 for i in range(2000): x_batch, y_batch = mnist.train.next_batch(100) _, loss = sess.run([tf.compat.v1.train.AdamOptimizer(0.001).minimize(cross_entropy), cross_entropy], feed_dict={ x_input: x_batch, y_label: y_batch }) if i % 100 == 0: test_acc = sess.run(accuracy, feed_dict={ x_input: mnist.test.images[:1000], y_label: mnist.test.labels[:1000] }) print('Step %d, loss=%.4f, test accuracy=%.4f' % (i, loss, test_acc)) # 生成对抗样本 adv_images = sess.run(adv_x, feed_dict={ x_input: mnist.test.images[:1000], y_label: mnist.test.labels[:1000] }) # 将对抗样本保存为npy文件 np.save('adv_images.npy', adv_images) # 展示一组原始图片和对应的对抗样本 fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 5)) for i in range(2): if i == 0: axes[i].imshow(mnist.test.images[0].reshape(28,28), cmap='gray') axes[i].set_title('Original Image') else: axes[i].imshow(adv_images[0].reshape(28,28), cmap='gray') axes[i].set_title('Adversarial Image') plt.show() ``` 以上代码使用了FGSM算法生成对抗样本,其中epsilon为0.1,表示对抗样本中每个像素点的最大变化量为0.1。生成对抗样本后,使用numpy库将对抗样本保存为npy文件,方便后续使用。最后,展示了一组原始图片和对应的对抗样本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值