亲,双击屏幕即可自动滚动
5.2.1 使用PyTorch生成对抗样本
    5.2.1 使用pytorch生成对抗样本
    下面介绍在pytorch平台生成对抗样本的基本过程,示例代码为:
    https://github.com/duoergun0729/adversarial_examples/blob/master/code/5-case2-pytorch.ipynb
    在示例中,通过损失函数在反向传递过程中直接调整原始图像的值,直到满足最大迭代次数或者对抗样本预测值达到预期为止,如图5-6所示。
    图5-6 使用pytorch生成对抗样本的流程
    首先加载使用的库文件,其中被攻击的模型在torchvision中。
    import torch
    import torchvision
    from torchvision import datasets, transforms
    from torch.autograd import variable
    import torch.nn as nn
    from torchvision import models
    import numpy as np
    import cv2
    定义使用的计算设备,默认使用的是cpu资源,如果有可用的gpu资源也将会自动使用。
    #获取计算设备,默认是cpu
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    加载经典的熊猫图片并进行标准化处理。opencv默认加载图片的方式是bgr格式而不是rgb格式,需要手工进行转换。攻击的对象是alexnet,对应的预训练模型是基于imagenet 2012数据集进行训练的,因此对数据进行标准化时,不能简单地进行归一化处理,而是需要使用特定的均值mean和标准差std。另外需要强调的是,opencv默认保存图片时使用的是whc格式,即第三个维度是通道数据,比如本例中为[224,224,3],但是pytorch是cwh格式,因此需要转换成[3,224,224],这一过程是调用img.transpose完成的。
    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
    #whc转换成cwh
    img = img.transpose(2, 0, 1)
    #[3,224,224]-> [1,3,224,224]
    img=np.expand_dims(img, axis=0)
    img = variable(torch.from_numpy(img).to(device).float())
    实例化alexnet模型,并设置为预测模式。预测模式和训练模式的主要区别是,dropout和bn层的行为不一样,比如预测模式时,dropout会让全部数据通过,训练模式时会按照设定值随机丢失一部分数据。对原始数据进行预测,预测的标签值为388,表示为熊猫。
    #使用预测模式主要影响dropout和bn层的行为
    model = models.alexnet(pretrained=true).to(device).eval()
    label=np.argmax(model(img).data.cpu().numpy())
    print("label={}".format(label))
    label=388
    设置仅输入数据可以计算梯度并被反向传递调整,模型的其他参数锁定不修改。在pytorch中通过设置requires_grad值即可完成上述设置。优化器torch.optim.adam只可调整输入数据img。
    #图像数据梯度可以获取
    img.requires_grad = true
    #设置为不保存梯度值,自然也无法修改
    for param in model.parameters():
    param.requires_grad = false
    optimizer = torch.optim.adam([img])
    设置损失函数,在本例中要进行定向攻击,可以使用交叉熵torch.nn.crossentropyloss。
    target=288
    target=variable(torch.tensor([float(target)]).to(device).long())
    loss_func = torch.nn.crossentropyloss()
    下面进行迭代计算,优化的目标是让损失函数(或称目标函数)最小化。迭代过程中优化器不断调整输入数据img,这一过程需要手工调用loss.backward进行反向传递,通过optimizer.step调整img。整个迭代过程的退出条件是达到最大迭代次数或者img预测值达到预期。
    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
    loss.backward()
    optimizer.step()
    经过26轮迭代后,预测的label从388变成了288,即豹子。imagenet 2012可以识别1000种物品和动物,具体的对应列表可以参考https://blog.csdn.net/u010165147/article/details/72848497,需要指出的是label(标签)是从0开始编号,该文献是从1开始编号,因此label为288的物体,在文献中需要查找的id为289。
    epoch=22 loss=3.264517307281494 label=388
    epoch=23 loss=2.973933696746826 label=293
    epoch=24 loss=2.72090482711792 label=290
    epoch=25 loss=2.472418785095215 label=290
    epoch=26 loss=2.244988441467285 label=288
    最后我们将对抗样本数据还原成图像数据,并和原始数据进行对比。如图5-7所示,一共分为三个子图,分别表示原始图像、对抗样本和扰动量。
    adv=img.data.cpu().numpy()[0]
    #[3,224,244]->[224,224,3]
    adv = adv.transpose(1, 2, 0)
    #还原成正常图片像素范围并取整
    adv = (adv * std) + mean
    adv = adv * 255.0
    adv = np.clip(adv, 0, 255).astype(np.uint8)
    show_images_diff(orig,388,adv,target.data.cpu().numpy()[0])
    图5-7 原始数据和对抗样本的对比示意图