对抗样本之DeepFool原理&coding-程序员宅基地

技术标签: deepfool原理  deepfool之代码实现  深度学习  AI安全之对抗学习  对抗样本生成之deepfool  torch实现deepfool  对抗样本  

1 笔者言

  • 虽说标题有DeepFool原理,但能力有限,这个我确实讲不清楚。与FGSM,BIM,MIM,CW等生成对抗样本的方法相比,deepfool是最难的了。给你推荐一个我看懂了的文章,但切记,想要真正明白deepfool的原理,就一定要耐下性子认真看,还要多动笔画示意图。言至此,传送门
  • 本文主要讲解代码,完成生成deepfool对抗样本的完整过程。直接复制代码就能跑。相信我,不骗你。
  • 使用pytorch实现deepfool。pytorch不会?跳转
  • CVPR 2016 论文地址

2 coding

  • 实验步骤:
  1. 训练一个简单模型(mnist手写数字分类任务)
  2. 通过该模型生成对抗样本
  3. 测试生成对抗样本的鲁棒性
  4. 可视化展示对抗样本效果
2.1 训练模型
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 tqdm import tqdm

# 加载mnist数据集
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=False, download=True, transform=transforms.Compose([
            transforms.ToTensor(),
            ])),
        batch_size=10, shuffle=True)
train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([
            transforms.ToTensor(),
            ])),
        batch_size=10, shuffle=True)

# 超参数设置
batch_size = 10
epoch = 1
learning_rate = 0.001
# 生成对抗样本的个数
adver_nums = 1000


# LeNet Model definition
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x, dim=1)

# 选择设备
device = torch.device("cuda" if (torch.cuda.is_available()) else "cpu")

# 初始化网络,并定义优化器
simple_model = Net().to(device)
optimizer1 = torch.optim.SGD(simple_model.parameters(),lr = learning_rate,momentum=0.9)
print (simple_model)

Output:
在这里插入图片描述

  • 如上代码,下载数据,设置超参数以及构建模型
  • 如下代码,开始训练模型,并进行测试,观察模型准确率
# 训练模型
def train(model,optimizer):
  for i in range(epoch):
    for j,(data,target) in tqdm(enumerate(train_loader)):
      data = data.to(device)
      target = target.to(device)
      logit = model(data)
      loss = F.nll_loss(logit,target)
      model.zero_grad()
      # 如下:因为其中的loss是单个tensor就不能用加上一个tensor的维度限制
      loss.backward()
      # 如下有两种你形式表达,一种是原生,一种是使用optim优化函数直接更新参数
      # 为什么原生的训练方式没有效果???代表参数没有更新,就离谱。
      # 下面的detach与requires_grad_有讲究哦,终于明白了;但是为什么下面代码不能work还是没搞懂
      # for params in model.parameters():
      #   params = (params - learning_rate * params.grad).detach().requires_grad_()
      optimizer.step()
      if j % 1000 == 0:
        print ('第{}个数据,loss值等于{}'.format(j,loss))
train(simple_model,optimizer1)

# eval eval ,老子被你害惨了
# 训练完模型后,要加上,固定DROPOUT层
simple_model.eval()

# 模型测试
def test(model,name):
  correct_num = torch.tensor(0).to(device)
  for j,(data,target) in tqdm(enumerate(test_loader)):
    data = data.to(device)
    target = target.to(device)
    logit = model(data)
    pred = logit.max(1)[1]
    num = torch.sum(pred==target)
    correct_num = correct_num + num
  print (correct_num)
  print ('\n{} correct rate is {}'.format(name,correct_num/10000))
test(simple_model,'simple model')

Output:
在这里插入图片描述

2.2 DeepFool对抗样本生成
import numpy as np
from torch.autograd import Variable
import torch as torch
import copy
# 下面导入的类是很早版本的,现在版本已经没有了
# from torch.autograd.gradcheck import zero_gradients


def deepfool(image, net, num_classes=10, overshoot=0.02, max_iter=100):
    is_cuda = torch.cuda.is_available()
    if is_cuda:
        # print("Using GPU")
        image = image.cuda()
        net = net.cuda()
        
    f_image = net.forward(Variable(image[None, :, :, :], requires_grad=True)).data.cpu().numpy().flatten()
    I = (np.array(f_image)).flatten().argsort()[::-1]

    I = I[0:num_classes]
    label = I[0]

    input_shape = image.cpu().numpy().shape
    pert_image = copy.deepcopy(image)
    w = np.zeros(input_shape)
    r_tot = np.zeros(input_shape)

    loop_i = 0

    x = Variable(pert_image[None, :], requires_grad=True)
    fs = net.forward(x)
    fs_list = [fs[0,I[k]] for k in range(num_classes)]
    k_i = label

    while k_i == label and loop_i < max_iter:

        pert = np.inf
        fs[0, I[0]].backward(retain_graph=True)
        grad_orig = x.grad.data.cpu().numpy().copy()

        for k in range(1, num_classes):
            if x.grad is not None:
              x.grad.zero_()

            fs[0, I[k]].backward(retain_graph=True)
            cur_grad = x.grad.data.cpu().numpy().copy()

            # set new w_k and new f_k
            w_k = cur_grad - grad_orig
            f_k = (fs[0, I[k]] - fs[0, I[0]]).data.cpu().numpy()

            pert_k = abs(f_k)/np.linalg.norm(w_k.flatten())

            # determine which w_k to use
            if pert_k < pert:
                pert = pert_k
                w = w_k

        # compute r_i and r_tot
        # Added 1e-4 for numerical stability
        r_i =  (pert+1e-4) * w / np.linalg.norm(w)
        r_tot = np.float32(r_tot + r_i)

        if is_cuda:
            pert_image = image + (1+overshoot)*torch.from_numpy(r_tot).cuda()
        else:
            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.cpu().numpy().flatten())

        loop_i += 1
    r_tot = (1+overshoot)*r_tot
    return r_tot, loop_i, label, k_i, pert_image


# 这几个变量主要用于之后的测试以及可视化
adver_example_by_FOOL = torch.zeros((batch_size,1,28,28)).to(device)
adver_target = torch.zeros(batch_size).to(device)
clean_example = torch.zeros((batch_size,1,28,28)).to(device)
clean_target = torch.zeros(batch_size).to(device)
# 从test_loader中选取1000个干净样本,使用deepfool来生成对抗样本
for i,(data,target) in enumerate(test_loader):
  if i >= adver_nums/batch_size :
    break
  if i == 0:
    clean_example = data
  else:
    clean_example = torch.cat((clean_example,data),dim = 0)
    
  cur_adver_example_by_FOOL = torch.zeros_like(data).to(device)

  for j in range(batch_size):
    r_rot,loop_i,label,k_i,pert_image = deepfool(data[j],simple_model)
    cur_adver_example_by_FOOL[j] = pert_image
  
  # 使用对抗样本攻击VGG模型
  pred = simple_model(cur_adver_example_by_FOOL).max(1)[1]
  # print (simple_model(cur_adver_example_by_FOOL).max(1)[1])
  if i == 0:
    adver_example_by_FOOL = cur_adver_example_by_FOOL
    clean_target = target
    adver_target = pred
  else:
    adver_example_by_FOOL = torch.cat((adver_example_by_FOOL , cur_adver_example_by_FOOL), dim = 0)
    clean_target = torch.cat((clean_target,target),dim = 0)
    adver_target = torch.cat((adver_target,pred),dim = 0)

print (adver_example_by_FOOL.shape)
print (adver_target.shape)
print (clean_example.shape)
print (clean_target.shape)

  • 如上,最核心的代码就是deepfool函数。deepfool的函数,论文作者是发表在了github上的,飞过去

  • deepfool函数实现也不难,只要你理解了deepfool的原理,只要按照如下图的算法实现即可。关键还是理解难啊!!!
    在这里插入图片描述

  • 如上,对抗样本已经生成,存储在变量 adver_example_by_FOOL 中。

  • 其中 adver_target,clean_example,clean_target,都是为了后面的可视化作准备。

2.3 测试鲁棒性
  • 我们希望生成的对抗样本能使得靶模型有很低的准确率,即攻击效果好
import torch.utils.data as Data
def adver_attack_vgg(model,adver_example,target,name):
  adver_dataset = Data.TensorDataset(adver_example, target)
  loader = Data.DataLoader(
    dataset=adver_dataset,      # 数据,封装进Data.TensorDataset()类的数据
    batch_size=batch_size      # 每块的大小
    )
  correct_num = torch.tensor(0).to(device)
  for j,(data,target) in tqdm(enumerate(loader)):
    data = data.to(device)
    target = target.to(device)
    pred = model.forward(data).max(1)[1]
    num = torch.sum(pred==target)
    correct_num = correct_num + num
  print (correct_num)
  print ('\n{} correct rate is {}'.format(name,correct_num/adver_nums))
      
adver_attack_vgg(simple_model,adver_example_by_FOOL,clean_target,'simple model')

Output:

  • 可以看出,deepfool的攻击效果是非常好的,模型分类准确度只达到了一个百分点。
    在这里插入图片描述
2.4 可视化展示
def plot_clean_and_adver(adver_example,adver_target,clean_example,clean_target):
  n_cols = 5
  n_rows = 5
  cnt = 1
  cnt1 = 1
  plt.figure(figsize=(n_cols*4,n_rows*2))
  for i in range(n_cols):
    for j in range(n_rows):
      plt.subplot(n_cols,n_rows*2,cnt1)
      plt.xticks([])
      plt.yticks([])
      plt.title("{} -> {}".format(clean_target[cnt], adver_target[cnt]))
      plt.imshow(clean_example[cnt].reshape(28,28).to('cpu').detach().numpy(),cmap='gray')
      plt.subplot(n_cols,n_rows*2,cnt1+1)
      plt.xticks([])
      plt.yticks([])
      # plt.title("{} -> {}".format(clean_target[cnt], adver_target[cnt]))
      plt.imshow(adver_example[cnt].reshape(28,28).to('cpu').detach().numpy(),cmap='gray')
      cnt = cnt + 1
      cnt1 = cnt1 + 2
  plt.show()

plot_clean_and_adver(adver_example_by_FOOL,adver_target,clean_example,clean_target)

Output:

  • 效果多好,你放大了看。左边是干净样本,右边是对抗样本,放在一起好对比效果。
    在这里插入图片描述

附录

代码地址:
https://colab.research.google.com/drive/1g9i75kwQbw5vJbAjrZ87NA0P_wb7KXfw?usp=sharing

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41466575/article/details/118978636

智能推荐

java代码 EXCEL实战(一) 导入手机号_java设置某一单元格为手机号-程序员宅基地

文章浏览阅读2k次。一、需求描述:管理后台用户管理模块下某用户操作界面,需要一个批量导入手机号的功能。二、前端代码:2.1 html代码&lt;table&gt; &lt;tr&gt; &lt;td&gt; &lt;input type="radio" name="state" value="2"&gt;多用户 &lt;/td&gt_java设置某一单元格为手机号

浮出雾海的真实:从ET大脑到产业AI-程序员宅基地

文章浏览阅读420次。最近我们一直在探讨这样一个问题:为什么AI飞速发展,普通人还是觉得距离AI非常遥远?实际上,推而广之这可能不单单是媒体和技术爱好者的疑问。上升到更大的国家与社会经济层面,对真实可用、能快速见到实效的AI需求已经十分迫切。从“新一代人工智能”政策出台,到国家AI创新开放平台公布首批名单,再到近期工信部发布三年规划,可以看到政策层面一直在将AI向实用化、产业化、融合化方向推进。换言之,如何让浮在实验室

免费教材丨第51期:数学基础课程----概率论教程、机器学习中的数学基础-程序员宅基地

文章浏览阅读1.3k次。小编说 过去几个月里,有不少人联系我,向我表达他们对人工智能、数据科学、对利用机器学习技术探索统计规律性,开发数据驱动的产品的热情。但是,我发现他们中有些人实际上缺少为了获取有用结果的必要的数学直觉和框架。所以接下来,我们会分几期发放一些数学基础书籍和数学理论相关的教学视频哦,有需要的朋友记得按时领取哦!之前我们发放的是《漫画线性代数》和《微积分超入门》两本书籍以及北京大学张筑生老师的《数_机器学习 概率论 教材

上传头像(上传文件)功能与from表单一起提交_修改用户头像 头像和表单一起提交吗-程序员宅基地

文章浏览阅读6.5k次。工作中很容易遇到一个from表单中除了填一些基本信息外,还需要上传个头像或者文件也实属正常需求,如果是两者功能拆分开实现还是挺容易的,但是如果要一起提交的话,是得注意一下提交方式;在这里呢我记录一下,方便自己以后可以使用查看,也希望能帮到大家。如果需要上传,下载文件和上传头像后回显代码。请选择:最简单的js实现上传头像并正常回显javaWeb文件上传下载(复制粘贴即可使用)ja..._修改用户头像 头像和表单一起提交吗

Spring Boot + Shiro 使用 DefaultWebSessionManager 导致 Druid Monitor 监听不到 Session 问题解决方案_若依druid websession监控没有-程序员宅基地

文章浏览阅读2.9k次。一、问题配置项目中使用了 shiro-spring 快速集成 Shiro 到当前 Spring 环境中,配置如下:pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org_若依druid websession监控没有

后缀自动机(SAM)学习指南_sam编程是什么-程序员宅基地

文章浏览阅读4.9k次。*在学习后缀自动机之前需要熟练掌握WA自动机、RE自动机与TLE自动机*什么是后缀自动机后缀自动机 Suffix Automaton (SAM) 是一个用 O(n) 的复杂度构造,能够接受一个字符串所有后缀的自动机。它最早在陈立杰的 2012 年 noi 冬令营讲稿中提到。在2013年的一场多校联合训练中,陈立杰出的 hdu 4622 可以用 SAM 轻松水过,由此 S_sam编程是什么

随便推点

Python使用numpy实现对数组随机打乱并排序_numpy 打乱数组顺序-程序员宅基地

文章浏览阅读5.2k次。Python使用numpy实现对数组排序并恢复在使用Python进行科学运算时,我们经常会将列表打乱,进行一些运算以后再将列表恢复成原来的顺序,那具体该怎么实现呢?下面给出参考代码:import numpy as np# 给出原始列表raw = np.array([1,3,5,7,9,2,4,6,8])# 将列表(numpy数组)打乱index = np.random.permutation(raw.size)raw = raw[index] # 此时数组已经打乱# .......经过了一些_numpy 打乱数组顺序

【原创】Java与数据结构(上篇:排序算法)-程序员宅基地

文章浏览阅读111次。花了两天的时间坐在图书馆里,终于写完了所有主要的数据结构,包括其中的算法部分,呵呵,保研和面试的第一关估计没问题了,下面就是看OS和Network了心得:纸上得来终觉浅,绝知此事要躬行!当自己回想着算法的整个过程,然后一行一行的敲下来,发现算法太精辟了,看似简单,写起来可真不是那么回事,而且,写多了,熟悉了,思路就快了,写起来就得心应手了!可能你会觉得算法和数据结构对现在的程序员来..._the last node is not leaf报错

[OpenGL] 体积雾-程序员宅基地

文章浏览阅读4.5k次。开发环境:Qt, OpenGL(注 : 水平有限,实现细节不一定完全正确,可能相比一般的体积雾实现过程会显得过于复杂,所以仅供参考;动图有闪烁和条纹现象是录屏软件的问题)概念引入 体积雾,简单来说就是有体积的区域雾,在体积雾内的物体,会显得模糊;而在体积雾外的物体,则是物体的原颜色。 现在我们已经明确了,如果物体落在体积雾内,我们需要在雾的颜色和物体..._体积雾

为tmux和vim开启斜体和真彩色-程序员宅基地

文章浏览阅读1.3k次。为tmux和vim开启斜体和真彩色这篇文章是我上一篇博客:为tmux和vim开启真彩色的拓展一般终端会支持斜体,但是tmux中是无法显示斜体的。网上的很多教程都已经过时,今天我来分享下如何在tmux中启用斜体吧。参考:reference1. 检查tmux中能否显示斜体echo -e "\e[3mitalic\e[23m"2. 创建新的终端类型我们创建一种新的终端类型tmux-256c...

基于Eclipse的Hadoop应用开发环境配置_通过eclipse安装()可实现hadoop开发环境的图形化-程序员宅基地

文章浏览阅读1.2k次。Hadoop集群(第7期)_Eclipse开发环境设置1、Hadoop开发环境简介1.1 Hadoop集群简介  Java版本:jdk-6u31-linux-i586.bin  Linux系统:CentOS6.0  Hadoop版本:hadoop-1.0.0.tar.gz1.2 Windows开发简介  Java版本:jdk-6u31-win_通过eclipse安装()可实现hadoop开发环境的图形化

C++ sort函数详解_sort(起始地址,末尾地址+1)意思-程序员宅基地

文章浏览阅读748次,点赞2次,收藏4次。Sort函数,是C++里面常用函数,一般用于排序 有三个参数: 在 algorithm 函数包里面(1)第一个是要排序的数组的起始地址。(2)第二个是结束的地址(最后一位要排序的地址的下一地址)(3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。代码示例:#include <iostream>#i..._sort(起始地址,末尾地址+1)意思

推荐文章

热门文章

相关标签