摘要

基于WGAN提出了一个攻击黑盒分类器的自然对抗样本生成框架。这些自然对抗样本可以帮助解释黑盒模型的决策行为和评估的准确性。最后在图像分类、文本蕴含和机器翻译上进行了测试。

网络训练

相比于GAN/WGAN,NaturalGAN多了一个部件逆变器(Inverter$I$).逆变器的作用是将原始样本x映射回低维的稠密向量空间.

这样做的原因在于:GAN生成器的输入是随机的低维高斯噪声z(比如dim=100),通过生成器G映射到高维空间,这个分布在高维空间是低维流形,也就是所谓的“撑不满”整个高维空间(这个在之前在WGAN中提到过).换句话说生成的对抗样本$x'$的分布其实很大程度上取决于低维噪声的分布.而GAN直接拿随机噪声作为G的输入,而忽略了真实样本x的分布$p_r$,尽管通过设置损失函数(JS散度或EM距离)可以拉近$p_r$和$p_g$,但是缺少了原始样本x的特征信息,会导致最终生成的对抗样本不够自然.

在AdvGAN中,是通过输入样本x或样本x的特征图来解决上述不足

image-20230412110629371

回到NatualGAN,训练共分两个步骤.首先是按照WGAN的策略,训练好生成器G和判别器C.这里G的输入暂时还是随机的高斯噪声.最大最小博弈函数为(EM距离)

$$ \operatorname*{min}_{\theta}\operatorname*{max}_{\omega}\mathbb{E}_{x\rightarrow p_{x(x)}}\left[C_{\omega}(x)\right]-\mathbb{E}_{z\sim p_{z(z)}}\left[C_{\omega}(G_{\theta}(z))\right] $$

训练第二步即上图中的黄色部分,固定判别器C,训练逆变器$I$和生成器G.$I$的输入是原始样本$x$,输出是和噪声$z$在同一维度空间的$z'$.对于判别器$I$,我们想得到的效果是:

  • 输入原始样本$x$产生的输出$z'$,将其输入到$G$中生成的新样本$x'$,应该和$x$足够接近(保证自然).这一部分可以称为重建误差(reconstruction error).
  • 对于某一噪声$z$经$G$生成的样本$x'$,将其输入到$I$中得到的输出$z'$.$z$和$z'$的分布应该足够接近.这一部分是训练逆变器$I$的映射能力.

综上两点可以得到$I$(也包括$G$)的损失函数.

$$ \operatorname*{min}_{\gamma}\mathbb{E}_{x\sim p_{x}(x)}\|G_{\theta}(I_{\gamma}(x))-x\|+\lambda\cdot\mathbb{E}_{z\sim p_{z}(z)}[{\mathcal{L}}(z,I_{\gamma}(G_{\theta}(z)))] $$

当距离$\mathcal{L}$使用$L_2 distance$时,$\lambda$=0.1(for images)

当距离$\mathcal{L}$使用$Jensen-Shannon distance$时,$\lambda$=1(for text data)

对抗样本生成

$G,C,I$都训练结束后,可以定义对抗样本为

$$ x^{*}=G_{\theta}(z^{*}){\mathrm{~where~}}z^{*}=\arg\operatorname*{min}_{\tilde{z}}\parallel\tilde{z}-\mathbb{Z}_{\gamma}(x)\parallel\operatorname{s.t.}f(G_{\theta}({\tilde{z}}))\neq f(x) $$

首先将原始样本x输入到逆变器$I$中,得到低维向量$z'$,随后对$z'$产生一定扰动生成$\tilde z$.找到扰动最小的$z$,使$f(G(\tilde z))\neq f(x) $.

扰动最小自然是为了保证对抗样本足够自然

image-20230412114336821

关于$\tilde z$的搜索,论文给了两种算法.一种是逐渐增加扰动的暴搜算法,另一个是基于二分的粗粒度到细粒度的搜索算法.具体看图~

image-20230412114707401

image-20230412114757180

算法二先通过二分确定扰动范围(<$\delta r$),然后再爆搜.算法效率是算法一的四倍,效果可以持平算法一

生成结果

先看一对有意思的图,图片(a)是数据x的分布情况,图(b)是逆变器的输出结果,可以看到$z'$基本满足正态分布,图(c)显示了二元分类器的决策边界以及NatualGAN生成的对抗样本$x^*(Our)$的位置.而通过FGSM生成的对抗样本可能在右侧.虽然两者都使辨别器犯错,但是$x^*(Our)$在蓝色样本的流形上,无疑会更加自然.

image-20230412115228352

最后是在MNIST手写数字数据集上的对比测试.

image-20230412115741285

相对于原始GAN的改进点:

  • 解决GAN训练不稳定的问题,不再需要小心平衡生成器和判别器的训练程度
  • 基本解决了模式坍塌(collapse mode)的问题,确保了生成样本的多样性
  • 训练过程中可以使用WGAN判别器loss函数的值来判断训练效果(近似Wasserstein距离的相反数),这个数值越小代表GAN训练得越好,代表生成器产生的图像质量越高
  • 只需要将原始GAN的算法流程改进四点就可以实现以上效果

原始GAN的缺点

对于原始GAN中生成器G的第一种损失函数,由Theory 1./2. 证明其不足.

$$ Loss_{G1} = \mathbb{E}_{x\sim p_{g}[log(1-D(x))]} $$

对于原始GAN中G的第二种损失函数,由Theory 3. 证明其不足.

$$ Loss_{G2}=\mathbb{E}_{x\sim p_{g}[-log(D(x))]} $$

Theory 1.两个不重叠分布的JS散度=log2

首先回顾一下GAN的价值函数V(G,D),因为第一项和G无关,所以最小化V(G,D)等效于最小化我们刚刚提到的$Loss_{G1}$.

$$ \mathop{min}\limits_{G}\mathop{max}\limits_{D}V(G,D)=\mathbb{E}_{x\sim p_{r(X)}[logD(x)]} + \mathbb{E}_{z\sim p_{z(z)}log[1-D(G(z))]} $$

在GAN中证明过:固定G的参数时,最优的D为

$$ D^*_G(x) = \frac{p_{r}(x)}{p_{r}(x)+p_g(X)} $$

将$D_G^*$带入V(G,D)并进一步化简,可以得到(具体证明见GAN一节)

$$ C(G)=-\log(4)+2\cdot J S D\left(p_{\mathrm{r}}||p_{g}\right) $$

由此我们可以得到以下结论:根据原始GAN定义的判别器loss,我们可以得到最优判别器的形式$D_G^*$;而在最优判别器下,我们可以把$Loss_{G1}$等价变换为最小化真实分布$P_r$与生成分布$P_g$之间的JS散度。我们越训练判别器,它就越接近最优,最小化生成器的loss也就会越近似于最小化$P_r$和$P_g$之间的JS散度

上述理论也很好解释,因为我们最终希望G可以生成以假乱真的图片,即尽可能将$P_g$拉向$P_r$.但是直接用JS散度做损失函数容易出现梯度消失的现象,具体证明如下:

首先考虑两个概率分布基本不重叠的情况(实际上$P_r$和$P_g$就属于这种情况),先将JS散度写回KL散度的形式.

$$ JSD(p_{data}||p_g)=\frac 12K L\left(p_{\mathrm{data}}\Big|\Big|\frac{p_{\mathrm{data}}+p_{g}}{2}\right)+\frac 12K L\left(p_{g}\Big|\Big|\frac{p_{\mathrm{data}}+p_{g}}{2}\right) $$

根据KL散度的定义继续替换($p$为$p_{data}$,$q$为$p_g$)

$$ \begin{align} JSD(p||q) &= \frac12 \sum p(x)\log(\frac {2p(x)}{p(x)+g(x)})+\frac 12 \sum q(x)\log(\frac {2q(x)}{p(x)+q(x)}) \\ &=\frac12 \sum p(x)\log(\frac {p(x)}{p(x)+g(x)})+\frac 12 \sum q(x)\log(\frac {q(x)}{p(x)+q(x)})+\log(2) \end{align} $$

化简到这个式子,可以看出来前两项就是0了.因为对于任意x,p和q就四种情况:

  • p(x)=0,q(x)=0.显然前两项为0
  • p(x)!=0,q(x)!=0.根据假设,两个分布基本不存在重叠,因而log项里面可以认为是1.(例如p(x)足够大时,可以认为p(x)+q(x)≈p(x))所以前两项值为0.
  • p(x)=0,q(x)!=0.第一项为0,第二项log中的值为1,故同样都是0.
  • q(x)=0,p(x)!=0.同上

因此在两个分布基本没有重叠时,他们的JS散度就是常数$\log2$.常数对于随机梯度下降法意味着梯度为0,从而导致生成器G无法训练(或出现梯度消失).

Theory 2. $p_r$和$p_g$的不重叠概率

那么$p_r$和$p_g$有多大的概率基本没有重叠呢?答案是大多数情况下两个分布都没有较大重叠.即训练G时梯度为0/梯度消失的情况是个常态.严谨的说法是:当$p_r$与$p_g$的支撑集(support)是高维空间中的低维流形(manifold)时,两个分布重叠部分测度(measure)为0的概率为1

  • 支撑集(support)其实就是函数的非零部分子集,比如ReLU函数的支撑集就是(0,+∞),一个概率分布的支撑集就是所有概率密度非零部分的集合
  • 流形(manifold)是高维空间中曲线、曲面概念的拓广,我们可以在低维上直观理解这个概念,比如我们说三维空间中的一个曲面是一个二维流形,因为它的本质维度(intrinsic dimension)只有2,一个点在这个二维流形上移动只有两个方向的自由度。同理,三维空间或者二维空间中的一条曲线都是一个一维流形。(所以可以理解成高维空间中某个形状上的自由度?)
  • 测度(measure)是高维空间中长度、面积、体积概念的拓广,可以理解为“超体积”

对于“当$p_r$与$p_g$的支撑集是高维空间中的低维流形时”这句话,原因是GAN中的生成器一般是从某个低维(比如100维)的随机分布中采样出一个编码向量,再经过一个神经网络生成出一个高维样本(比如64x64的图片就有4096维)。当生成器的参数固定时,生成样本的概率分布虽然是定义在4096维的空间上,但它本身所有可能产生的变化已经被那个100维的随机分布限定了,其本质维度就是100,再考虑到神经网络带来的映射降维,最终可能比100还小,所以生成样本分布的支撑集就在4096维空间中构成一个最多100维的低维流形,“撑不满”整个高维空间。

那么当$p_g$的支撑集"撑不满"整个高维空间时,$p_r$和$p_g$就很难重叠.例如在二维空间中随意取两条曲线,它们正好存在重叠线段的概率是0.(?)虽然它们很大可能会存在交叉点,但是相比于两条曲线而言,交叉点比曲线低一个维度,长度(测度)为0,可以忽略.从低维空间拓展到高维空间,就有了如下逻辑:因为一开始生成器随机初始化,所以$p_g$几乎不可能与$p_r$有什么关联,所以它们的支撑集之间的重叠部分要么不存在,要么就比它们两个的最小维度还要低至少一个维度,故而测度为0。所谓“重叠部分测度为0”,就是上文所言“不重叠或者重叠部分可忽略”的意思。

综上,我们可以做以下总结:

  • $p_r$与$p_g$之间几乎不可能有不可忽略的重叠,所以无论它们之间的“缝隙”多狭小,都肯定存在一个最优分割曲面把它们隔开,最多就是在那些可忽略的重叠处隔不开而已。
  • 由于判别器作为一个神经网络可以无限拟合这个分隔曲面,所以存在一个最优判别器,对几乎所有真实样本给出概率1,对几乎所有生成样本给出概率0,而那些隔不开的部分就是难以被最优判别器分类的样本,但是它们的测度为0,可忽略。
  • 最优判别器情况下,生成器G的loss值为常数,造成梯度为0或梯度消失的情况。

有了以上理论分析,可以解释GAN训练不稳定的原因:如果判别器D训练得太好,生成器G梯度消失,G无法继续训练;D训练得不好,G梯度不准,无法收敛。只有D训练得不好不坏才行,但是这个火候又很难把握,甚至在同一轮训练的前后不同阶段这个火候都可能不一样,所以GAN才那么难训练.

以下图例测试了G梯度消失的现象.先分别将DCGAN训练1,20,25个epoch,然后固定G不动,判别器重新随机初始化从头开始训练,根据$Loss_{G1}$可以打印出其尺度的变化曲线,可以看到随着判别器的训练,生成器的梯度均迅速衰减。注意y轴是对数坐标轴。

v2-8715a60c1a8993953f125e03938125d7_1440w

Theory 3. $Loss_{G2}$的矛盾之处

我们刚刚推得在$D^*$下,V(G,D)可以写成

$$ V(G,D)=\mathbb{E}_{x\sim p_{r(X)}[logD(x)]} + \mathbb{E}_{x\sim p_{g}log(1-D(x))} = -2\log(2)+2\cdot J S D\left(p_{\mathrm{r}}||p_{g}\right)\\ $$

我们发现$KL(p_g||p_r)$可以写成含$D^*$的形式

image-20230411163743206

从而得到

image-20230411163858498

其中后两项与G无关,所以最小化上式意味着最小化前两项

$$ KL(p_g||p_r)-2JS(p_r||p_g) $$

这个等价的最小化目标代表它同时要最小化生成分布与真实分布的KL散度,却又要最大化两者的JS散度,这是前后矛盾的.其次,KL散度是不对称的,这也导致了模式坍塌的问题.(详见令人拍案叫绝的Wasserstein GAN - 知乎 (zhihu.com))

下图测试了G使用$Loss_{G2}$时出现的梯度不稳定现象.先分别将DCGAN训练1,20,25个epoch,然后固定G不动,判别器重新随机初始化从头开始训练,对于$Loss_{G2}$产生的梯度可以打印出其尺度的变化曲线,可以看到随着判别器的训练,蓝色和绿色曲线中生成器的梯度迅速增长,说明梯度不稳定,红线对应的是DCGAN相对收敛的状态,梯度才比较稳定

v2-b85cdb4d79d7618213c320cfb3a6d4bf_1440w

总结

在原始GAN的(近似)最优判别器$D^*$下,$Loss_{G1}$面临梯度消失问题,$Loss_{G2}$面临优化目标荒谬、梯度不稳定、对多样性与准确性惩罚不平衡导致模式坍塌这几个问题。

WGAN的解决方案

Plan 1. Epsilon

方案1思想比较简单,即对生成样本和真实样本加噪声.直观上说,加完噪声后使得原本的两个低维流形“弥散”到整个高维空间,强行让它们产生不可忽略的重叠。而一旦存在重叠,JS散度就能真正发挥作用,此时如果两个分布越靠近,它们“弥散”出来的部分重叠得越多,JS散度也会越小而不会一直是一个常数,于是(在第一种原始GAN形式下)梯度消失的问题就解决了。在训练过程中,可以对所加的噪声进行退火(annealing),慢慢减小其方差,到后面两个低维流形“本体”都已经有重叠时,就算把噪声完全拿掉,JS散度也能照样发挥作用,继续产生有意义的梯度把两个低维流形拉近,直到它们接近完全重合。

在这个解决方案下可以放心地把D训练到接近最优,不必担心梯度消失的问题。而当判别器最优时,D的损失函数可以表示为

$$ Loss_D = 2\log2 - 2JS(p_{r+\epsilon}||p_{g+\epsilon}) $$

按照此式我们可以反推出两个分布的JS散度,从而得到两个分布的距离度量----------可惜答案是不行,因为每次训练$\epsilon$即噪声并非固定的,随着噪声的退火,前后的JS散度无法做数值比较.

Plan 2. Earth-Mover

方案2也是WGAN最后的解决方案.它将JS散度替换成了Earth-Mover(EM)距离.

$$ W(P_{r},P_{g})=\mathrm{inf}_{\gamma\sim\Pi(P_{r},P_{g})} \mathbb{E}_{(x,y)\sim\gamma}||x-y|| $$

解释如下:是$p_r$和$p_g$组合起来的所有可能的联合分布的集合,反过来说,中每一个分布的边缘分布都是$p_r$和$p_g$。对于每一个可能的联合分布$\gamma$而言,可以从中采样(x,y)∼$\gamma$得到一个真实样本x和一个生成样本y,并算出这对样本的距离$||x−y||$,所以可以计算该联合分布$\gamma$下样本对距离的期望值。在所有可能的联合分布中能够对这个期望值取到的下界,就定义为Wasserstein距离.换句话说就是找到一个最理想的分布$\gamma$使从中采样到的x,y期望距离最小,即“最优路径规划”下的“最小消耗”.

Wasserstein距离相比KL散度、JS散度的优越性在于,即便两个分布没有重叠,Wasserstein距离仍然能够反映它们的远近。

考虑如下二维空间中的两个分布$p1$和$p2$,$p1$在线段AB上均匀分布,$p2$在线段CD上均匀分布,通过控制参数$\theta$可以控制着两个分布的距离远近。

v2-c9cc9f8c879e7fe93d6e3bfafd41bd8a_1440w

可以得到

$$ K L(P_{1}||P_{2})=K L(P_{1}||P_{2})=\left\{\begin{array}{l l}{{+\infty}}&{{\mathrm{if}\theta\not=0}}\\ {{0}}&{{\mathrm{if}\theta=0}}\end{array}\right.\\ J S(P_{1}||P_{2})=\left\{\begin{array}{l l}{{\log2}}&{{\mathrm{if}\theta\not=0}}\\ {{0}}&{{\mathrm{if}\theta=0}}\end{array}\right.\\ W(P_1||P_2)=|\theta| $$

KL散度和JS散度是突变的,要么最大要么最小,Wasserstein距离却是平滑的,如果我们要用梯度下降法优化$\theta$这个参数,前两者根本提供不了梯度,Wasserstein距离却可以。类似地,在高维空间中如果两个分布不重叠或者重叠部分可忽略,则KL和JS既反映不了远近,也提供不了梯度,但是Wasserstein却可以提供有意义的梯度。整个指标也可以反应GAN网络的训练效果.

接下来将Wasserstein距离应用到生成器G的损失函数中,因为$\mathrm{inf}_{\gamma\sim\Pi(P_{r},P_{g})}$无法直接求解,所以需要先将其转换成如下形式

$$ W(P_{r},P_{g})=\frac{1}{K}\,\mathrm{Sup}_{||f||_{L}\leq K}\,{\mathbb{E}}_{x\sim P_{r}}\,[f(x)]-\mathbb{E}_{x \sim P_{g}}\,[f(x)] $$

其中的K是某个常数,$||f||_L$是函数$f$的Lipschitz常数.

Lipschitz常数是指:一个连续函数f如果存在一个常数K使定义域内的任意两个元素x1,x2都满足$|f(x1)-f(x2)|\leqslant K|x1-x2|$,那么函数f是Lipschitz连续的,且Lipschitz常数为K.有一个更强的限制就是f处处导数的绝对值都小于K.(之后近似会用到)

对于函数$f$我们可以使用神经网络来拟合,需要满足$f$的Lipschitz常数小于K.这个条件我们可以直接限制函数$f$的参数$w$来实现,如果将$w$都限制在[-c,c]之间,那么关于输入x的导数$\frac {\partial f_w}{\partial x}$也被限制在了某个范围内,从而存在常数K肯定大于$f$的Lipschitz常数.满足上式.具体实现就是每次更新完$w$后对其进行clip操作即可.

综上,我们可以构造一个含参数$w$、最后一层不是非线性激活层的判别器网络D($f_w$),在限制$w$不超过某个范围的条件下,使得

$$ L=\mathbb{E}_{x\sim P_{r}}\left[f_{w}\left(\mathcal{x}\right)\right]\,-\,\mathbb{E}_{x\sim P_{g}}\left[\mathcal{f}_{w}\left(\mathcal{x}\right)\right] $$

尽可能取到最大,此时L就会近似真实分布与生成分布之间的Wasserstein距离(忽略常数倍数K).

注意原始GAN的判别器做的是真假二分类任务,所以最后一层是sigmoid,但是现在WGAN中的D($f_w$)做的是近似拟合Wasserstein距离,属于回归任务,所以要把最后一层的sigmoid拿掉。

接下来生成器要近似地最小化Wasserstein距离,可以最小化L,由于Wasserstein距离的优良性质,我们不需要担心生成器梯度消失的问题。再考虑到L的第一项与G无关,就可以得到了WGAN中G和D的Loss

$$ Loss_G = -\,\mathbb{E}_{x\sim P_{g}}\left[\mathcal{f}_{w}\left(\mathcal{x}\right)\right]\\ Loss_D=-\mathbb{E}_{x\sim P_{r}}\left[f_{w}\left(\mathcal{x}\right)\right]\,+\,\mathbb{E}_{x\sim P_{g}}\left[\mathcal{f}_{w}\left(\mathcal{x}\right)\right] $$

最后贴一个算法流程,使用的优化器是RMSProp

v2-6be6e2ef3d15c4b10c2a943e9bf4db70_1440w

总结

至此,我们终于可以总结WGAN算法和GAN算法的4点不同了.

  • 判别器最后一层去掉sigmoid(从二分类->回归)
  • 生成器和判别器的loss不取$log$(EM距离)
  • 每次更新判别器的参数之后把它们的绝对值截断到不超过一个固定常数c(使D满足Lipschitz连续)
  • 不要用基于动量的优化算法(包括momentum和Adam),推荐RMSProp/SGD(经验所谈)

简介

本篇论文主要是对AdvGAN进行了一些小改进,证明了在非定向攻击中,潜在特征作为对抗生成的先验比整个输入图像更好,同时消除了对生成器遵循基于编码器-解码器的架构的需要,从而减少了训练/推理开销。
论文地址:只有4页的AdvGAN++

网络框架

跟AdvGAN相比,主要区别在于生成器G的输入产生了变化,从原来的原始图像x变成了图像x的特征图与噪声向量的级联。而图像x的特征图是通过目标网络M的特征提取器f得到的。这两点就是AdvGAN和原始版本的最大区别。

image-20230410170801954

损失函数

与AdvGAN类似,损失函数为

$$ L(G,D)=L_{GAN}+\alpha L_{adv}+\beta L_{pert} $$

其中

$$ L_{GAN}=E_x[\log D(x)+E_xlog(1-D(G(z|f(x)))]\\ L_{adv}=E_x[M_t(G(z|f(x)))]\\ L_{pert}=E_x||x-G(z|f(x))||_2 $$

$L_{adv}$中的$M_t$是指目标模型M将输入识别成类别t的概率(softmax处理后).其他部分与AdvGAN的损失函数一致,这里不再赘述.AdvGAN - JJJYmmm Blog

训练过程

训练过程如算法1所示,跟AdvGAN/GAN的方法一致,使用min-max博弈依次迭代G/D.详见Generative Adversarial Nets - JJJYmmm Blog的理论证明.

image-20230410171717232

总结

AdvGAN++算是一个对原版的小迭代.一句话总结:使用目标模型的特征抽取器抽取原始样本的潜在特征,并将其作为生成器G的输入,取消了G需要是"encoder-decoder"架构的限制,达到了比原版更好的性能和效果.

简介

论文地址 Generating Adversarial Examples with Adversarial Networks|IJCAI 2018

本篇论文基于GAN生成对抗样本。首先提出训练一个产生扰动的前馈网络(G)来生成不同的对抗样本,再通过一个判别网络(D)判别扰动图像的真实性。并在半白盒黑盒两种场景下进行实验。由于条件GANs能够生成高质量的图像,他们使用了类似的范例(LSGAN)来生成对抗样本。

在以前的白盒攻击中,如FGSM和优化方法,对手需要有攻击的目标模型的架构和所有参数。然而,通过部署AdvGAN,一旦G得到训练,它可以立即为任何输入样本产生扰动,而不再需要访问模型本身。此攻击场景称之为半白盒。

网络主体

问题定义

假设$X \subseteq R^n$为特征空间,n为特征维度。设训练集中的一个样本($x_i,y_i$),其中$x \subseteq X$,并服从分布$\mathbf{x_{i}}\sim\mathbf{P}_{\mathrm{data}}$,且$y_i \in Y$.攻击的目标网络是一个分类器$\mathbf{f}:{\boldsymbol{X}}\rightarrow{\boldsymbol{Y}}$,将样本空间映射到分类集合的大小,$|Y|$即分类输出的数量.对于一个样本x,攻击者的目标是生成对抗样本$x_A$,使$f(x_A) \neq y$(非定向攻击),其中y是x对应的真实标签;或者实现$f(x_A) = t$(定向攻击),其中t是目标类别.除此之外,$x_A$也应该在$L_2$等其他度量上与原始样本x足够接近(隐蔽性).

AdvGAN框架与白盒攻击

下图展示了AdvGAN的总体架构,主要由生成器$G$、判别器$D$和目标神经网络$f$三部分组成。这里生成器$G$以初始实例$x$为输入,产生扰动$G(x)$。然后将$x+G(x)$发送给判别器$D$,用来区分生成的数据和原始实例$x$。首先执行白盒攻击,在本例中,目标模型是$f$。$f$以$x + G ( x )$为输入输出其损失$L_{adv}$,表示预测类别与目标类 $t$ (定向攻击)之间的距离,或预测值(除去真实类别外概率最大的那个类别的概率值)与真实类别的(非定向攻击)之间的距离。

image-20230409175741414

对于GAN那一部分,损失函数可以写成

$$ \left.{\mathcal{L}}_{\mathrm{GAN}}=\mathbb{E}_{x}\log D(\mathbf{x})+\mathbb{E}_{x}\log(1-{\mathcal{D}}(\mathbf{x}+{\mathcal{G}}(\mathbf{x}))\right) $$

这部分和原始的GAN网络的损失函数基本一致,只不过原始公式第二项$D$的输入直接是$G(z)$,而不是原始图像加扰动值$x+G(x)$.因为GAN是用$G$生成图像,这里的$G$只是生成扰动.通过$L_{GAN}$,$G$和$D$将不断迭代,最终使$G$生成与原图差别不大(加上$x$后)的扰动$G(x)$.

对于需要攻击的目标模型,我们定义$f$的损失函数为

$$ {\mathcal{L}}_{\mathrm{adv}}^{\mathrm{f}}=\operatorname{E}_{\mathbf{x}}1_{\mathbf{f}}\left(\mathbf{x}+g(\mathbf{x}),\mathbf{t}\right) $$

这里的$t$是目标类别,$l_f$是目标模型$f$的损失函数(比如交叉熵),通过$L_{adv}$,可以鼓励$x+G(x)$被目标模型$f$分类到错误的类别$t$中.当然这是定向攻击的写法,对于非定向攻击,将在代码部分讲解.

还有一项损失是Hinge Loss,用于限制扰动的大小,之前在机器学习常常用作正则项.(有时间可以单独写一章细说).

$$ {\mathcal{L}}_{\mathrm{hinge}}=\mathbb{E}_{\mathbf{x}}{\mathrm{max}}(0,||G(\mathbf{x})||_{\mathbf{x}}-\mathbf{c}) $$

这里的$c$是超参数,限定扰动$G(x)$的$L_2$范数在c之内.至此损失函数就结束了,总结为

$$ {\mathcal{L}}={\mathcal{L}}_{\mathrm{adv}}^{\mathrm{f}}+\alpha{\mathcal{L}}_{\mathrm{GAN}}+\beta{\mathcal{L}}_{\mathrm{huge}} $$

这里的$\alpha,\beta$分别是$L_{GAN},L_{Hinge}$的权重.训练时和GAN一致,使用最大最小博弈迭代$G,D$.训练完成后,可以用$G$对输入的任何样本产生扰动,这个时候就不再需要对$f$进行查询了,所以是一种半白盒的攻击方式.

$$ argmin_Gmax_D{\mathcal{L}} $$

黑盒攻击与动态蒸馏

对于黑盒攻击,我们假设攻击者事先不知道训练数据或模型本身。所以需要随机抽取与黑箱模型训练数据不相交的数据进行蒸馏。为了实现黑盒攻击,首先需要在黑盒模型b的输出基础上构建一个蒸馏网络$f$[Hinton et al., 2015]。一旦得到蒸馏网络$f$,就可以执行与白盒设置相同的攻击策略。这里,通过最小化以下期望使蒸馏模型与和黑盒模型足够接近.

$$ \arg\operatorname*{min}_{f}\mathbb{E}_{\mathbf{x}}\mathcal H(\mathbf{f}(\mathbf{x}),\mathbf{b}(\mathbf{x})) $$

其中f(x)和b(x)分别表示蒸馏模型和黑盒模型对给定训练图像x的输出,$\mathcal H$表示常用的交叉熵损失。通过对所有训练图像的目标进行优化,可以得到一个非常接近黑盒模型$b$的模型$f$,然后对蒸馏网络进行攻击。

值得注意的是,不像训练判别器 $\mathcal D$那样(输入原始样本和扰动样本),这里只使用真实数据来训练蒸馏模型$f$,并使用所有类的数据来训练。(很好理解吧)

动态蒸馏

上述的蒸馏方法是一种静态蒸馏,即提前训练好蒸馏模型$f$,接下来的攻击都用它来代替黑盒模型$b$.这有个坏处是:我们不清楚黑盒和蒸馏模型对生成的对抗样本的判断情况(因为压根没训练过),也就无法保证对于对抗样本它们俩的表现一致.

动态蒸馏的方式是在训练生成器$D,G$过程中,联合训练蒸馏模型$f$.具体来说分为以下两个步骤.

  1. 对于固定的$f_{i-1}$,训练$G,D$

​ 这里和白盒攻击的优化策略一致,对于固定的$f_{i-1}$,通过最大最小博弈更新$G,D$.

$$ G_{\mathrm{i}},\mathrm{D}_{\mathrm{i}}\,=\,\mathrm{arg\,min}_{\mathcal{G}}\,\mathrm{max}_{\mathcal{D}}\,\mathcal{L}_{\mathrm{adv}}^{\mathrm{f}_{\mathrm{i}}\,-\,1}\,+\alpha\mathcal{L}_{\mathrm{GAN}}\,+\,\beta\mathcal{L}_{\mathrm{hinge}} $$

  1. 对于固定的生成器$G$,更新$f_i$

​ 更新$f_{i}$的损失函数有两项,第一项保证对于原始样本x,$f$的表现和黑盒模型$b$一致.第二项保证了对于对抗样本$x+G(x)$的相同表现.

$$ \mathrm{f}_{i}=\arg\operatorname*{min}\mathbb{E}_{x}\,H(\mathbf{f}(\mathbf{x}),\mathrm{b}(\mathbf{x}))+\mathbb{E}_{x}\,H(\mathbf{f}(\mathbf{x}+{\mathcal{G}}_{\mathrm{i}}(\mathbf{x})),\mathrm{b}(\mathbf{x}+{\mathcal{G}}_{\mathrm{i}}(\mathbf{x}))) $$

作者在这里测试了一下静态蒸馏与动态蒸馏的性能,突出动态蒸馏的优越性.

第一行是几个模型对于原始样本的准确率,第二行是半白盒攻击的攻击成功率,第三行和第四行分别是动态蒸馏和静态蒸馏下的攻击成功率.可以看到效果还是很明显的.

image-20230409183354295

实验结果

作者首先评估AdvGAN在MNISTCIFAR-10上的半白盒和黑盒攻击方式,还对ImageNetdataset进行了半白盒攻击。随后对于不同防御手段下测试了AdvGAN的攻击成功率,并表明与其他现有攻击策略相比,AdvGAN方法可以实现更高的攻击成功率。(在基于$L_\infty$约束的MNIST的0.3阈值和CIFAR-10的8阈值上进行公平比较)。Adv-GAN具有优于其他白盒和黑盒攻击的几个优点。例如,关于计算效率,AdvGAN执行速度比其他人快得多,甚至包括有效的FGSM,尽管AdvGAN需要额外的训练时间来训练生成器。除了基于可转移性的攻击,所有对比的模型都可以进行定向攻击,(通过集合策略可以帮助改进Trans Attack)。此外,FGSM和优化方法只能执行白盒攻击,而AdvGAN能够在半白盒设置中进行攻击
image-20230409184102798

代码部分

参考这里的半白盒攻击实现mathcbc/advGAN_pytorch,使用MNIST数据集

和之前的GAN源码主要有以下几点不同:

  • 训练AdvGAN前还需要训练所攻击的目标模型
  • 生成器$G$的实现,这里采用了encoder-decoder架构,并且输出的是扰动
  • 损失函数,多了Hinge Loss和$L_{adv}$.

目标模型

MNIST数据集是识别手写数字的(0~9),输入的图片维度是[1,28,28].训练参数采用Adam,lr=0.001,用了学习率衰减,详见代码.

# Target Model definition
class MNIST_target_net(nn.Module):
    def __init__(self):
        super(MNIST_target_net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3)
        self.conv2 = nn.Conv2d(32, 32, kernel_size=3)
        self.conv3 = nn.Conv2d(32, 64, kernel_size=3)
        self.conv4 = nn.Conv2d(64, 64, kernel_size=3)

        self.fc1 = nn.Linear(64*4*4, 200)
        self.fc2 = nn.Linear(200, 200)
        self.logits = nn.Linear(200, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv3(x))
        x = F.relu(self.conv4(x))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 64*4*4)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, 0.5)
        x = F.relu(self.fc2(x))
        x = self.logits(x)
        return x
# train
target_model = MNIST_target_net().to(device)
target_model.train()
opt_model = torch.optim.Adam(target_model.parameters(), lr=0.001)
epochs = 40
for epoch in range(epochs):
    loss_epoch = 0
    if epoch == 20:
        opt_model = torch.optim.Adam(target_model.parameters(), lr=0.0001)
    for i, data in enumerate(train_dataloader, 0):
        train_imgs, train_labels = data
        train_imgs, train_labels = train_imgs.to(device),                                                    train_labels.to(device)
        logits_model = target_model(train_imgs)
        loss_model = F.cross_entropy(logits_model, train_labels)
        loss_epoch += loss_model
        opt_model.zero_grad()
        loss_model.backward()
        opt_model.step()

生成模型$G$

这里的G与GAN中的G不同.GAN中的G以噪声z为输入,直接通过转置卷积生成图像.而这里的G输入是原始图像,生成扰动.所以采用encoder-decoder结构,首先通过卷积构成的encoder提取输入样本的信息,再通过转置卷积decoder输出生成的扰动.

值得注意的是,这里的decoder中使用了InstanceNorm2d而不是BatchNorm.两者的区别是BatchNorm是针对一批样本中的相同特征做归一化,而InstanceNorm是在单个样本内做归一化.(LayerNorm也是在样本内做归一化)

在encoder与decoder之间还使用了残差块组成的bottle_neck.

class Generator(nn.Module):
    def __init__(self,
                 gen_input_nc,
                 image_nc,
                 ):
        super(Generator, self).__init__()
        encoder_lis = [
            # MNIST:1*28*28
            nn.Conv2d(gen_input_nc, 8, kernel_size=3, stride=1, padding=0, bias=True),
            nn.InstanceNorm2d(8),
            nn.ReLU(),
            # 8*26*26
            nn.Conv2d(8, 16, kernel_size=3, stride=2, padding=0, bias=True),
            nn.InstanceNorm2d(16),
            nn.ReLU(),
            # 16*12*12
            nn.Conv2d(16, 32, kernel_size=3, stride=2, padding=0, bias=True),
            nn.InstanceNorm2d(32),
            nn.ReLU(),
            # 32*5*5
        ]
        bottle_neck_lis = [ResnetBlock(32),
                           ResnetBlock(32),
                           ResnetBlock(32),
                           ResnetBlock(32),]
        decoder_lis = [
            nn.ConvTranspose2d(32, 16, kernel_size=3, stride=2, padding=0, bias=False),
            nn.InstanceNorm2d(16),
            nn.ReLU(),
            # state size. 16 x 11 x 11
            nn.ConvTranspose2d(16, 8, kernel_size=3, stride=2, padding=0, bias=False),
            nn.InstanceNorm2d(8),
            nn.ReLU(),
            # state size. 8 x 23 x 23
            nn.ConvTranspose2d(8, image_nc, kernel_size=6, stride=1, padding=0, bias=False),
            nn.Tanh()
            # state size. image_nc x 28 x 28
        ]

        self.encoder = nn.Sequential(*encoder_lis)
        self.bottle_neck = nn.Sequential(*bottle_neck_lis)
        self.decoder = nn.Sequential(*decoder_lis)

    def forward(self, x):
        x = self.encoder(x)
        x = self.bottle_neck(x)
        x = self.decoder(x)
        return x

AdvGAN损失函数

损失函数分为三部分,代码实现里$\sigma$是10,$\alpha$是1,$\beta$是1.跟论文里不太一样.

$$ {\mathcal{L}}=\sigma{\mathcal{L}}_{\mathrm{adv}}^{\mathrm{f}}+\alpha{\mathcal{L}}_{\mathrm{GAN}}+\beta{\mathcal{L}}_{\mathrm{huge}} $$

  • 对于第二部分$L_{GAN}$,与原始GAN中的实现类似.区别在于损失计算不再使用当时的BCELoss,改用MSELoss.除此之外还是用了clipping trick限制了扰动的范围.(使$L_\infty$限制在一定范围内,上述提到MNIST数据集给的阈值是0.3).关键代码如下
# optimize D
perturbation = self.netG(x)

# add a clipping trick
adv_images = torch.clamp(perturbation, -0.3, 0.3) + x
adv_images = torch.clamp(adv_images, self.box_min, self.box_max)

self.optimizer_D.zero_grad()
pred_real = self.netDisc(x)
loss_D_real = F.mse_loss(pred_real, torch.ones_like(pred_real, device=self.device)) #use MSELoss instead of BCELoss
loss_D_real.backward()

pred_fake = self.netDisc(adv_images.detach())
loss_D_fake = F.mse_loss(pred_fake, torch.zeros_like(pred_fake, device=self.device)) # MSE
loss_D_fake.backward()
loss_D_GAN = loss_D_fake + loss_D_real
self.optimizer_D.step()

# optimize G
pred_fake = self.netDisc(adv_images)
loss_G_fake = F.mse_loss(pred_fake, torch.ones_like(pred_fake, device=self.device)) #MSE
loss_G_fake.backward(retain_graph=True)
  • 对于第三部分Hinge Loss,不同于论文的实现.代码部分直接用$G(x)$的$L_2$范数作为损失函数.也许是效果更好 ?
# calculate perturbation norm
C = 0.1
loss_perturb = torch.mean(torch.norm(perturbation.view(perturbation.shape[0], -1), 2, dim=1))
# loss_perturb = torch.max(loss_perturb - C, torch.zeros(1, device=self.device))
  • 对于第一部分$L_{adv}$,这里给出的是不定向攻击的实现.使用真实类别的概率和其他类别中的最大概率之间的差值作为损失项.起到了拉低真实类别的概率(拉高其他类别的概率)的作用.这里独热编码和取出其他类别概率的最大值写的很好
# cal adv loss
logits_model = self.model(adv_images)
probs_model = F.softmax(logits_model, dim=1)
# 生成对角线矩阵,对于每行来说就是对应类别的独热编码,这里取出真实类别对应的掩码
onehot_labels = torch.eye(self.model_num_labels, device=self.device)[labels]

# C&W loss function
# 通过掩码得到模型预测的正确类别的概率值
real = torch.sum(onehot_labels * probs_model, dim=1)
# 这里*10000是让让真实类别对应的位置值很小,从而让max取不到(其实没什么必要吧,之前做了softmax了)
other, _ = torch.max((1 - onehot_labels) * probs_model - onehot_labels * 10000, dim=1)
zeros = torch.zeros_like(other)
# 衡量真实类别与其他类别的差距,如果<=0就负0
loss_adv = torch.max(real - other, zeros)
loss_adv = torch.sum(loss_adv)

训练结果

首先训练目标模型,对于测试集accuracy可以达到99.38%

image-20230409193220803

接下来训练AdvGAN,训练60个epoch后损失如下.其实D训练的很好,说明对抗样本隐蔽性做的不太行,$L_{adv}$不高,说明攻击效果应该是达到的.

image-20230409194134066

接下来使用对抗样本测试一下目标模型的accuracy.这里同样使用的测试集(增加扰动后).最后得到的准确率大约为0.3%(训练集的准确率是0.15%).好!

image-20230409194338820

输出一下生成的对抗样本图像结束AdvGAN.(经典雪碧图)

5

今天开始看GAN生成对抗样本的相关工作,这几天应该会出个AdvGAN(++)