zoukankan      html  css  js  c++  java
  • pytorch的backward

    在学习的过程中遇见了一个问题,就是当使用backward()反向传播时传入参数的问题:

    net.zero_grad() #所有参数的梯度清零
    output.backward(Variable(t.ones(1, 10))) #反向传播

    这里的backward()中为什么需要传入参数Variable(t.ones(1, 10))呢?没有传入就会报错:

    RuntimeError: grad can be implicitly created only for scalar outputs

    这个错误的意思就是梯度只能为标量(即一个数)输出隐式地创建

    比如有一个例子是:

    1)

    #使用Tensor新建一个Variable
    x = Variable(t.ones(2, 2),requires_grad = True)
    x

    返回:

    tensor([[1., 1.],
            [1., 1.]], requires_grad=True)

    此时查看该值的grad和grad_fn是没有返回值的,因为没有进行任何操作

    x.grad_fn
    x.grad

    进行求和操作,查看梯度

    y = x.sum()
    y

    返回:

    tensor(4., grad_fn=<SumBackward0>)

    这时候可查看:

    y.grad_fn

    返回:

    <SumBackward0 at 0x122782978>

    可知y是变量Variable x进行sum操作求来的,但是这个时候y.grad是没有返回值的,因为没有使用y进行别的操作

    这个时候的x.grad也是没有值的,虽然使用x进行了sum操作,但是还没有对y反向传播来计算梯度

    y.backward()#反向传播,计算梯度

    然后再查看:

    #因为y = x.sum() = (x[0][0] + x[0][1] + x[1][0] + x[1][1])
    #每个值的梯度都为1
    x.grad

    返回:

    tensor([[1., 1.],
            [1., 1.]])

     在这里我们可以看见y能够求出x的梯度,这里的y是一个数,即标量

    如果这里我们更改一下y的操作,将y设置为一个二维数组:

    from __future__ import print_function
    import torch as t
    from torch.autograd import Variable
    x = Variable(t.ones(2, 2),requires_grad = True)
    y = x + 1
    y.backward()

    然后就会报上面的错误:

    RuntimeError: grad can be implicitly created only for scalar outputs

    总结:

    因此当输出不是标量时,调用.backwardI()就会出错

    解决办法:

    显示声明输出的类型作为参数传入,且参数的大小必须要和输出值的大小相同

    x.grad.data.zero_() #将之前的值清零
    x.grad

    返回:

    tensor([[0., 0.],
            [0., 0.]])

    进行反向传播:

    y.backward(y.data)
    x.grad

    也可以写成,因为Variable和Tensor有近乎一致的接口

    y.backward(y)
    x.grad

    返回:

    tensor([[2., 2.],
            [2., 2.]])

    但是这里返回值与预想的1不同,这个原因是得到的梯度会与参数的值相乘,所以最好传入值为1,如:

    y.backward(Variable(t.ones(2, 2)))
    x.grad

    这样就能够成功返回想要的值了:

    tensor([[1., 1.],
            [1., 1.]])

    更加复杂的操作:

    在上面的例子中,x和y都是(2,2)的数组形式,每个yi都只与对应的xi相关

    1)如果每个yi都与多个xi相关时,梯度又是怎么计算的呢?

    比如x = (x1 = 2, x2 = 4), y = (x12+2x2, 2x1+3x22)

    (i,j)的值就是传入.backward()的参数的值

    x = Variable(t.FloatTensor([[2, 4]]),requires_grad = True)
    y = Variable(t.zeros(1, 2))
    y[0,0] = x[0,0]**2 + 2 * x[0,1]
    y[0,1] = 2 * x[0,0] + 3 * x[0,1]**2
    y.backward(Variable(t.ones(1, 2))) #(i,j)= (1,1)
    x.grad

    返回:

    tensor([[ 6., 26.]])

    2)如果x和y不是相同的数组形式,且每个yi都与多个xi相关时,梯度又是怎么计算的呢?

    比如x = (x1 = 2, x2 = 4, x3=5), y = (x12+2x2+4x3, 2x1+3x22+x32)

    x = Variable(t.FloatTensor([[2, 4, 5]]),requires_grad = True)
    y = Variable(t.zeros(1, 2))
    y[0,0] = x[0,0]**2 + 2 * x[0,1] + 4 * x[0,2]
    y[0,1] = 2 * x[0,0] + 3 * x[0,1]**2 + x[0,2]**2
    y.backward(Variable(t.ones(1, 2)))
    x.grad

    返回:

    tensor([[ 6., 26., 14.]])

    如果(i, j) = (2,2),结果是否为(12, 52, 28)呢?

    x = Variable(t.FloatTensor([[2, 4, 5]]),requires_grad = True)
    y = Variable(t.zeros(1, 2))
    y[0,0] = x[0,0]**2 + 2 * x[0,1] + 4 * x[0,2]
    y[0,1] = 2 * x[0,0] + 3 * x[0,1]**2 + x[0,2]**2
    y.backward(Variable(t.FloatTensor([[2, 2]])))
    x.grad

    返回:

    tensor([[12., 52., 28.]])

    3)如果你想要分别得到y1,y2对x1,x2,x3的求导值,方法是:

    x = Variable(t.FloatTensor([[2, 4, 5]]),requires_grad = True)
    y = Variable(t.zeros(1, 2))
    y[0,0] = x[0,0]**2 + 2 * x[0,1] + 4 * x[0,2]
    y[0,1] = 2 * x[0,0] + 3 * x[0,1]**2 + x[0,2]**2
    j = t.zeros(3,2)#用于存放求导的值
    #(i,j)=(1,0)这样就会对应只求得y1对x1,x2和x3的求导
    #retain_variables=True的作用是不在反向传播后释放内存,这样才能够再次反向传播
    y.backward(Variable(t.FloatTensor([[1, 0]])),retain_variables=True) 
    j[:,0] = x.grad.data
    x.grad.data.zero_() #将之前的值清零
    #(i,j)=(1,0)这样就会对应只求得y2对x1,x2和x3的求导
    y.backward(Variable(t.FloatTensor([[0, 1]]))) 
    j[:,1] = x.grad.data
    print(j)

    报错:

    TypeError: backward() got an unexpected keyword argument 'retain_variables'

    原因是新版本使用的参数名为retain_graph,改了即可:

    x = Variable(t.FloatTensor([[2, 4, 5]]),requires_grad = True)
    y = Variable(t.zeros(1, 2))
    y[0,0] = x[0,0]**2 + 2 * x[0,1] + 4 * x[0,2]
    y[0,1] = 2 * x[0,0] + 3 * x[0,1]**2 + x[0,2]**2
    j = t.zeros(3,2)#用于存放求导的值
    #(i,j)=(1,0)这样就会对应只求得y1对x1,x2和x3的求导
    #retain_graph=True的作用是不在反向传播后释放内存,这样才能够再次反向传播
    y.backward(Variable(t.FloatTensor([[1, 0]])),retain_graph=True) 
    j[:,0] = x.grad.data
    x.grad.data.zero_() #将之前的值清零
    #(i,j)=(1,0)这样就会对应只求得y2对x1,x2和x3的求导
    y.backward(Variable(t.FloatTensor([[0, 1]]))) 
    j[:,1] = x.grad.data
    print(j)

    返回:

    tensor([[ 4.,  2.],
            [ 2., 24.],
            [ 4., 10.]])
  • 相关阅读:
    开网页自动进入路由器设置界面的解决办法(腾达路由器)
    SQL基本语句
    驱动调试配置
    【转】snort
    【转】snort.conf分析(中文)
    【转】snort 笔记2 ----- 规则编写
    【转】Snort语法规则说明及实例讲解
    【转】Snort 命令参数详解
    POST教程笔记
    POST教程笔记
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10616344.html
Copyright © 2011-2022 走看看