当前位置: 首页 > article >正文

pytorch 的自动求导功能简介

pytorch 的自动求导功能简介

  • 一、反向传播算法简介
  • 二、pytorch 的自动求导功能
    • 1. 前言
    • 2. 我们需要自动求导机制做什么
    • 3. 一个简单的例子
    • 4. 模型训练过程中使用自动求导(略)
    • 5. 关闭和打开自动求导
    • 6. 自动求导和原地替换操作
    • 7. 自动求导的性能分析器(略)
    • 8. 高阶话题:关于自动求导更多的细节
    • 9. 高级 API

自动求导是 pytorch 的一项重要功能,它使得 pytorch 能够灵活快速地构建神经网络模型。反向传播算法是优化神经网络模型参数的一个重要方法,在反向传播过程中需要不断计算损失函数对参数的导数,然后更新相应的模型参数,首先简单介绍一下反向传播算法。

一、反向传播算法简介

这部分的主要参考资料来自这里:https://www.cnblogs.com/charlotte77/p/5629865.html,原文的计算过程非常详细,有需要可以去阅读原文,另外原文中有一处疏漏之处,就是也需要计算损失函数对偏置量b1b_1b1b2b_2b2的导数并且更新偏置量。

回到正题,对于一个典型的神经网络模型,如下所示:
nn
我们可以将它看作是一个映射(F\mathscr{F}F),即是从输入向量[i1,i2][i1, i2][i1,i2]到输出向量[o1,o2][o1, o2][o1,o2]之间的映射,而这个映射关系完全是由网络的连接关系w1-w8、b1、b2这些参数决定的。在模型的训练过程中,输入和输出数据是固定的,我们要做的是调节这些控制参数来使得通过神经网络计算的输出与实际输出的误差达到最小,误差的具体形式通过损失函数(L\mathscr{L}L)来定义,由于输入和输出数据是固定的,同时在网络结构固定的情况下,损失函数(L\mathscr{L}L)仅是w1-w8、b1、b2这些控制参数的函数,神经网络的优化过程用公式表达就是:
findw10−w80,b10,b20stL0(w10−w80,b10,b20)=minL(w1−w8,b1,b2)find \quad w1_0-w8_0,b1_0,b2_0 \quad st \\ \mathscr{L}_0(w1_0-w8_0,b1_0,b2_0) = min\mathscr{L}(w1-w8, b1, b2) findw10w80,b10,b20stL0(w10w80,b10,b20)=minL(w1w8,b1,b2)
反向传播过程就是通过不断计算损失函数(L\mathscr{L}L)对w1-w8、b1、b2这些控制参数的导数,根据梯度下降法的原理,更新参数,使得损失函数(L\mathscr{L}L)向着减小的方向优化,优化完成即代表损失函数达到最小。

需要注意的一点是,在网络参数优化过程中,我们是通过损失函数(L\mathscr{L}L)对控制参数求梯度来优化的,而不是通过网络模型(F\mathscr{F}F)对控制参数求梯度,损失函数(L\mathscr{L}L)是一个标量函数,而网络模型(F\mathscr{F}F)通常是一个向量函数,我们说的求梯度是对多变量的标量函数求梯度,参数的更新方法用公式可以表达为:
[w1,…,w8,b1,b2]=[w1,…,w8,b1,b2]−α[∂L∂w1,…,∂L∂w8,∂L∂b1,∂L∂b2][w1, \dots, w8, b1, b2] = [w1, \dots, w8, b1, b2] - \alpha[\frac{\partial{\mathscr{L}}}{\partial{w1}}, \dots, \frac{\partial{\mathscr{L}}}{\partial{w8}}, \frac{\partial{\mathscr{L}}}{\partial{b1}}, \frac{\partial{\mathscr{L}}}{\partial{b2}}] [w1,,w8,b1,b2]=[w1,,w8,b1,b2]α[w1L,,w8L,b1L,b2L]
其中α\alphaα学习率

一个完整的神经网络训练过程可以总结以下两步循环进行:

  1. 前向传播:计算在当前参数下,带入输入数据后模型的输出
  2. 反向传播:计算损失函数对网络各个控制参数的梯度,然后更新控制参数。这里要注意的是由于一些参数前后有耦合关系,因此在某些求导中需要用到链式法则

二、pytorch 的自动求导功能

pytorch 能够自动求导,这为构建神经网络提供了很大的方便。以下的内容大部分都翻译自 pytorch 官方的内容:https://pytorch.org/tutorials/beginner/introyt/autogradyt_tutorial.html,部分有所改动,以官网为准。

1. 前言

pytorch 的自动求导特征使得其能够快速灵活地构建机器学习项目,在复杂的计算中它能够快速且容易地计算多变量偏导数(梯度),在基于反向传播的神经网络模型中这一操作是核心。

pytorch 的自动求导能力来自于在程序运行过程中,它会动态地追踪你的计算,这意味着如果你的模型有条件分支,或者循环的步数在程序运行之前是未知的,它同样能够正确地追踪计算,你能够得到正确的结果来继续学习过程,所有的这些,结合你建立在 python 之上的模型,相比于传统的用于计算梯度的固定结构,能够提供一个更加灵活的框架。

2. 我们需要自动求导机制做什么

机器学习模型是一个有输入和输出的函数,在这个讨论中,我们将把输入看作是一个i维的向量x⃗\vec{x}x,其中的元素写作xix_ixi。我们可以把模型写作M,它是关于输入的一个向量函数:y⃗=M⃗(x⃗)\vec{y}=\vec{M}(\vec{x})y=M(x)(这里我们把M的输出值看作是一个向量是因为总的来说,一个模型可以有任意数量的输出)。

由于接下来我们将主要讨论在训练过程中的自动求导过程,所以我们对输出的兴趣主要集中在模型的损失上,损失函数L(y⃗)=L(M⃗(x⃗))L(\vec{y})=L(\vec{M}(\vec{x}))L(y)=L(M(x))是关于模型输出的标量函数,这个函数表示的是针对特定输入我们模型的输出与理想输出的差别多大。注意:在这以后,我们将会在上下文提示清楚的地方省略向量符号,例如写yyy而不是y⃗\vec{y}y

在训练模型的过程中,我们想要最小化损失函数,对于一个完美模型的理想情况是调节它的学习权重——即是调节函数的参数使得针对所有的输入模型的损失是0,在真实世界中,它意味着我们需要一步步地轻微改变学习权重,反复迭代直到对于大部分的输入我们都能获得一个可以容忍的损失。

那么我们要如何决定权重离理想值多远以及优化的权重的方向呢?我们想要最小化损失,这意味着使它对输入的一阶导数为0,即是:∂L∂x=0\frac{\partial{L}}{\partial{x}}=0xL=0

不过,回想一下,损失不是直接从输入中得出的,而是模型输出的函数(模型输出是输入的直接函数),即∂L∂x=∂L(y⃗)∂x\frac{\partial{L}}{\partial{x}}=\frac{\partial{L(\vec{y})}}{\partial{x}}xL=xL(y),根据微积分的链式求导法则,有:∂L(y⃗)∂x=∂L∂y∂y∂x=∂L∂y∂M(x)∂x\frac{\partial{L}(\vec{y})}{\partial{x}}=\frac{\partial{L}}{\partial{y}}\frac{\partial{y}}{\partial{x}}=\frac{\partial{L}}{\partial{y}}\frac{\partial{M(x)}}{\partial{x}}xL(y)=yLxy=yLxM(x),其中∂M(x)∂x\frac{\partial{M(x)}}{\partial{x}}xM(x)会使计算变得复杂。这一项表示的是模型的输出相对于输入的偏导数,如果我们根据链式法则扩展这一表达式,将会涉及到模型中每个相乘的学习权重,每个激活函数,以及每个其它数学变换的局部偏导数。每个变量的偏导数的完整表达式是计算图中我们试图计算变量的每条可能路径上变量的局部偏导数的乘积之和。

特别地,我们对学习权重的梯度非常感兴趣——因为它们告诉我们使得损失函数更接近0的学习权重的优化方向。

由于这些局部导数(每个对应于模型计算图中的单独路径)的数量将随着神经网络的深度呈指数增长,计算它们的复杂度也会相应增加。这就是自动求导产生的原因:它会追踪每次计算的历史,pytorch 模型中每个计算张量都有它的输入张量的历史纪录以及用于创建它的函数,结合 pytorch 中每个作用于张量的函数都有一个内置的实现来计算它们的导数,这样大大加快了学习所需的局部导数的计算过程。

3. 一个简单的例子

(这部分内容参考官网,但有所改动)
这里我们简单地利用 pytorch 的自动求导机制来计算函数 y=sin2xy=sin2xy=sin2x在一个周期上的导数,代码如下:

import torch
import matplotlib.pyplot as plt
%matplotlib inlinex = torch.linspace(0, torch.pi, 100000, requires_grad=True)
y = torch.sin(2.0*x)### 反向传播求导
out = y.sum()
out.backward()### 画图
plt.figure()
plt.plot(x.detach(), y.detach(), label='y=sin2x')
plt.plot(x.detach(), x.grad.detach(), '--r', label='y\'')
plt.grid()
plt.xlabel('x')
plt.ylabel('y')
plt.legend()

结果如下图所示:
result
这里我们可以来分析一下实现自动求导的具体过程,首先需要定义需要求导的自变量,例如上面例子中显然 x 为需要求导的自变量,然后是始终要记住:要使用标量函数对自变量求导而不是矢量函数。例如上面中我们求出的 y 是矢量函数,而不是标量函数,因此首先需要用 out=y.sum() 语句,转换成标量函数,用公式表示就是:
out=∑i=0nyiout = \sum_{i=0}^{n}y_i out=i=0nyi
然后运行 out.backward() 语句即可完成反向传播求导过程。

4. 模型训练过程中使用自动求导(略)

5. 关闭和打开自动求导

在某些情况下,你需要对是否进行自动求导进行精确控制,有多种方法可以做到这一点,视情况而定,最简单的方法是直接通过改变 requires_grad 参数来控制:

a = torch.ones(2, 3, requires_grad=True)
print(a)
a.requires_grad = False        ### 关闭自动求导

上述方法会永久关闭自变量的自动求导过程,如果你只是想要暂时关闭自动求导过程,可以使用 torch.no_grad():

a = torch.ones(2, 3, requires_grad=True) * 2
b = torch.ones(2, 3, requires_grad=True) * 3c1 = a + b
print(c1)with torch.no_grad():      ### 该语句下级的所有计算都不会自动计算导数c2 = a + bprint(c2)c3 = a * b
print(c3)

另外,值得一提的是,torch.no_grad() 也可以用作函数装饰器,用该装饰器装饰的函数,输出的变量不能自动求导。

最后,你可能有一个需要追踪计算导数的张量,但是你需要一个不能计算导数的备份。对于这一要求,Tensor 对象有 detach 方法,它可以创建一个原来张量的备份,但是不保存计算历史。

当我们想要绘制一些张量时,我们需要这么做,这是因为 matplotlib 需要一个 numpy 数组作为输入,但是对于具有 requires_grad = True 的张量,不能实现从 pytorch 张量到 numpy 数组的隐式转换。在这种情况下,需要使用 detach 产生的不保存计算历史的张量。

6. 自动求导和原地替换操作

原地替换操作是指函数运行完成后,会用输出值替换掉输入变量的值,例如语句:

x = torch.sin(2.0*x)

这就是一个原地替换操作。在之前的例子中,我们都会新建变量来保存计算过程中的中间变量,自动求导需要这些中间变量来进行梯度计算,由于这个原因,当要使用自动求导时,你必须小心使用原地替换操作,因为这样可能会销毁你在 backward() 求导时需要用到的信息,当你对需要自动求导的网络中的叶变量使用原地替换操作时,pytorch 将会停止运行,如下:

a = torch.linspace(0, 2.0*torch.pi, steps=25, requires_grad=True)
torch.sin_(a)

运行之后改代码会抛出 runtime error 错误,这里 sin_() 函数就是一个原地替换操作,会把计算结果直接赋给 a 变量,从而导致错误。

7. 自动求导的性能分析器(略)

8. 高阶话题:关于自动求导更多的细节

如果你有一个n维输入m维输出的函数,y⃗=f(x⃗)\vec{y}=f(\vec{x})y=f(x),完整的梯度是每个输出相对于每个输入的偏导数矩阵,也叫做雅可比矩阵:
J=(∂y1∂x1⋯∂y1∂xn⋮⋱⋮∂ym∂x1⋯∂ym∂xn)J = \begin{pmatrix} \frac{\partial{y_1}}{\partial{x_1}} & \cdots & \frac{\partial{y_1}}{\partial{x_n}} \\ \vdots & \ddots & \vdots \\ \frac{\partial{y_m}}{\partial{x_1}} & \cdots & \frac{\partial{y_m}}{\partial{x_n}} \\ \end{pmatrix} J=x1y1x1ymxny1xnym
如果你有第二个函数,l=g(y⃗)l=g(\vec{y})l=g(y),其输入为m维向量(与上面函数的输出维度相同),其输出值为一个标量,那么你就可以将它对y⃗\vec{y}y的梯度表示为一个列向量,即是v=(∂l∂y1⋯∂l∂ym)Tv=(\frac{\partial{l}}{\partial{y_1}} \quad \dotsb \quad \frac{\partial{l}}{\partial{y_m}})^Tv=(y1lyml)T——即是只有一列的雅可比矩阵。

更具体地说,将第一个函数想象为神经网络模型(可能有许多输入和输出),将第二个函数想象为损失函数(模型的输出作为输入,损失值作为标量输出)。

如果我们将第一个函数的雅可比矩阵乘以第二个函数的梯度,并应用链式法则,得到:
JT⋅v=(∂y1∂x1⋯∂ym∂x1⋮⋱⋮∂y1∂xn⋯∂ym∂xn)(∂l∂y1⋮∂l∂ym)=(∂l∂x1⋮∂l∂xn)J^T \cdot v = \begin{pmatrix} \frac{\partial{y_1}}{\partial{x_1}} & \cdots & \frac{\partial{y_m}}{\partial{x_1}} \\ \vdots & \ddots & \vdots \\ \frac{\partial{y_1}}{\partial{x_n}} & \cdots & \frac{\partial{y_m}}{\partial{x_n}} \\ \end{pmatrix} \begin{pmatrix} \frac{\partial{l}}{\partial{y_1}} \\ \vdots \\ \frac{\partial{l}}{\partial{y_m}} \end{pmatrix}= \begin{pmatrix} \frac{\partial{l}}{\partial{x_1}}\\ \vdots \\ \frac{\partial{l}}{\partial{x_n}} \end{pmatrix} JTv=x1y1xny1x1ymxnymy1lyml=x1lxnl
计算得到的列向量是第二个函数相对于第一个函数输入的梯度——或者在我们的模型和损失函数情况下,损失函数对模型输入的梯度。

torch.autograd就是计算这些乘积的引擎,这就是我们在反向传播过程中如何累积学习权重的梯度。

因此,backward() 函数也可以将向量作为传入参数来调用,计算结果为该输入向量与自动求导计算的雅可比矩阵的乘积,让我们通过一个特定例子来看下:

x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:y = y * 2v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) # stand-in for gradients
y.backward(v)
print(x.grad)

结果为:

tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])

上面代码中 backward() 函数将向量 v 作为输入参数,实际计算中就相当于用 y 对 x 求梯度产生的雅可比矩阵的转置(JTJ^TJT)来乘以vvv,计算结果与以下代码的结果等价:

x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:y = y * 2v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) # stand-in for gradients
yv = y*v
l = yv.sum()
l.backward()
print(x.grad)

此处在 backward() 函数中并没有传入参数,而是定义了损失函数,用公式表达为:
l=y⃗⋅v⃗l = \vec{y}\cdot \vec{v} l=yv

9. 高级 API

在 autograd 上有一个 API,可以让你直接访问重要的微分矩阵和向量运算,特别地,它允许你计算特定函数在特定输入下的雅可比矩阵海森矩阵(海森矩阵和雅可比矩阵类似,但表示的是完整的二阶偏导数)。它还提供了获取这些矩阵向量积的方法:

  • torch.autograd.functional.jacobian():计算给定输入下函数的雅可比矩阵
  • torch.autograd.functional.hessian():计算给定输入下函数的海森矩阵
  • 更多的高级 API 可以参考官方文档:https://pytorch.org/docs/stable/autograd.html#functional-higher-level-api

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

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

相关文章:

pytorch 的自动求导功能简介

pytorch 的自动求导功能简介一、反向传播算法简介二、pytorch 的自动求导功能1. 前言2. 我们需要自动求导机制做什么3. 一个简单的例子4. 模型训练过程中使用自动求导(略)5. 关闭和打开自动求导6. 自动求导和原地替换操作7. 自动求导的性能分析器(略)8. 高阶话题&#xff1a;关…...

Rock派(基于瑞芯微RK3308B)开发记录-上篇

本文作者&#xff1a;Linux兵工厂&#xff0c;一个嵌入式软件领域的攻城狮。欢迎指教公一众-号&#xff1a;Linux兵工厂&#xff0c;获取硬核Linux资料和文章 前言 根据项目需求并且经过各方面评估最终选择了这款Rock Pi(Rock派)系列中的Rock Pi S产品。正式它的各方面的特性…...

【图像增强】暗通道图像去雾【含GUI Matlab源码 740期】

⛄一、简介 1 暗通道先验图像去雾方法 1.1 光线透射率模型 光在传播中由于散射使得从光源发出的辐射只有部分能到达接收传感器&#xff0c;其他则被散射到传播介质中。假设距离较小时散射光强与距离是线性关系&#xff0c;当光源距离传感器无限接近时&#xff0c;光的衰减值可…...

四六级口语|考研复试口语|满满干货

目录 1.Which do you prefer to use, credit cards or cash?/Do you prefer the credit card or cash? 2.When you shop, do you prefer to go by yourself or with someone?...

深入理解Maven的全部配置

深入理解Maven的全部配置1. Introduction1.1 Big Data -- Postgres2. Install2.1 Maven Install2.2 Config Setting.xml2.3 Local Package Install Maven3. Project4.AwakeningMaven Document: https://maven.apache.org/. Maven Download: https://maven.apache.org/download.…...

CSS 与 地图可视化 三维旋转 注记篇 (八)

地图可视化来到Css 篇章了&#xff0c;这次一口气是兼容了7个地图引擎, 这次直接上包应用 类库兼容地图引擎有 mapbox maptalks arcgis 4 leaflet openlayer 高德地图 百度地图 在线安装 npmi haibalai/gismap4-css 如果是react 项目可以不用安装react&#xff0c; react-do…...

事业编招聘:南方科技大学附属实验学校2022年·面向应届毕业生招聘在编教师公告

南方科技大学是在中国高等教育改革发展背景下创建的一所高起点公办创新型大学&#xff0c;2022年2月14日&#xff0c;教育部等三部委公布第二轮“双一流”建设高校及建设学科名单&#xff0c;南方科技大学入选“双一流”建设高校名单。 南方科技大学附属实验学校&#xff0c;地…...

Python基础学习—字典

一、什么是字典 1、字典是Python的一种常见数据结构&#xff0c;用于存储含有映射关系的数据。 2、字典通过key-value方式存储数据&#xff0c;key是字典中的关键词&#xff0c;可以根据字典中的关键词寻找value。 3、因为key是字典中的关键词&#xff0c;所以字典中key是唯一…...

提面优秀资格上岸浙大MBA经验分享

三百多天的备考&#xff0c;终于等来了一个好的结果。曾经的我也一直在犹豫到底要不要放弃对于浙大的执念&#xff0c;虽然说我的工作是还行&#xff0c;但是我的第一学历只是一个不入流的二本院校&#xff0c;当时咨询的老师说是对于浙大提面来说&#xff0c;我肯定是要吃学历…...

Python+Yolov5人脸口罩识别

程序示例精选 PythonYolov5人脸口罩识别 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 Yolov5比较Yolov4,Yolov3等其他识别框架&#xff0c;速度快&#xff0c;代码结构简单&#xff0c;识别效率高&#xf…...

Hadoop回收站trash

Hadoop回收站trash&#xff0c;默认是关闭的。 1.修改conf/core-site.xml,增加 Xml代码 <property> <name>fs.trash.interval</name> <value>1440</value> <description>Number of minutes between trash checkpoints. …...

Pig 安装

Pig 的安装 1.下载文件 在官方上下载下来 http://pig.apache.org/releases.html#Download 我个人下载的 版本是pig-0.11.0.tar.gz 2.安装 上传到服务器指定位置 由于我个人是新创建了一个pig用户来创建的&#xff0c;所以上传到了 /home/pig/这个目录 &#xff08;用…...

pig入门学习

个人目前理解pig是对mapreduce的一种封装扩展&#xff0c;使写mapreduce简单化&#xff0c;可维护性更高一点&#xff0c;可透明性更清晰一点&#xff0c;操作数据更简单一点吧。 1. Pig中的模式 pig中模式就是说pig数据的数据格式是什么样的。 比如当执行 grunt> de…...

Linux Vim使用

高级一些的编辑器&#xff0c;都会包含宏功能&#xff0c;vim当然不能缺少了&#xff0c;在vim中使用宏是非常方便的&#xff1a; :qx 开始记录宏&#xff0c;并将结果存入寄存器xq 退出记录模式x 播放记录在x寄存器中的宏命令稍微解释一下&#xff0c;当在normal模…...

pig 指定行分割符和列分隔符号

由于我们的hdfs上抽取的数据是存储行分隔符和列分割符不是用的\n和\t。所以就想能看看是否能指定行分隔符&#xff0c;查了半天没查到。。可能是查找能力有限&#xff0c;呵呵&#xff0c;后来下载下来pig-0.11.0的源码看了一下PigStorage的类&#xff0c;输入inputFormat类指定…...

pig基础实例运算

基础运算 加减乘除&#xff08; 、-、*、/、bincond &#xff09; 查看一下简单的文本内容 grunt> cat A; 0,1,2 1,3,4 grunt> a load A usingPigStorage(,)as(c1:int,c2:double,c3:float); grunt> b foreach a generate $0$1 asc1_c2; grunt>dump b; (…...

修改linux 系统编码为utf-8

vi /etc/sysconfig/i18n LANG"zh_CN.GBK" 修改为LANG"zh_CN.UTF-8".保存退出source /etc/sysconfig/i18n 检查编码&#xff1a;locale...

linux开发常用的命令

学习资料&#xff1a;http://download.csdn.net/detail/ruishenh/6586391 查看端口信息 netstat -tln 或者-a lsof -i:8080 查看占用端口的程序netstat -apn | grep 8083tcp 0 0 192.168.2.17:8083 0.0.0.0:* LISTEN 387…...

pig关系操作符实例

cogroup 对两个对象模式&#xff0c;分别按指定的字段进行分组&#xff0c;然后按照指定的key列来分组 grunt> cat A; 0,1,2 1,3,4 grunt> cat B; 0,5,2 1,7,8 grunt> b load B usingPigStorage(,) as (c1:int,c2:int,c3:int); grunt> a load A usingPig…...

mapreduce之组件,join,排序原理

第一部分&#xff1a;重要的组件Combiner•什么是Combiner•combine函数把一个map函数产生的<key,value>对&#xff08;多个key, value&#xff09;合并成一个新的<key2,value2>. 将新的<key2,value2>作为输入到reduce函数中&#xff0c;其格式与reduce函数相…...