十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MXNet Gluon autograd 自动微分完全指南:从梯度原理到动态图实战

MXNet Gluon autograd 自动微分完全指南:从梯度原理到动态图实战 人工智能深度学习机器学习【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址https://gitcode.com/gh_mirrors/mxne/mxnet点击查看免费下载导读本文围绕 MXNet Gluon 的autograd模块系统讲解自动微分的原理、autograd.record()上下文的使用方法以及冻结参数、非参数梯度、Python 控制流动态图、自定义 head gradient 等高级特性。读者学完后将能理解反向模式自动微分的底层工作机制并掌握在 MXNet Gluon 中写出可正确计算梯度的训练代码的完整套路。为什么需要计算梯度简短回答梯度是训练神经网络的核心要素。它告诉我们应该如何调整网络的参数例如卷积核中的权重从而让网络的性能变得更好。上图直观展示了梯度的几何含义沿着负梯度方向调整参数损失函数的值就会下降。完整的训练流程正是不断重复「计算梯度 → 更新参数」直到收敛的过程。详细回答神经网络在底层由各种算子求和、乘积、卷积等组成其中一部分算子依赖参数完成计算。训练的目标就是为这些参数找到最优取值而梯度正是引导我们走向最优解的信号。梯度度量的是当我们改变某个变量时依赖于它的另一个变量会增加或减少多少。在训练场景中我们关心的是每个参数对网络性能的影响通常用一个**损失度量loss metric**来刻画网络预测的糟糕程度回归任务最小化 L2 损失即预测值与真实值的欧氏距离。从源码看其数学形式为L 1/2 * Σ |label - pred|²内部通过np.square(label - pred)计算见 python/mxnet/gluon/loss.py分类任务最小化 Softmax 交叉熵损失定义于同一文件python/mxnet/gluon/loss.py的SoftmaxCrossEntropyLoss类。假设已经算出了损失对每个参数的梯度就可以借助优化器如随机梯度下降让参数沿梯度的相反方向微微移动从而降低损失。关于这些优化方法可进一步参考 Optimizers。重复「算梯度 → 更新参数」这一过程直到参数稳定并收敛到一组好的解模型就训练完成了。如何计算梯度简短回答求导。MXNet Gluon 使用反向模式自动微分Reverse Mode Automatic Differentiation即autograd把梯度从损失度量反向传播回网络参数。上图展示了前向传播实线从左到右计算输出与反向传播虚线从右到左传播梯度的对应关系。详细回答计算梯度有哪些可行方案逐一分析其缺陷方案思路缺陷手算求导翻出微积分教材手工推导耗时且极易出错符号微分Symbolic Differentiation为每个梯度推导出公式网络越深、算子越复杂公式急剧膨胀到无法处理有限差分Finite Differencing对每个参数施加微小扰动观察损失变化计算昂贵且数值精度差正确答案是自动微分。通过反向传播backpropagation用一种动态规划的方式高效计算梯度这也被称为反向模式自动微分。它在「扇入fan-in」场景下极其高效——即众多参数共同影响单个损失度量这正是神经网络训练的情形。虽然也存在前向模式自动微分但它更适合「扇出fan-out」场景少数参数影响多个度量并不适用于神经网络训练。autograd 是如何工作的简短回答自动微分分为两个阶段阶段 1前向传播记录网络为做出预测并计算损失所使用的所有算子形成一张计算图阶段 2反向传播沿着这张记录反向遍历逐个算子求偏导数一直回溯到网络参数。详细回答MXNet 中的每个算子都定义了两个方法forward方法按预期执行算子backward方法返回偏导数即输出对输入的导数。即便是在极少数需要自定义算子的场景下你定义的也是同样的两个方法——这与 python/mxnet/autograd.py 中Function类的设计完全一致子类实现forward与backwardautograd 在求梯度时就会调用用户自定义的backward而非默认链式法则。前向传播时autograd 记录下网络用到的算子即forward调用序列并用图结构保存每个算子的输入含取值与输出以及算子之间的依赖关系。反向传播时autograd 从损失度量也可以从任意输出出发沿图反向调用每个算子的backward计算损失对该算子输入的梯度输入可能是参数。从 C 实现层面看MarkVariables会为每个需要梯度的变量创建一个变量节点variable node并把梯度缓冲与grad_req梯度需求关联到该节点的AGInfo上见 src/imperative/imperative.cc反向过程则通过算子注册的FGradient属性推导输入梯度见同文件GetBackwardDependency的实现src/imperative/imperative.cc#L190 起。autograd 的优势是什么简短回答灵活、自动、高效。你可以在网络中使用原生 Python 控制流例如if条件与while循环autograd 依然能够正确反向传播梯度。详细回答使用autograd的巨大收益在于定义网络时的灵活性你可以在每一次迭代中改变运算autograd 仍能正确反向传播梯度。这种能力常被称为「动态图」在要求静态图的框架如 TensorFlow中实现起来要复杂得多。正如其名autograd 是自动的反向传播的复杂细节都被封装好了。你要做的仅仅是在需要记录梯度时告知 autogradrecord上下文并指明想计算哪个量的梯度——绝大多数情况下就是损失度量。而这些梯度计算也会被高效地执行。如何在 MXNet Gluon 中使用 autograd第一步导入并搭建网络第一步是导入autograd包from mxnet import autograd下面用一个简单的回归模型对应前文图示演示完整流程稍后用 autograd 自动计算损失对每个权重参数的梯度import mxnet as mx from mxnet.gluon.nn import HybridSequential, Dense from mxnet.gluon.loss import L2Loss # 定义网络 net HybridSequential() net.add(Dense(units3)) net.add(Dense(units1)) net.initialize() # 定义损失 loss_fn L2Loss() # 创建模拟数据 x mx.np.array([[0.3, 0.5]]) y mx.np.array([[1.5]])第二步在 record 上下文中完成前向传播接下来进行第一次前向传播并让 autograd 记录计算图以便求梯度。最简单的方式就是把网络和损失代码放进autograd.record上下文的作用域内with autograd.record(): y_hat net(x) loss loss_fn(y_hat, y)注意只有我们确实想记录的操作才应放进record上下文因为记录有计算开销。现在 autograd 已经为这些操作构建好了一张计算图随时可以进行反向传播。第三步调用 backward 启动反向传播调用目标量的backward方法即可开始反向传播这里目标量是loss因为我们想求损失对参数的梯度loss.backward()重要提醒如果loss不是单个标量值例如它是每个样本的损失而非整批的平均损失backward启动反向传播前会隐式地先做一次sum求和最终算出的梯度是「损失之和」对参数的梯度。第四步读取梯度至此所有 autograd 的「魔法」已完成网络每个参数都有了梯度可供优化器更新参数值。例如查看第一层的权重梯度net[0].weight.grad()底层上loss.backward()会调用MXAutogradBackwardExC API见 python/mxnet/ndarray/ndarray.py由 C 端逐节点反向求导并把结果写入参数对应的梯度缓冲。高级训练模式与推理模式的切换部分神经网络层在训练与推理时的行为不同。典型例子Dropout训练时随机将激活置 0推理时保持不变BatchNorm训练时用当前批次的统计量做归一化推理时使用全局统计量。在 MXNet Gluon 中autograd对训练/推理模式的切换至关重要。默认情况下网络运行在推理模式一旦 autograd 开始记录例如处于autograd.record()上下文作用域内网络就运行在训练模式。用一个单独的Dropout块来演示dropout mx.gluon.nn.Dropout(rate0.5) data mx.np.ones(shape(3,3)) output dropout(data) is_training autograd.is_training() print(is_training:, is_training, output)上面这段代码在 autograd未记录时调用dropout网络处于推理模式因此输入没有任何 dropout输出仍全是 1。可以用autograd.is_training()确认当前模式。with autograd.record(): output dropout(data) print(is_training:, is_training, output)这次在 autograd记录期间调用dropout网络处于训练模式输入发生了 dropout。由于 dropout 概率为 50%输出会被自动乘以1/0.5 2以保持激活的平均值不变。也可以强制某些算子在推理模式下仍然表现得像训练时一样例如给 Dropout 算子设置modealways但这种用法并不常见。从实现看record(train_modeTrue)返回一个_RecordingStateScope(True, True)作用域见 python/mxnet/autograd.py进入上下文时会把「记录」与「训练」两个状态位同时置位内部调用MXAutogradSetIsRecording/MXAutogradSetIsTraining见 python/mxnet/autograd.py退出时自动恢复。配套的上下文还有pause()、train_mode()与predict_mode()分别用于「不记录但切换模式」等更细粒度的控制。仓库测试 tests/python/unittest/test_autograd.py 也验证了这一点record()内 Dropout 生效pause()内 Dropout 透传。高级跳过参数梯度的计算用 MXNet Gluon 创建网络时默认假设你需要损失对每个网络参数的梯度——因为通常要训练整个网络。调用net.initialize()后网络参数被惰性初始化同时也会为梯度分配内存这相当于让每个参数占用的空间翻倍。完成一次前向 反向传播后所有参数都会有梯度。但有时我们并不需要全部参数的梯度一个典型场景是**「冻结」某些层的参数**既然不需要更新它们的值也就不需要梯度。把参数的grad_req属性设为null即可告知 autograd 跳过这些梯度节省计算时间与内存net[0].weight.grad_req nullgrad_req的取值定义在 python/mxnet/ndarray/ndarray.py 的_GRAD_REQ_MAP中grad_req 取值底层编码含义null0不计算该参数的梯度write1每次 backward 直接覆盖写入梯度默认值add3每次 backward 把梯度累加到已有值上其中add在分布式训练等需要累积梯度的场景非常有用。Parameter.grad_req的 setter 会对取值做合法性校验见 python/mxnet/gluon/parameter.py并将null下的梯度缓冲释放、数据节点与计算图脱离detach从而真正省下内存。高级计算非参数的梯度虽然最常见的做法是处理网络参数Parameter是 MXNet Gluon 对可训练参数的一层抽象但有些场景需要对非参数的量求梯度——例如生成对抗样本时需要计算损失对输入数据的梯度。用 autograd 实现很简单但与参数相比有一个关键区别参数默认就要求梯度非参数不会。你需要显式调用.attach_grad()来声明需要梯度然后在backward之后通过.grad访问梯度。来看一个简单例子设y 2x²用 autograd 计算y对x在三个不同取值处的梯度。手算可知dy/dx 4x正好用来检验 autograd。由于x是ndarray而非Parameter必须先调用x.attach_grad()x mx.np.array([1, 2, 3]) x.attach_grad() with autograd.record(): y 2 * x ** 2 y.backward() print(x.grad)期望输出[ 4. 8. 12.]与dy/dx 4x在x 1, 2, 3处的取值完全一致。从实现看attach_grad会创建一个与x同 shape、初始化为 0 的梯度缓冲并通过MXAutogradMarkVariables把该数组标记为需要梯度的变量见 python/mxnet/ndarray/ndarray.py。x.grad属性则通过MXNDArrayGetGrad取出这个缓冲python/mxnet/ndarray/ndarray.py#L2925-L2933。如果你想更精细地控制非参数梯度autograd.mark_variables(variables, gradients, grad_reqswrite)还可以在标记时自定义梯度缓冲的初始值与grad_req见 python/mxnet/autograd.py。高级使用 Python 控制流构建动态图如前所述autograd 的一大优势是能自动计算动态图的梯度——即每次前向传播的算子都可能不同的图。一个实际例子是用树结构循环网络结合句子的解析树来解析句子我们可以用 Python 控制流算子来构造这种依赖数据的动态流程而不必使用 MXNet 自己的控制流算子。下面写一个动态网络的玩具函数加入一个if条件和一个迭代次数可变的循环两者都依赖输入数据。虽然这些现在也能用条件算子放进静态图但用原生控制流要自然得多import math def f(x): y x # 稍后会修改 y但仍需保留 x if x 0.75: # num_loops 可变因为它依赖 x num_loops math.floor(1/(1-x.item())) for i in range(num_loops): y y * x # 提高多项式次数 else: # 否则水平直线 y y * 0 return y将函数在x ∈ [0, 1]上绘制出来可以识别出若干分段函数[0, 1/2]是二次曲线[1/2, 2/3]是三次曲线[2/3, 3/4]是四次曲线之后是水平直线。由于存在控制流这个函数没有向量化形式我们另外写一个用 autograd 求梯度的辅助函数def get_grad(f, x): x.attach_grad() with autograd.record(): y f(x) y.backward() return x.grad xs mx.np.arange(0.0, 1.0, step0.1) grads [get_grad(f, x).item() for x in xs] print(grads)这个玩具例子可以手算验证对前面讨论的四个分段期望梯度分别是2x、3x²、4x³和0。抽查x 0.6时手算梯度3x² 1.08与 autograd 计算出的1.08完全一致。高级自定义 head gradient大多数情况下 autograd 了解完整的计算图并能自动算出梯度。但在少数场景你可能有 MXNet Gluon 之外的外部后处理组件却仍想计算对 MXNet Gluon 网络参数的梯度。autograd 通过在.backward()中传入自定义 head gradient 来支持这一功能。当不传任何东西时绝大多数情况autograd 默认使用全 1 作为 head gradient。假设我们想算dz/dx但只通过 MXNet Gluon 计算了中间变量y那就需要先手工或借助其他工具算出 head gradientdz/dy再传给.backward()autograd 会按照链式法则用它计算出dz/dx。举个例子设y x³用mxnet计算、z y²用numpy计算。手算dz/dy 2y仍用numpy把它作为 head gradient 交给 autograd让它自动算dz/dx。按链式法则手算dz/dx 6x⁵当x 2时期望dz/dx 192。验证 autograd 是否给出相同结果x mx.np.array([2,]) x.attach_grad() # 在 mxnet 中在 autograd 下计算 y with autograd.record(): y x**3 # 在 mxnet 外部计算 dz/dy y_np y.asnumpy() z_np y_np**2 dzdy_np 2*y_np # 在 mxnet 内部计算 dz/dx给定 dz/dy dzdy mx.np.array(dzdy_np) y.backward(dzdy) print(x.grad)如预期所示x的梯度为192。需要说明的是.backward(head_grads)的完整签名还支持retain_graph与train_mode参数retain_graphTrue可以保留计算图以支持对同一张图再次反向传播train_mode则控制反向传播时按训练还是推理模式处理见 python/mxnet/autograd.py。如果你希望梯度以新数组返回而不是写回variable.grad也可以使用autograd.grad(heads, variables, ...)接口它支持create_graphTrue以记录梯度图、进一步计算高阶梯度注意目前仅有限的一小部分算子支持高阶梯度见python/mxnet/autograd.py#L272-L346。总结围绕 MXNet Gluon 的autograd本文完整覆盖了梯度为什么重要损失函数与参数优化、反向模式自动微分相比手算/符号微分/有限差分在效率与精度上的优势、recordbackward的完整使用范式以及训练/推理模式切换、grad_reqnull冻结参数、attach_grad()计算非参数梯度、Python 控制流动态图、自定义 head gradient 等高级用法。核心要点可浓缩为四句话用with autograd.record():包住前向传播autograd 就会记录计算图对损失或任意输出调用.backward()梯度会写回已标记变量的.grad参数默认需要梯度普通ndarray需先.attach_grad()用grad_reqnull冻结层、用head_grads打通外部组件可应对各类特殊训练需求。更多 API 细节可查阅 autograd 官方 API 文档完整的训练闭环结合优化器与评估指标可参考 Gluon 训练教程 与 Optimizers 文档。赞分享人工智能深度学习机器学习【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址https://gitcode.com/gh_mirrors/mxne/mxnet点击查看免费下载相关推荐MXNet Autograd 自动微分 API 完全指南record、backward 与自定义梯度 FunctionMXNet Autograd 自动微分 API 完全指南record、backward 与自定义梯度 Function 本文以 MXNet Python 包中深度学习机器学习人工智能Ring-2.6-1T长文本处理突破256K上下文窗口的技术实现Ring 2.6 1T长文本处理突破256K上下文窗口的技术实现 Ring 2.6 1T是一款万亿参数级旗舰推理模型专为复杂任务场景设计其核心突破在于通过计算化学新突破AIMNet2-wb97m-d3如何通过torch.compile实现5倍GPU加速计算化学新突破AIMNet2 wb97m d3如何通过torch.compile实现5倍GPU加速 AIMNet2 wb97m d3作为计算化学领域的创新工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表