AI 基础知识四 torch入门基础
要实现自定义求导通过继承torch::autograd::Function,定义前向传播(forward)和反向传播(backward)逻辑代码,假设我们要实现一个自定义函数,它的导数1. 前向传播(forward):实现的计算,用上下文torch::autograd::AutogradContext保存x值 给反向传播求导时使用2. 反向传播(backward):实现导数计算从 前向传播 取当时
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}; 数组转张量 |
2.自动求导
Autograd 系统提供了一系列强大的特性,使得它成为深度学习和自动微分中的重要工具。
这些特性不仅提高了编程的灵活性和效率,还使得复杂的优化和计算变得可行。
1.torch 内置求导
假设有函数 (1) (2)
对函数z求导 我们手动推导z的导数函数是
, 当x==1时
, 当x==2时
,下面用张量(标量)内置带有自动求导功能来实现函数与求导
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)逻辑代码,假设我们要实现一个自定义函数 ,它的导数
1. 前向传播(forward):实现的计算,
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):实现导数计算
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;
}
感谢大家的支持,如要问题欢迎提问指正。
更多推荐



所有评论(0)