昇思25天学习打卡营第3天|函数式自动微分

news2024/11/24 19:55:36

文章目录

      • 昇思MindSpore快速入门
        • 基于MindSpore的函数式自动微分
          • 1、简介
          • 2、函数与计算图算例
          • 3、微分函数与梯度计算
          • 4、Stop Gradient(停止梯度计算)
          • 5、Auxiliary data
          • 6、神经网络梯度计算
      • Reference

昇思MindSpore快速入门

基于MindSpore的函数式自动微分
1、简介

神经网络的训练主要使用反向传播算法。反向传播顾名思义,将模型前向传播的预测值(logits)与正确标签(label)送入损失函数(loss function)获得loss值,然后沿着网络结构中的反向信息流传播,通过链式求导法则对每枝路线上神经元的参数求偏导,从而实现反向传播计算,利用求得的梯度(gradients),最终更新至模型参数(parameters):
在这里插入图片描述
至于为什么要求导(微分),求梯度,是为了最优化(最小化)使目标函数(损失函数loss function),通常采用梯度下降算法。

Mindspore提供的自动微分(Autograd)能够计算可导函数在某点处的导数值,解决了将一个复杂的数学运算分解为一系列简单的基本运算,该功能对用户屏蔽了大量的求导细节和过程(不用手动推公式链式求导了,感兴趣的同学可以尝试对一个小型的BP神经网络进行反向传播梯度下降求导计算),大大降低了框架的使用门槛。

MindSpore提供更接近于数学语义的自动微分接口gradvalue_and_grad

2、函数与计算图算例

下面我们根据这个计算图中单个神经元构成的网络结构构造计算函数并进行反向传播参数计算:
在这里插入图片描述
在这个模型中, 𝑥 为输入, 𝑦 为正确值(Ground Truth, GT), 𝑤 和 𝑏 是我们需要优化的参数,使得预测值 z = w x + b z=wx+b z=wx+b近似于 𝑦,也就是使 z z z y y y之间的 loss 最小。

# 导入必要的MindSpore接口
import numpy as np 
import mindspore
from mindspore import nn
from mindspore import ops
from mindspore import Tensor, Parameter

x = ops.ones(5, mindspore.float32)  # input tensor,输入5x1的全1向量
y = ops.zeros(3, mindspore.float32)  # expected output,GT为3x1的全0向量
w = Parameter(Tensor(np.random.randn(5, 3), mindspore.float32), name='w') # weight,参数w为5行3列的形状,对应5个输入和3个输出
b = Parameter(Tensor(np.random.randn(3,), mindspore.float32), name='b') # bias,参数bias为3x1的形状
print(x)
print(y)
print(w)
print(b)

# print_log:
[1. 1. 1. 1. 1.]
[0. 0. 0.]
Parameter (name=w, shape=(5, 3), dtype=Float32, requires_grad=True)
Parameter (name=b, shape=(3,), dtype=Float32, requires_grad=True)

# 定义网络中的神经元功能,并使用二元交叉熵损失作为损失函数返回loss值
def function(x, y, w, b):  
    z = ops.matmul(x, w) + b
    loss = ops.binary_cross_entropy_with_logits(z, y, ops.ones_like(z), ops.ones_like(z))
    return loss

loss = function(x, y, w, b)
print(loss)

# print_log:
1.189936
3、微分函数与梯度计算

为了优化模型参数,需要求参数对loss的导数: ∂ l o s s ∂ w \frac{\partial loss}{\partial w} wloss ∂ l o s s ∂ b \frac{\partial loss}{\partial b} bloss
,通过调用mindspore.grad函数,来获得function的微分函数。

这里使用了grad函数的两个入参,分别为:
fn:待求导的function;
grad_position:指定求导输入位置的索引。
由于我们对 𝑤 和 𝑏 求导,因此配置其在grad_fn入参对应的位置(2, 3)。

使用grad获得微分函数是一种函数变换,即输入为函数,输出也为函数:

grad_fn = mindspore.grad(function, (2, 3))

grads = grad_fn(x, y, w, b)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=   # w的偏导数
 [[ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01]]),
 Tensor(shape=[3], dtype=Float32, value= [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01]) # b的偏导数)
4、Stop Gradient(停止梯度计算)

通常情况下,求导时会求 loss 对参数的偏导数,因此函数的输出只有loss一项。当我们希望函数输出多项时,微分函数会求所有输出项对参数的导数。此时如果想实现对某个输出项的梯度截断,或消除某个Tensor对梯度的影响,需要用到Stop Gradient操作。

这里通过将 function 改为同时输出 loss 和 z 的 function_with_logits,获得微分函数并执行:

def function_with_logits(x, y, w, b):
    z = ops.matmul(x, w) + b
    loss = ops.binary_cross_entropy_with_logits(z, y, ops.ones_like(z), ops.ones_like(z))
    return loss, z

grad_fn = mindspore.grad(function_with_logits, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

# print_log
(Tensor(shape=[5, 3], dtype=Float32, value=    # w的偏导数
 [[ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00],
  [ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00],
  [ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00],
  [ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00],
  [ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00]]),
 Tensor(shape=[3], dtype=Float32, value= [ 1.06568694e+00,  1.05373347e+00,  1.30146706e+00]) # b的偏导数)

def function_stop_gradient(x, y, w, b):
    z = ops.matmul(x, w) + b
    loss = ops.binary_cross_entropy_with_logits(z, y, ops.ones_like(z), ops.ones_like(z))
    return loss, ops.stop_gradient(z)  # 屏蔽掉z对梯度的影响,即仍只求参数对loss的导数

grad_fn = mindspore.grad(function_stop_gradient, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=   # w的偏导数
 [[ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
  [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01]]),
 Tensor(shape=[3], dtype=Float32, value= [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01]) # b的偏导数)

由于上述算例中输入的是全1向量,w参数对应的偏导数变化不是很明显,为了区分,可以尝试输入一组随机噪声作为网络的样本观测数据,这在实际深度学习的网络验证时也是常见的作法:

x_rand = np.random.rand(5)
x = Tensor(x_rand, mindspore.float32)
# x = ops.ones(5, mindspore.float32)   # input tensor
y = ops.zeros(3, mindspore.float32)  # expected output
w = Parameter(Tensor(np.random.randn(5, 3), mindspore.float32), name='w')  # weight
b = Parameter(Tensor(np.random.randn(3,), mindspore.float32), name='b')    # bias
print(x)
print(y)
print(w)
print(b)

# print_log:
[0.7097724  0.98406976 0.6969981  0.5849176  0.6543453 ]
[0. 0. 0.]
Parameter (name=w, shape=(5, 3), dtype=Float32, requires_grad=True)
Parameter (name=b, shape=(3,), dtype=Float32, requires_grad=True)

loss = function(x, y, w, b)
print(loss)

# print_log:
1.0480353

grad_fn = mindspore.grad(function, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=
[[ 1.82850987e-01,  1.81477696e-01,  4.38495465e-02],
 [ 2.53515244e-01,  2.51611233e-01,  6.07955605e-02],
 [ 1.79560080e-01,  1.78211510e-01,  4.30603549e-02],
 [ 1.50685996e-01,  1.49554282e-01,  3.61360498e-02],
 [ 1.68571889e-01,  1.67305842e-01,  4.04252708e-02]]), Tensor(shape=[3], dtype=Float32, value= [ 2.57619172e-01,  2.55684346e-01,  6.17797263e-02]))

# 再求一篇梯度,并考虑return loss, z
grad_fn = mindspore.grad(function_with_logits, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=
[[ 8.92623365e-01,  8.91250134e-01,  7.53621936e-01],
 [ 1.23758495e+00,  1.23568106e+00,  1.04486537e+00],
 [ 8.76558185e-01,  8.75209630e-01,  7.40058482e-01],
 [ 7.35603571e-01,  7.34471917e-01,  6.21053636e-01],
 [ 8.22917163e-01,  8.21651161e-01,  6.94770575e-01]]), Tensor(shape=[3], dtype=Float32, value= [ 1.25761914e+00,  1.25568438e+00,  1.06177974e+00]))

# Stop Gradient
grad_fn = mindspore.grad(function_stop_gradient, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=
[[ 1.82850987e-01,  1.81477696e-01,  4.38495465e-02],
 [ 2.53515244e-01,  2.51611233e-01,  6.07955605e-02],
 [ 1.79560080e-01,  1.78211510e-01,  4.30603549e-02],
 [ 1.50685996e-01,  1.49554282e-01,  3.61360498e-02],
 [ 1.68571889e-01,  1.67305842e-01,  4.04252708e-02]]), Tensor(shape=[3], dtype=Float32, value= [ 2.57619172e-01,  2.55684346e-01,  6.17797263e-02]))

可以看到加入Stop Gradient后,和第一次的梯度求解效果一致,梯度信息不再更新。

5、Auxiliary data

Auxiliary data意为辅助数据,是函数除第一个输出项外的其他输出。通常我们会将函数的loss设置为函数的第一个输出,其他的输出即为辅助数据。

gradvalue_and_grad提供has_aux参数,当其设置为True时,可以自动实现前文手动添加stop_gradient的功能,满足返回辅助数据的同时不影响梯度计算的效果。

下面仍使用function_with_logits,配置has_aux=True,并执行。

grad_fn = mindspore.grad(function_with_logits, (2, 3), has_aux=True)
grads, (z,) = grad_fn(x, y, w, b)
print(grads, z)

# print_log:
((Tensor(shape=[5, 3], dtype=Float32, value=
  [[ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
   [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
   [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
   [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01],
   [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01]]),
  Tensor(shape=[3], dtype=Float32, value= [ 6.56869709e-02,  5.37334494e-02,  3.01467031e-01])),
 Tensor(shape=[3], dtype=Float32, value= [-1.40476596e+00, -1.64932394e+00,  2.24711204e+00]))

可以看到,求得 𝑤 、 𝑏 对应的梯度值与初始function求得的梯度值一致,同时z能够作为微分函数的输出返回。

6、神经网络梯度计算

我们了解了基于MindSpore的函数式自动微分后,可以进一步熟悉实际的神经网络构造往往是继承自面向对象编程范式的nn.Cell
在MindSpore中可以通过Cell构造同样的神经网络,利用函数式自动微分来实现反向传播。

首先以继承nn.Cell构造单层线性变换神经网络为例。这里我们直接使用前文的 𝑤 、 𝑏 作为模型参数,使用mindspore.Parameter进行封装后,作为内部属性,并在construct内实现相同的Tensor操作,并使用value_and_grad接口获得微分函数,用于计算梯度:

# Define model
class Network(nn.Cell):
    def __init__(self):
        super().__init__()
        self.w = w
        self.b = b

    def construct(self, x):
        z = ops.matmul(x, self.w) + self.b
        return z

# Instantiate model,实例化定义的模型
model = Network()
# Instantiate loss function
loss_fn = nn.BCEWithLogitsLoss()

# Define forward function,定义前向传播函数
def forward_fn(x, y):
    z = model(x)
    loss = loss_fn(z, y)
    return loss

# 使用value_and_grad接口获得微分函数,计算梯度
grad_fn = mindspore.value_and_grad(forward_fn, None, weights=model.trainable_params())
loss, grads = grad_fn(x, y)
print(grads)

# print_log:
(Tensor(shape=[5, 3], dtype=Float32, value=
[[ 1.82850987e-01,  1.81477696e-01,  4.38495465e-02],
 [ 2.53515244e-01,  2.51611233e-01,  6.07955605e-02],
 [ 1.79560080e-01,  1.78211510e-01,  4.30603549e-02],
 [ 1.50685996e-01,  1.49554282e-01,  3.61360498e-02],
 [ 1.68571889e-01,  1.67305842e-01,  4.04252708e-02]]), Tensor(shape=[3], dtype=Float32, value= [ 2.57619172e-01,  2.55684346e-01,  6.17797263e-02]))
2024-06-28 10:41:54 Wayn_Fan-sail

Reference

mindspore.cn/lab/tree/初学入门/初学教程/07-函数式自动微分
昇思大模型平台
昇思25天学习打卡训练营打卡指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1873877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Keil5中:出现:failed to execute ‘...\ARMCC\bin\ArmCC‘

点三个点,去自己的磁盘找自己的ARM\ARMCC\bin

vue-cil搭建项目

目录 一、使用 HbuilderX 快速搭建一个 vue-cli 项目 1.需要的环境——Node.js 2.搭建Vue-cil项目 二、组件路由 1.安装vue-router 2.创建router目录 3.使用路由 4.在main.js中配置路由 vue-cli 官方提供的一个脚手架,用于快速生成一个 vue 的项目模板;…

Windows/Linux/Mac 系统局域网服务发现协议及传输速度比较

简介 分析 / 验证对比常见局域网服务发现协议在 Windows/Linux/Mac 等不同系统下的支持和表现 在使用不同系统的智能硬件时,如常见的树莓派 / Openwrt 路由器 / Debian/Fedora/Windows/Mac 等系统是,系统间相互发现以及网络共享本应是系统的基础服务&a…

游戏AI的创造思路-技术基础-深度学习(3)

继续填坑,本篇介绍深度学习中的长短期记忆网络~~~~ 目录 3.3. 长短期记忆网络(LSTM) 3.3.1. 什么是长短期记忆网络 3.3.2. 形成过程与运行原理 3.3.2.1. 细胞状态与门结构 3.3.2.2. 遗忘门 3.3.2.3. 输入门 3.3.2.4. 细胞状态更新 3.…

vscode 生成项目目录结构 directory-tree 实用教程

1. 安装插件 directory-tree 有中文介绍,极其友好! 2. 用 vscode 打开目标项目 3. 快捷键 Ctrl Shift p,输入 Directory Tree 后回车 会在 README.md 文件的底部生成项目目录(若项目中没有 README.md 文件,则会自动创…

数据结构速成--图

由于是速成专题,因此内容不会十分全面,只会涵盖考试重点,各学校课程要求不同 ,大家可以按照考纲复习,不全面的内容,可以看一下小编主页数据结构初阶的内容,找到对应专题详细学习一下。 目录 …

CO-DETR利用coco数据集训练和推理过程

CO-DETR利用coco数据集训练和推理过程,参考链接 Co-DETR训练自己的数据集 文章目录 前言训练过程推理过程总结 前言 环境:PyTorch 1.11.0 Python 3.8(ubuntu20.04) Cuda 11.3 先是在github上下载CO-DETR模型 !git clone https://github.com/Sense-X/Co…

JAVA设计模式-大集合数据拆分

背景 我们在做软件开发时,经常会遇到把大集合的数据,拆分成子集合处理。例如批量数据插入数据库时,一次大约插入5000条数据比较合理,但是有时候待插入的数据远远大于5000条。这时候就需要进行数据拆分。数据拆分基本逻辑并不复杂&…

复分析——第9章——椭圆函数导论(E.M. Stein R. Shakarchi)

第 9 章 椭圆函数导论 (An Introduction to Elliptic Functions) The form that Jacobi had given to the theory of elliptic functions was far from perfection; its flaws are obvious. At the base we find three fundamental functions sn, cn and dn. These functio…

一款轻量级的WPF UI库---Adonis UI

Adonis UI适用于 WPF 应用程序的轻型 UI 工具包,提供经典但增强的 Windows 视觉对象 组件内容 几乎所有 WPF 控件的模板的默认样式为方便起见,可根据需要使用两种配色方案(浅色和深色),也可用于自定义样式支持在运行时更改配色方案支持其他自定义配色方案提供水印等功能的…

华为BGP路由实验基础1------用物理口建立对等体

1.用物理口做BGP建立对等体建立BGP连接 实验拓扑: 实验步骤: 1.完成基本配置 sys [Huawei]sys AR1 [AR1]undo in e [AR1]int g0/0/0 [AR1-GigabitEthernet0/0/0]ip add 1.1.1.1 24 [AR1-GigabitEthernet0/0/0]q [AR1] sys [Huawei]sys AR2 [AR2]undo i…

2024年危化品安全员生产单位(生产管理人员)考试精选题库

31.《危险化学品安全管理条例》所称重大危险源,是指生产、储存、使用或者搬运危险化学品,且危险化学品的数量等于或者超过()的单元(包括场所和设施)。 A.标准 B.一定量 C.临界量 答案:C 32.《危险化学品生产企业安全生产许可证实施办法》…

Sui创始团队在竞速环节中的快问快答

在Sui Basecamp活动期间,Sui区块链的最初贡献者在Oracle红牛赛车模拟器上展示了他们的技术能力,在驾驶圈时回答了有关Sui的问题。 Evan Cheng(又名Revvin’ Evan)在解释Mysticeti创下区块链最终性记录的同时保持着他的驾驶线路。…

C++——string类用法指南

一、前言 在C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍…

Linux—系统安全及应用

目录 一、账号安全控制 1、系统账号清理 1.1、将用户账号设置为无法登录 1.2、锁定长期不使用的账号 1.3、删除无用的账号 1.4、锁定账号文件passwd、shadow 2、密码安全控制 2.1、设置密码有效期 2.1.1、适用于新建用户 2.1.2、适用于已有用户 2.2、强制用户下次登录…

Python学习笔记26:进阶篇(十五)常见标准库使用之性能测试cProfile模块学习使用

前言 本文是根据python官方教程中标准库模块的介绍,自己查询资料并整理,编写代码示例做出的学习笔记。 根据模块知识,一次讲解单个或者多个模块的内容。 教程链接:https://docs.python.org/zh-cn/3/tutorial/index.html 本文主要…

Qt—贪吃蛇项目(由0到1实现贪吃蛇项目)

用Qt实现一个贪吃蛇项目 一、项目介绍二、游戏大厅界面实现2.1完成游戏大厅的背景图。2.2创建一个按钮,给它设置样式,并且可以跳转到别的页面 三、难度选择界面实现四、 游戏界面实现五、在文件中写入历史战绩5.1 从文件里提取分数5.2 把贪吃蛇的长度存入…

华为OceanStor磁盘阵列存储恢复出厂设置命令 LUN不处于在线状态,不能执行此操作解决方案

环境 OceanStor S2600T V2老版本 客户现场有一台Oceanstor 2600 V2的存储,因和另一台磁盘扩展框做了跨设备LUN需要进行配置清除,配置结束后需要重新划分存储空间并对接服务器,保证业务能够正常上线!在清除配置回退的过程中&#…

Firefox 编译指南2024 Windows10篇- 编译Firefox(三)

1.引言 在成功获取了Firefox源码之后,下一步就是将这些源码编译成一个可执行的浏览器。编译是开发流程中的关键环节,通过编译,我们可以将源代码转换为可执行的程序,测试其功能,并进行必要的优化和调试。 对于像Firef…

Milvus【部署 01】向量数据库Milvus在Linux环境下的在线+离线安装

向量数据库Milvus在Linux环境下的在线离线安装 1.千问简介2.在线安装2.离线安装 1.千问简介 Milvus 是一款专为处理高维向量数据设计的开源云原生数据库,旨在满足海量向量数据的实时召回需求。它由 Zilliz 公司开发并维护,基于Apache许可证2.0版本发布。…