1 参考资料
Introduction | Foolbox (jonasrauber.de) 官方的指导书
https://github.com/bethgelab/foolbox 代码
本文完结撒花✿✿ヽ(°▽°)ノ✿
bushi不过其实有上面两个就可以靠自己查查找找来做实验了,所以下面是本人残存记忆中还有的部分注意点。
2 部分代码解析
2.1 dataset
从本地读取图片
# 定义图像预处理操作
transform = transforms.Compose(
[transforms.ToTensor()])
batch_size = 100 # 就是图片的数量
# 本地
data_path = r'./data' # 得查一查datasets.ImageFolder对于本地文件夹结构的要求,大概是要求data/类别名/.png
testset = datasets.ImageFolder(data_path, transform=transform)
testloader = torch.utils.data.DataLoader(testset, shuffle=True, batch_size=batch_size,
num_workers=0) # 暂时没做归一化,后面做
with torch.no_grad():
for data in testloader:
images, labels = data[0], data[1]
2.2 model
model = resnet.cifar10_resnet20()
state_dict_path = f'./model_resnet20.pth'
model.load_state_dict(torch.load(state_dict_path))
model.eval().to(device)
preprocessing = dict(mean=[0.4914, 0.4822, 0.4465], std=[0.2023, 0.1994, 0.2010], axis=-3) # 敲黑板,归一化了哈,看清参数
fmodel = fb.PyTorchModel(model, bounds=(0, 1), preprocessing=preprocessing)
fmodel = fmodel.transform_bounds((0, 1)) # 转换边界
上面读入图片的时候我没做归一化,但是这里是做了的,参数如上图,我真的永远会为归一化参数而流泪,很想知道大佬都是怎么知道是归一化参数的问题的,是怎么找到正确的参数的(参考如何使CIFAR-10测试集的分类准确率从40%提升到90% - 知乎 (zhihu.com))
就是因为这个参数导致我的运行结果对标签的判定一直有点问题,找了好久好久呜呜呜
2.3 遍历对每一张图片进行攻击
(1)因为本人需要从dataset里面单拎image出来处理,所以我的数据维度下降了1D,就需要用到unsqueeze来升维
(2)注意model在gpu上,要把iamge和label也放到gpu上
(3)本人记录的是使得攻击后模型分类失败的那个eplison。注意:少数图片不加干扰就分类失败,可以用手动检查的方式,不把分类结果和真是标签对比,以其分类结果第一次改变时的eplison为准
for image, label in zip(images, labels):
image = torch.unsqueeze(image, dim=0) # 将image从3D -> 4D
label = label.unsqueeze(0) # 使用unsqueeze方法增加一个维度
epsilon = 0
# eplison == 0
raw, clipped, is_adv = attack(model, image.to(device), label.to(device), epsilons=0)
predict0 = model(clipped)
_, predicted0 = torch.max(predict0.data, 1)
flag = is_adv
for i in range(1, 4001, 1):
eps = i * 0.0001
raw, clipped, is_adv = attack(model, image.to(device), label.to(device), epsilons=eps)
# print(f"eps:{eps}\t\tis_adv:{is_adv}")
# # 手动检查:将攻击后的剪裁输入到模型内分类,再对比分类结果与攻击前分类结果
# predict = model(clipped)
# _, predicted = torch.max(predict.data, 1)
#
# if predicted != predicted0:
# epsilon += eps
# print(f"the picture's robust accuracy{j + 1} is {eps}")
# break
# 用is_adv参数
if is_adv != flag:
epsilon += eps
print(f"the picture's robust accuracy is {eps}")
return
else:
flag = is_adv
if eps == 0.4:
print(f"the picture's robust accuracy is out of range\n\n\n")
return
2.4 hook
hook是一种可以获取中间层输出的方法,通过给model的前向传播中注入一个钩子,让每次模型输入处理时都可以将目标层的输出/特征值等信息返回。
class HookTool:
def __init__(self):
self.fea = None
def hook_fun(self, module, fea_in, fea_out):
'''
注意用于处理feature的hook函数必须包含三个参数[module, fea_in, fea_out],参数的名字可以自己起,但其意义是
固定的,第一个参数表示torch里的一个子module,比如Linear,Conv2d等,第二个参数是该module的输入,其类型是
tuple;第三个参数是该module的输出,其类型是tensor。注意输入和输出的类型是不一样的,切记。
'''
self.fea = fea_out
def get_feas_by_hook(model):
"""
需要找到目标层的名字
"""
fea_hooks = []
for name, module in model.named_children():
if name == '目标层的名字':
cur_hook = HookTool()
module.register_forward_hook(cur_hook.hook_fun)
fea_hooks.append(cur_hook)
return fea_hooks
hook = get_feas_by_hook(model)
output = model(image0)
target = hook[0].fea
这里目标层的名字或许可以通过看model.forward来解决。例如我的forward长这样
其中''layer1' | 'avgpool' | 'fc' 等都是可以的目标层。
3 其他
似乎还记得出现过的问题:
(1)attack的时候报错说数据超出上限还是下限来着,然后实际是因为某个数据没有转化边界??总之是下面的两句之一吧
fmodel = fb.PyTorchModel(model, bounds=(0, 1), preprocessing=preprocessing)
fmodel = fmodel.transform_bounds((0, 1)) # 转换边界