• 2-2 自动微分机制


    神经网络是如何更新网络参数的呢?
    为什么需要自动微分机制?
    以及什么是自动微分机制?
    神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情。而深度学习框架可以帮助我们自动地完成这种求梯度运算。
    Pytorch一般通过反向传播 backward 方法 实现这种求梯度计算。该方法求得的梯度将存在对应自变量张量的grad属性下。
    除此之外,也能够调用torch.autograd.grad 函数来实现求梯度计算。
    这就是Pytorch的自动微分机制。

    一、利用backward方法求导数

    backward 方法通常在一个标量张量上调用,该方法求得的梯度将存在对应自变量张量的grad属性下。
    如果调用的张量非标量,则要传入一个和它同形状的gradient参数张量。
    相当于用该gradient参数张量与调用张量作向量点乘,得到的标量结果再反向传播

    1.标量的反向传播

    import numpy as np 
    import torch 
    
    # f(x) = a*x**2 + b*x + c的导数
    
    x = torch.tensor(0.0,requires_grad = True) # x需要被求导
    a = torch.tensor(1.0)
    b = torch.tensor(-2.0)
    c = torch.tensor(1.0)
    y = a*torch.pow(x,2) + b*x + c 
    
    y.backward() # 利用backward() 注意是y调用这个方法 而前面已经说明 x 需要被求导
    dy_dx = x.grad # 
    print(dy_dx)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image.png

    2.非标量的反向传播

    如果调用的张量非标量,则要传入一个和它同形状 的gradient参数张量。

    import numpy as np 
    import torch 
    
    # f(x) = a*x**2 + b*x + c
    
    x = torch.tensor([[0.0,0.0],[1.0,2.0]],requires_grad = True) # x需要被求导
    a = torch.tensor(1.0)
    b = torch.tensor(-2.0)
    c = torch.tensor(1.0)
    y = a*torch.pow(x,2) + b*x + c 
    
    gradient = torch.tensor([[1.0,1.0],[1.0,1.0]])
    
    print("x:\n",x)
    print("y:\n",y)
    y.backward(gradient = gradient)
    x_grad = x.grad
    print("x_grad:\n",x_grad)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    image.png

    3.非标量的反向传播可以用标量的反向传播实现

    接2 2就相当于用该gradient参数张量与调用张量作向量点乘,得到的标量结果再反向传播。

    import numpy as np 
    import torch 
    
    # f(x) = a*x**2 + b*x + c
    
    x = torch.tensor([[0.0,0.0],[1.0,2.0]],requires_grad = True) # x需要被求导
    a = torch.tensor(1.0)
    b = torch.tensor(-2.0)
    c = torch.tensor(1.0)
    y = a*torch.pow(x,2) + b*x + c 
    
    gradient = torch.tensor([[1.0,1.0],[1.0,1.0]])
    z = torch.sum(y*gradient) # torch.sum计算标量相乘获取标量之后再反向传播
    
    print("x:",x)
    print("y:",y)
    z.backward()
    x_grad = x.grad
    print("x_grad:\n",x_grad)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    image.png

    二、利用autograd.grad方法求导数

    import numpy as np 
    import torch 
    
    # f(x) = a*x**2 + b*x + c的导数
    
    x = torch.tensor(0.0,requires_grad = True) # x需要被求导
    a = torch.tensor(1.0)
    b = torch.tensor(-2.0)
    c = torch.tensor(1.0)
    y = a*torch.pow(x,2) + b*x + c
    
    
    # create_graph 设置为 True 将允许创建更高阶的导数 
    dy_dx = torch.autograd.grad(y,x,create_graph=True)[0] # 去除[0]报错 AttributeError: 'tuple' object has no attribute 'data'
    print(dy_dx.data) # 一阶导
    
    # 求二阶导数
    dy2_dx2 = torch.autograd.grad(dy_dx,x)[0] 
    
    print(dy2_dx2.data) # 二阶导
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    image.png

    import numpy as np 
    import torch 
    
    x1 = torch.tensor(1.0,requires_grad = True) # x需要被求导
    x2 = torch.tensor(2.0,requires_grad = True)
    
    y1 = x1*x2
    y2 = x1+x2
    
    
    # 允许同时对多个自变量求导数
    (dy1_dx1,dy1_dx2) = torch.autograd.grad(outputs=y1,inputs = [x1,x2],retain_graph = True)
    print(dy1_dx1,dy1_dx2)
    
    # 如果有多个因变量,相当于把多个因变量的梯度结果求和
    (dy12_dx1,dy12_dx2) = torch.autograd.grad(outputs=[y1,y2],inputs = [x1,x2])
    print(dy12_dx1,dy12_dx2)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    image.png

    三、利用自动微分和优化器求最小值

    import numpy as np 
    import torch 
    
    # f(x) = a*x**2 + b*x + c的最小值
    
    x = torch.tensor(0.0,requires_grad = True) # x需要被求导
    a = torch.tensor(1.0)
    b = torch.tensor(-2.0)
    c = torch.tensor(1.0)
    
    optimizer = torch.optim.SGD(params=[x],lr = 0.01)
    
    
    def f(x):
        result = a*torch.pow(x,2) + b*x + c 
        return(result)
    
    for i in range(500):
        optimizer.zero_grad()
        y = f(x)
        y.backward()
        optimizer.step()
       
    print("y=",f(x).data,";","x=",x.data) # 当 x=0 时 y取得最小值0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    image.png
    参考:https://github.com/lyhue1991/eat_pytorch_in_20_days

  • 相关阅读:
    TA 数学函数与美术的结合
    两个事务并发写,能保证数据唯一吗?
    【UML】软件工程中常用图:类图、部署图、时序图、状态图
    Linux 中 Netcat 工具的使用
    详解WebSocket
    OpenCV-3.4.1+VTK7.1.1+PCL1.8.1编译安装教程(Ubuntu16.04,Ubuntu18.04系统,ARM/X86架构都适用)
    华南理工大学建筑学院建筑设计及其理论专业考研上岸经验分享
    并发编程的12种业务场景
    PostgreSQL 事务与并发控制
    坐标正反算(含高程),把要素内置化(无需改程序文件,即可更换路线,同时存两条线要素
  • 原文地址:https://blog.csdn.net/hxhabcd123/article/details/132863950