Practical no-box adversarial attacks against DNNs
论文地址：https://dl.acm.org/doi/abs/10.5555/3495724.3496802摘要与引言目前针对深度神经网络对抗脆弱性的研究进展迅速(如对抗样本、鲁棒性检验等)。现有的攻击需要内部访问（如受害者模型架构，参数或训练集）或外部访问（查询受害者模型）。然而在许多场景中，这两种访问都可能是不可行的或成本昂贵的。本文研究了无框对抗的例子，其中攻击者既不能访问模型信息或训练集，也不能查询模型。相反，攻击者只能从与受害者模型相同的问题域中收集少量示例。这种更强大的威胁模型大大扩展了对抗性攻击的适用性。实验表明，在prototypical auto-encoding models上制作的对抗样本可以很好地转移到各种图像分类和人脸验证模型中。在www.example.com拥有的商业名人识别系统上clarifai.com，此方法将系统的平均预测准确率显著降低到仅15.40%，这与从预训练的Arcface模型中转移对抗样本的攻击相当。No-box攻击相比于现在基于查询或转移的黑盒攻击来说，攻击条件更为严格:攻击者既不能访问大规模的训练数据，也无法查询模型，只能通过从同一问题域中收集少量的辅助样本。文章的亮点在于基于无框攻击的假设，提出了在少样本(few-shot)情况下训练"替代"模型的方法。具体来说有三种训练机制：1）估计每张旋转图像的正视图；2）估计每个拼图的正确组合；3）原型图像重建。这三种策略中，前两种属于自监督学习(Self-Supervised Learning)，第三种属于监督学习。结果是第三种基于图像重建的训练方式效果最好。替代模型采用的是编码器-解码器架构的image-to-image auto-encoding models；模型预测时，首先将样本重建，通过判断重建后的样本与样本库之间的相似性确定其类别。制作对抗样本则采用在"替代"模型上应用I-FGSM方法(源码中还使用了PGD)。最后在编码器输出上还使用了ILA。ILA aims to enlarge intermediate-level perturbations in the direction of guiding examples,by maximizing projections on their mid-layer representations训练策略目标：假设良性样本$x_0$被扰动，使得被错误分类到其他标签中。我们的目标是在一个小型的数据集$\textstyle{\mathcal{X}}:=\{(x_{i},y_{i})\}_{i=0}^{n-1}$上训练一个判别(替代)模型，包括扰动的实例$x_0$。我们首先考虑仅涉及两个类的数据集，即，$y_i ∈ \{0,1\}$。在本文中，如果没有其他说明，限制n ≤ 20,从而满足few-shot的条件。对于传统监督学习来说，比如交叉熵损失+Softmax的分类DNN，少样本训练很容易带来过拟合的问题。模型的train loss会很快下降收敛，但是valid loss下降到一定程度就进入平台期(极端一点来说，就是模型记住了每张训练集图像，并没有从训练集中学到有用的特征拿来分类)。虽然可以通过数据增广、权重衰退、BN层等正则化手段弥补少样本的不足，但是在无框假设下这些手段还是不能很好地训练替代模型。(毕竟才20张图片，实在是太少了)下图是传统监督学习(以VGG/Resnet为例)在小型数据集的表现，第二行加入了各种正则化手段，基本没什么效果。(最近打比赛深刻认识到数据质量/数量的重要性在传统监督学习效果有限的情况下，作者采用了image to image方法，它在建模数据的内部分布比较成功，可能适合无框攻击的假设。在这种情况下，作者提出了他的受害者替代模型：编码器-解码器架构的image-to-image auto-encoding models。并在此基础上提出了三种训练策略(传统的交叉熵损失在这里不适用)。Reconstruction from chaos前两种方法属于自监督学习。一个是预测图像旋转角度，另一个是拼图重建，这两种方法已经证明可以提高DNN的辨别能力。这两个人物的学习目标可以表述为：$$ {\cal L}_{\mathrm{rotation/jigsaw}}={\frac{1}{n}}\sum_{i=0}^{n-1}\left|\right|\mathrm{Dec}(\mathrm{Enc}(T(x_{i})))-x_{i}|^{2} $$其中$T(.)$指旋转操作或裁剪拼接操作。虽然学习任务仍然是无监督的，但pretext information(前置任务的语义信息)是与内容相关的，应该有利于分类。Prototypical image reconstruction原型图像重建是一种监督学习。具体策略是：鼓励模型重建特定类的原型，例如输入属于1类的样本，模型的输出应该更靠近1类的图像。形式化上是最小化以下公式$$ {\cal L}_{\mathrm{prototypical}}=\frac{1}{n}\sum_{i=0}^{n-1}((1-y_{i})||\mathrm{Dec}(\mathrm{Enc}(x_{i}))-x^{(0)}||^{2}+y_{i}||\mathrm{Dec}(\mathrm{Enc}(x_{i}))-x^{(1)}||^{2}) $$其中$x^{(0)}∈ {x_i|y_i = 0}$和$x^{(1)}∈ {x_i|y_i = 1}$是分别是从两个类中随机选择的图像原型。这种训练策略迫使模型必须区分具有不同标签的样本，从而达到分类的效果。上式采用的是一个编码器的情况，其实也可以将模型拓展到多个解码器，将在实验中提及。模型分类当使用训练好的模型分类时，由于模型输出同样是张图片，所以无法使用softmax等基于概率的分类方法。这里借鉴无监督学习的思想，首先将预测样本输入模型得到重建图像，通过比对重建图像与样本库每张图片的距离(如欧式距离或余弦距离),将最接近的那张图像的标签作为预测值。形式化表示为$$ \hat{y}=\arg\mathrm{min}||\mathrm{Dec}(\mathrm{Enc}(x))-x^{(y)}||. $$通过这种预测方法，实验证明它相比于传统的监督学习方法更少收到过拟合的影响，并且在此基础上生成的对抗样本更具有迁移性。对抗样本生成在image-to-image auto-encoding models采用基于梯度的攻击如I-FSGM、PGD来生成对抗样本。形式化来说就是最大化下式$$ L_{\mathrm{adverarial}}=-\log p(y_{i}|x_{i})\quad{\mathrm{where}}\quad p(y_{i}|x_{i})={\frac{\exp{(-\lambda||\mathrm{Dec}(\mathrm{Enc}(x_{i}))}-{\tilde{x}_{i}}||^{2})}{\sum_{j}\exp{(-\lambda||\mathrm{Dec}(\mathrm{Enc}(x_{i}))-{\tilde{x}}_{j}}||^{2})}} $$其中$\lambda$是缩放因子，使用I-FSGM时取$\lambda=1$.其中$\tilde{x}_{i}$是正类样本，$\tilde{x}_{j}$是负类样本。上式的含义就是最大化对抗样本与正样本之间的距离，最小化对抗样本与负类样本之间的距离。在encoders输出后，还结合了ILA进行攻击。ILA通过最大化中间层表示的投影，在指导样本的方向上扩大中间层的扰动。(不是很了解)总结来说，首先对模型应用基于梯度的基线攻击（例如I-FGSM）最大化$L_{adversarial}$，然后对编码器的输出执行ILA。实验论文在两个任务上进行了实验，图片分类与人脸识别。对于图像分类，我们基于良性ImageNet图像，在最大扰动($L_\inf$)不大于0.1或0.08的约束下制作对抗样本。这些图像是从ImageNet验证集中选择的。从一半的ImageNet类中随机选择了5000张图像（即，500个类和每个类10个图像）来生成对抗样本。对于人脸验证，首先在LFW数据集上攻击开源模型，最大扰动不超过0.1，然后使用clarifai.com拥有的商业系统进行测试。我们从LFW的400多个身份中随机抽取了2110张图像，这些图像保证尚未用于训练所有相关的受害者模型。实验配置采用CycleGAN 的生成器作为替代模型，即上文提到的auto-encoding models使用来自两个类的不超过20个图像（即n ≤ 20）来训练每个替代模型，并在此基础上执行像I-FGSM的基于梯度的基线攻击。训练策略总结为两个无监督（根据旋转和拼图的重构）和一个受监督的（原型重建）训练机制。其中旋转只考虑了四个方向：0°、90°、180°和270°；对于拼图，将原始图像均匀地切割成四个图块，然后进行洗牌。所有模型都使用ADAM进行了最多15000次迭代的训练，固定学习率为0.001。如果在每个小训练集上达到性能平台期，训练可以提前停止。替代模型训练结束，首先运行基线攻击（I-FGSM）进行200次迭代，然后再迭代100次运行ILA。对于所有实验，λ简单地设定为1。其中I-FGSM的优化步长为1/255(ImageNet和LFW都一样)。对比模型共对比了以下四种模型做替代模型的效果。naive †：是传统监督学习的Resnet，使用数据增强和正则化进行训练naive ‡：是无监督学习的自动编码器(网络架构和原型重建一致，但是只使用旋转和拼图两种无监督学习的训练方式)Beyonder：是大规模预训练的模型，甚至可能与受害者模型共享数据集(传统基于迁移的黑盒攻击)Prototypical：论文中提到的基于图像重建的监督学习方法。攻击模型在以下模型上测试了对抗样本的迁移性与攻击成功率。在ImageNet上测试了非常不同的受害者模型，包括带BN的VGG-19 、Inception v3 、ResNet-152 、DenseNet 、SENet 、广域ResNet（WRN）、PNASNet和MobileNet v2。对于人脸验证，我们采用了FaceNet 和Cosface。实验效果首先是Prototypical模型的训练效果。观察loss可知，相比于之前的迅速收敛，这里Test Loss还有下降的趋势，可以说受到过拟合的影响较小。Test的精度较传统VGG来说也有所提升。还对比了不同解码器数量的Prototypical模型表现，当使用多个解码器时，模型预测的标签表示为$$ \hat{y}=\mathrm{arg\space min}\,\frac{1}{K}\sum_{k=0}^{K-1}||\mathrm{Dec}_{k}{\big(}\mathrm{E}\mathrm{n}{\mathrm{c}}{\big(}x{\big)}{\big)}-x_{k}^{(y)}|| $$其效果与单个解码器相比也有提升。受害者模型的预测精度如下图，可以发现$Prototypical*$产生的对抗样本平均攻击效果最好。对于人脸验证攻击，展示了不同攻击方式下受害者模型的ROC曲线。可以发现有时候基于图像重建的方式甚至效果比大规模的预训练模型Beyonder还好。对于人脸验证的对抗样本生成，对抗损失需要稍作修改，因为一对面孔通常通过计算嵌入空间中的余弦相似度来进行比较。结论文章考虑了一个现实的威胁模型来执行对抗攻击，基于无框的假设，查询模型和大规模训练、获取受害者模型的内部信息等方式都被禁止。文章设法使用有限的数据来训练模型(few-shot)，基于这些数据可以制作可转移的对抗样本。具体提出了无框设置中的替代模型训练的不同机制(3种)。其中表现最好的是有监督的图像重建，它产生的对抗样本可以很好地转移到各种先进分类器上。这种基于few-shot和Self-Supervised Learning的方法的有效性在图像分类和人脸验证上进行了测试。实验结果表明，该方法的性能优于传统的监督学习方法，有时甚至可以与传统基于黑盒的攻击(共享训练集)相媲美。
简介本篇论文主要是对AdvGAN进行了一些小改进,证明了在非定向攻击中，潜在特征作为对抗生成的先验比整个输入图像更好，同时消除了对生成器遵循基于编码器-解码器的架构的需要，从而减少了训练/推理开销。论文地址：只有4页的AdvGAN++网络框架跟AdvGAN相比，主要区别在于生成器G的输入产生了变化，从原来的原始图像x变成了图像x的特征图与噪声向量的级联。而图像x的特征图是通过目标网络M的特征提取器f得到的。这两点就是AdvGAN和原始版本的最大区别。损失函数与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的理论证明.总结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$ (定向攻击)之间的距离，或预测值(除去真实类别外概率最大的那个类别的概率值)与真实类别的(非定向攻击)之间的距离。对于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$.具体来说分为以下两个步骤.对于固定的$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}} $$对于固定的生成器$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}))) $$作者在这里测试了一下静态蒸馏与动态蒸馏的性能,突出动态蒸馏的优越性.第一行是几个模型对于原始样本的准确率,第二行是半白盒攻击的攻击成功率,第三行和第四行分别是动态蒸馏和静态蒸馏下的攻击成功率.可以看到效果还是很明显的.实验结果作者首先评估AdvGAN在MNIST和CIFAR-10上的半白盒和黑盒攻击方式,还对ImageNetdataset进行了半白盒攻击。随后对于不同防御手段下测试了AdvGAN的攻击成功率，并表明与其他现有攻击策略相比，AdvGAN方法可以实现更高的攻击成功率。(在基于$L_\infty$约束的MNIST的0.3阈值和CIFAR-10的8阈值上进行公平比较)。Adv-GAN具有优于其他白盒和黑盒攻击的几个优点。例如，关于计算效率，AdvGAN执行速度比其他人快得多，甚至包括有效的FGSM，尽管AdvGAN需要额外的训练时间来训练生成器。除了基于可转移性的攻击,所有对比的模型都可以进行定向攻击,(通过集合策略可以帮助改进Trans Attack)。此外，FGSM和优化方法只能执行白盒攻击，而AdvGAN能够在半白盒设置中进行攻击。代码部分参考这里的半白盒攻击实现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 xAdvGAN损失函数损失函数分为三部分,代码实现里$\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%接下来训练AdvGAN,训练60个epoch后损失如下.其实D训练的很好,说明对抗样本隐蔽性做的不太行,$L_{adv}$不高,说明攻击效果应该是达到的.接下来使用对抗样本测试一下目标模型的accuracy.这里同样使用的测试集(增加扰动后).最后得到的准确率大约为0.3%(训练集的准确率是0.15%).好!输出一下生成的对抗样本图像结束AdvGAN.(经典雪碧图)