训练完成打上标签和时间
函数式自动微分
神经网络的训练主要使用反向传播算法,模型预测值(logits)与正确标签(label)送入损失函数(loss function)获得loss,然后进行反向传播计算,求得梯度(gradients)最终更新至模型参数(parameters)。自动微分能够计算可导函数在某点处的导数值是反向传播算法的一般化。自动微分主要解决的问题是将一个复杂的数学运算分解为一系列简单的基本运算,该功能对用户屏蔽了大量的求导细节和过程,大大降低了框架的使用门槛。
Mindspore使用函数式自动微分的设计理念,提供更接近于数学语义的自动微分接grad 和 value_and grad 。下面我们使用一个简单的单层线性变换模型进行介绍。
函数与计算图
计算图是用图论语言表示数学函数的一种方式,也是深度学习框架表达神经网络模型的统一方法。我们将根据下面的计算图构造计算函数和神经网络。
在这个模型中,x为输入,y为正确值,w和b是我们需要优化的参数
我们根据计算图描述的计算过程,构造计算函数。其中,inary_cross_entropy_with_logits 是一个损失函数,计算预测值和目标值之间的二值交叉熵损失。
执行计算函数,可以获得计算的loss值。
微分函数与梯度计算
为了优化模型参数,需要求参数对loss的导数:
此时我们调用 mindspore.grad 函数,来获得 function 的微分函数。
这里使用了 grad 函数的两个入参,分别为:
–fn :待求导的函数。
–grad_ position :指定求导输入位置的索引.
由于我们对w和b求导,因此配置其在 function 入参对应的位置(2,3)。
–使用 grad 获得微分函数是一种函数变换,即输入为函数,输出也为函数。
执行微分函数,即可获得w、b对应的梯度。
Stop Gradient
通常情况下,求导时会求10s;对参数的导数,因此函数的输出只有l05s一项。当我们希望函教输出多项时,微分函数会求所有输出项对参数的导数。此时如果想实现对某个输出项的梯度截断,或消除某个Tensor对梯度的影响,需要用到Stop Gradient操作。
这里将 function 改为同时输出loss和z的 function with logits ,获得微分函数并执行。
可以看到求得w、b对应的梯度值发生了变化。此时如果想要屏蔽掉z对梯度的影响,即仍只求参数对loss;的导数,可以使用 ops.stop_gradient 接口,将梯度在此处裁断。我们将function 实现加入 stop_ gradient ,并执行。
可以看到,求得w、b对应的梯度值与初始 function 求得的梯度值一致。
Auxiliary data
Auxiiary data意为辅助数据,是函数除第一个输出项外的其他输出。通常我们会将函数的loss设置为函数的第一个输出,其他的输出即为辅助数据。
grad 和 value and_grad 提供 has_aux 参数,当其设置为 True时,可以自动实现前文手动添加 stop_gradient 的功能,满足返回辅助数据的同时不影响梯度计算的效果。
下面仍使用 function with_ logits ,配置 has aux=True ,并执行。
可以看到,求得w、b对应的梯度值与初始 function 求得的梯度值一致,同时z能够作为微分函数的输出返回。
神经网络梯度计算
前述草节主要根据计算图对应的四数介绍了Mndsore的函数式自动微分,但我们的神经网络构造是继承自面向对象编程范式的 nn.Cell。接下来我们通过 Cell构造同样的神经网络、利用函数式自动微分来实现反向传播。
首先我们继承 nn.Cell构造单层线性变换神经网络。这里我们直接使用前文的w、b作为模型参数,使用mindspore,parameter 进行包装后,作为内部属性,并在 construct 内实现相同的Tensor操作。
实例化模型和损失函数。
完成后,由于需要使用函数式自动微分,需要将神经网络和损失函数的调用封装为一个前向计算函数。
完成后,我们使用 value_and_grad 接囗获得微分函数,用于计算梯度。
由于使用Cell封装神经网络模型,模型参数为Cell的内部属性,此时我们不需要使用 grad_position 指定对函数输入求导,因此将其配置为 None。对模型参数求导时,我们使用 weights 参数,使用model.trainable_params()方法从Cell中取出可以求导的参数.
执行微分函数,可以看到梯度值和前文 function 求得的梯度值一致。