美文网首页
Torch requires_grad / backward /

Torch requires_grad / backward /

作者: winddy_akoky | 来源:发表于2020-01-06 15:23 被阅读0次

    本教程解决以下问题:

    求一个变量(如loss值)关于某个变量的梯度值是,即便变量的requires_grad属性设置成True,该变量的grad还是等于None。

    介绍

    如果你也跟我一样是在学习对抗样本,那肯定离不开要对loss函数关于输入样本求梯度值,进而构造对抗样本。但是我一开始并不知道如何求loss函数关于输入的梯度值,也不知道如何获取这个梯度值。即便我在网上找到了源代码,也很难理解。而这篇文章就记录了我的学习过程以及理解。

    求对抗样本

    下面是我整合网上的代码自己写的一份关于FSGM的代码。你会发现获取Loss函数关于输入的梯度的关键是这句:

    input_for_gradient = Variable(input, requires_grad=True)
    

    其中requires_grad=True属性设置成True后,使得我们在调用backward后,input_for_gradient 变量的梯度值能够被保存下来。因此,实际上,一般情况下只要把这个属性设置成True,我们就可以正常求关于某一个变量的梯度值了。

    class FGSM:
        def __init__(self, model, criterion, epsilon, device):
            self.model = model
            self.criterion = criterion
            self.epsilon = epsilon
            self.device = device
            assert isinstance(model, torch.nn.Module), "Input parameter model is not nn.Module. Check the model"
            assert isinstance(criterion, torch.nn.Module), "Input parameter criterion is no Loss. Check the criterion"
            assert (0 <= epsilon <= 1), "episilon must be 0 <= epsilon <= 1"
            self.model.eval()
    
    
        def __call__(self, input, labels):
            # For calculating gradient
            input_for_gradient = Variable(input, requires_grad=True).to(self.device)
            out = self.model(input_for_gradient)
            loss = self.criterion(out, Variable(labels))
    
            # Calculate gradient
            loss.backward()
    
            # Calculate sign of gradient
            signs = torch.sign(input_for_gradient.grad.data)
    
            # Add
            input_for_gradient.data = input_for_gradient.data + (self.epsilon * signs)
    
            return input_for_gradient, signs
    

    问题

    上面说的情况都很理想,一旦你手动实践后,就很容易碰到各种各样的bug,比如下面这个bug困惑了我一下下午:

    xx = Variable(torch.Tensor([1,2,3]), requires_grad=True)
    # xx = torch.Tensor([1,2,3])
    # xx.requires_grad = True
    xx = xx.to(DEVICE)
    
    yy = xx ** 2
    zz = yy.mean()
    # # zz.backward(xx, retain_graph=True)
    zz.backward()
    
    print(xx.grad)
    print(xx.is_leaf)
    print(xx.requires_grad)
    
    
    None
    False
    True
    

    xx 是一个Variable(后面会再来讨论这个),该变量的requires_grad属性设置成True,然后把这个变量移入CUDA中,再做几个简单的算术操作后,调用zz.backward()来计算梯度值。但是,从结果上看,xx的grad竟然为None!!!而且竟然不是叶子节点!!!

    解决方法

    先直接上代码:

    # xx = Variable(torch.Tensor([1,2,3]), requires_grad=True)
    xx = torch.Tensor([1,2,3])
    xx = xx.to(DEVICE)
    xx.requires_grad = True
    
    yy = xx ** 2
    zz = yy.mean()
    # # zz.backward(xx, retain_graph=True)
    zz.backward()
    
    print(xx.grad)
    print(xx.is_leaf)
    print(xx.requires_grad)
    
    tensor([0.6667, 1.3333, 2.0000], device='cuda:7')
    True
    True
    

    也就是说,放弃用Variable,用Tensor就行,且,注意了(敲黑板),先将变量传入CUDA,再将变量的requires_grad属性设置成True!!!

    你要是先设置requires_grad属性,再传入CUDA,那么这个变量的requires_grad属性设变成False了。如下面所示:


    image.png

    总结

    看一下Variable和Tensor有什么区别。先看一下官网对Variable和Tensor的定义。

    Variable

    image.png

    (图片截自2020/1/6)
    !!!我kao,Variable已经被弃用了!!!不过虽然已经被弃用了,但是Variabel(tensor), Variable(tensor, requires_grad)还是可以正常调用,只不过这个函数不返回Variable对象,而是返回一个Tensor对象。。。。。。

    那么Variable返回的对象跟Tensor一模一样罗?那为毛这个会报错:


    image.png

    先将变量传入CUDA,在修改requires_grad属性就报错???

    但我直接用Tensor是可以这么操作的:


    image.png

    所以,我觉得这两个东西还是不完全一样的!!!

    Tensor

    image.png

    emmmm看一下几个关键的属性

    image.png

    也就是说grad这个值默认是None,但调用backward的时候这个变量可能会被填充。

    image.png

    这里他说,一个张量需要计算梯度的事实并不意味着会填充grad属性。。。。。看下一个

    image.png

    哦,这个意思是说,只有是叶子张量,在调用backward时,这个张量的grad属性才会被填充。。。。。

    然后,我通过检查张量的is_leaf属性,最后我才发现了这个问题的问题所在。

    相关文章

      网友评论

          本文标题:Torch requires_grad / backward /

          本文链接:https://www.haomeiwen.com/subject/ookkactx.html