【对抗样本】利用U-Net进行人脸图像局部对抗实验

1、 实验题目
训练一个 G 网络,对一个简单的人脸分类器进行目标对抗(局部对抗)。注意:人脸分类器不参与训练,只提供分类结果。

2、 实验内容
(1) 实验数据集:ORL 数据集,40 个类别,每个类别包括同一个人的 10 张图像,共 400
张数据集,训练集:测试集=9:1。

(2)CNN 人脸分类器网络结构:
在这里插入图片描述

(3)生成器 G 网络分别采用了简单的U-net
在这里插入图片描述3、 实验结果
为了保证生成的对抗样本的图像质量和被对抗类别的识别率,生成网络使用两个损失函数,
一个用来衡量真实样本和相应的对抗样本之间的距离,另一个用来衡量人脸分类误差。
损失函数如下:
在这里插入图片描述y为分类网络的预测标签,yt为目标标签,CE为交叉熵损失函数,MSE为均方误差损失函数,即L2损失,xi和xi‘分别为输入和生成图像

(1)测试不同a的取值对实验结果的影响(攻击区域为左下四分之一区域)

  • a = 0.1

在这里插入图片描述在这里插入图片描述

对抗成功率:67.5%

  • a = 1
    在这里插入图片描述在这里插入图片描述对抗成功率:30%

  • a = 10
    在这里插入图片描述在这里插入图片描述
    对抗成功率:0%

(2)测试不同对抗区域对实验结果的影响(取a=1)

  • 攻击区域:左上四分之一
    在这里插入图片描述在这里插入图片描述对抗成功率:0%

  • 攻击区域:中心四分之一
    在这里插入图片描述
    在这里插入图片描述对抗成功率:7.5%

(3)测试不同对抗区域大小对实验结果的影响(取a=1,对抗区域为左下/左)

  • 区域大小:左下十六分之一
    在这里插入图片描述在这里插入图片描述对抗成功率:15%

  • 区域大小:左侧二分之一
    在这里插入图片描述对抗成功率:65%

(4)测试分类器的性能对实验结果的影响(取a=1,对抗区域为左下)

  • 分类器训练epoch次数:10(测试准确率95%)
    在这里插入图片描述在这里插入图片描述对抗成功率:0%

  • 分类器训练epoch次数:50(测试准确率100%)
    在这里插入图片描述在这里插入图片描述对抗成功率:42.5%

  • 分类器训练epoch次数:80(测试准确率100%)
    在这里插入图片描述在这里插入图片描述对抗成功率:5%

4、实现代码

  • 待攻击的分类网络CNN模型
class CNN2(nn.Module):
    def __init__(self):
        super(CNN2, self).__init__()

        # 卷积层
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)

        # 池化层
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)

        # 全连接层
        self.fc1 = nn.Linear(32 * 32 * 32, 128)
        self.fc2 = nn.Linear(128, 40)

    def forward(self, x):
        x = x.float()
        # 卷积层和池化层
        x = self.pool(torch.relu(self.conv1(x)))
        x = self.pool(torch.relu(self.conv2(x)))

        # 展平特征图
        x = x.view(-1, 32 * 32 * 32)

        # 全连接层
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)

        return x.double()
  • 生成网络U-Net模型
class UNet(nn.Module):
    def __init__(self):
        super(UNet, self).__init__()

        # 编码器部分
        self.encoder1 = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size=3, padding=1),
            nn.ReLU()
        )
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.encoder2 = nn.Sequential(
            nn.Conv2d(32, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU()
        )
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)

        self.bottleneck = nn.Sequential(
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(128, 128, kernel_size=3, padding=1),
            nn.ReLU()
        )

        # 解码器部分
        self.upconv1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.decoder1 = nn.Sequential(
            nn.Conv2d(128, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, padding=1),
            nn.ReLU()
        )

        self.upconv2 = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2)
        self.decoder2 = nn.Sequential(
            nn.Conv2d(64, 32, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size=3, padding=1),
            # nn.Tanh()
        )

        self.output = nn.Conv2d(32, 1, kernel_size=1)

    def forward(self, x):
        # 编码器部分
        x = x.float()
        enc1 = self.encoder1(x)
        enc1_pool = self.pool1(enc1)

        enc2 = self.encoder2(enc1_pool)
        enc2_pool = self.pool2(enc2)

        bottleneck = self.bottleneck(enc2_pool)

        # 解码器部分
        dec1 = self.upconv1(bottleneck)
        dec1_cat = torch.cat([dec1, enc2], dim=1)
        dec1_out = self.decoder1(dec1_cat)

        dec2 = self.upconv2(dec1_out)
        dec2_cat = torch.cat([dec2, enc1], dim=1)
        dec2_out = self.decoder2(dec2_cat)

        output = self.output(dec2_out)

        return output.double()
  • 训练及测试
# 训练过程
num_epochs = 100
loss_list = []
for epoch in range(num_epochs):
    model_generate.train()
    for i, (images, labels) in enumerate(train_loader):
        images = images.to(device)
        labels = labels.to(device)
        # 裁剪左下四分之一部分作为补丁的原始输入
        cropped_images = images[:,:, images.size(2) // 2:, :images.size(3) // 2]
        # # 裁剪左上四分之一部分作为补丁的原始输入
        # cropped_images = images[:,:, :images.size(2) // 2, :images.size(3) // 2]
        # # 裁剪中心四分之一部分
        # cropped_images = images[:, :, images.size(2) // 4:images.size(2) // 4 * 3, images.size(2) // 4:images.size(2) // 4 * 3]
        # # 裁剪左下十六分之一部分
        # cropped_images = images[:, :, images.size(2) // 4 * 3:, :images.size(3) // 4]
        # # 裁剪左侧二分之一部分
        # cropped_images = images[:, :, :, :images.size(3) // 2]
        outputs = model_generate(cropped_images) #得到输出的补丁
        loss1 = criterion1(outputs, cropped_images) #补丁图像质量L2损失
        adv_images = images.clone()
        adv_images[:,:, images.size(2) // 2:, :images.size(3) // 2] = outputs #拼接而成的对抗图像样本
        # _, predicted = torch.max(model_classify(adv_images).data, 1)
        # print(predicted)
        loss2 = criterion2(model_classify(adv_images), torch.full_like(labels, target_class-1)) #图像分类交叉熵损失
        loss = a * loss1 + loss2
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        print(f"Epoch [{epoch + 1}/{num_epochs}], Batch [{i + 1}/{len(train_loader)}], Loss: {loss:.4f}")
    loss_list.append(loss.item())
print("训练完毕!")
# 保存模型参数
torch.save(model_generate.state_dict(), './model_generate_patch.pth')

# 测试过程
model_generate.load_state_dict(torch.load('model_generate_patch.pth'))
model_generate.eval()
model_generate.to(device)
correct = 0
total = 0
with torch.no_grad():
    for original_image, label in test_loader:
        original_image = original_image.to(device)
        #裁剪左下四分之一部分
        cropped_image = original_image[:,:, original_image.size(2) // 2:, :original_image.size(3) // 2]
        # #裁剪左上四分之一部分
        # cropped_image = original_image[:, :, :original_image.size(2) // 2, :original_image.size(3) // 2]
        # # 裁剪中心四分之一部分
        # cropped_image = original_image[:, :, original_image.size(2) // 4:original_image.size(2) // 4 * 3, original_image.size(2) // 4:original_image.size(2) // 4 * 3]
        # # 裁剪左下十六分之一部分
        # cropped_image = original_image[:,:, original_image.size(2) // 4 * 3:, :original_image.size(3) // 4]
        # # 裁剪左侧二分之一部分
        # cropped_image = original_image[:, :, :, :original_image.size(3) // 2]
        label = label.to(device)
        output = model_generate(cropped_image) # 输出局部补丁图像
        adversarial_image = original_image.clone()
        adversarial_image[:,:, original_image.size(2) // 2:, :original_image.size(3) // 2] = output #生成合成后的对抗图像
        _, predicted = torch.max(model_classify(adversarial_image).data, 1)
        total += label.size(0)
        correct += (predicted == torch.full_like(label, target_class - 1)).sum().item()
        for i in range(len(label)):
            # if (label[i] != predicted[i]):
            print(f"正确标签: {label[i]}, 识别结果为:{predicted[i]}")
            # 对比展现原始图片和对抗样本图片
            # 将对抗性图像和原始图像移动到CPU上
            adversarial_image = adversarial_image.cpu()
            adversarial_image = adversarial_image.squeeze(0)
            original_image = original_image.cpu()
            original_image = original_image.squeeze().numpy()
            adversarial_image = adversarial_image.squeeze().detach().numpy()
            show_images_diff(original_image, label, adversarial_image, predicted)
accuracy = 100 * correct / total
print(f"对抗成功率:: {accuracy:.2f}%")
# plt.plot(loss_list)
# plt.title('Loss Curve')
# plt.xlabel('Epoch')
# plt.ylabel('Loss')
# plt.show()

5、实验总结

  • 相同条件下,对抗区域大小越大,对抗成功率越高
  • 不同对抗区域的对抗成功率呈现出明显差异
  • 相同条件下,分类器拟合程度越高,对抗效果越好,即越容易受到干扰
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值