BIM&ILLC算法源码解析

论文链接:https://arxiv.org/abs/1607.02533
源码出处:https://github.com/Harry24k/adversarial-attacks-pytorch/tree/master


源码

import torch
import torch.nn as nn

from ..attack import Attack


class BIM(Attack):
    r"""
    BIM or iterative-FGSM in the paper 'Adversarial Examples in the Physical World'
    [https://arxiv.org/abs/1607.02533]

    Distance Measure : Linf

    Arguments:
        model (nn.Module): model to attack.
        eps (float): maximum perturbation. (Default: 8/255)
        alpha (float): step size. (Default: 2/255)
        steps (int): number of steps. (Default: 10)

    .. note:: If steps set to 0, steps will be automatically decided following the paper.

    Shape:
        - images: :math:`(N, C, H, W)` where `N = number of batches`, `C = number of channels`,        `H = height` and `W = width`. It must have a range [0, 1].
        - labels: :math:`(N)` where each value :math:`y_i` is :math:`0 \leq y_i \leq` `number of labels`.
        - output: :math:`(N, C, H, W)`.

    Examples::
        >>> attack = torchattacks.BIM(model, eps=8/255, alpha=2/255, steps=10)
        >>> adv_images = attack(images, labels)
    """
    def __init__(self, model, eps=8/255, alpha=2/255, steps=10):
        super().__init__("BIM", model)
        self.eps = eps
        self.alpha = alpha
        if steps == 0:
            self.steps = int(min(eps*255 + 4, 1.25*eps*255))
        else:
            self.steps = steps
        self.supported_mode = ['default', 'targeted']

    def forward(self, images, labels):
        r"""
        Overridden.
        """
        self._check_inputs(images)

        images = images.clone().detach().to(self.device)
        labels = labels.clone().detach().to(self.device)

        if self.targeted:
            target_labels = self.get_target_label(images, labels)

        loss = nn.CrossEntropyLoss()

        ori_images = images.clone().detach()

        for _ in range(self.steps):
            images.requires_grad = True
            outputs = self.get_logits(images)

            # Calculate loss
            if self.targeted:
                cost = -loss(outputs, target_labels)
            else:
                cost = loss(outputs, labels)

            # Update adversarial images
            grad = torch.autograd.grad(cost, images,
                                       retain_graph=False,
                                       create_graph=False)[0]

            adv_images = images + self.alpha*grad.sign()
            a = torch.clamp(ori_images - self.eps, min=0)
            b = (adv_images >= a).float()*adv_images + (adv_images < a).float()*a
            c = (b > ori_images+self.eps).float()*(ori_images+self.eps) + (b <= ori_images + self.eps).float()*b
            images = torch.clamp(c, max=1).detach()

        return images

解析

BIM算法(Basic Iterative Method)又叫迭代FGSM算法(I-FGSM),FGSM算法假设目标损失函数 J ( x , y ) J(x,y) J(x,y) x x x之间是近似线性的,即 J ( x , y ) ≈ w T x J(x ,y)≈w^Tx J(x,y)wTx,所以沿着梯度上升的方向改变输入 x x x可以增大损失,从而达到使模型分类错误的目的。但是这个假设不一定正确,即 J ( x , y ) J(x,y) J(x,y) x x x之间可能不是线性的,也就是说改变在 ( 0 , ϵ s i g n ( ▽ x J ( θ , x , y ) ) ) (0,\epsilon sign(\bigtriangledown_{x}J(\theta,x,y))) (0,ϵsign(xJ(θ,x,y)))之间可能存在某个扰动,使得 J J J增加得更多。于是本篇论文就提出迭代的方式来找各个像素点的扰动,而不是一次性所有像素都改那么多,即迭代式的FGSM。公式如下所示:
X 0 a d v = X , X N + 1 a d v = C l i p X , ϵ { X N a d v + α s i g n ( ▽ x J ( X N a d v , y t r u e ) ) } X^{adv}_0=X,X^{adv}_{N+1}=Clip_{X,\epsilon}\{X^{adv}_N+\alpha sign(\triangledown_{x}J(X^{adv}_N,y_{true}))\} X0adv=X,XN+1adv=ClipX,ϵ{XNadv+αsign(xJ(XNadv,ytrue))}
迭代的含义:每次在上一步的对抗样本的基础上,各个像素增长 α \alpha α(或者减少),然后再执行裁剪,保证新样本的各个像素都在 x x x ϵ \epsilon ϵ邻域内。这种迭代的方法是有可能在各个像素变化小于 ϵ \epsilon ϵ的情况下找到对抗样本的,如果找不到,最差的效果就跟原始的FGSM一样。
裁剪( C l i p Clip Clip)的作用:在迭代更新过程中,随着增加扰动的次数的增加,样本的部分像素值可能会溢出,比如超出0到1的范围,这时需将这些值用0或1代替,最后才能生成有效的图像。
论文还提出了ILLC算法(Iterative Least Likely Class Attack),即使用类似的迭代的方法进行有目标攻击,公式如下所示: X 0 a d v = X , X N + 1 a d v = C l i p X , ϵ { X N a d v − α s i g n ( ▽ x J ( X N a d v , y t a r g e t ) ) } X^{adv}_0=X,X^{adv}_{N+1}=Clip_{X,\epsilon}\{X^{adv}_N-\alpha sign(\triangledown_{x}J(X^{adv}_N,y_{target}))\} X0adv=X,XN+1adv=ClipX,ϵ{XNadvαsign(xJ(XNadv,ytarget))}其中 y t a r g e t = arg min ⁡ y ( y ∣ X ) y_{target}=\argmin\limits_y(y\mid X) ytarget=yargmin(yX)也就是与样本 X X X偏离最远的错误类。

eps:即 ϵ \epsilon ϵ,表示最大扰动。
alpha:即 α \alpha α,表示每次迭代中扰动的增加量(或减少量)。
steps:表示迭代次数,论文中将其设为 m i n ( ϵ + 4 , 1.25 ϵ ) min(\epsilon+4,1.25\epsilon) min(ϵ+4,1.25ϵ),因为论文中认为这足够对抗性示例到达最大范数球的边缘,同时也保证实验的计算成本可控。由于代码中图像已经被归一化为 [ 0 , 1 ] [0,1] [0,1] ϵ \epsilon ϵ也在该范围内,所以steps即为 m i n ( ϵ × 255 + 4 , 1.25 ϵ × 255 ) min(\epsilon\times255+4,1.25\epsilon\times255) min(ϵ×255+4,1.25ϵ×255)
images = images.clone().detach().to(self.device)clone()将图像克隆到一块新的内存区(pytorch默认同样的tensor共享一块内存区);detach()是将克隆的新的tensor从当前计算图中分离下来,作为叶节点,从而可以计算其梯度;to()作用就是将其载入设备。
target_labels = self.get_target_label(images, labels):若是有目标攻击的情况,获取目标标签。目标标签的选取有多种方式,例如可以选择与真实标签相差最大的标签,也可以随机选择除真实标签外的标签。
loss = nn.CrossEntropyLoss():设置损失函数为交叉熵损失。
ori_images = images.clone().detach():保存原始图像,用于裁剪过程。
outputs = self.get_logits(images):获得图像的在模型中的输出值。
cost = -loss(outputs, target_labels):有目标情况下计算损失
cost = loss(outputs, labels):无目标情况下计算损失
grad = torch.autograd.grad(cost, images, retain_graph=False, create_graph=False)[0]costimages求导,得到梯度grad
adv_images = images + self.alpha*grad.sign():根据公式在图像上沿着梯度上升方向以步长为 α \alpha α增加扰动。

a = torch.clamp(ori_images - self.eps, min=0)  # a为图像最小值,即减去最大扰动值
b =(adv_images >= a).float()*adv_images + (adv_images < a).float()*a  # 将对抗图像小于a的部分设为a
c = (b > ori_images+self.eps).float()*(ori_images+self.eps) + (b <= ori_images + self.eps).float()*b  # 将b中超出扰动范围的值设为最大值
images = torch.clamp(c, max=1).detach()  # 将c中超出1的值设为1

以上四行代码为裁剪( C l i p Clip Clip)过程,得到有效的图像。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值