1 对抗样本的基本原理
从数学角度来描述对抗样本,输入数据为x,分类器为f,对应的分类结果表示为f(x)。假设存在一个非常小的扰动
ϵ
\epsilon
ϵ,使得:
f
(
x
+
ϵ
)
≠
f
(
x
)
f(x+\epsilon)\ne f(x)
f(x+ϵ)=f(x)
即分类结果发生了改变,那么 x + ϵ x+\epsilon x+ϵ就是一个对抗样本。
以一个例子来说明对抗样本的基本原理:
from sklearn import datasets
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
import torch
from torch import nn
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
device='cuda' if torch.cuda.is_available() else 'cpu'
print('the device used is: ',device)
n_features=1000
x,y=datasets.make_classification(n_samples=4000,n_features=n_features,n_classes=2,random_state=0)
x=MinMaxScaler().fit_transform(x)
x=torch.from_numpy(x).to(device)
y=torch.from_numpy(y).to(device)
class TestDatasets(Dataset):
def __init__(self,x,y):
self.x=x
self.y=y
def __len__(self):
return x.shape[0]
def __getitem__(self,idx):
data=self.x[idx]
label=self.y[idx]
return data,label
class TestNetwork(nn.Module):
def __init__(self):
super(TestNetwork,self).__init__()
self.layer1=nn.Linear(1000,2)
def forward(self,x):
x=self.layer1(x)
return x
def train_loop(dataloader,model,loss_fn,optimizer):
size=len(dataloader.dataset)
for batch,(x,y) in enumerate(dataloader):
pred=model(x)
loss=loss_fn(pred,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss=loss.item()
print('bach is: ',batch,' loss is: ',loss)
model=TestNetwork().to(device).to(torch.float64)
logits=model(x)
pred_probab=nn.Softmax(dim=1)(logits)
y_pred=pred_probab.argmax(1)
print(y_pred)
training_data=TestDatasets(x,y)
train_dataloader=DataLoader(training_data,batch_size=32,shuffle=True)
loss_fn=nn.CrossEntropyLoss()
learning_rate=1e-3
batch_size=64
epochs=1000
optimizer=torch.optim.SGD(model.parameters(),lr=learning_rate)
for i in range(epochs):
print('start the ',i,' epochs')
train_loop(train_dataloader,model,loss_fn,optimizer)
for i in range(10):
data,label=training_data[i]
logits=model(data)
pred_probab=nn.Softmax()(logits)
y_pred=pred_probab.argmax()
print('the predicted label is: ',logits,' the truth is: ',label)
通过datasets库可以随机生成样本数据,其中n_samples表示生成的对抗样本数量,n_features为每个样本的特征数,n_classes表示生成的样本对应的标签的种类数,random_state为随机种子值。在上面的例子中我们生成4000个维度(即特征数量)为1000的样本,标签种类只有两种。
4 FGM FGSM算法
4.1 FGM FGSM基本原理
这个算法在《Explaining and Harnessing Adversarial Examples》论文中提出,可以作为无定向攻击和定向攻击算法使用。假设图片原始数据为 x x x,图片识别的结果为 y y y,原始图像上叠加细微的变化 η \eta η,数学公式表示如下:
x ~ = x + η \tilde{x}=x+\eta x~=x+η
将修改后的图像输入分类模型中, x x x与参数矩阵 ω T \omega^T ωT相乘:
ω T x ~ = ω T x + ω T η \omega^T\tilde{x}=\omega^Tx+\omega^T\eta ωTx~=ωTx+ωTη
对分类结果的影响还要受到激活函数的作用,攻击样本的生成过程就是追求以微小的修改,通过激活函数的作用,对分类结果产生最大化的变化。Goodfellow指出,如果我们的变化量与梯度的变化方向完全一致,那么将会对分类结果产生较大的变化(sign函数可以保证与梯度方向一致???)。
η = sign ( ω ) \eta=\text{sign}(\omega) η=sign(ω)
当 x x x的维数为 n n n时,模型的参数在每个维度的平均值为 m m m, η \eta η的无穷范数为 ϵ \epsilon ϵ,每个维度的微小修改与梯度函数方向一致,累计的效果为 n m ϵ nm\epsilon nmϵ。当数据的维度 n n n很大时,虽然 η \eta η非常小,但是累计的效果可以较大,该效果作用到激活函数上,有可能对分类结果产生较大影响。
4.2 使用Pytorch实现FGM
device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
img.requires_grad=True
for param in model.parameters():
param.requires_grad=False
optimizer=torch.optim.Adam([img])
loss_func=torch.nn.CrossEntropyLoss()
epochs=100
e=0.001
target=288
target=Variable(torch.Tensor([float(target)]).to(device).long())
for epoch in range(epochs):
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):
print("success")
break
optimizer.zero_grad()
loss.backward()
img.data=img.data=e*torch.sign(img.grad.data)
5 DeepFool算法
5.1 DeepFool基本原理
DeepFool算法在《deepFool: a simple and accurate method to fool deep neural networks》中提出。它通常作为一种无定向攻击算法,相对于FGM而言,不用指定学习速率 ϵ \epsilon ϵ,算法本身可以计算出相对FGM更小的扰动来达到供给目的。
假设分割平面是一个直线,直线的两侧分别对应不同的分类结果,如果想改变其中某点 x 0 x_0 x0的分类结果,我们一定要跨过分割平面。显然最短的移动距离就是垂直分割平面进行移动,我们将这个距离记作 r ∗ ( x 0 ) r_*(x_0) r∗(x0),分类器为 f f f,那么存在如下关系:
r ∗ ( x 0 ) = a r g m i n ∥ r ∥ 2 s u b j e c t t o s i g n ( f ( x 0 + r ) ) ≠ s i g n ( f ( x 0 ) ) r_*(x_0)=argmin\Vert r\Vert^2\\ subject\ to\quad sign(f(x_0+r))\ne sign(f(x_0)) r∗(x0)=argmin∥r∥2subject tosign(f(x0+r))=sign(f(x0))
二分类的DeepFool算法可以用伪代码描述为:
DeepFool
输入:图像
x
x
x,分类器
f
f
f
初始化
x
0
=
x
,
i
=
0
x_0=x,i=0
x0=x,i=0
w
h
i
l
e
s
i
g
n
(
f
(
x
i
)
)
=
=
s
i
g
n
(
f
(
x
0
)
)
d
o
:
while\ sign(f(x_i))==sign(f(x_0))\ do:
while sign(f(xi))==sign(f(x0)) do:
r
i
−
=
f
(
x
i
)
∥
∇
f
(
x
i
)
∥
2
2
∇
f
(
x
i
)
\quad r_i-=\frac{f(x_i)}{\Vert\nabla f(x_i)\Vert_2^2}\nabla f(x_i)
ri−=∥∇f(xi)∥22f(xi)∇f(xi)
x
r
+
1
=
x
r
+
r
i
\quad x_{r+1}=x_r+r_i
xr+1=xr+ri
i
=
i
+
1
\quad i=i+1
i=i+1
e
n
d
w
h
i
l
e
end\ while
end while
r
e
t
u
r
n
r
^
=
∑
r
i
return\ \hat{r}=\sum r_i
return r^=∑ri
定义分类器 f f f在第 k k k种分类上的概率为 f k ( x ) f_k(x) fk(x),那么分类的最终标签可以表示为其中概率最大的那个分类:
k ^ ( x ) = arg max k f k ( x ) \hat{k}(x)=\argmax_k f_k(x) k^(x)=kargmaxfk(x)
多分类的DeepFool攻击算法可以当成二分类的扩展,伪码描述为:
DeepFool
输入:图像
x
x
x,分类器
f
f
f,分类输出
k
^
(
x
)
\hat{k}(x)
k^(x)
初始化:
x
0
=
x
,
i
=
0
x_0=x,i=0
x0=x,i=0
w
h
i
l
e
k
^
(
x
)
=
=
k
^
(
x
0
)
d
o
while\ \hat{k}(x)==\hat{k}(x_0)\ do
while k^(x)==k^(x0) do
f
o
r
k
≠
k
^
(
x
0
)
d
o
\quad for\ k\ne\hat{k}(x_0)\ do
for k=k^(x0) do
ω
k
′
=
∇
f
k
(
x
i
)
−
∇
f
k
^
(
x
0
)
(
x
i
)
\quad\quad \omega'_k=\nabla f_k(x_i)-\nabla f_{\hat{k}(x_0)}(x_i)
ωk′=∇fk(xi)−∇fk^(x0)(xi)
f
k
′
=
f
k
(
x
i
)
−
f
k
^
(
x
0
)
(
x
i
)
\quad\quad f'_k=f_k(x_i)-f_{\hat{k}(x_0)}(x_i)
fk′=fk(xi)−fk^(x0)(xi)
e
n
d
f
o
r
\quad end\ for
end for
待补充