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

文章目录

      • 昇思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.mfbz.cn/a/754328.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在flask中加载mnist模型,并预测图片

一、在tensorflow中新建及保存模型 启动Jupyter Notebook 新建Notebook 生成 mnist_model.h5 模型的代码 import tensorflow as tf from tensorflow.keras.datasets import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import…

ASUS/华硕天选Air 2021 FX516P系列 原厂win10系统

安装后恢复到您开箱的体验界面,带原机所有驱动和软件,包括myasus mcafee office 奥创等。 最适合您电脑的系统,经厂家手调试最佳状态,性能与功耗直接拉满,体验最原汁原味的系统。 原厂系统下载网址:http:…

java设计模式(二)工厂方法模式(pattern of factory method)

1、模式介绍: 工厂方法模式(pattern of factory method)是一种创建型设计模式,它定义了一个用于创建对象的接口,但将实际创建对象的工作延迟到子类中,这样可以在不改变整体结构的情况下,通过子…

OpenGL3.3_C++_Windows(23)

伽ga马校正 物理亮度 光子数量 线性空间:光子数(亮度)和颜色值的线性关系人眼感知的亮度:对比较暗的颜色变化更敏感,感知亮度基于人的感觉非线性空间:光子数(亮度)和 颜色值^2.2,恰好符合屏幕…

GIT版本管理工具轻松入门 | TortoiseGit

目录 一、下载git 二、下载tortoisegit(可视化git) 三、Git本地仓库创建 四、git克隆 五、添加,提交,推送,拉取 六、分支 七、冲突 八、忽略文件(修改gitignore文件) 一、下载git 安装…

<Linux> 缓冲区谁维护?

缓冲区是谁提供的&#xff1f; 来看一段代码 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> int main() {const char *str1 "a";printf("%s", str1);const char *str2 "b";writ…

AI副业新风口项目,AI绘画+古诗文视频,轻松吸引大批粉丝,变现简单,收益稳

前言 一个不吹牛逼不聊人生不谈理想只聊赚钱的自媒体从业者 问题都在过程中产生&#xff0c;努力做了也许会成功&#xff0c;但不做永远没有机会 — — — 小红书副业新风口项目&#xff0c;AI绘画古诗文视频&#xff0c;轻松吸引大批粉丝&#xff0c;变现简单&#xff0c;收…

2020年全国大学生数学建模竞赛C题中小微企业信贷决策(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码&#xff08;一&#xff09;数据处理代码&#xff08;二&#xff09;熵权法与TOPSIS代码&#xff08;三&#xff09;最小二乘法代码&#xff08;四&#xff09;粒子群代码 四、完整word版论文和源代码&#xff08;两种获取方式…

FireFox 编译指南2024 Windows10篇-环境准备(一)

1. 引言 在开源浏览器项目中&#xff0c;Firefox因其高性能和灵活性而备受开发者青睐。为了在本地环境中编译和定制Firefox&#xff0c;开发者需要做好充分的环境准备工作。这不仅是编译成功的基础&#xff0c;也是后续调试、优化和二次开发的关键步骤。 编译Firefox是一个复…

缓存双写一致性(笔记)

缓存更新方案 旁路缓存模式 这是比较多的 旁路缓存模式&#xff1a;缓存有就返回&#xff0c;没有数据库查询&#xff0c;放入缓存返回。 还有些常用缓存策略 读穿透模式 读穿透和旁路很相似&#xff0c;程序不需要关注从哪里读取数据&#xff0c;它只需要从缓存查询数据。…

PPT录屏怎么录?PPT录屏,3种方法简单操作

在数字化时代&#xff0c;PPT已经成为我们日常工作、学习和生活中不可或缺的一部分。无论是商务报告、教学课件还是产品展示&#xff0c;PPT都能帮助我们更加生动、直观地传递信息。然而&#xff0c;有时候我们会面临PPT录屏怎么录的问题。这时&#xff0c;一个好的PPT录屏功能…

OpenAI发布CriticGPT:纠错GPT生成错误代码?

OpenAI发布CriticGPT&#xff1a;纠错GPT生成错误代码&#xff1f; 前言 OCriticGPT 就在6月28日&#xff0c;OpenAI 训练出了一个基于 GPT-4 的模型—— CriticGPT&#xff0c;它可以用于查验ChatGPT 写出的代码是否出错。OpenAI的团队说&#xff0c;用户从 CriticGPT 获得帮助…

1panel 搭建多个网站

1panel 部署多个网站&#xff0c;另外的域名&#xff0c;或无域端口搭建方法。 当我们已经部署好一个网站后&#xff0c;想再部署一个网站在我们的服务器上时&#xff0c; 步骤&#xff1a;&#xff08;另外的域名&#xff0c;部署在同一个服务器方法&#xff09; 运行环境里…

营销翻车,杜国楹出面道歉,小罐茶的“大师作”故事仓皇结尾

“小罐茶&#xff0c;大师作”&#xff0c;这句slogan曾一度在央视平台长时间、高密度播放&#xff0c;成为家喻户晓的广告词&#xff0c;也打响了小罐茶品牌的名号。但同时&#xff0c;市场上关于“大师作”真实性的质疑也从未停息。 就在6月25日小罐茶十二周年发布会上&#…

光伏设计:光伏项目开发中最关键的一环

随着全球对可再生能源的需求不断增长&#xff0c;光伏技术作为其中的佼佼者&#xff0c;已经成为许多国家实现能源转型和应对气候变化的重要手段。在光伏项目的开发过程中&#xff0c;光伏设计作为最关键的一环&#xff0c;其重要性不言而喻。本文将从光伏设计的角度&#xff0…

【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行,可以在多个 GPU 上分摊工作负载,从而加快训练速度

【深度学习】单机多卡 | DataParallel将计算任务在多个 GPU 上并行执行&#xff0c;可以在多个 GPU 上分摊工作负载&#xff0c;从而加快训练速度 写在最前面DataParallel (DP) 简介使用 DataParallel 的场景使用 DataParallel 的基本步骤 代码部分train.py简单的代码示例代码解…

抗击.michevol勒索病毒:保障数据安全的新策略

导言&#xff1a; 在今天高度互联的数字化环境中&#xff0c;数据安全面临着越来越复杂和普遍的威胁&#xff0c;勒索病毒如.michevol已成为了用户和企业普遍面临的风险。本文91数据恢复将探讨.michevol勒索病毒的特点、感染方式以及创新的防御策略&#xff0c;旨在帮助读者更…

Typora 2024 安装教程

本章教程&#xff0c;介绍一下如何使用Typora 最新版本1.9.4&#xff0c;仅供学习交流&#xff0c;切勿滥用。 一、下载安装包 下载地址&#xff1a;https://www.alipan.com/s/8pvKf5ns6GH 当然&#xff0c;你也可以去官网下载&#xff0c;但是官网有可能随时更新&#xff0c;该…

MySQL事务——Java全栈知识(31)

1、事务的特性 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务是不可分割的最小操作单元&#xff0c;要么全部成功&#xff0c;要么全部失败。 一致性&#xff08;Consistency&#xff09;&#xff1a;事务完成时&#xff0c;必须使所有的数据都保持一致状态。 隔离…

《互联网政务应用安全管理规定》自2024年7月1日起,关于日志存储至少保存一年说明

学习目标&#xff1a;《互联网政务应用安全管理规定》第二十条 机关事业单位应当留存互联网政务应用相关的防火墙、主机等设备的运行日志&#xff0c;以及应用系统的访问日志、数据库的操作日志&#xff0c;留存时间不少于1年&#xff0c;并定期对日志进行备份&#xff0c;确保…