其他
【强基固本】AI 框架基础技术之自动求导机制 (Autograd)
“强基固本,行稳致远”,科学研究离不开理论基础,人工智能学科更是需要数学、物理、神经科学等基础学科提供有力支撑,为了紧扣时代脉搏,我们推出“强基固本”专栏,讲解AI领域的基础知识,为你的科研学习提供助力,夯实理论基础,提升原始创新能力,敬请关注。
地址:https://www.zhihu.com/people/openmmlab
01
前向传播 计算损失 反向传播(计算参数的梯度) 更新参数
自动求导机制介绍 SenseParrots 自动求导实现
02
基于对偶图变换的自动求导机制 基于 reverse mode 的自动求导机制
2.1 基于对偶图的自动求导机制
s = (5, 10, 5)
t = relay.TensorType((5, 10, 5))
x = relay.var("x", t)
y = relay.var("y", t)
z = x + y
fwd_func = run_infer_type(relay.Function([x, y], z))
bwd_func = run_infer_type(gradient(fwd_func))
x_data = np.random.rand(*s).astype(t.dtype)
y_data = np.random.rand(*s).astype(t.dtype)
intrp = relay.create_executor(ctx=ctx, target=target)
op_res, (op_grad0, op_grad1) = intrp.evaluate(bwd_func)(x_data, y_data)
2.2 基于 reverse mode 的自动求导机制
3. 在进行反向传播时,基于给定的输出
03
3.1 自动求导机制组件
DArray: 计算数据的数据结构, 可以想象成多维数组, 其中包含参与运算的数据、其梯度及以其作为输出的 GradFn。 Function: 一个基本的运算单元,包括一个操作的正向计算函数及其反向计算函数,每个计算过程对应一个 Function。比如一个 ReLU 激活函数的 Function 包括如下两部分
Class ReLU : Function {
DArray forward(const DArray& x) {
DArray y = ...; // ReLU正向计算过程
return y;
}
DArray backward(const DArray& dy) {
DArray dx = ...; // ReLU反向计算过程
return dx;
}
};
GradFn: 计算图中的节点,每个 Function 在执行正向计算的时候会产生一个 GradFn 对象,保存了输入和输出的梯度信息的指针、Function 指针以确定反向计算要调用的函数、后继 GradFn 节点指针,该对象保存在该 Function 前向计算的输出 DArray 中。
3.2 自动求导机制的控制选项
DArray 的 requires_grad 属性标志该数据是否需要求梯度。requires_grad 设置为 True 时计算梯度,并且会生成 LeafGradFn(GradFn 的子类)来标识该节点为叶子节点,计算图的构造依赖于输入的 requires_grad 属性; 框架是否开启求导。默认情况下框架是开启求导的,也提供了显示的开关求导的接口:torch.no_grad()、torch.enable_grad(),在框架关闭求导功能的情况下,不会构造计算图。
3.3 前向传播过程中构造计算图
如果输入都不需要求梯度,则不会构造计算图,直接调用函数计算得到输出, 并将输出的 requires_grad 设置为 False; 如果输入中有需要求梯度的,则调用函数计算得到输出, 并将输出的 requires_grad 设置为 True, 同时会相应生成一个 GradFn 对象,并完成如下关联工作(“保存”都是以 shared_ptr 方式): 将该 Function 记录进该 GradFn 对象,以表明在反向求导时,用 GradFn 中记录的 Function 的反向计算函数来进行梯度计算; 将该 Function 前向计算函数的输入 DArray 的梯度记录进 GradFn 对象,将该 Function 前向计算函数的输出 DArray 的梯度记录进 GradFn 对象; 将该 Function 前向计算函数的输入 DArray 中所记录的 GradFn 记录为 GradFn 的后继节点; 将该 GradFn 保存进 Function 前向计算函数的所有输出当中。
import torch
x1 = torch.randn((2,3,4), requires_grad=True)
x2 = torch.randn((2,3,4), requires_grad=True)
x3 = torch.randn((2,3,4))
x4 = torch.randn((2,3,4))
y1 = x1 + x2
y2 = x3 + x4
z = y1 * y2
z += x2
3.4 基于输出的梯度信息对输入自动求导
z.backward(torch.ones_like(z))
基于给定的 z 的梯度信息,更新z中的梯度值; 基于计算图进行拓扑排序,获得 GradFn 的执行队列(一个可能的序列为:GF3 -> GF2 -> GF1 -> LeafGF1 -> LeafGF2); 开始反向求导,首先执行 GF3,GF3 是一个 inplace 操作,以 z 的梯度作为输入,调用 "+=" Function 的反向计算函数,计算并更新 z、x2 的梯度,此时执行队列为(GF2 -> GF1 -> LeafGF1 -> LeafGF2);
“强基固本”历史文章
主成分分析(PCA)
深度学习算法收敛性证明
深度学习:数学知识基础
从Binary到Swish——激活函数深度详解
“卷积”其实没那么难以理解
DNN加速器性能分析
如何通俗易懂地让女朋友明白什么是语言模型?
深度学习从入门到放飞自我:完全解析triplet loss
卡尔曼滤波器
Reinforcement learning入门:从马尔可夫,动态规划到强化学习
算法工程师应该了解的浮点数知识
神经网络量化简介
样本量极少如何机器学习?Few-Shot Learning概述
我们真的需要深度图神经网络吗?
深度强化学习(Deep Reinforcement Learning)入门
伪标签(Pseudo-Labelling)——锋利的匕首
基础算法:使用numpy实现逻辑回归随机梯度下降(附代码)
脉冲神经网络(SNN)
分享、点赞、在看,给个三连击呗!