torch基础核心基础知识包括以下五个模块

1.张量
2.自动求导(Autograd)
3.神经网络模块
4.损失函数与优化器
5.数据加载

其中神经网络模块损失函数与优化器” 前几章有简单提过,"数据加载"后面用实例讲解,所以本章只讲张量自动求导

1.张量

定义张量变量可以在设备切换(GPU/CPU,支持GPU硬件加速)和自动求导两大功能

张量简单的说张量就是标量、向量、矩阵的统称, 它们之间可以直接进行加减乘除运算

张量创建方式 特点
torch::tensor(...) 手动输入数据 

torch::tensor({ 0.050,0.10 }, torch::kDouble);

两行一列矩阵(向量)

torch::randn(...) 这些随机数服从标准正态分布(均值为 0,标准差为 1)

torch::randn({2, 2}) 

两行两列矩阵

torch::rand(...)  随机数均匀分布在区间 [0, 1) 内

torch::rand({2, 3})

两行三列矩阵 随机在[0,1) 等于0 小于1的小数

torch::randint(...) 随机整数 [low, high)

torch::randint(0, 10, {2, 3});

两行三列矩阵 随机在[0,10) 等于0 小于10的整数

torch::zeros(...)  torch::ones(...)  torch::full(...)

固定值填充

 全 0、全 1 或指定常数

torch::ones({ 2, 2 })

两行两列矩阵 全是 1

torch::from_blob(...) 共享内存,高效转换

float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
torch::Tensor tensor = torch::from_blob(
    data,
    {2, 2},  
    torch::kFloat32  
);

数组转张量

2.自动求导

Autograd 系统提供了一系列强大的特性,使得它成为深度学习和自动微分中的重要工具。
这些特性不仅提高了编程的灵活性和效率,还使得复杂的优化和计算变得可行。

1.torch 内置求导

假设有函数 (1) y=x+2  (2) z=y^{2}*3   对函数z求导 我们手动推导z的导数函数是 z'=6(x+2) , 当x==1时 z'=18, 当x==2时 z'=24,下面用张量(标量)内置带有自动求导功能来实现函数与求导

auto x = torch::tensor(1.0f).requires_grad_();
auto y = x + 2;
auto z = y * y * 3;
std::cout << "y.grad_fn " << y.grad_fn()->name() << std::endl;
std::cout << y << std::endl;

std::cout << "z.grad_fn " << z.grad_fn()->name() << std::endl;
std::cout << z << std::endl;

z.backward();
std::cout << "z.backward() d(z)/dx " << x.grad() << std::endl;

1. 创建张量时默认是不记录梯度计算,需要tensor.requires_grad_()明示记录梯度计算

2. tensor.requires_grad()是否记录梯度计算,tensor.grad_fn()->name()记录计算名称

3.tensor.backward()反向传播求导,tensor.grad()梯度值

可以对矩阵记录梯度计算    x = torch::ones({ 2, 2 }, torch::requires_grad()); 

 z.backward(torch::tensor({ { 1,1 },{1,1} }));  默认是1,内部实现见下面“自定义求导”

4. 可对记录梯度计算过虑某个运算

    {   
        torch::NoGradGuard no_grad;
        std::cout << "torch::NoGradGuard  " << x.pow(2).requires_grad() << std::endl;
    }

这部份可结合运行结果来理解


2.自定义求导

要实现自定义求导通过继承torch::autograd::Function,定义前向传播(forward)和反向传播(backward)逻辑代码,假设我们要实现一个自定义函数  y=x^{2},它的导数y'=2x

1. 前向传播(forward):实现y=x^{2}的计算,

  static torch::Tensor forward(
      torch::autograd::AutogradContext* ctx,  
      const torch::Tensor& x ) 
  {
      ctx->save_for_backward({ x });
      return x * x;
  }

 用上下文torch::autograd::AutogradContext保存x值 给反向传播求导时使用

2. 反向传播(backward):实现导数计算 y'=2x

    static torch::autograd::tensor_list backward(torch::autograd::AutogradContext* ctx, torch::autograd::tensor_list grad_output)
    {
        
        auto saved = ctx->get_saved_variables();
        torch::Tensor x = saved[0];

        torch::Tensor grad_x = 2 * x * grad_output[0];
        return { grad_x };
    }

     1. auto saved = ctx->get_saved_variables(); 从 前向传播 取当时x值,这个好理解

     2. grad_output默认是1,所以y=2 * x 也可以写成y=2 * x * 1 ,作用是可以手动调整梯度值

3.Function.apply 用于执行自定义操作的前向传播(forward)

  torch::Tensor x = torch::tensor(3.0, torch::requires_grad(true));
  torch::Tensor y = SquareX::apply(x);

  y.backward();
  std::cout << std::endl << std::endl <<"-------------------------SquareX: y=x^2  x=3-----------------------------" << std::endl;
  std::cout << "d(y)/dx= " << x.grad() << std::endl;  

本章完整代码 

#include <torch/torch.h>
#include <iostream>

using namespace torch::autograd;


void basic_autograd()
{
  
    std::cout << "------------------- basic autograd ---------------" << std::endl;
   
    auto x = torch::tensor(1.0f).requires_grad_();
    auto y = x + 2;
    auto z = y * y * 3;
    std::cout << "y.grad_fn " << y.grad_fn()->name() << std::endl;
    std::cout << y << std::endl;
    
    std::cout << "z.grad_fn " << z.grad_fn()->name() << std::endl;
    std::cout << z << std::endl;
    
    z.backward();
    std::cout << "z.backward() d(z)/dx " << x.grad() << std::endl;

    {   
        torch::NoGradGuard no_grad;
        std::cout << "torch::NoGradGuard  " << x.pow(2).requires_grad() << std::endl;
    }


    std::cout << "------------------------------------" << std::endl;

    std::cout  << std::endl << "-------------------2---------------" << std::endl<< std::endl;
    x = torch::ones({ 2, 2 }, torch::requires_grad());

    std::cout <<"x " << std::endl << x << std::endl;

    y = x + 2;
    z = y * y * 3;

    std::cout << "y.grad_fn " << y.grad_fn()->name() << std::endl;
    std::cout << y << std::endl;

    std::cout << "z.grad_fn " << z.grad_fn()->name() << std::endl;
    std::cout << z << std::endl;

    z.backward(torch::tensor({ { 1,1 },{1,1} }));
    std::cout << "z.backward() d(z)/dx "<< std::endl << x.grad() << std::endl;
    std::cout << "-----------------------------------" << std::endl;


    std::cout << std::endl << "-----------------------------------" << std::endl << std::endl;
    x = torch::tensor({ 1, 2 }, torch::kFloat).requires_grad_();
    
    std::cout << "x " << std::endl << x << std::endl;

    y = x + 2;
    z = y * y * 3;

    std::cout << "y.grad_fn " << y.grad_fn()->name() << std::endl;
    std::cout << y << std::endl;

    std::cout << "z.grad_fn " << z.grad_fn()->name() << std::endl;
    std::cout << z << std::endl;

    z.backward(torch::tensor({ 1,1 }));
    std::cout << "z.backward() d(z)/dx " << std::endl << x.grad() << std::endl;
    std::cout << std::endl << "-------------------basic autograd ---------------" << std::endl;

}


class SquareX : public torch::autograd::Function<SquareX>
{

public:
    static torch::Tensor forward(
        torch::autograd::AutogradContext* ctx,  
        const torch::Tensor& x ) 
    {
        ctx->save_for_backward({ x });
        return x * x;
    }

    
    static torch::autograd::tensor_list backward(torch::autograd::AutogradContext* ctx, torch::autograd::tensor_list grad_output)
    {
        
        auto saved = ctx->get_saved_variables();
        torch::Tensor x = saved[0];

        torch::Tensor grad_x = 2 * x * grad_output[0];
        return { grad_x };
    }
};



int AutogradMain() 
{
  std::cout << std::boolalpha;

  basic_autograd();

  torch::Tensor x = torch::tensor(3.0, torch::requires_grad(true));
  torch::Tensor y = SquareX::apply(x);

  y.backward();
  std::cout << std::endl << std::endl <<"-------------------------SquareX: y=x^2  x=3-----------------------------" << std::endl;
  std::cout << "d(y)/dx= " << x.grad() << std::endl;  

  return 0;
}

感谢大家的支持,如要问题欢迎提问指正。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐