• 【PyTorch】PyTorch基础知识——自动求导


    1 Autograd简介

    PyTorch 中,所有神经网络的核心是 autograd 包。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。

    本节主要内容:

    • autograd的求导机制
    • 梯度的反向传播

    torch.Tensor 是这个包的核心类。如果设置它的属性 .requires_gradTrue,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性。

    注意:在 y.backward() 时,如果 y 是标量,则不需要为 backward() 传入任何参数;否则,需要传入一个与 y 同形的Tensor

    要阻止一个张量被跟踪历史,可以调用.detach()方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad(): 中。在评估模型时特别有用,因为模型可能具有 requires_grad = True 的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。

    还有一个类对于autograd的实现非常重要:FunctionTensorFunction 互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor 自身的Function(除非这个张量是用户手动创建的,即这个张量的grad_fnNone )。下面给出的例子中,张量由用户手动创建,因此grad_fn返回结果是None

    from __future__ import print_function
    import torch
    x = torch.randn(3,3,requires_grad=True)
    print(x.grad_fn)
    
    >>None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果需要计算导数,可以在 Tensor 上调用 .backward()。如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,该参数是形状匹配的张量。

    创建一个张量并设置requires_grad=True用来追踪其计算历史

    x = torch.ones(2, 2, requires_grad=True)
    print(x)
    
    >>tensor([[1., 1.],
            [1., 1.]], requires_grad=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    做一次运算,由于y是计算的结果,所以它有grad_fn属性。:

    y = x**2 #平方
    print(y)
    print(y.grad_fn)
    
    >>tensor([[1., 1.],
            [1., 1.]], grad_fn=<PowBackward0>)
    <PowBackward0 object at 0x7fb9f80bc5e0>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    y 进行更多操作:

    z = y * y * 3
    out = z.mean()
    
    print(z)
    print(out)
    >>tensor([[3., 3.],
            [3., 3.]], grad_fn=<MulBackward0>)
    tensor(3., grad_fn=<MeanBackward0>)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    .requires_grad_(...) 原地改变了现有张量的requires_grad标志。如果没有指定的话,默认输入的这个标志是 False

    a = torch.randn(2, 2) # 缺失情况下默认 requires_grad = False
    a = ((a * 3) / (a - 1))
    print(a.requires_grad)
    a.requires_grad_(True)
    print(a.requires_grad)
    b = (a * a).sum()
    print(b.grad_fn)
    
    >>False
    True
    <SumBackward0 object at 0x7fba28382fa0>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2 梯度

    现在开始进行反向传播,因为 out 是一个标量,因此out.backward()out.backward(torch.tensor(1.)) 等价。

    out.backward()
    
    • 1

    输出 d ( o u t ) / d x d(out)/dx d(out)/dx:

    print(x.grad)
    >>tensor([[3., 3.],
            [3., 3.]])
    
    • 1
    • 2
    • 3

    注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。

    # 再来反向传播⼀一次,注意grad是累加的
    out2 = x.sum()
    out2.backward()
    print(x.grad)
    
    out3 = x.sum()
    x.grad.data.zero_()
    out3.backward()
    print(x.grad)
    
    >>tensor([[4., 4.],
            [4., 4.]])
    tensor([[1., 1.],
            [1., 1.]])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    看一个雅可比向量积的例子:

    x = torch.randn(3, requires_grad=True)
    print(x)
    
    y = x * 2
    i = 0
    while y.data.norm() < 1000:
        y = y * 2
        i = i + 1
    print(y)
    print(i)
    
    >>tensor([ 0.2176, -0.3949,  0.3115], requires_grad=True)
    tensor([ 445.6994, -808.8474,  637.9488], grad_fn=<MulBackward0>)
    10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这种情况下,y 不再是标量。torch.autograd 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward

    v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
    y.backward(v)
    
    print(x.grad)
    
    >>tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    也可以通过将代码块包装在 with torch.no_grad(): 中,来阻止 autograd 跟踪设置了.requires_grad=True的张量的历史记录。

    print(x.requires_grad)
    print((x ** 2).requires_grad)
    
    with torch.no_grad():
        print((x ** 2).requires_grad)
        
    >>True
    True
    False
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果我们想要修改 tensor 的数值,但是又不希望被 autograd 记录(即不会影响反向传播), 那么我们可以对 tensor.data 进行操作。

    x = torch.ones(1,requires_grad=True)
    
    print(x.data) # 还是一个tensor
    print(x.data.requires_grad) # 但是已经是独立于计算图之外
    
    y = 2 * x
    x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
    
    y.backward()
    print(x) # 更改data的值也会影响tensor的值 
    print(x.grad)
    
    >>tensor([1.])
    False
    tensor([100.], requires_grad=True)
    tensor([2.])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    C#的LINQ to XML 类中使用最多的三个类:XElement、XAttribute 和 XDocument
    Docker目录映射
    SaaSBase:Blue Prism是什么?
    javaweb 之 JDBC 详解
    微信小程序wx.previewImage实现图片预览
    全解析异步编程的几种方法
    【Java面试】HashMap是如何解决hash冲突的?
    10.DesignForSymbols\QuickSearchSymbol
    ai创作工具,怎么使用AI创作工具
    【CC3200AI 实验教程10】疯壳·AI语音人脸识别(会议记录仪/人脸打卡机)-WIFI-TCP网络通信
  • 原文地址:https://blog.csdn.net/qq_42251120/article/details/126837608