PyTorch入门5——张量自动微分模块

PyTorch入门 5 —— 张量自动微分模块

在 Pytorch 中,自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新。

1. 梯度基本计算

使用 backward 方法、grad 属性来实现梯度的计算和访问。注意:需要求导的张量,张量的值类型必须是浮点类型。

import torch

def test01():  # 1. 单标量梯度的计算
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)  # 定义需要求导的张量
    f = x ** 2 + 20  # 变量经过中间运算
    f.backward()     # 自动微分
    print(x.grad)    # 打印 x 变量的梯度,backward 函数计算的梯度值存储在张量的 grad 属性中

def test02():  # 2. 单向量梯度的计算
    x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)
    f1 = x ** 2 + 20  # 变量经过中间计算
    f2 = f1.mean()    # 注意:由于求导的结果必须是标量,而 f1 的结果是张量,所以, 不能直接自动微分,需要将结果计算为标量才能进行自动微分!
    f2.backward()     # 自动微分
    print(x.grad)     # 打印 x 变量的梯度

def test03():  # 3. 多标量梯度计算
    x1 = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    x2 = torch.tensor(20, requires_grad=True, dtype=torch.float64)
    
    y = x1 ** 2 + x2 ** 2 + x1 * x2  # 变量经过中间计算
    y = y.sum()                      # 将输出结果变为标量
    y.backward()                     # 自动微分
    print(x1.grad, x2.grad)          # 打印两个变量的梯度

def test04():  # 4. 多向量梯度计算    
    x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)  # 定义需要计算梯度的张量 
    x2 = torch.tensor([30, 40], requires_grad=True, dtype=torch.float64)
    y = x1 ** 2 + x2 ** 2 + x1 * x2  # 经过中间的计算
    print(y)     # tensor([1300., 2800.], dtype=torch.float64, grad_fn=<AddBackward0>)
    y = y.sum()  # 将输出结果变为标量
    y.backward()  # 自动微分
    print(x1.grad, x2.grad)  # 打印两个变量的梯度

test01()  # tensor(20., dtype=torch.float64)
test02()  # tensor([ 5., 10., 15., 20.], dtype=torch.float64)
test03()  # tensor(40., dtype=torch.float64)  tensor(50., dtype=torch.float64)
test04()  # tensor([50., 80.], dtype=torch.float64)  tensor([ 70., 100.], dtype=torch.float64)

2. 控制梯度计算

在模型进行评估或者预测时一般不再进行求导,因此可以通过一些方法使得在 requires_grad 为 True 的张量不进行梯度计算,示例如下:

import torch

def test01():  # 1. 控制不计算梯度
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    print(x.requires_grad)  # True

    # 第一种方式: 对代码进行装饰
    with torch.no_grad():
        y = x ** 2
    print(y.requires_grad)  # False

    # 第二种方式: 对函数进行装饰
    @torch.no_grad()
    def my_func(x):
        return x ** 2
    print(my_func(x).requires_grad)  # False

    # 第三种方式
    torch.set_grad_enabled(False)
    y = x ** 2
    print(y.requires_grad)  # False

def test02():  # 2. 注意: 累计梯度
    x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)  # 定义需要求导张量
    for _ in range(3):
        f1 = x ** 2 + 20
        f2 = f1.mean()
        
        # 默认张量的 grad 属性会累计历史梯度值,所以需要每次手动清理上次的梯度
        if x.grad is not None:  # 注意: 一开始梯度不存在, 需要做判断
            x.grad.data.zero_()
            
        f2.backward()
        print(x.grad)  # tensor([ 5., 10., 15., 20.], dtype=torch.float64)

def test03():  # 3. 梯度下降优化最优解
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    for _ in range(100):
        f = x ** 2     # 正向计算
        if x.grad is not None:
            x.grad.data.zero_()           # 梯度清零
        f.backward()                      # 反向传播计算梯度
        x.data = x.data - 0.05 * x.grad  # 更新参数
        print('%.10f' % x.data)

test01()
test02()
test03()

3. 梯度计算注意

当对设置 requires_grad 为 True 的张量使用 numpy 函数进行转换时,会出现如下报错:Can’t call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
此时,需要先使用 detach 函数将张量进行分离,再使用 numpy 函数
注意:detach 之后会产生一个新的张量,新的张量作为叶子结点,并且该张量和原来的张量共享数据,但是分离后的张量不需要计算梯度。

import torch

def test01():  # 1. detach 函数用法
    x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)
    print(x.numpy())  # RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
    print(x.detach().numpy())  # [10. 20.]

def test02():  # 2. detach 前后张量共享内存
    x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)
    x2 = x1.detach()         # x2 作为叶子结点
    print(id(x1.data), id(x2.data))  # 一样: 51365424 51365424
    x2.data = torch.tensor([100, 200])
    print(x1, x2)  # tensor([10., 20.], dtype=torch.float64, requires_grad=True) tensor([100, 200])
    print(x2.requires_grad)  # False(x2 不会自动计算梯度)

test01()
test02()

以上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值