实操教程|PyTorch AutoGrad C++层实现

PyTorch C++ API笔记 学习PyTorch C++ API的笔记 阅读详情

作者丨xxy-zhihu@知乎

来源丨https://zhuanlan.zhihu.com/p/339039943

编辑丨极市平台

极市导读

 

本文为一篇实操教程,作者介绍了PyTorch AutoGrad C++层实现中各个概念的解释。

autograd依赖的数据结构

at::Tensor:shared ptr 指向 TensorImpl

TensorImpl:对 at::Tensor 的实现

  • 包含一个类型为 [AutogradMetaInterface](c10::AutogradMetaInterface) 的autograd_meta_,在tensor是需要求导的variable时,会被实例化为 [AutogradMeta](c10::AutogradMetaInterface) ,里面包含了autograd需要的信息

Variable: 就是Tensor,为了向前兼容保留的

  • using Variable = at::Tensor;

  • 概念上有区别, Variable 是需要计算gradient的, Tensor 是不需要计算gradient的

  • Variable的 AutogradMeta是对 [AutogradMetaInterface](c10::AutogradMetaInterface)的实现,里面包含了一个 Variable,就是该variable的gradient

  • 带有version和view

  • 会实例化 AutogradMeta , autograd需要的关键信息都在这里

AutoGradMeta : 记录 Variable 的autograd历史信息

  • 包含一个叫grad_的 Variable, 即 AutoGradMeta 对应的var的梯度tensor

  • 包含类型为 Node 指针的 grad_fn (var在graph内部时)和 grad_accumulator(var时叶子时), 记录生成grad_的方法

  • 包含 output_nr ,标识var对应 grad_fn的输入编号

  • 构造函数包含一个类型为 Edge的gradient_edge, gradient_edge.function 就是 grad_fn, 另外 gradient_edge.input_nr 记录着对应 grad_fn的输入编号,会赋值给 AutoGradMeta 的 output_nr

autograd::Edge: 指向autograd::Node的一个输入

  • 包含类型为 Node 指针,表示edge指向的Node

  • 包含 input_nr, 表示edge指向的Node的输入编号

autograd::Node: 对应AutoGrad Graph中的Op

  • 是所有autograd op的抽象基类,子类重载apply方法

    • next_edges_记录出边

    • input_metadata_记录输入的tensor的metadata

  • 实现的子类一般是可求导的函数和他们的梯度计算op

  • Node in AutoGrad Graph

    • Variable通过Edge关联Node的输入和输出

    • 多个Edge指向同一个Var时,默认做累加

  • call operator

    • 最重要的方法,实现计算

  • next_edge

    • 缝合Node的操作

    • 获取Node的出边,next_edge(index)/next_edges()

    • add_next_edge(),创建

前向计算

PyTorch通过tracing只生成了后向AutoGrad Graph.

代码是生成的,需要编译才能看到对应的生成结果

  • gen_variable_type.py生成可导版本的op

  • 生成的代码在 pytorch/torch/csrc/autograd/generated/

  • 前向计算时,进行了tracing,记录了后向计算图构建需要的信息

  • 这里以relu为例,代码在pytorch/torch/csrc/autograd/generated/VariableType_0.cpp

Tensor relu(const Tensor & self) {                                                                                                                                                                   auto& self_ = unpack(self, "self", 0);                                                                                                                                                             std::shared_ptr<ReluBackward0> grad_fn;                                                                                                                                                            if (compute_requires_grad( self )) { // 如果输入var需要grad    // ReluBackward0的类型是Node                                                                                                                                                                grad_fn = std::shared_ptr<ReluBackward0>(new ReluBackward0(), deleteNode);                                                                                                                          // collect_next_edges(var)返回输入var对应的指向的    // grad_fn(前一个op的backward或者是一个accumulator的)的输入的Edge    // set_next_edges(),在grad_fn中记录这些Edge(这里完成了后向的构图)    grad_fn->set_next_edges(collect_next_edges( self ));     // 记录当前var的一个版本                                                                                                                                              grad_fn->self_ = SavedVariable(self, false);                                                                                                                                                     }                                                                                                                                                                                                  #ifndef NDEBUG                                                                                                                                                                                     c10::optional<Storage> self__storage_saved =                                                                                                                                                         self_.has_storage() ? c10::optional<Storage>(self_.storage()) : c10::nullopt;                                                                                                                    c10::intrusive_ptr<TensorImpl> self__impl_saved;                                                                                                                                                   if (self_.defined()) self__impl_saved = self_.getIntrusivePtr();                                                                                                                                   #endif                                                                                                                                                                                             auto tmp = ([&]() {                                                                                                                                                                                  at::AutoNonVariableTypeMode non_var_type_mode(true);                                                                                                                                               return at::relu(self_); // 前向计算                                                                                                                                                                          })();                                                                                                                                                                                              auto result = std::move(tmp);                                                                                                                                                                      #ifndef NDEBUG                                                                                                                                                                                     if (self__storage_saved.has_value())                                                                                                                                                                 AT_ASSERT(self__storage_saved.value().is_alias_of(self_.storage()));                                                                                                                             if (self__impl_saved) AT_ASSERT(self__impl_saved == self_.getIntrusivePtr());                                                                                                                      #endif                                                                                                                                                                                             if (grad_fn) {                   // grad_fn增加一个输入,记录输出var的metadata作为grad_fn的输入      // 输出var的AutoGradMeta实例化,输出var的AutoGradMeta指向起grad_fn的输入                                                                                                                                                                            set_history(flatten_tensor_args( result ), grad_fn);                                                                                                                                           }                                                                                                                                                                                                  return result;                                                                                                                                                                                   }

  • 可以看到和 grad_fn 相关的操作trace了一个op的计算,构建了后向计算图.

后向计算

autograd::backward():计算output var的梯度值,调用的 run_backward()

autograd::grad() :计算有output var和到特定input的梯度值,调用的 run_backward()

autograd::run_backward()

  • 对于要求梯度的output var,获取其指向的grad_fn作为roots,是后向图的起点

  • 对于有input var的,获取其指向的grad_fn作为output_edges, 是后向图的终点

  • 调用 autograd::Engine::get_default_engine().execute(...) 执行后向计算

autograd::Engine::execute(...)

  • 创建 GraphTask ,记录了一些配置信息

  • 创建 GraphRoot ,是一个Node,把所有的roots作为其输出边,Node的apply()返回的是roots的grad【这里已经得到一个单起点的图】

  • 计算依赖 compute_dependencies(...)

    • 从GraphRoot开始,广度遍历,记录所有碰到的grad_fn的指针,并统计grad_fn被遇到的次数,这些信息记录到GraphTask中

  • GraphTask 初始化:当有input var时,判断后向图中哪些节点是真正需要计算的

  • GraphTask 执行

    • 选择CPU or GPU线程执行

    • 以CPU为例,调用的 autograd::Engine::thread_main(...)

autograd::Engine::thread_main(...)

  • evaluate_function(...) ,输入输出的处理,调度

    • call_function(...) , 调用对应的Node计算

    • 执行后向过程中的生成的中间grad Tensor,如果不释放,可以用于计算高阶导数;(同构的后向图,之前的grad tensor是新的输出,grad_fn变成之前grad_fn的backward,这些新的输出还可以再backward)

  • 具体的执行机制可以支撑单独开一个Topic分析,在这里讨论到后向图完成构建为止.

PyTorch内部机制深度解析:从Autograd到Dispatcher的底层原理与实践 深度学习框架的核心在于自动微分与计算图机制,它们构成了现代机器学习模型训练的技术基石。自动微分通过动态构建计算图来记录前向传播操作,并在反向传播时高效计算梯度,这一原理使得研究人员能够专注于模型设计而非手动求导。在工程实践中,算子分发系统(Dispatcher)负责将高层运算映射到底层硬件内核,直接影响模型性能与跨平台兼容性。理解这些底层机制对于解决显存管理、自定义算子梯度异常、多进程数据加载等常见开发痛点至关重要。本文基于PyTorch核心开发者的内部技术文档,深入剖析Autograd引擎、C10张量库和 阅读详情

相关推荐

libtorch c++调用 (九)打印tensor 信息

libtorch c++调用 (九)打印tensor信息1、打印cuda信息2、打印tensor信息3、打印cv::mat信息 1、打印cuda信息 void test_libtorch_version() { std::cout << "cuDNN : " << torch::cuda::cudnn_is_available() << std::endl; std::cout << "CUDA : " << torch::cuda::is_a

juluwangriyue的博客 1629

c++ libtorch tensor 赋值 浮点数

【代码】c++ libtorch tensor 赋值 浮点数。

reyyy的博客 241

Pytorch使用手册—C++ 前端中的自动求导(Autograd)(专题四十九)

将一个新的基本操作添加到需要为每个操作实现一个新的子类。是用来计算结果和梯度,并编码操作历史的工具。forward和backward,详细要求请参见此链接。下面是来自torch::nn的一个Linearpublic:然后,我们可以通过以下方式使用输出public:然后,我们可以通过以下方式使用输出5.50005.5000。

154

C++ 数组与字符串

qq_33441744的博客 172

标准高斯过程回归(Gaussian Processes Regression, GPR)从零开始,公式推导

标准高斯过程回归从零开始公式推导

qq_43587949的博客 1万+

实践指南——【SPSS操作解析】线性混合效应模型在重复测量数据中的应用

本文详细解析了线性混合效应模型在重复测量数据分析中的应用,并提供了SPSS操作指南。文章阐述了该模型如何通过区分固定效应与随机效应,有效处理数据非独立性和缺失值问题,从而获得更可靠的统计推断。

oo7890的博客 554

Pytorch中autograd.Variable使用过程中的问题

y.grad_fn.saved_variables AttributeError Traceback (most recent call last) <ipython-input-21-eceb35361ea8> in <module> 1 #使用retain_graph来保存这些buffer ----&g...

qq_44787464的博客 4325

linux操作系统使用以太网连接ubuntu,设置固定ipv4

首先,您需要确定要配置的网络接口。可以使用ifconfig -a或ip addr命令来列出当前系统上的所有网络接口及其相关信息。找到您要配置的网络接口名称,例如eth0、ens33或enp0s3;保存并关闭文件后,您需要重启网络服务以使更改生效。在linux操作系统中,要使用交换机连接以太网可以直接使用交互界面;如果你的操作系统没有交互界面,你可以使用命令方式配置;点击ipv4,方式为手动,可以设置ip和子网了;开头,后接网络接口名称。第三步:点击有线最右边圆形控件;第二步:点击有线设置;

2301_79311842的博客 2406

PyTorch 源码解读之 torch.autograd:梯度计算详解

前言 本篇笔记以介绍 pytorch 中的 autograd 模块功能为主,主要涉及 torch/autograd 下代码,不涉及底层的 C++ 实现。本文涉及的源码以 PyTorch 1.7 为准。 torch.autograd.function (函数的反向传播) torch.autograd.functional (计算图的反向传播) torch.autograd.gradcheck (数值梯度检查) torch.autograd.anomaly_mode (在自动求导时检测错误产生路径)

qq_39967751的博客 4402

OnePlus救砖指南:线刷包与OTA全量包实战解析

本文详细解析了OnePlus手机救砖的两种核心方法:线刷包与OTA全量包。针对手机变砖的不同状况(如黑屏、能进Recovery等),文章提供了清晰的判断标准和对应的实战操作指南。重点阐述了线刷包作为终极救砖手段的底层原理与详细步骤,并对比了OTA全量包作为温和修复方案的使用场景与卡刷流程,旨在帮助用户安全、有效地恢复手机系统。

weixin_28312391的博客 1329

Camera HAL Camx — DRQ总结

Camera HAL — DRQ(延迟请求队列)总结 DRQ是CAMX核心层的依赖驱动延迟执行调度器,解决Node处理请求时依赖未就绪的问题。其主要特点包括: 核心功能:当Node处理请求时缺少必要输入(property/metadata/fence),DRQ将其挂起并在依赖满足后重新调度,避免阻塞流水线或丢帧。 系统架构:Session持有主DRQ实例,管理该Session下所有Pipeline Node的依赖;CHI context另有独立DRQ实例专门处理Chi fence同步。 关键数据结构: De

DU_YULIN的博客 77

PyTorch 高层 API 到底层 C++ 实现的桥接机制解析

PyBind11 是一个高级库,用来将 C++ 函数绑定到 Python,这样 Python 层的操作就可以直接调用预编译的 C++ 扩展。,该函数可以判断当前使用的是 CPU 还是 GPU,并进一步调用特定设备的实现(如 CPU 实现或 GPU 上的 cuDNN 实现)。这种架构设计使得 PyTorch 既能够提供友好的 Python API,又能够利用高效的 C++ 实现来执行底层计算。在 Python 中的实现,实际上是一个被 PyBind11 绑定的 C++ 函数。具体来说,它的工作流程是通过。

m980828的博客 1996

Playwright MCP:开启浏览器自动化的新篇章

在当今数字化时代,浏览器自动化技术在众多领域发挥着至关重要的作用,从简单的网页爬取到复杂的自动化测试,再到智能代理与网页的交互等场景,都离不开高效、可靠的浏览器自动化工具。而微软推出的项目,正是在这一领域的一次创新性探索,它为开发者带来了全新的视角和强大的功能支持。本文将深入探讨的概念、架构、代码示例、应用场景、注意事项以及未来发展方向,帮助读者全面了解这一前沿技术。

加入“Super Entity”,与全能开发团队共探AI智能体与数字人项目,开启前沿技术之旅。 5268

pytorch 梯度none_PyTorch 源码解读之 torch.autograd

AI编辑:我是小将本文作者:OpenMMLab @000255https://zhuanlan.zhihu.com/p/321449610本文已由原作者授权前言本篇笔记以介绍 pytorch 中的 autograd 模块功能为主,主要涉及 torch/autograd 下代码,不涉及底层的 C++ 实现。本文涉及的源码以 PyTorch 1.7 为准。torch.autograd.fu...

weixin_39727863的博客 774

华为OD机试 - 最快到达医院的方法(Python/JS/C/C++ 2024 E卷 100分)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、发现新题目,随时更新,全天优快云在线答疑。

学Java,找哪吒 2172

BES2700的PMU

BES2700ZP-BES2700芯片的PMU架构分析摘要:本文档分析了BES2700芯片的电源管理单元(PMU)架构。实际应用中仅需为VBAT引脚供电,其余电源均由芯片内部PMU提供。文档包含架构示意图、问题列表和参考链接,涉及PMU工作原理、外部供电要求等关键技术点。待解决问题包括具体供电参数和内部电源转换机制等。通过添加日志信息和代码片段,为开发人员提供了调试PMU功能的实用方法。

云淡风轻 119

C++ 前端中的 Autograd

(改编自本教程向添加新的基本操作需要为每个操作实现一个新的子类。用于计算结果和梯度,并对操作历史进行编码。forward和backward,有关详细要求,请参见此链接。在下面,您可以从torch::nn找到Linearpublic :} return {} };然后,我们可以通过以下方式使用4 , 3 } ]public :} };然后,我们可以通过以下方式使用2 } ]有关的更多信息,请参见其文档。

yanglamei1962的博客 236

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

python求导_PyTorch的自动求导功能(Autograd)原理解析

It's automatic我们知道,深度学习最核心的其中一个步骤,就是求导:根据函数(linear + activation function)求weights相对于loss的导数(还是loss相对于weights的导数?)。然后根据得出的导数,相应的修改weights,让loss最小化。各大深度学习框架Tensorflow,Keras,PyTorch都自带有自动求导功能,不需要我们手...

weixin_40000457的博客 1197

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

pytorch 生成全零tensor 指定type_PyTorch Autograd代码的动态生成

背景在Gemfield:PyTorch ATen代码的动态生成 一文中,我们知道PyTorch的的代码主要由C10、ATen、torch三大部分组成的。其中:C10,来自于Caffe Tensor Library的缩写;ATen,来自于 A TENsor library for C++11的缩写,PyTorch的C++ tensor library,ATen部分有大量的代码是来声明和定义Tenso...

weixin_39949506的博客 598

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

PyTorch源码解读之Autograd:动态计算图的构建与自动微分实现——深入框架核心,理解魔法之源

本文深入剖析PyTorch框架核心自动微分引擎Autograd的实现原理,系统解析动态计算图的构建机制与反向传播算法实现。文章从计算图理论基础出发,详细解读Tensor的梯度追踪设计、Function类的算子抽象、Edge数据结构连接关系,揭示前向传播如何隐式构建计算图、反向传播如何动态执行梯度计算。通过源码级分析和可视化案例,深入探讨PyTorch自动微分的工程实现细节、内存优化策略及扩展机制,为深入理解深度学习框架核心原理提供全面技术参考。

AladdinEdu,你的AI学习实践工作坊。让想法落地,让研究加速。助力高校AI人才成长,点亮创新未来。 1617

PyTorch内部机制深度解析:从Autograd到执行引擎的核心原理

自动微分是现代深度学习框架的核心技术,它通过构建计算图自动计算梯度,极大简化了神经网络的训练过程。其原理基于链式法则,在动态计算图中记录前向传播的操作序列,并在反向传播时按逆序执行梯度计算。这一机制的技术价值在于实现了模型训练的自动化,让开发者能够专注于模型设计而非繁琐的梯度推导。在PyTorch等主流框架中,自动微分广泛应用于神经网络训练、优化器更新以及自定义层实现等场景。本文将结合PyTorch Internals手册,深入剖析Tensor元数据、Autograd引擎与执行调度的协同工作机制,帮助开发者

weixin_34208185的博客 385

PyTorch C++ API

这些页面提供了 PyTorch C++ API 公共部分的文档。这个 API 大致可以分为五个部分: ATen: 构建所有其他内容的基础张量和数学运算库。 Autograd: 通过自动微分增强 ATen。 C++ Frontend: 用于训练和评估机器学习模型的高级构造。 TorchScript: TorchScript JIT 编译器和解释器的接口。 C++ Extensions: 一种使用自定义 C++ 和 CUDA 例程扩展 Python API 的方法。 结合起来,这些构建块形成了一个研究和生产

wolaiyeptx的博客 3130

PyTorch学习笔记之自动求导(AutoGrad)

1、使用PyTorch 计算梯度数值 PyTorch 的 Autograd 模块实现了深度学习的算法中的反向传播求导数,在张量(Tensor类)上的所有操作, Autograd 都能为他们自动提供微分,简化了手动计算导数的复杂过程。 在04以前的版本中, Pytorch 使用 Variabe 类来自动计算所有的梯度 Variable 类主要包含三个属性 Var...

汤姆鱼 1315

PyTorch 2.2 中文官方教程(十一)

使用 PyTorch C++ 前端 原文:pytorch.org/tutorials/advanced/cpp_frontend.html 译者:飞龙 协议:CC BY-NC-SA 4.0 PyTorch C++ 前端是 PyTorch 机器学习框架的纯 C++ 接口。虽然 PyTorch 的主要接口自然是 Python,但这个 Python API 坐落在一个庞大的 C++ 代码库之上,提供了基础数据结构和功能,如张量和自动微分。C++ 前端暴露了一个纯 C++11 API,扩展了这个底层 C++ 代

龙哥盟 1708
上一篇: CPU比GPU训练神经网络快十几倍,英特尔:别用矩阵运算了
下一篇: 为什么有些数学专业的看不惯甚至鄙视 Deep Learning?
机器学习算法那些事
博客等级 码龄9年 1633粉丝 62原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值