昇思25天学习打卡营第6天|简单的深度学习模型实战 - 函数式自动微分

news2024/12/25 9:02:28

自动微分(Automatic Differentiation)是什么?微分是函数在某一处的导数值,自动微分就是使用计算机程序自动求解函数在某一处的导数值。自动微分可用于计算神经网络反向传播的梯度大小,是机器学习训练中不可或缺的一步。

这些公式难免让人头大,好在自动微分就是帮助我们“自动”解决微分问题的。机器学习平台如TensorFlow、PyTorch都实现了自动微分,使用非常的方便,不过有必要理解其原理。要理解“自动微分”,需要先理解常见的求解微分的方式,可分为以下四种:

  • 手动求解法(Manual Differentiation)
  • 数值微分法(Numerical Differentiation)
  • 符号微分法(Symbolic Differentiation)
  • 自动微分法(Automatic Differentiation)

所谓手动求解法就是手动算出求导公式,然后将公式编写成计算机代码完成计算。比如对于函数求微分,首先根据求导公式表找出其导数函数 ,然后将这个公式写成计算机程序,对于任意的输入 都能用这段程序求出其导数,也就是此时的微分。是不是很简单?

这样做虽然直观,但却有两个明显的缺点:

  • 每次都要根据手动算出求导公式然后编写代码,导致程序很难复用。
  • 更让人难受的是,复杂的函数普通人很难轻易写出求导公式

函数式自动微分

神经网络的训练主要使用反向传播算法,模型预测值(logits)与正确标签(label)送入损失函数(loss function)获得loss,然后进行反向传播计算,求得梯度(gradients),最终更新至模型参数(parameters)。自动微分能够计算可导函数在某点处的导数值,是反向传播算法的一般化。

自动微分主要解决的问题是将一个复杂的数学运算分解为一系列简单的基本运算,该功能对用户屏蔽了大量的求导细节和过程,大大降低了框架的使用门槛。

MindSpore使用函数式自动微分的设计理念,提供更接近于数学语义的自动微分接口grad和value_and_grad。下面我们使用一个简单的单层线性变换模型进行介绍。

%%capture captured_output
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
import numpy as np
import mindspore
from mindspore import nn
from mindspore import ops
from mindspore import Tensor, Parameter

函数与计算图

计算图是用图论语言表示数学函数的一种方式,也是深度学习框架表达神经网络模型的统一方法。我们将根据下面的计算图构造计算函数和神经网络。

compute-graph

在这个模型中, 𝑥为输入, 𝑦为正确值, 𝑤和 𝑏是我们需要优化的参数。

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

我们根据计算图描述的计算过程,构造计算函数。 其中,binary_cross_entropy_with_logits 是一个损失函数,计算预测值和目标值之间的二值交叉熵损失。

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值。

loss = function(x, y, w, b)
print(loss)
Tensor(shape=[], dtype=Float32, value= 0.914285)

微分函数与梯度计算

为了优化模型参数,需要求参数对loss的导数: ∂loss∂𝑤和 ∂loss∂𝑏,此时我们调用mindspore.grad函数,来获得function的微分函数。

这里使用了grad函数的两个入参,分别为:

  • fn:待求导的函数。
  • grad_position:指定求导输入位置的索引。

由于我们对 𝑤和 𝑏求导,因此配置其在function入参对应的位置(2, 3)。使用grad获得微分函数是一种函数变换,即输入为函数,输出也为函数。

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

执行微分函数,即可获得 𝑤 、 𝑏对应的梯度。

grads = grad_fn(x, y, w, b)
print(grads)
(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]))

在这里插入图片描述

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)

(Tensor(shape=[5, 3], dtype=Float32, value=
[[ 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]))

可以看到求得 𝑤、 𝑏对应的梯度值发生了变化。此时如果想要屏蔽掉z对梯度的影响,即仍只求参数对loss的导数,可以使用ops.stop_gradient接口,将梯度在此处截断。我们将function实现加入stop_gradient,并执行。

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)
grad_fn = mindspore.grad(function_stop_gradient, (2, 3))
grads = grad_fn(x, y, w, b)
print(grads)

(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]))

可以看到,求得 𝑤 、 𝑏对应的梯度值与初始function求得的梯度值一致。

在这里插入图片描述

Auxiliary data

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

grad和value_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)

((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能够作为微分函数的输出返回。

神经网络梯度计算

根据计算图对应的函数介绍了MindSpore的函数式自动微分,但我们的神经网络构造是继承自面向对象编程范式的nn.Cell。接下来我们通过Cell构造同样的神经网络,利用函数式自动微分来实现反向传播。

首先我们继承nn.Cell构造单层线性变换神经网络。这里我们直接使用前文的 𝑤、 𝑏作为模型参数,使用mindspore.Parameter进行包装后,作为内部属性,并在construct内实现相同的Tensor操作。

# 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接口获得微分函数,用于计算梯度。

由于使用Cell封装神经网络模型,模型参数为Cell的内部属性,此时我们不需要使用grad_position指定对函数输入求导,因此将其配置为None。对模型参数求导时,我们使用weights参数,使用model.trainable_params()方法从Cell中取出可以求导的参数。

grad_fn = mindspore.value_and_grad(forward_fn, None, weights=model.trainable_params())
loss, grads = grad_fn(x, y)
print(grads)

(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]))
执行微分函数,可以看到梯度值和前文function求得的梯度值一致。

在这里插入图片描述

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

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

相关文章

抛弃 Neofetch?众多优秀替代方案等你体验!

目录 抛弃 Neofetch?众多优秀替代方案等你体验Neofetch 的替代品FastfetchscreenFetchmacchina 抛弃 Neofetch?众多优秀替代方案等你体验 NeoFetch 是用 Bash 3.2 编写的命令行系统信息工具,该项目的主要开发人员已将 GitHub 存储库存档&…

【C++11:右值引用,列表初始化】

统一列表初始化: 构造函数的函数名与函数体之间增加一个列表,用于对成员初始化 在实例化对象时,支持单/多参数的隐式转化,同时也可以省略符号,让代码更简洁 右值的引用 左值: 左值与右值的重要区别就是能…

用代码实现加载中动图 loading.gif

效果 仅使用htmlcss&#xff0c;效果如下所示 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>加载中</title><style>.el-loading-spinner .circular {height: 42px;width: 42px;…

7.1作业

初始化 /******rcc章节初始化********/ |//1.使能gpiob组控制器 |RCC->MP_AHB4ENSETR |(0X1<<1); |//2.使能gpiog组控制器 |RCC-&…

【TB作品】矩阵键盘电话拨号,ATMEGA16单片机,Proteus仿真 atmega16矩阵键盘电话拨号

atmega16矩阵键盘电话拨号 c代码和仿真图&#xff1a; 使用ATmega16实现矩阵键盘电话拨号功能 项目背景 在电子设计和嵌入式系统开发中&#xff0c;矩阵键盘是常见的人机交互方式。它可以实现较多按键的输入&#xff0c;同时节省单片机的I/O资源。结合LCD显示和蜂鸣器&am…

四城联动 | 亚控KingSCADA4.0信创版发布会圆满收官

圆满收官 随着科技的飞速发展&#xff0c;智能制造和工业自动化已成为推动产业升级的重要力量。在这样的背景下&#xff0c;北京亚控科技发展有限公司&#xff08;以下简称“亚控科技”&#xff09;于2024年6月18日至27日&#xff0c;分别在沈阳、大连、长春和哈尔滨四地成功举…

【PYG】Planetoid中边存储的格式,为什么打印前十条边用edge_index[:, :10]

edge_index 是 PyTorch Geometric 中常用的表示图边的张量。它通常是一个形状为 [2, num_edges] 的二维张量&#xff0c;其中 num_edges 表示图中边的数量。每一列表示一条边&#xff0c;包含两个节点的索引。 实际上这是COO存储格式&#xff0c;官方文档里也有写&#xff0c;…

暑假假期规划 离不开宝藏待办计划管理工具

暑假来临&#xff0c;两个月的自由时间&#xff0c;如何过得充实而有意义&#xff0c;成了我最近思考的问题。毕竟&#xff0c;一个合理的假期规划&#xff0c;不仅能让我的假期生活更加丰富多彩&#xff0c;还能为新学期的到来做好充分的准备。 我幻想着在这个暑假里&#xf…

激光雷达数据处理

激光雷达技术以其高精度、高效率的特点&#xff0c;已经成为地表特征获取、地形建模、环境监测等领域的重要工具。掌握激光雷达数据处理技能&#xff0c;不仅可以提升工作效率&#xff0c;还能够有效提高数据的质量和准确性&#xff0c;为决策提供可靠的数据支持。 第一章、激…

【漏洞复现】和丰多媒体信息发布系统 QH.aspx 任意文件上传漏洞

0x01 产品简介 和丰多媒体信息发布系统也称数字标牌&#xff08;Digital Signage&#xff09;&#xff0c;是指通过大屏幕终端显示设备&#xff0c;发布商业、财经和娱乐信息的多媒体专业视听系统&#xff0c;常被称为除纸张媒体、电台、电视、互联网之外的“第五媒体”。该系…

centos 安装zabbix 6.4.16 server client

Zabbix Server 采用源码包部署&#xff0c;数据库采用 MySQL8.0 版本&#xff0c;zabbix-web 使用 nginxphp 来实现。具体信息如下&#xff1a; 软件名 版本 安装方式 Zabbix Server 6.4.16 源码安装 Zabbix Agent 6.4.16 源码安装 MySQL 8.0.28 yum安装 Nginx 1.…

赛氪网:企业竞赛组织平台新选择,一键操作助力赛事成功举办

在数字化时代&#xff0c;传统的赛事报名方式已经逐渐难以满足组织者和参赛者的需求。企业如何寻找一种既方便又高效的赛事报名工具&#xff1f;赛氪网平台凭借其便捷的操作流程、完善的管理功能和技术保障&#xff0c;成为众多企业和高校举办竞赛时的首选。 赛氪网作为一款先…

祝贺《华为战略管理法:DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一(宏观战略类书籍)

祝贺《华为战略管理法&#xff1a;DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一 2024年4月23日&#xff08;周二&#xff09;下午13:00&#xff0c;《中国企业家》杂志如期举办“每天都是读书日”线下活动。 《中国企业家》杂志携手商界大咖共同推选50本枕边书…

算法刷题之路之链表初探(二)Leecode21合并两个有序链表

算法刷题之路之链表初探&#xff08;二&#xff09; 今天来学习的算法题是leecode141环形链表&#xff0c;是一道简单的入门题&#xff0c;话不多说&#xff01;直接上&#xff01; 条件&#xff08;Leecode21&#xff09; 重点&#xff01;&#xff01;&#xff01; 我直接把…

数据结构 - C/C++ - 链表

目录 结构特性 内存布局 结构样式 结构拓展 单链表 结构定义 节点关联 插入节点 删除节点 常见操作 双链表 环链表 结构容器 结构设计 结构特性 线性结构的存储方式 顺序存储 - 数组 链式存储 - 链表 线性结构的链式存储是通过任意的存储单元来存储线性…

yaml文件的介绍-K8S

yaml 文件是我们使用K8S管理应用程序常用的部署方式&#xff0c;它主要是通过一系列键值对组成&#xff0c;键和值使用冒号和空格分隔。以下是对yaml的介绍 首先我们可以使用命令生成一个简单的YAML模版文件 Kubectl run nginx-pod –imagenginx:latest –port80 –dry-runcli…

【MySQL备份】Percona XtraBackup实战篇

目录 1. 前言 2.准备工作 2.1.创建备份目录 2.2.配置/etc/my.cnf文件 2.3.授予root用户BACKUP_ADMIN权限 3.全量备份 4.准备备份 5.数据恢复 6.总结 "实战演练&#xff1a;利用Percona XtraBackup执行MySQL全量备份操作详解" 1. 前言 本文将继续上篇【My…

R语言 | 带P值的相关性热图绘制教程

原文链接&#xff1a;带P值的相关性热图绘制教程 本期教程 往期教程部分内容 **注意&#xff1a;若是在MarkDown格式中无法运行成功&#xff0c;请新建有一个R script文件 ** 一、加载R包 if (!require(corrplot)) install.packages("corrplot") if (!require(Hmi…

Spring Cloud - 项目搭建

1、新建maven项目 新建maven项目&#xff0c;该项目为主项目 1、新建maven项目 2、设置项目类型 3、选择项目原型 4、设置参数 5、等着完成 2、设置项目信息 1、右键&#xff0c;项目属性 2、设置jdk版本 3、选择jdk17 4、修改编译版本 5、右键项目&#xff0c;选择maven->u…

使用zdppy_api+onlyoffice word文档在线共同编辑,附完整的vue3前端代码和python后端代码

参考文档&#xff1a; https://api.onlyoffice.com/zh/editors/basic https://api.onlyoffice.com/zh/editors/coedit 基本的架构思考&#xff1a; 文档表&#xff1a;记录的是文档信息 key&#xff1a;这个key可以标识唯一的一个文档&#xff0c;可以是文档的hash值fileType…