对抗样本常见攻击算法与模拟——CW
九涅·烧包包儿 / / / 阅读量
参考论文: Carlini, Nicholas , and D. Wagner . "Towards Evaluating the Robustness of Neural Networks." Security & Privacy IEEE, 2017.

CW算法

CW通常被认为是攻击能力最强的白盒攻击算法之一,同时也是一种基于优化的对抗样本生成算法。大多数文献将其归类为是基于梯度的攻击算法,和deepfool,FGSM同为一类。而实际上它是一种基于优化的对抗样本生成算法。

作者公开了源码,可以在github 上查到。

算法原理

CW的创新之处在于对损失函数(目标函数)的定义上。在定向攻击的过程中,目标函数经常使用交叉熵,迭代优化的过程就是不断减少目标函数的过程。

假设在原始数据上增加扰动,生成对抗样本$x+\delta$。那么定义对抗样本与原始数据之间的距离为$D(x,x+\delta)$于是,优化函数可以定义为:

\begin{aligned}
&\text { minimize } c \cdot\left|x-x^{\prime}\right|{2}^{2}+\operatorname{loss}{F, l}\left(x^{\prime}\right)\
&\text { such that } x^{\prime} \in[0,1]^{n}
\end{aligned}

其中c是一个引入的参数。之后会讨论如何选择c。

这个距离D可以是某种关于距离的矩阵,文中列举了$L_0,L_2,L_{\inf}$的算法。会在之后讨论。如果关于这些范数的概念很模糊的话,推荐一篇优质的博客

通过现有的优化算法,在适当的优化情况下,可以解决这个问题。有很多优化方法可以做到这一点,于是需要讨论目标函数的选取问题。

目标函数的选取

现在定义原始数据为$x$,分类结果为$C(x)$。定义$(e)^+ $为$max(e,0)$的简写,$Loss_{F,s}(x)$是交叉熵的简写。对于目标函数的选择,有非常多的选项,如下所示

$$\begin{aligned}
&f_{1}\left(x^{\prime}\right)=-\operatorname{loss}{F, t}\left(x^{\prime}\right)+1\
&f{2}\left(x^{\prime}\right)=\left(\max {i \neq t}\left(F\left(x^{\prime}\right){i}\right)-F\left(x^{\prime}\right){t}\right)^{+}\
&f{3}\left(x^{\prime}\right)=\operatorname{softplus}\left(\max {i \neq t}\left(F\left(x^{\prime}\right){i}\right)-F\left(x^{\prime}\right){t}\right)-\log (2)\
&f{4}\left(x^{\prime}\right)=\left(0.5-F\left(x^{\prime}\right){t}\right)^{+}\
&f{5}\left(x^{\prime}\right)=-\log \left(2 F\left(x^{\prime}\right){t}-2\right)\
&f{6}\left(x^{\prime}\right)=\left(\max {i \neq t}\left(Z\left(x^{\prime}\right){i}\right)-Z\left(x^{\prime}\right){t}\right)^{+}\
&f{7}\left(x^{\prime}\right)=\operatorname{softplus}\left(\max {i \neq t}\left(Z\left(x^{\prime}\right){i}\right)-Z\left(x^{\prime}\right)_{t}\right)-\log (2)
\end{aligned}$$

作者在文中指出,在相同数据集的情况下攻击同一模型,$mean$代表平均的扰动大小,$prob$代表攻击成功率,其中,$f_6$的表现最出色。

超参数c的选取

作者通过实验发现,在相同数据集的情况下攻击统一模型。左侧坐标为成功率,对应虚线。右侧坐标为平均扰动大小,对应实线。c越大,成功率和平均扰动都会变大。其中c=0.1是临界点。可见,综合考量成功率与平均扰动,可以选出尽可能小的c值。让攻击算法达到最优。

图片标题

梯度截断方法

之前在写代码的过程中,也常常会遇到这个问题。像素点一般为[0,255]或者[0,1]但是算梯度的很多时候会超出这个范围。在JSMA算法中,我直接将其超出范围的像素点改为了边界。而CW对于数据截断的处理很有特色。

一般常见的截断处理方法有3种。

  • Projected gradient descent(梯度投影下降)
  • 直接截断,把截断后的对抗样本带入下一轮。也是我在之前最常用的方法。
  • 容易将截断带来的误差给代入下一轮迭代,这样会让误差越来越大。
  • Clipped  gradient  descent(梯度截断下降)
  • 不直接截断对抗样本,而是优化目标函数,避免目标函数层的数据溢出。
  • 假设原目标函数为$f(x+\delta)$,则优化后的目标函数为
  • $f(\min(\max(x+\delta,0),1)$。
  • 容易出现梯度消失的问题
  • (我有个疑问啊,交叉熵函数不会超出1吧....)
  • Change  of  variables(变量替换)
  • 引入新的变量$\omega$,对抗样本呢可以表示为
  • $x+\delta = \cfrac{1}{2}(\tanh(\omega)+1$
  • 由于tanh的值域为[-1,1]这样就不会有溢出的问题了。
  • 这也是CW所采用的方法。做着发现在相同数据集的情况下攻击统一模型,在目标函数相同的情况下,该方法在mean和成功率的表现均优于以上两个方法

L2攻击

如果具体到定向L2攻击,假设攻击标签t,Logits模型行出为Z,则对于给定的x,选择一个攻击标签,并根据如下方程来搜索w。

$$\text { minimize }\left|\frac{1}{2}(\tanh (w)+1)-x\right|_{2}^{2}+c \cdot f\left(\frac{1}{2}(\tanh (w)+1)\right.$$

而其目标函数定义类似于$f_6$,但是在其基础上增加了一个阈值$\kappa$,其实也就是最低的损失函数的最低阈值。作者发现$\kappa$取40时表现最佳。

$$f\left(x^{\prime}\right)=\max \left(\max \left{Z\left(x^{\prime}\right){i}: i \neq t\right}-Z\left(x^{\prime}\right){i},-\kappa\right)$$

代码实现与ART框架使用

由于代码能力有限,没有写出实际的CW攻击代码,这里就用了IBM的ART工具。感觉这个工具强无敌好吧。可能这就是从手写SVM=>调用sklearn的退步与进步吧。

具体参考了 tf2.0的写法

另外好像下载minist的链接烂掉了。就用keras来load数据集了。

训练模型

TensorFlowV2Classifier是art经过封装的model类。可以用_model获取原始model的信息。

model = TensorFlowModel()
loss_object = tf.keras.losses.CategoricalCrossentropy(from_logits=True)

classifier = TensorFlowV2Classifier(model=model, loss_object=loss_object, train_step=train_step, nb_classes=10,
                                    input_shape=(28, 28, 1), clip_values=(0, 1))
classifier.fit(x_train, y_train, batch_size=64, nb_epochs=3)

选取图片进行攻击

img = np.stack([x_train[123]])
label = np.stack([y_train[1]])

# Step 6: Generate adversarial test examples
attack = CarliniL2Method(classifier=classifier, targeted=True)
img_adv = attack.generate(x=img, y=label)

随便选取一张图片(7),进行攻击,真的封装的特别好。target参数代表了是否是定向攻击。如果选择了定向攻击,后面必须制定y的参数。

from shaobaobao_adversarial_samples_learning import show_d

predict1 = classifier.predict(img)
predict2 = classifier.predict(img_adv)
print(predict1.shape)
title1 = np.argmax(predict1, axis=1)
title2 = np.argmax(predict2, axis=1)

show_d(img[0], img_adv[0], title1, title2)
图片标题

结果如上所示。成功将7改为了0。

Noise L_0 norm: 134 17%
Noise L_2 norm: 5.569888741935934 61%
Noise L_inf norm: 0.9882358305594501 1%

同样可以采用Linf攻击。

img = np.stack([x_train[3]])
label = np.stack([[0, 0, 0, 0, 0, 0, 0, 0, 8, 0]])
# Step 6: Generate adversarial test examples
attack = CarliniLInfMethod(classifier=classifier, targeted=True)
img_adv = attack.generate(x=img, y=label)

predict1 = classifier.predict(img)
predict2 = classifier.predict(img_adv)
title1 = np.argmax(predict1, axis=1)
title2 = np.argmax(predict2, axis=1)
show_d(img[0], img_adv[0], title1, title2)
图片标题
Noise L_0 norm: 94 12%
Noise L_2 norm: 2.3308406663753307 31%
Noise L_inf norm: 0.30000033425349815 1%

然而我发现,CW在该数据集上的表现并不优秀....目前还在找原因。总体正确率只在10%左右。我还按照论文的样子吧k值改成了40。这就更加不太行了。。。

支付宝捐赠
请使用支付宝扫一扫进行捐赠
微信捐赠
请使用微信扫一扫进行赞赏
有 0 篇文章