在前面的章节中,我们已经看到了深度学习模型在解决各种计算机视觉任务方面的强大能力。我们在不同的数据集上训练和测试多个模型。现在,我们将把注意力转向这些模型的健壮性。

在本章中,我们将介绍对抗样本。对抗样本是一种输入数据,它可以显著地改变模型预测,而不被人眼注意到。由于这一事实,对抗样本可能令人担忧,特别是在安全或医疗保健领域等关键任务中。在开始考虑可能的解决方案之前,了解这些攻击是如何工作的将是有益的。

一个对抗样本如下图所示:

左边的图像是原始图像,而右边的图像是同一图像的对抗性版本。
尽管这两张图片看起来一样,分类器的预测概率,显示在标题上,是显著不同的。在本章中,我们将开发这种对抗性攻击。

有两种类型的对抗性攻击:白盒攻击和黑盒攻击。在白盒攻击中,攻击者拥有用于训练模型的模型、输入函数和损失函数的知识。通过使用这些知识,攻击者可以改变输入来破坏预测的输出。输入的变化量通常很小,人眼无法分辨。一种常见的白盒攻击被称为Fast Gradient Sign(FGS)攻击,其工作原理是通过改变输入来使损失最大化。

在FGS攻击中,给定一个输入和一个预先训练的模型,我们计算相对于输入的损失梯度。然后,我们在输入中加入一小部分梯度的绝对值。

在本教程中,您将学习如何为本书“二值图像分类”中介绍的二值分类模型开发FGS攻击。你可能还记得,我们在Histo数据集中开发了一个分类器来分类癌症图像。我们将使用相同的数据集和经过训练的模型。

为了方便,数据集类和模型分别定义在Python文件mydataset.py和mymodel.py中。此外,cnn_weights.pt提取码:123a文件中提供了模型的预训练权值。

mydataset.py文件代码书写

import torch
import os
import numpy as np
import pandas as pd
from PIL import Image
from torch.utils.data import Dataset, Subset, DataLoader
import torchvision.transforms as transforms
np.random.seed(1)
torch.manual_seed(1)class histoCancerDataset(Dataset):def __init__(self,data_dir,transform,data_type="train"):path2data=os.path.join(data_dir,data_type)self.filenames=os.listdir(path2data)self.full_filenames=[os.path.join(path2data,f) for f in self.filenames]csv_filename=data_type+"_labels.csv"path2csvLabels=os.path.join(data_dir,csv_filename)labels_df=pd.read_csv(path2csvLabels)labels_df.set_index("id", inplace=True)self.labels=[labels_df.loc[filename[:-4]].values[0] for filename in self.filenames]self.transform=transformdef __len__(self):return len(self.full_filenames)def __getitem__(self,idx):image=Image.open(self.full_filenames[idx])image=self.transform(image)return image, self.labels[idx]
data_dir="./data/"
data_transformer=transforms.Compose([transforms.ToTensor()])
hist_ds=histoCancerDataset(data_dir, data_transformer, data_type="train")test_idex=np.random.randint(hist_ds.__len__(),size=100)
test_ds=Subset(hist_ds,test_index)
test_dl=DataLoader(test_ds,batch_size=1,shuffle=False)

mymodel.py文件代码书写

import torch.nn as nn
import numpy as np
import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transformsdef findConv2dOutShape(H_in,w_in,conv,pool=2):kernel_size=conv.kernel_sizestride=conv.stridepadding=conv.paddingdilation=conv.dilationH_out=np.floor((H_in+2*padding[0]-dilation[0]*(kernel_size[0]-1)-1)/stride[0]+1)W_out=np.floor((W_in+2*padding[1]-dilation[1]*(kernel_size[1]-1)-1)/stride[1]+1)if pool:H_out/=poolW_in/=poolreturn int(H_out), int(W_out)class Net(nn.Module):def __init__(self, params):super(Net,self).__init__()C_in,H_in,W_in=params["input_shape"]init_f=params["initial_filters"]num_fc1=params["num_fc1"]num_classes=params["num_classes"]self.dropout_rate=params["dropout_rate"]self.conv1=nn.Conv2d(C_in,init_f,kernel_size=3)h,w=findConv2dOutShape(H_in,W_in,self.conv1)self.conv2=nn.Conv2d(init_f,init_f*2,kernel_size=3)h,w=findConv2dOutShape(h,w,self.conv2)self.conv3=nn.Conv2d(init_f*2,init_f*4,kernel_size=3)h,w=findConv2dOutShape(h,w,self.conv3)self.conv4=nn.Conv2d(init_f*4,init_f*8,kernel_size=3)h,w=findConv2dOutShape(h,w,self.conv4)self.num_flatten=h*w*8*init_fself.fc1=nn.Linear(self.num_flatten, num_fc1)self.fc2=nn.Linear(self.num_fc1,num_classes)def forward(self,x):x = F.relu(self.conv1(x))x = F.max_pool2d(x,2,2)x = F.relu(self.conv2(x))x = F.max_pool2d(x,2,2)x = F.relu(self.conv3(x))x = F.max_pool2d(x,2,2)x = F.relu(self.conv4(x))x = F.max_pool2d(x,2,2)x = x.view(-1, self.num_flatten)x = F.relu(self.fc1(x))x = F.dropout(x,self.dropout_rate,training=self.training)x=self.fc2(x)return F.log_softmax(x,dim=1)
params_model={"input_shape":(3,96,96),"initial_filters":8,"num_fc1":100,"dropout_rate":0.25,"num_classes":2,
}
model = Net(params_model)
path2weights="./cnn_weights.pt"
model.load_state_dict(torch.load(path2weights,map_location="cpu")
model.eval()

导入数据集

#1. 导入mydataset
import mydataset#2. 定义数据加载器对象并且获得一小批数据
test_dl=mydataset.test_dl
for xb,yb in test_dl:print(xb.shape,yb.shape)break
# torch.Size([1,3,96,96]) torch.Size([1])

导入预训练权重

我们将加载预先训练的模型,冻结其参数,并在测试数据集上验证其性能:

#1. 导入预训练权重
import mymodel
#2. 定义预训练模型的对象
model=mymodel.model
#3. 将模型移到GPU设备上
import torch
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model=model.to(device)
#4. 定义冻结模型参数的辅助函数
def freeze_model(model):for child in model.children():for param in child.parameters():param.requires_grad=Falseprint("model frozen")return model
#5. 调用辅助函数
model=freeze_model(model)
#6. 在测试样本上部署预训练模型
from sklearn.metrics import accuracy_score
def deploy_model(model, test_dl):y_pred=[]y_gt=[]with torch.no_grad():for x, y in test_dl:y_gt.append(y.item())out=model(x.to(device)).cpu().numpy()out=np.argmax(out,axis=1)[0]y_pred.append(out)return y_pred,y_gt
y_pred,y_gt=deploy_model(model,test_dl)
#7. 在测试数据上验证模型的性能
from sklearn.metrics import accuracy_score
acc=accuracy_score(y_pred,y_gt)
print("accuracy:%.2f"%acc)
# accuracy:0.94

实现攻击相关代码

#1. 导入需要的包
from torchvision.transforms.functional import to_pil_image
import torch.nn.functional as F
import matplotlib.pyplot as plt#2. perturb_input辅助函数定义如下
def perturb_input(xb,yb,model,alfa):xb=xb.to(device)xb.requires_grad=True  # 输入数据可导out=model(xb).cpu()loss=F.nll_loss(out,yb)# 模型结构用F.log_softmax激活函数,此处用F.nll_loss即可model.zero_grad()loss.backward()xb_grad=xb.grad.data  # 输入数据的梯度值xb_p=xb+alfa*xb_grad.sign() # xb_grad.sign()表示输入数据的梯度值的正负号,梯度上升xb_p=torch.clamp(xb_p,0,1)  # 输入数据数值约束在[0,1]return xb_p, out.detach()
#3. 从数据加载器中获取批数据并调用perturb_input函数
y_pred=[]
y_pred_p=[]
for xb,yb in test_dl:  # batch_size为1xb_p,out=perturb_input(xb,yb,model,alfa=0.005)#4. 计算扰动前后的预测概率with torch.no_grad():pred=out.argmax(dim=1,keepdim=False).item()y_pred.append(pred)prob=torch.exp(out[:,1])[0].item()# 模型结构用F.log_softmax激活函数,用torch.exp得到概率out_p=model(xb_p).cpu()pred_p=out_p.argmax(dim=1,keepdim=False).item()y_pred_p.append(pred_p)prob_p=torch.exp(out_p[:,1])[0].item()#5. 显示原图和扰动后的图plt.subplot(1,2,1)plt.imshow(to_pil_image(xb[0].detach().cpu()))plt.title(prob)plt.subplot(1,2,2)plt.imshow(to_pil_image(xb_p[0].detach().cpu()))plt.title(prob_p)plt.show()#6. 让我们计算扰动数据的模型精度:
acc=accuracy_score(y_pred_p, y_gt)
print("accuracy:%.2f"%acc)
# accuracy:0.31

上面的代码片段将显示扰动前后的测试图像。如以下截图显示:


代码解析:
在加载数据集小节中,我们使用二值图像分类中开发的脚本加载数据集。在步骤1中,我们导入了mydataset.py文件。此文件包含自定义数据集类。在步骤2中,我们定义了一个数据加载器的对象,并提取了一个小批处理。批大小设为1,因此,形状张量(1,3、96、96)。

在加载预训练模型小节中,我们加载了二值图像分类中开发的预训练模型。在第1步中,我们将mymodel.py文件作为包导入。这个文件包含模型类定义。在步骤2中,我们创建了模型类的一个对象。在步骤3中,我们将模型移动到CUDA设备(如果有的话)。在第4步中,我们定义了一个辅助函数来冻结模型参数。这是为了避免对模型参数的任何更新。记住,在对抗性攻击中,我们试图更新输入以更改模型预测。

辅助函数输入:model。在函数中,我们遍历了模型参数,并将requires_grad属性设置为False。在第5步中,我们调用了freeze_model辅助函数。在步骤6中,我们将预先训练好的模型部署到测试数据集上。这一步是为了验证预先训练的模型。在步骤7中,我们在测试数据集上验证了预训练模型的性能。如所见,当前在原始测试数据集上的精度很高。这验证了预先训练的模型是正确加载的。我们将看到在引入对抗性攻击时该性能会受到怎样的影响。

在实现攻击小节中,我们开发了FSG攻击。在步骤1中,我们导入了所需的包。

在步骤2中,我们定义了perturb_input辅助函数。函数输入如下:

  • xb:要被扰动的输入,形状为[1,3,height,width]的PyTorch张量
  • yb:目标标签,形状为[1]的张量
  • model:预训练模型
  • alfa:扰动参数,设置为0.005

在辅助函数中,我们首先将输入张量的requires_grad属性设置为True。这样做是为了能够计算出损失相对于输入的梯度。接下来,我们将输入传递给预先训练的模型,并获得它的输出。

接下来,我们通过比较模型输出和目标标签来计算损失值。然后,我们计算了相对于输入张量的损失梯度。最后,我们通过添加部分带符号的梯度来干扰输入。alfa系数定义了添加到输入中的扰动量。更高的alfa值显然会导致更大的扰动。我们把alfa设为0.005,使它足够小,可以改变预测,而我们的眼睛仍然看不见变化。

在步骤3中,我们启动了一个循环来遍历测试数据集。然后我们从数据加载器获得一个小批处理,并将它传递给perturb_input辅助函数。

第4步,对模型进行扰动输入,得到模型预测结果。注意,我们在这一步使用torch.no_grad()方法来停止跟踪梯度。在第5步中,我们显示了原始输入和扰动输入以及预测的概率。正如所见,扰动量不明显的输入,预测却几乎翻转。在步骤6中,我们计算了扰动后数据的模型精度。可以看出,在数据扰动的情况下,模型的精度明显下降。

我们用一个小系数来干扰输入。尝试改变alfa系数,看看它对输入图像和模型预测的影响。您还可以尝试使用FGS攻击来欺骗其他模型。

Pytorch基础知识(13)对抗样本相关推荐

  1. 第02章 PyTorch基础知识

    文章目录 第02章 Pytorch基础知识 2.1 张量 2.2 自动求导 2.3 并行计算简介 2.3.1 为什么要做并行计算 2.3.2 CUDA是个啥 2.3.3 做并行的方法 补充:通过股票数 ...

  2. 深入浅出Pytorch:02 PyTorch基础知识

    深入浅出Pytorch 02 PyTorch基础知识 内容属性:深度学习(实践)专题 航路开辟者:李嘉骐.牛志康.刘洋.陈安东 领航员:叶志雄 航海士:李嘉骐.牛志康.刘洋.陈安东 开源内容:http ...

  3. pytorch基础知识+构建LeNet对Cifar10进行训练+PyTorch-OpCounter统计模型大小和参数量+模型存储与调用

    整个环境的配置请参考我另一篇博客.ubuntu安装python3.5+pycharm+anaconda+opencv+docker+nvidia-docker+tensorflow+pytorch+C ...

  4. Pytorch基础知识(15)基于PyTorch的多标签图像分类

    早在 2012 年,神经网络就首次赢得了 ImageNet 大规模视觉识别挑战.Alex Krizhevsky,Ilya Sutskever 和 Geoffrey Hinton 彻底改变了图像分类领域 ...

  5. Pytorch基础知识(9)单目标分割

    目标分割是在图像中寻找目标物体边界的过程.目标分割有很多应用.例如,通过勾勒医学图像中的解剖对象,临床专家可以了解有关患者病情的有用信息. 根据图像中目标的数量,我们可以进行单目标或多目标分割任务.本 ...

  6. 深度学习FPGA实现基础知识13(向专家致敬--深度学习-LeCun、Bengio和Hinton的联合综述)

    需求说明:深度学习FPGA实现知识储备 来自:http://www.csdn.net/article/2015-06-01/2824811 [编者按]三大牛Yann LeCun.Yoshua Beng ...

  7. Pytorch基础知识整理(六)参数初始化

    参数初始化的目的是限定网络权重参数的初始分布,试图让权重参数更接近参数空间的最优解,从而加速训练.pytorch中网络默认初始化参数为随机均匀分布,设定额外的参数初始化并非总能加速训练. 1,模板 在 ...

  8. pytorch基础知识整理(五) 优化器

    深度学习网络必须通过优化器进行训练.在pytorch中相关代码位于torch.optim模块中. 1, 常规用法 optimizer = torch.optim.Adam(model.paramete ...

  9. pytorch基础知识整理(一)自动求导机制

    torch.autograd torch.autograd是pytorch最重要的组件,主要包括Variable类和Function类,Variable用来封装Tensor,是计算图上的节点,Func ...

  10. pytorch基础知识整理(二)数据加载

    pytorch数据加载组件位于torch.utils.data中. from torch.utils.data import DataLoader, Dataset, Sampler 1, torch ...

最新文章

  1. USEARCH11发布,新功能简介
  2. EEGLAB处理脑电视频教程 part1-3
  3. 迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)...
  4. 一张图,一棵大树,六种人, 六载人生 ~
  5. 重装了java然后说找不到路径,Java第三次作业第五题
  6. php sprintf 后面补0,PHP数字前补0的自带函数sprintf 和number_format的用法(详解)
  7. mac系统 PDO连接数据库报错处理
  8. amap vueamap 与_vue 使用高德地图vue-amap组件过程解析
  9. 从“信件装错”问题向递推问题的延伸
  10. [FFmpeg] 在 ffplay 源码中嵌入 YOLO 算法实现实时物体检测
  11. 都是自私惹的祸? 论蹭网再触道德底线
  12. python程序设计简明教程课后答案_Python简明教程最后的练习题
  13. vue引入图片url变量
  14. 方差、标准差、协方差
  15. win10 cmd中文显示乱码
  16. FPGA零基础学习:数字电路中的组合逻辑
  17. 柯西分布——正态分布的兄弟
  18. 第二证券|鲍威尔发声:释放重磅信号,美股大涨!中概股狂涨
  19. 解决pip3 install waring ‘The script xxx is installed in ‘/home/xxx/bin‘ which is not on PATH‘
  20. 【阴阳师】真蛇10层记录

热门文章

  1. php数据库输出文字怎么竖着的,关于文字竖排的详细介绍
  2. 宣传单印刷价格明细报价的影响因素有哪些?
  3. oracle rman list,Oracle RMAN的show,list,crosscheck,delete
  4. 《黑客帝国》说的是什么?
  5. bin和obj文件夹
  6. Android开机速度优化
  7. [杀形态验证]3D准确率达98
  8. 为什么小企业更应该关注人工智能?
  9. 嵌入式系统工程师的职位要求
  10. 霏霏暮雨 eclipse+HBASE开发环境搭建(已实践