深度学习-自动求导

2024-04-26 13:04
文章标签 学习 深度 自动 求导

本文主要是介绍深度学习-自动求导,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 向量链式法则
    • 标量链式法则
  • 拓展到向量
    • 例题1
    • 例题2
  • 符号求导
  • 数值求导
  • 自动求导
    • 计算图
    • 自动求导的两种模式
      • 链式法则
      • 正向累积(从x出发)
      • 反向累积(反向传递--先计算最终的函数即y)
      • 反向累积总结
  • 自动求导
  • 计算y关于x的梯度,使用requires_grad(True)
    • 计算y
  • 通过调用反向传播函数来自动计算y关于x每个分量的梯度
  • PyTorch会累积梯度,使用zero_()函数清除梯度
  • 批量中每个样本单独计算的偏导数之和
  • 将某些计算移动到记录的计算图之外
  • 即使构建函数的计算图通过Python控制流仍可以计算变量的梯度
  • 问题
    • 多个loss(损失函数)分别反向的时候是不是需要累积梯度?
    • 需要正向和反向都要算一遍吗?
    • 为什么Pytorch会默认累积梯度?
    • 为什么获取.grad前需要backward?

向量链式法则

标量链式法则

在这里插入图片描述




拓展到向量

在这里插入图片描述




例题1

在这里插入图片描述

过程:
在这里插入图片描述


在这里插入图片描述


在这里插入图片描述





例题2

在这里插入图片描述

过程:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
然后将分解的回代




符号求导

在这里插入图片描述

数值求导

在这里插入图片描述

自动求导

自动求导计算一个函数在指定值上的导数

计算图

将代码分解为操作子
将计算表示成一个无环图
在这里插入图片描述
显示构造
在这里插入图片描述
隐式构造
在这里插入图片描述




自动求导的两种模式

链式法则

在这里插入图片描述

正向累积(从x出发)

在这里插入图片描述

反向累积(反向传递–先计算最终的函数即y)

在这里插入图片描述

这里的反向先计算z的函数
在这里插入图片描述




反向累积总结

构造计算图
前向:执行图,存储中间结果
反向:从相反方向执行图
去除不需要的枝

在这里插入图片描述

计算复杂度:O(n),n是操作子个数
通常正向和方向的代价类似
内存复杂度:O(n),因为需要存储正向的所有中间结果

正向累积:
它的内存复杂度是O(1),即不管多深我不需要存储它的结果,而反向累积则需要存储。

反向从根节点向下扫,可以保证每个节点只扫一次;
正向从叶节点向上扫,会导致上层节点可能需要被重复扫多次。

(正向中 子节点比父节点先计算,因此也无法像反向那样把本节点的计算结果传给每个子节点。)




自动求导

假设我们对函数 y=2 x T x^T xTx 求导

import torch
x = torch.arange(4.0)
print(x)

结果:在这里插入图片描述




计算y关于x的梯度,使用requires_grad(True)

import torch
x = torch.arange(4.0, requires_grad=True)
print(x.grad)

结果:在这里插入图片描述

计算y

import torch
x = torch.arange(4.0, requires_grad=True)
y = 2 * torch.dot(x, x)
print(y)

结果:在这里插入图片描述




通过调用反向传播函数来自动计算y关于x每个分量的梯度

import torch
x = torch.arange(4.0, requires_grad=True)
print(x)
y = 2 * torch.dot(x, x)
y.backward() #求导
print(x.grad) #x.grad访问导数

结果:在这里插入图片描述
y=2 x 2 x^2 x2然后使用求导函数backward()实质是y导=4x(下面验证)。

import torch
x = torch.arange(4.0, requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward() #求导
print(x.grad == 4*x)

结果:在这里插入图片描述




PyTorch会累积梯度,使用zero_()函数清除梯度

import torch
x = torch.arange(4.0, dtype=torch.float32, requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward()
print(x.grad)x.grad.zero_() #梯度清零
y = x.sum()
y.backward() #求导
print(x.grad)

因为求向量的sum()所以梯度是全1
y是标量
y是对x的的求和:y= x 1 x_1 x1+ x 2 x_2 x2+ x 3 x_3 x3+ x 4 x_4 x4
对y进行x的偏导:dy/ d x 1 dx_1 dx1,dy/ d x 2 dx_2 dx2,dy/ d x 3 dx_3 dx3,dy/ d x 4 dx_4 dx4

在这里插入图片描述




批量中每个样本单独计算的偏导数之和

import torch
x = torch.arange(4.0, dtype=torch.float32, requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward()
print(x.grad)x.grad.zero_() #梯度清零,如果不清零执行y=x*x然后对y求和再求导可以通过x.grad查看得[0.,1.,4.,*.]
y = x*x #x是向量,y即向量
print(y) #输出查看
y.sum().backward() #求导
print(x.grad)

梯度(求导)清零:必须先存在梯度,如果没有y.backward()则x.grad.zero_()会报错。
结果:在这里插入图片描述




将某些计算移动到记录的计算图之外

import torch
x = torch.arange(4.0, dtype=torch.float32, requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward()
print(x.grad)x.grad.zero_() #梯度清零,如果不清零执行y=x*x然后对y求和再求导可以通过x.grad查看得[0.,1.,4.,*.]
y = x * x #x是向量,y即向量
print(y) #输出查看
u = y.detach()#把y当作一个常数,而不是关于x的函数,把它做成u
z = u * x #相当于z=常数*x
z.sum().backward()
print(x.grad == u)

结果:这里的z就是为了后续求导检查是否与detach()后一致。
在这里插入图片描述


import torch
x = torch.arange(4.0, dtype=torch.float32, requires_grad=True)
y = 2 * torch.dot(x, x)
y.backward()
print(x.grad)x.grad.zero_() #梯度清零,如果不清零执行y=x*x然后对y求和再求导可以通过x.grad查看得[0.,1.,4.,*.]
y = x * x #x是向量,y即向量
y.sum().backward()
print(x.grad == 2 * x)

结果:
在这里插入图片描述




即使构建函数的计算图通过Python控制流仍可以计算变量的梯度

import torchdef f(a):b = a * 2while b.norm() < 1000:#norm()计算张量的范数, 计算了张量 b 的L2范数b = b * 2if b.sum(): #检查 b 所有元素的总和是否非零c = b #非0的时候的操作else:c = 100 * breturn ca = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
print(a.grad == d / a) #梯度验证

结果:在这里插入图片描述




问题

多个loss(损失函数)分别反向的时候是不是需要累积梯度?

是的

需要正向和反向都要算一遍吗?

是的

为什么Pytorch会默认累积梯度?

设计上的理念,通常一个大的批量无法一次计算出,所以分为多次,然后累加起来。

为什么获取.grad前需要backward?

不进行backward时不会计算梯度,因为计算梯度是一个很“贵”的事情

这篇关于深度学习-自动求导的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/937658

相关文章

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

MyBatis-Plus 自动赋值实体字段最佳实践指南

《MyBatis-Plus自动赋值实体字段最佳实践指南》MyBatis-Plus通过@TableField注解与填充策略,实现时间戳、用户信息、逻辑删除等字段的自动填充,减少手动赋值,提升开发效率与... 目录1. MyBATis-Plus 自动赋值概述1.1 适用场景1.2 自动填充的原理1.3 填充策略

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机