• MindSpore自动微分小技巧


    技术背景

    基于链式法则的自动微分技术,是大多数深度学习框架中所支持的核心功能,旨在更加快速的进行梯度计算,并且可以绕开符号微分的表达式爆炸问题和手动微分的困难推导问题。本文主要基于MindSpore框架,记录一下几种自动微分的使用技巧。MindSpore版本信息:

    Name: mindspore
    Version: 2.2.13
    Summary: MindSpore is a new open source deep learning training/inference framework that could be used for mobile, edge and cloud scenarios.
    Home-page: https://www.mindspore.cn
    Author: The MindSpore Authors
    Author-email: contact@mindspore.cn
    License: Apache 2.0
    Location: /home/dechin/anaconda3/envs/mindspore-latest/lib/python3.7/site-packages
    Requires: packaging, pillow, protobuf, asttokens, numpy, psutil, scipy, astunparse
    Required-by: 
    

    函数微分

    首先我们定义一个E" role="presentation">E的函数表达形式:

    E=xz+y" role="presentation">E=xz+y

    其中包含有三个输入变量,写成python函数就是

    def fE(x, y, z):
        return x*z+y
    

    那么在mindspore中对这个函数进行自动微分,只需要使用grad函数即可:

    import mindspore as ms
    from mindspore import ops, Tensor
    gfE = ops.grad(fE)
    x = Tensor([2.0], ms.float32)
    y = Tensor([5.0], ms.float32)
    print (gfE(x, y, Tensor([3.], ms.float32)))
    # [3.]
    

    从这个输出结果来说,我们发现默认的配置求得的导数为:Ex" role="presentation">Ex。假如说我们需要计算Ey" role="presentation">EyEz" role="presentation">Ez的话,只需要在函数中配置一下求导位置即可:

    gfE = ops.grad(fE, grad_position=(2, ))
    print (gfE(x, y, Tensor([3.], ms.float32)))
    # [2.]
    gfE = ops.grad(fE, grad_position=(0, 1, 2))
    print (gfE(x, y, Tensor([3.], ms.float32)))
    # (Tensor(shape=[1], dtype=Float32, value= [ 3.00000000e+00]), 
    #  Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]), 
    #  Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]))
    

    如果得到的导数只有一项,那么返回的结果是一个Tensor。而如果是针对多个输入的求导,得到的结果是一个Tuple类型。

    类求导

    这里就抛开面向过程和面向对象的优劣,单纯讨论如何对MindSpore中的一个Cell类进行求导。MindSpore的Cell类的基础结构是这样的:

    class Net(nn.Cell):
        def __init__(self, z):
            super().__init__()
            self.z = z
        def construct(self, x, y):
            return x + y
    

    但是在Cell类里面会多出来一个超参数的概念,也就是上面这个函数中的z。如果是从深度学习的角度来划分参数,可以大致的划分成参数和超参数,超参数大多数时候是初始化之后可以保持不变的内容,而参数是每一次迭代计算都要参与的。如果是从变量的角度来划分参数,则主要可以分为可变参数(Parameter)和不可变参数(Tensor)。之所以要这样划分,是因为在求导上的形式会有所差异。这里我们先把函数E写成Cell类的形式:

    import mindspore as ms
    from mindspore import nn, Parameter, Tensor
    class E(nn.Cell):
        def __init__(self):
            super(E, self).__init__()
            self.z = Parameter(Tensor([3.], ms.float32), requires_grad=True, name='z')
        def construct(self, x, y):
            return x*self.z+y
    

    在这个示例中,我们把z作为一个可变参数,x和y作为不可变的输入参数。类似于函数求导的形式,我们也可以用grad函数直接对这个类的对象进行求导:

    nt = E()
    gE = ops.grad(nt)
    print (gE(x, y))
    # [3.]
    gE = ops.grad(nt, grad_position=(0, 1))
    print (gE(x, y))
    # (Tensor(shape=[1], dtype=Float32, value= [ 3.00000000e+00]), 
    #  Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]))
    

    但是这里就发现了一个问题,这种形式的求导只能对不可变的输入参数进行求导,而我们在初始化对象时用到的可变参数,不在这个求导函数所支持的范围内。不过不用担心,MindSpore内部也支持了一些可以对Cell类进行求导的类,例如GradOperation。这里先看一个GradOperation的普通使用方法:

    nt = E()
    grad_net = ops.GradOperation(get_all=True)(nt)
    print (grad_net(x, y))
    # (Tensor(shape=[1], dtype=Float32, value= [ 3.00000000e+00]),
    #  Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]))
    

    我们配置一个get_all参数,表示对所有的不可变参量进行求导。如果不加任何配置,那么默认是只对第一个不可变参量进行求导:

    nt = E()
    grad_net = ops.GradOperation()(nt)
    print (grad_net(x, y))
    # [3.]
    

    但是不同于grad函数,GradOperation类支持get_by_list参数,这样我们就可以配置其中的一些可变参量作为求导对象:

    nt = E()
    grad_net = ops.GradOperation(get_by_list=True)(nt, nt.z)
    print (grad_net(x, y))
    # [2.]
    

    这里就表示计算了Ez" role="presentation">Ez的值。

    总结概要

    不同于符号微分和手动微分,基于链式法则的自动微分不仅有极高的速度,还不需要去手动推导微分,在深度学习领域有非常广泛的应用。本文主要通过几个案例,分别介绍了一下在MindSpore深度学习框架中,如何使用grad函数和GradOperation类,分别对函数和类进行自动微分计算。

    版权声明

    本文首发链接为:https://www.cnblogs.com/dechinphy/p/grad_operation.html

    作者ID:DechinPhy

    更多原著文章:https://www.cnblogs.com/dechinphy/

    请博主喝咖啡:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

  • 相关阅读:
    vue项目使用lodash节流防抖函数问题与解决
    蓄电池建模、分析与优化(Matlab代码实现)
    12 【操作mongodb数据库】
    C++标准模板库——vector的使用及其模拟实现
    SAP中框架订单中的总体限制和期望值有什么区别
    【MYSQL】库和数据表
    ES6之与Symbol.match
    【uboot】Uboot的启动流程
    阿里云WAF应用防火墙核心概念与购买使用
    01-HelloVueJs
  • 原文地址:https://www.cnblogs.com/dechinphy/p/18124310/grad_operation