深度学习(三):梯度计算

文章目录

1 概念

  Tensor是autograd包的核心类,若将其属性.requires_grad设置为True,它将开始追踪在其上的所有操作。完成计算后,可以调用 .backward()来完成所有梯度计算。此Tensor的梯度将累计到.grad属性中。若要停止追踪,则方法如下:

  • 调用.detach()
  • with torch.no_grad(): 包裹的代码块将不会被追踪

2 示例

示例1

import torch

def my_grad():
    x = torch.ones(2, 2, requires_grad=True)
    print(x)
    print(x.grad_fn)    #该tensor若为计算所得,则输出一个与这些计算相关的对象,否则为None
    y = x + 2
    print(y)
    print(y.grad_fn)
    
if __name__ == "__main__":
    my_grad()

  运行结果:

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
None
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x00000178B6D15C18>

  像x这种直接创建的的称为 叶子节点

示例2:计算指定函数关于x的梯度

import torch

def my_grad():
    x = torch.ones(2, 2, requires_grad=True)
    y = x + 2
    z = y * y * 3
    z_mean = z.mean()
    z_mean.backward()    #等价z_mean.backward(torch.tensor(1.))
    print(x.grad)

if __name__ == "__main__":
    my_grad()

  运行结果:

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

  需要注意的是.backward(),如果当前tensor是标量,则不需要为backward()传入参数;否则,需要传入一个与当前tensor同行的tensor。这样做的好处在于避免tensor对tensor求导,只允许标量对tensor求导,则求导结果是和自变量同形的tensor。
  当然,grad在反向传播的过程中是累加的,这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前把梯度清零,如以下:

示例3:梯度清零

import torch

def my_grad():
    x = torch.ones(2, 2, requires_grad=True)
    y = x + 2
    z = y * y * 3
    z_mean = z.mean()
    z_mean.backward()

    z_sum1 = x.sum()
    z_sum1.backward()
    print(x.grad)
    
    z_sum2 = x.sum()
    x.grad.data.zero_()    #梯度清零
    z_sum2.backward()
    print(x.grad)

if __name__ == "__main__":
    my_grad()

  运行结果:

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

示例4:当前tensor非标量

import torch

def my_grad():
    x = torch.tensor([[1.0, 2.0], [3.0, 4.0]], requires_grad=True)
    y = x * 2
    print(y)
    z = torch.tensor([[1.0, 1.0], [1.0, 1.0]])    #传入与y同形的权重向量
    y.backward(z)
    print(x.grad)

if __name__ == "__main__":
    my_grad()

  运行结果:

tensor([[2., 4.],
        [6., 8.]], grad_fn=<MulBackward0>)
tensor([[2., 2.],
        [2., 2.]])

  需要注意的是:
  (1)z = torch.tensor([[1.0, 1.0], [1.0, 1.0]]) 为权重向量,代表求梯度时的偏置;
  (2)x.grad与x同形。

示例5:中断梯度追踪

import torch

def my_grad():
    x = torch.tensor(1.0, requires_grad=True)
    y = x ** 2
    with torch.no_grad():
        y2 = x ** 2
    y3 = y + y2
    
    print(x.requires_grad)
    print(y, y.requires_grad)
    print(y2, y2.requires_grad)
    print(y3, y3.requires_grad)
    y3.backward()
    print(x.grad)

if __name__ == "__main__":
    my_grad()

  运行结果:

True
tensor(1., grad_fn=<PowBackward0>) True
tensor(1.) False
tensor(2., grad_fn=<AddBackward0>) True
tensor(2.)

  可以看到y2没有grad_fn,y2.requires_grad=False,且y3对x求梯度为2.,原因在于y2被with torch.no_grad():包裹,与y2相关的梯度没有回传。

示例6:操作tensor.data
  对tensor.data操作的好处在于,既可以修改tensor的值,又不会被autograd记录,即不会反向传播。

import torch

def my_grad():
    x = torch.tensor(1.0, requires_grad=True)
    
    print(x.data)    #依旧是一个tensor
    print(x.data.requires_grad)     #但不会影响反向传播
    
    y = x * 2
    x.data *= 100    #只是改变了值
    
    y.backward()
    print(x)    #更改data的值也会影响tensor的值
    print(x.grad)

if __name__ == "__main__":
    my_grad()

  运行结果:

tensor(1.)
False
tensor(100., requires_grad=True)
tensor(2.)
原创文章 35 获赞 44 访问量 8631

猜你喜欢

转载自blog.csdn.net/weixin_44575152/article/details/104559116