自动微分
在训练神经网络时,最常用的算法是反向传播,在该算法中,根据损失函数对于给定参数的梯度来调整参数(模型权重)。
MindSpore计算一阶导数方法mindspore.ops.GradOperation (get_all=False, get_by_list=False, sens_param=False)
,其中get_all
为False
时,只会对第一个输入求导,为True
时,会对所有输入求导;get_by_list
为False
时,不会对权重求导,为True
时,会对权重求导;sens_param
对网络的输出值做缩放以改变最终梯度。下面用MatMul算子的求导做深入分析。
首先导入本文档需要的模块和接口,如下所示:
[1]:
import numpy as np
import mindspore.nn as nn
import mindspore.ops as ops
from mindspore import Tensor
from mindspore import ParameterTuple, Parameter
from mindspore import dtype as mstype
对输入求一阶导
如果需要对输入进行求导,首先需要定义一个需要求导的网络,以一个由MatMul算子构成的网络\(f(x,y)=z * x * y\)为例。
定义网络结构如下:
[2]:
class Net(nn.Cell):
def __init__(self):
super(Net, self).__init__()
self.matmul = ops.MatMul()
self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')
def construct(self, x, y):
x = x * self.z
out = self.matmul(x, y)
return out
接着定义求导网络,__init__
函数中定义需要求导的网络self.net
和ops.GradOperation
操作,construct
函数中对self.net
进行求导。
求导网络结构如下:
[3]:
class GradNetWrtX(nn.Cell):
def __init__(self, net):
super(GradNetWrtX, self).__init__()
self.net = net
self.grad_op = ops.GradOperation()
def construct(self, x, y):
gradient_function = self.grad_op(self.net)
return gradient_function(x, y)
定义输入并且打印输出:
[4]:
x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)
y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)
output = GradNetWrtX(Net())(x, y)
print(output)
[[4.5099998 2.7 3.6000001]
[4.5099998 2.7 3.6000001]]
若考虑对x
、y
输入求导,只需在GradNetWrtX
中设置self.grad_op = GradOperation(get_all=True)
。
对权重求一阶导
若需要对权重的求导,将ops.GradOperation
中的get_by_list
设置为True
:
则GradNetWrtX
结构为:
[5]:
class GradNetWrtX(nn.Cell):
def __init__(self, net):
super(GradNetWrtX, self).__init__()
self.net = net
self.params = ParameterTuple(net.trainable_params())
self.grad_op = ops.GradOperation(get_by_list=True)
def construct(self, x, y):
gradient_function = self.grad_op(self.net, self.params)
return gradient_function(x, y)
运行并打印输出:
[6]:
output = GradNetWrtX(Net())(x, y)
print(output)
(Tensor(shape=[1], dtype=Float32, value= [ 2.15359993e+01]),)
若需要对某些权重不进行求导,则在定义求导网络时,对相应的权重中requires_grad
设置为False
。
self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z', requires_grad=False)
梯度值缩放
可以通过sens_param
参数对网络的输出值做缩放以改变最终梯度。首先将ops.GradOperation
中的sens_param
设置为True
,并确定缩放指数,其维度与输出维度保持一致。
缩放指数self.grad_wrt_output
可以记作如下形式:
self.grad_wrt_output = Tensor([[s1, s2, s3], [s4, s5, s6]])
则GradNetWrtX
结构为:
[7]:
class GradNetWrtX(nn.Cell):
def __init__(self, net):
super(GradNetWrtX, self).__init__()
self.net = net
self.grad_op = ops.GradOperation(sens_param=True)
self.grad_wrt_output = Tensor([[0.1, 0.6, 0.2], [0.8, 1.3, 1.1]], dtype=mstype.float32)
def construct(self, x, y):
gradient_function = self.grad_op(self.net)
return gradient_function(x, y, self.grad_wrt_output)
output = GradNetWrtX(Net())(x, y)
print(output)
[[2.211 0.51 1.49 ]
[5.588 2.68 4.07 ]]