技术背景

当前主流的深度学习框架,除了能够便捷高效的搭建机器学习的模型之外,其自动并行和自动微分等功能还为其他领域的科学计算带来了模式的变革。本文我们将探索如何用MindSpore去实现一个多维的自动微分,并且得到该多元函数的雅可比矩阵。

函数形式与雅可比矩阵形式

首先我们给定一个比较简单的z关于自变量x的函数形式(其中y和I是一些参数):

\[z_{i,j}(x)=y_ix_j
\]

比如我们考虑一个3*3的z,我们最终需要计算的是这样一个雅可比矩阵:

\[J_z(x)=
\left[
\begin{array}{l}
\frac{\partial z_0}{\partial x_0} & \frac{\partial z_0}{\partial x_1} & \frac{\partial z_0}{\partial x_2}\\
\frac{\partial z_1}{\partial x_0} & \frac{\partial z_1}{\partial x_1} & \frac{\partial z_1}{\partial x_2}\\
\frac{\partial z_2}{\partial x_0} & \frac{\partial z_2}{\partial x_1} & \frac{\partial z_2}{\partial x_2}
\end{array}
\right]
\]

假如我们给定一些简单的初始值:

\[x=[1,2,3]\\
y=[1,3,2]
\]

那么理论上我们应该得到的结果是:

\[J_z(x)=\left[
\begin{array}{l}
1 & 0 & 0\\
0 & 0 & 3\\
0 & 2 & 0\\
\end{array}
\right]
\]

接下来我们看看如何在MindSpore的自动微分框架下实现这一功能。

初步尝试Grad自动微分

我们先按照上一章节中的公式的基本内容,直接写一个Net函数用于表示z,然后再用grad函数对其进行微分,代码内容如下所示:

from mindspore import nn, Tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp class Net(nn.Cell):
def __init__(self, y, index):
super(Net, self).__init__()
self.y = y
self.index = index
self.norm = nn.Norm(-1) def construct(self, x):
return self.y[self.index]*x x = Tensor(np.array([1,2,3]).astype(np.float32))
y = Tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = Tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0]) output = grad(Net(y,index))(x)
print(output)
# [6. 6. 6.]

在这个案例中,我们得到的结果,首先维度就不对,我们理想中的雅可比矩阵应该是3*3大小的,可见MindSpore中自动微分的逻辑是把其中的一个维度进行了加和,类似于这样的形式:

\[\left[
\frac{\partial z_0}{\partial x_0}+\frac{\partial z_1}{\partial x_0}+\frac{\partial z_2}{\partial x_0}, \frac{\partial z_0}{\partial x_1}+\frac{\partial z_1}{\partial x_1}+\frac{\partial z_2}{\partial x_1}, \frac{\partial z_0}{\partial x_2}+\frac{\partial z_1}{\partial x_2}+\frac{\partial z_2}{\partial x_2}
\right]
\]

所以为了得到我们的结果,需要对输入的x进行扩维。

尝试扩维输入的自动微分

在MindSpore中提供了BroadcastTo这样的接口,可以自动的在扩展维度填充待扩展张量的元素,我们需要把x的最外层维度扩展到与参数y一致,在这个案例中就是3*3的维度,具体代码实现如下所示:

from mindspore import nn, Tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp class Net(nn.Cell):
def __init__(self, y, index):
super(Net, self).__init__()
self.y = y
self.index = index
self.norm = nn.Norm(-1) def construct(self, x):
return self.y[self.index]*x x = Tensor(np.array([1,2,3]).astype(np.float32))
y = Tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = Tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0]) output = grad(Net(y,index))(ops.BroadcastTo(shape)(x))
print(output)
'''
[[1. 1. 1.]
[3. 3. 3.]
[2. 2. 2.]]
'''

从这个输出结果中我们发现,虽然维度上是被扩展成功了,但是那些本该为0的位置却出现了非0元素,这说明在自动微分计算的过程中,我们输入的参数y也被自动的Broadcast了,而实际上正确的计算过程中是不能使用Broadcast的。

为参数添加Mask

上一个章节中说道,如果利用Tensor本身的自动Broadcast会导致输入参数被扩维,会得到一个错误的微分结果。因此这里我们手动对输入参数进行正确的扩维,这个过程是添加一个Mask矩阵,用于标记每一个参数所对应的位置。这里我们假设输入一个这样的Mask矩阵:

\[I=\left[
\begin{array}{l}
1 & 0 & 0\\
0 & 0 & 1\\
0 & 1 & 0
\end{array}
\right]
\]

这样理论上最终微分结果的非0元素应该跟这个矩阵是一致的,相关代码如下所示:

from mindspore import nn, Tensor, ops
from mindspore.ops.functional import grad
import numpy as np
from mindspore import numpy as msnp class Net(nn.Cell):
def __init__(self, y, index, size):
super(Net, self).__init__()
self.y = y
self.index = index
self.norm = nn.Norm(-1)
self.mask = msnp.zeros((y.shape[0],size))
self.mask[msnp.arange(self.index.shape[0]),self.index] = 1 def construct(self, x):
return self.mask*self.y[self.index]*x x = Tensor(np.array([1,2,3]).astype(np.float32))
y = Tensor(np.array([[1],[2],[3]]).astype(np.float32))
index = Tensor(np.array([0,2,1]).astype(np.int32))
shape = (y.shape[0], x.shape[0]) output = grad(Net(y,index,x.shape[0]))(ops.BroadcastTo(shape)(x))
print(output)
'''
[[1. 0. 0.]
[0. 0. 3.]
[0. 2. 0.]]
'''

这里我们看到得到的结果就是正确的了。当然,需要说明的是,虽然这个案例只是非常简单的内容,但是这里给出的如何去计算多维函数的自动微分的方法,同样也适用于一些更加复杂的网络和函数。

总结概要

在本文中通过一个实际函数案例的多次尝试,给出了得到预期结果的一种解决方案。虽然MindSpore框架本身提供了Jvp和Vjp等功能,但是实际上和Grad没有太大的区别,只是用Tuple的形式增加了输入的一个维度。如果可以使用纯Tensor的输入,用这种Mask加上Grad或者GradOperation的方案会更加简单一些。同时我也尝试过使用HyperMap(类似于Jax中的vmap)来解决这个问题,只需要写好一条对z求导的函数形式,就可以自动对这个求导过程进行扩维,两者的结果是一致的。但是MindSpore的HyperMap在Graph模式下兼容效果不是很好,建议非必要不尝试。

版权声明

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

作者ID:DechinPhy

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

打赏专用链接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html

腾讯云专栏同步:https://cloud.tencent.com/developer/column/91958

MindSpore多元自动微分的更多相关文章

  1. MindSpore:自动微分

    MindSpore:自动微分 作为一款「全场景 AI 框架」,MindSpore 是人工智能解决方案的重要组成部分,与 TensorFlow.PyTorch.PaddlePaddle 等流行深度学习框 ...

  2. 附录D——自动微分(Autodiff)

    本文介绍了五种微分方式,最后两种才是自动微分. 前两种方法求出了原函数对应的导函数,后三种方法只是求出了某一点的导数. 假设原函数是$f(x,y) = x^2y + y +2$,需要求其偏导数$\fr ...

  3. pytorch学习-AUTOGRAD: AUTOMATIC DIFFERENTIATION自动微分

    参考:https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autog ...

  4. 自动微分(AD)学习笔记

    1.自动微分(AD) 作者:李济深链接:https://www.zhihu.com/question/48356514/answer/125175491来源:知乎著作权归作者所有.商业转载请联系作者获 ...

  5. <转>如何用C++实现自动微分

    作者:李瞬生转摘链接:https://www.zhihu.com/question/48356514/answer/123290631来源:知乎著作权归作者所有. 实现 AD 有两种方式,函数重载与代 ...

  6. (转)自动微分(Automatic Differentiation)简介——tensorflow核心原理

    现代深度学习系统中(比如MXNet, TensorFlow等)都用到了一种技术——自动微分.在此之前,机器学习社区中很少发挥这个利器,一般都是用Backpropagation进行梯度求解,然后进行SG ...

  7. PyTorch自动微分基本原理

    序言:在训练一个神经网络时,梯度的计算是一个关键的步骤,它为神经网络的优化提供了关键数据.但是在面临复杂神经网络的时候导数的计算就成为一个难题,要求人们解出复杂.高维的方程是不现实的.这就是自动微分出 ...

  8. 【tensorflow2.0】自动微分机制

    神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情. 而深度学习框架可以帮助我们自动地完成这种求梯度运算. Tensorflow一般使用梯度磁带tf.Gradi ...

  9. PyTorch 自动微分示例

    PyTorch 自动微分示例 autograd 包是 PyTorch 中所有神经网络的核心.首先简要地介绍,然后训练第一个神经网络.autograd 软件包为 Tensors 上的所有算子提供自动微分 ...

随机推荐

  1. gitlab新增ssh

    https://blog.csdn.net/u011925641/article/details/79897517

  2. 终-Echart可视化学习(十二)

    文档的源代码地址,需要的下载就可以了(访问密码:7567) https://url56.ctfile.com/f/34653256-527823386-04154f 进入官网寻找 里面可以搜素很多 这 ...

  3. 【Java】抽象类与抽象方法

    文章目录 抽象类与抽象方法 abstract关键字的使用 abstract修饰类:抽象类 abstract修饰方法:抽象方法 abstract使用上的注意点: 抽象类的匿名子类 模板方法设计模式 抽象 ...

  4. 论文解读GALA《Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learning》

    论文信息 Title:<Symmetric Graph Convolutional Autoencoder for Unsupervised Graph Representation Learn ...

  5. 返回值String是文本数据

    MyController类中: index.jsp中 修改text前: 改为text后: 还是有乱码是因为使用这个ISO-8859-1编码处理的 MyController中修改注解中属性

  6. HttpServletRequest类介绍

    HttpServletRequest类介绍 1,HttpServletRequest类作用: 每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Req ...

  7. 不难懂-----Mock基本使用

    一.mock解决的问题 开发时,后端还没完成数据输出,前端只好写静态模拟数据.数据太长了,将数据写在js文件里,完成后挨个改url.某些逻辑复杂的代码,加入或去除模拟数据时得小心翼翼.想要尽可能还原真 ...

  8. 一个简单的javaweb项目模板

    Controller包:表现层(视图)层.用来显示数据和接收用户数据 Service包:业务逻辑层,用来处理页面.先写接口,后写实现类 Dao包:持久层(数据访问层).用来操作数据库 其中Dao包处于 ...

  9. 论文解读(DFCN)《Deep Fusion Clustering Network》

    Paper information Titile:Deep Fusion Clustering Network Authors:Wenxuan Tu, Sihang Zhou, Xinwang Liu ...

  10. springboot 修改关闭banner的方法

    一.修改banner. 1.1 替换banner: 需要在resources(classpath)目录中创建文件 banner.txt 1.2 上图   banner.txt 里面可以使用文字,也可以 ...