5.5.2 使用PyTorch实现DeepFool
5.5.2 使用pytorch实现deepfool
下面介绍在pytorch平台实现deepfool算法的基本过程,示例代码为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/
5-deepfool-pytorch.ipynb
在示例中主要流程为,用原始图像的值初始化对抗样本,通过损失函数计算梯度,然后根据deepfool算法迭代更新对抗样本,直到满足最大迭代次数或者对抗样本预测值达到预期为止,如图5-15所示。
在本例中将使用pytorch的一些特有用法,所以先简单介绍一下使用到的一些pytorch知识。pytorch中可以直接使用tensor调用backward方法来计算梯度,梯度值保存在对应的tensor的grad字段中。假设y是x的一个计算图,即y是x的函数,当调用y.backward(x)时,x的梯度值为:
假设a和b都是浮点类型的tensor,b=2,a为b的平方,?a/?b=2b,输出结果为b乘以2b等于8。
b=variable(torch.floattensor([2]), requires_grad=true)
a=b*b
a.backward(b)
print(b.grad.data)
图5-15 pytorch实现deepfool(无定向攻击)示例
当直接调用y.backward()时,x的梯度值为:
继续上例,输出结果为4。
b=variable(torch.floattensor([2]), requires_grad=true)
a=b*b
a.backward()
print(b.grad.data)
backward函数还可以指定参数retain_graph,因为反向传播需要缓存一些中间结果,反向传播之后,这些缓存就被清空,可通过指定这个参数不清空缓存,实现多次反向传播。
下面我们介绍一下pytorch实现deepfool的核心代码,首先设置仅输入数据可以计算梯度,模型的其他参数不计算梯度。
#图像数据梯度可以获取
img.requires_grad = true
#设置为不保存梯度值自然也无法修改
for param in model.parameters():
param.requires_grad = false
定义一些全局参数,包括最大迭代次数、增益系数和类别数。
#最大迭代次数
epochs=100
#用作终止条件以防止类别更新
overshoot=0.02
#类别数
num_classes=1000
定义前向计算过程,初始化算法中需要迭代计算的参数w和累计扰动量r_tot。
# 前向计算过程
output = model(img)
input_shape = img.cpu().detach().numpy().shape
w = np.zeros(input_shape)
r_tot = np.zeros(input_shape)
下面开始迭代计算过程,迭代退出条件是达到最大迭代次数或者分类标签发生变化,无定向攻击成功。初始化最小移动距离pert为无穷大,计算当前输入下的梯度值。
for epoch in range(epochs):
scores=model(img).data.cpu().numpy()[0]
label=np.argmax(scores)
print("epoch={} label={} score={}".format(epoch,label,scores[label]))
#如果无定向攻击成功
if label != orig_label:
break
pert = np.inf
output[0, orig_label].backward(retain_graph=true)
grad_orig = img.grad.data.cpu().numpy().copy()
遍历各个分类,计算对应的梯度值,选择其中除了当前分类外,跨越分类边界距离最短的分类。
for k in range(1, num_classes):
if k == orig_label:
continue
#梯度清零
zero_gradients(img)
output[0, k].backward(retain_graph=true)
cur_grad = img.grad.data.cpu().numpy().copy()
# 设置w_k和f_k
w_k = cur_grad - grad_orig
f_k = (output[0, k] - output[0, orig_label]).data.cpu().numpy()
pert_k = abs(f_k)/np.linalg.norm(w_k.flatten())
# 选择pert最小值
if pert_k < pert:
pert = pert_k
w = w_k
计算累计扰动量并更新对抗样本,为了避免pert为0,导致r_i为0,会加上一个非常小的数,比如1e-8。np.linalg.norm默认计算向量的欧氏大小,即l2范数。
# 计算 r_i 和 r_tot
r_i = (pert+1e-8) * w / np.linalg.norm(w)
r_tot = np.float32(r_tot + r_i)
img.data=img.data + (1+overshoot)*torch.from_numpy(r_tot).to(device)
经过3轮迭代,无定向攻击成功,攻击效果如图5-16所示。
epoch=0 label=88 score=18.340900421142578
epoch=1 label=88 score=13.300100326538086
epoch=2 label=879 score=12.384634017944336
图5-16 原始数据和对抗样本的对比示意图(无定向攻击)
虽然一般认为deepfool是一种无定向攻击算法,但是可以通过少量修改算法支持定向攻击,示例代码为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/
5-deepfool-pytorch-t.ipynb
核心代码内容如下,首先定义最大迭代次数、增益系数以及定向攻击目标,由于是定向攻击,损失函数使用交叉熵便于查看攻击过程是否收敛。需要指出的是,这里的损失函数并不是用于优化器优化使用的,而是为了衡量目前的对抗样本识别情况与定向攻击的目标之间的差距。
epochs=100
overshoot=0.02
#攻击目标
target_label=288
target=variable(torch.tensor([float(target_label)]).to(device).long())
loss_func = torch.nn.crossentropyloss()
进行迭代计算,迭代退出条件是达到最大迭代次数或者分类标签等于定向攻击标签,定向攻击成功。
for epoch in range(epochs):
# 前向计算过程
output = model(img)
label=np.argmax(output.data.cpu().numpy())
loss = loss_func(output, target)
print("epoch={} label={} loss={}".format(epoch,label,loss))
#如果定向攻击成功
if label == target_label:
break
根据梯度计算定向攻击的最小扰动。
#梯度清零
zero_gradients(img)
output[0, target_label].backward(retain_graph=true)
w = img.grad.data.cpu().numpy().copy()
f = output[0, target_label].data.cpu().numpy()
pert = abs(f)/np.linalg.norm(w.flatten())
# 计算 r_i 和 r_tot
r_i = (pert+1e-8) * w / np.linalg.norm(w)
r_tot = np.float32(r_tot + r_i)
img.data=img.data + (1+overshoot)*torch.from_numpy(r_tot).to(device)
经过8轮迭代,定向攻击成功,攻击效果如图5-17所示。
epoch=4 label=88 loss=12.552578926086426
epoch=5 label=88 loss=10.39006233215332
epoch=6 label=88 loss=8.43277359008789
epoch=7 label=88 loss=6.093036651611328
epoch=8 label=288 loss=0.3813018798828125
图5-17 原始数据和对抗样本的对比示意图(定向攻击)
下面介绍在pytorch平台实现deepfool算法的基本过程,示例代码为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/
5-deepfool-pytorch.ipynb
在示例中主要流程为,用原始图像的值初始化对抗样本,通过损失函数计算梯度,然后根据deepfool算法迭代更新对抗样本,直到满足最大迭代次数或者对抗样本预测值达到预期为止,如图5-15所示。
在本例中将使用pytorch的一些特有用法,所以先简单介绍一下使用到的一些pytorch知识。pytorch中可以直接使用tensor调用backward方法来计算梯度,梯度值保存在对应的tensor的grad字段中。假设y是x的一个计算图,即y是x的函数,当调用y.backward(x)时,x的梯度值为:
假设a和b都是浮点类型的tensor,b=2,a为b的平方,?a/?b=2b,输出结果为b乘以2b等于8。
b=variable(torch.floattensor([2]), requires_grad=true)
a=b*b
a.backward(b)
print(b.grad.data)
图5-15 pytorch实现deepfool(无定向攻击)示例
当直接调用y.backward()时,x的梯度值为:
继续上例,输出结果为4。
b=variable(torch.floattensor([2]), requires_grad=true)
a=b*b
a.backward()
print(b.grad.data)
backward函数还可以指定参数retain_graph,因为反向传播需要缓存一些中间结果,反向传播之后,这些缓存就被清空,可通过指定这个参数不清空缓存,实现多次反向传播。
下面我们介绍一下pytorch实现deepfool的核心代码,首先设置仅输入数据可以计算梯度,模型的其他参数不计算梯度。
#图像数据梯度可以获取
img.requires_grad = true
#设置为不保存梯度值自然也无法修改
for param in model.parameters():
param.requires_grad = false
定义一些全局参数,包括最大迭代次数、增益系数和类别数。
#最大迭代次数
epochs=100
#用作终止条件以防止类别更新
overshoot=0.02
#类别数
num_classes=1000
定义前向计算过程,初始化算法中需要迭代计算的参数w和累计扰动量r_tot。
# 前向计算过程
output = model(img)
input_shape = img.cpu().detach().numpy().shape
w = np.zeros(input_shape)
r_tot = np.zeros(input_shape)
下面开始迭代计算过程,迭代退出条件是达到最大迭代次数或者分类标签发生变化,无定向攻击成功。初始化最小移动距离pert为无穷大,计算当前输入下的梯度值。
for epoch in range(epochs):
scores=model(img).data.cpu().numpy()[0]
label=np.argmax(scores)
print("epoch={} label={} score={}".format(epoch,label,scores[label]))
#如果无定向攻击成功
if label != orig_label:
break
pert = np.inf
output[0, orig_label].backward(retain_graph=true)
grad_orig = img.grad.data.cpu().numpy().copy()
遍历各个分类,计算对应的梯度值,选择其中除了当前分类外,跨越分类边界距离最短的分类。
for k in range(1, num_classes):
if k == orig_label:
continue
#梯度清零
zero_gradients(img)
output[0, k].backward(retain_graph=true)
cur_grad = img.grad.data.cpu().numpy().copy()
# 设置w_k和f_k
w_k = cur_grad - grad_orig
f_k = (output[0, k] - output[0, orig_label]).data.cpu().numpy()
pert_k = abs(f_k)/np.linalg.norm(w_k.flatten())
# 选择pert最小值
if pert_k < pert:
pert = pert_k
w = w_k
计算累计扰动量并更新对抗样本,为了避免pert为0,导致r_i为0,会加上一个非常小的数,比如1e-8。np.linalg.norm默认计算向量的欧氏大小,即l2范数。
# 计算 r_i 和 r_tot
r_i = (pert+1e-8) * w / np.linalg.norm(w)
r_tot = np.float32(r_tot + r_i)
img.data=img.data + (1+overshoot)*torch.from_numpy(r_tot).to(device)
经过3轮迭代,无定向攻击成功,攻击效果如图5-16所示。
epoch=0 label=88 score=18.340900421142578
epoch=1 label=88 score=13.300100326538086
epoch=2 label=879 score=12.384634017944336
图5-16 原始数据和对抗样本的对比示意图(无定向攻击)
虽然一般认为deepfool是一种无定向攻击算法,但是可以通过少量修改算法支持定向攻击,示例代码为:
https://github.com/duoergun0729/adversarial_examples/blob/master/code/
5-deepfool-pytorch-t.ipynb
核心代码内容如下,首先定义最大迭代次数、增益系数以及定向攻击目标,由于是定向攻击,损失函数使用交叉熵便于查看攻击过程是否收敛。需要指出的是,这里的损失函数并不是用于优化器优化使用的,而是为了衡量目前的对抗样本识别情况与定向攻击的目标之间的差距。
epochs=100
overshoot=0.02
#攻击目标
target_label=288
target=variable(torch.tensor([float(target_label)]).to(device).long())
loss_func = torch.nn.crossentropyloss()
进行迭代计算,迭代退出条件是达到最大迭代次数或者分类标签等于定向攻击标签,定向攻击成功。
for epoch in range(epochs):
# 前向计算过程
output = model(img)
label=np.argmax(output.data.cpu().numpy())
loss = loss_func(output, target)
print("epoch={} label={} loss={}".format(epoch,label,loss))
#如果定向攻击成功
if label == target_label:
break
根据梯度计算定向攻击的最小扰动。
#梯度清零
zero_gradients(img)
output[0, target_label].backward(retain_graph=true)
w = img.grad.data.cpu().numpy().copy()
f = output[0, target_label].data.cpu().numpy()
pert = abs(f)/np.linalg.norm(w.flatten())
# 计算 r_i 和 r_tot
r_i = (pert+1e-8) * w / np.linalg.norm(w)
r_tot = np.float32(r_tot + r_i)
img.data=img.data + (1+overshoot)*torch.from_numpy(r_tot).to(device)
经过8轮迭代,定向攻击成功,攻击效果如图5-17所示。
epoch=4 label=88 loss=12.552578926086426
epoch=5 label=88 loss=10.39006233215332
epoch=6 label=88 loss=8.43277359008789
epoch=7 label=88 loss=6.093036651611328
epoch=8 label=288 loss=0.3813018798828125
图5-17 原始数据和对抗样本的对比示意图(定向攻击)