Pytorch Tutorial 使用torch.autograd进行自动微分

Pytorch Tutorial 使用torch.autograd进行自动微分

本文翻译自 PyTorch 官网教程。

原文:https://pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html#optional-reading-tensor-gradients-and-jacobian-products

在训练神经网络时,最常使用到的算法就是反向传播(back propagation)。在该算法中,参数(模型权重)会根据损失函数相对于给定参数的**梯度(gradient)**进行更新。

为了计算这些梯度,PyTorch 中有一个内置的微分引擎,叫做 torch.autograd 。它可以自动地计算任意计算图的梯度。

考虑下面一个最简单的单层神经网络,其输入为 x,权重参数为 wb ,还有一个损失函数(此处采用二进制交叉熵)。在 PyTorch 中可以按照以下方式来定义上述神经网络:

import torchx = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

张量、函数和计算图

上述代码定义了这样一个计算图(computational graph)

在这里插入图片描述

在该网络中,wb 是需要优化更新的权重参数。因此,我们需要能够计算损失函数关于这两个变量的梯度。为此,我们设置这两个变量的 requires_grad 属性为 True

我们也可以在创建张量之后,再使用 x.requires_grad_(True) 方法来设置 requires_grad 的值。

我们将某个函数作用于张量来构建计算图,该函数实际上是 Function 类的一个对象。这个对象知道在前向传播时怎样计算该函数,也知道在反向传播时怎样计算它的微分。反向传播函数的是依据张量的 grad_fn 属性。更多信息请参考 Function 的[文档][https://pytorch.org/docs/stable/autograd.html#function]。

print('Gradient function for z =', z.grad_fn)
print('Gradient function for loss =', loss.grad_fn)

输出:

Gradient function for z = <AddBackward0 object at 0x7f06c1316a90>
Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward0 object at 0x7f06c1316a90>

计算梯度

为了优化更新网络中的权重参数,我们需要计算损失函数关于权重参数的导数,即我们需要在给定 xy 值的情况下的 ∂losss∂w\frac{\partial losss}{\partial w}wlosss∂losss∂b\frac{\partial losss}{\partial b}blosss。 为了计算这些导数,我们调用 loss.backward() 函数,然后再来查看 w.gradb.grad 的值。

loss.backward()
print(w.grad)
print(b.grad)

输出:

tensor([[0.2225, 0.2403, 0.3150],[0.2225, 0.2403, 0.3150],[0.2225, 0.2403, 0.3150],[0.2225, 0.2403, 0.3150],[0.2225, 0.2403, 0.3150]])
tensor([0.2225, 0.2403, 0.3150])
  • 我们只能得到 requires_grad 设置为 True 且为计算图中叶子结点grad 属性,计算图中的所有其他结点的梯度是无法得到的。
  • 通过 backward(),我们只能对某张计算图进行一次求梯度的运算,因为为了提升性能,通常计算图在进行一次求梯度的计算后就会被释放。如果我们需要多次对同一张计算图调用 backward() ,我们需要在调用 backward 的时候传入 retain_grap=True

禁用梯度跟踪

默认情况下,所有的 requires_grad=True 的张量都会追踪它们的计算历史,并支持梯度计算。然而,有些情况下我们并不需要做这些。比如说,我们已经训练好了一个模型然后想将它应用于某些输入数据,即我们只想要对网络进行前向计算(就是推理时)。我们可以将我们的计算代码放到一个上下文管理器 torch.no_grad() 中来停止梯度追踪:

z = torch.matmul(x, w)+b
print(z.requires_grad)with torch.no_grad():z = torch.matmul(x, w)+b
print(z.requires_grad)

输出:

True
False

另一种方式也可以实现相同的效果:调用张量的 detach() 方法:

z = torch.matmul(x, w)+b
z_det = z.detach()
print(z_det.requires_grad)

输出:

False

这里是我们可能会想要禁用梯度追踪的一些原因:

  • 将我们网络中的某些参数冻结,这时我们要微调一个预训练过的网络时很常见的场景。
  • 在我们只需要进行前向传播时加速运算,因为不进行梯度追踪的计算肯定会更高效。

更多关于计算图的知识

从概念上讲,autograd 在由 Function 对象组成的有向无环图 (DAG) 中记录数据(张量)和所有执行的操作(以及生成的新张量)。 在这个 DAG 中,叶子节点是输入张量,根节点是输出张量。 通过从根节点到叶子节点跟踪此图,可以使用链式法则自动计算梯度。

在前向传播中,autograd 同时完成这两件事情:

  • 运行指定的操作来计算结果向量
  • 将各操作的梯度函数保存在 DAG 中

当在 DAG 根上调用 .backward() 时,反向传递开始, 然后自动求导:

  • 根据各个 .grad_fn 计算梯度
  • 将它们累加到对应的张量的 .grad 属性中
  • 使用链式求导法则,一直传播到叶子张量

在 PyTorch 中 DAG 是动态的

需要注意的重要一点是,在每次 .backward() 调用之后,DAG 是从头开始重新创建的。autograd 开始构建一张新图。这也是为什么在 PyTorch 中支持控制流语句; 我们可以根据需要在每次迭代时更改形状、大小和操作。

选读:张量梯度和雅克比乘积

在许多情况下,我们得到的损失都是一个标量,然后我们去计算它关于各个权重参数的梯度。然而,在某些情况下我们会输出一个任意维度的张量,PyTorch 允许我们计算所谓的雅克比乘积,而非真实的梯度。

对于一个向量 y⃗=f(x⃗)\vec{y}=f(\vec{x})y=f(x),其中 x⃗=<x1,…,xn>\vec{x}=<x_1,\dots,x_n>x=<x1,,xn>y⃗=<y1,…,yn>\vec{y}=<y_1,\dots,y_n>y=<y1,,yn>,则 y⃗\vec{y}y 关于 x⃗\vec{x}x 的梯度可以由雅克比矩阵给出:

在这里插入图片描述

PyTorch 允许我们计算给定输入向量 v=(v1,…,vn)v=(v_1,\dots,v_n)v=(v1,,vn) 的雅克比乘积 vTv^TvT 而非雅克比矩阵本身。只需在调用 backward 的时候将 vvv 作为参数传入。vvv 的尺寸需要与我们想要计算雅克比乘积的原始张量相同:

inp = torch.eye(5, requires_grad=True)
out = (inp+1).pow(2)
out.backward(torch.ones_like(inp), retain_graph=True)
print("First call\n", inp.grad)
out.backward(torch.ones_like(inp), retain_graph=True)
print("\nSecond call\n", inp.grad)
inp.grad.zero_()
out.backward(torch.ones_like(inp), retain_graph=True)
print("\nCall after zeroing gradients\n", inp.grad)

输出:

First calltensor([[4., 2., 2., 2., 2.],[2., 4., 2., 2., 2.],[2., 2., 4., 2., 2.],[2., 2., 2., 4., 2.],[2., 2., 2., 2., 4.]])Second calltensor([[8., 4., 4., 4., 4.],[4., 8., 4., 4., 4.],[4., 4., 8., 4., 4.],[4., 4., 4., 8., 4.],[4., 4., 4., 4., 8.]])Call after zeroing gradientstensor([[4., 2., 2., 2., 2.],[2., 4., 2., 2., 2.],[2., 2., 4., 2., 2.],[2., 2., 2., 4., 2.],[2., 2., 2., 2., 4.]])

注意当我们使用相同的参数第二次调用 backward 时,梯度的值会不同。这是因为在调用 backward 进行反向传播时,PyTorch 会累加梯度,即计算梯度得到的值会被加到计算图中所有叶子结点的 .grad 属性上。因此一般区情况下,为了计算的梯度正确,我们需要再每一步计算梯度前先将已有梯度清零(zero_grad())。在实际的训练中,优化器会帮助我们做这一步。

译者注:有时也可以通过这一累加特性变相增大 batch_size。

之前我们调用无参数的 backward() 函数。 这本质上等同于调用 backward(torch.tensor(1.0)),这在标量值函数的情况下计算梯度时很有用,例如神经网络训练期间的损失。

扩展阅读

  • Autograd Mechanics

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

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

相关文章

TVM:编译深度学习模型快速上手教程

TVM&#xff1a;编译深度学习模型快速上手教程 本文将展示如何使用 Relay python 前端构建一个神经网络&#xff0c;并使用 TVM 为 Nvidia GPU 生成一个运行时库。 注意我们需要再构建 TVM 时启用了 cuda 和 llvm。 TVM支持的硬件后端总览 在本教程中&#xff0c;我们使用 cu…

TVM:设计与架构

TVM&#xff1a;设计与架构 本文档适用于想要了解 TVM 架构和/或积极开发项目的开发人员。页面组织如下&#xff1a; 示例编译流程概述了 TVM 将模型的高层描述转换为可部署模块所采取的步骤。要开始使用&#xff0c;请先阅读本节。 逻辑架构组件部分描述了逻辑组件。后面的部…

递归+回溯

递归-回溯 本文参考自代码随想录视频&#xff1a; https://www.bilibili.com/video/BV1cy4y167mM https://www.bilibili.com/video/BV1ti4y1L7cv 递归回溯理论基础 只要有递归&#xff0c;就会有回溯&#xff0c;递归函数的下面的部分通常就是回溯的逻辑。 回溯是纯暴力的搜索…

Nvidia CUDA初级教程1 CPU体系架构综述

Nvidia CUDA初级教程1 CPU体系架构综述 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p2 讲师&#xff1a;周斌 本节内容&#xff1a;了解现代CPU的架构和性能优化&#xff1a; 流水线 Pipelining分支预测 Branch Prediction超标量 Superscalar乱序执行 Out…

Nvidia CUDA初级教程2 并行程序设计概述

Nvidia CUDA初级教程2 并行程序设计概述 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p3 讲师&#xff1a;周斌 本节内容&#xff1a; 为什么需要&#xff1f;怎么做&#xff1f;一些技术和概念 串并行计算模式 串行计算模式 常规软件时串行的 设计运行…

Nvidia CUDA初级教程4 GPU体系架构概述

Nvidia CUDA初级教程4 GPU体系架构概述 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p5 讲师&#xff1a;周斌 本节内容&#xff1a; 为什么需要GPU三种方法提升GPU的处理速度实际GPU的设计举例&#xff1a; NVDIA GTX 480: FermiNVDIA GTX 680: Kepler GP…

Nvidia CUDA初级教程5 CUDA/GPU编程模型

Nvidia CUDA初级教程5 CUDA/GPU编程模型 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p6 讲师&#xff1a;周斌 本节内容&#xff1a; CPU和GPU互动模式GPU线程组织模型&#xff08;需要不停强化&#xff09;GPU存储模型基本的编程问题 CPU与GPU交互 各自…

Nvidia CUDA初级教程6 CUDA编程一

Nvidia CUDA初级教程6 CUDA编程一 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p7 讲师&#xff1a;周斌 GPU架构概览 GPU特别使用于&#xff1a; 密集计算&#xff0c;高度可并行计算图形学 晶体管主要被用于&#xff1a; 执行计算而不是 缓存数据控制指令…

由前中后遍历序列构建二叉树

由前/中/后遍历序列构建二叉树 基础 首先&#xff0c;我们需要知道前中后序三种深度优先遍历二叉树的方式的具体顺序&#xff1a; 前序&#xff1a;中左右中序&#xff1a;左中右后序&#xff1a;左右中 另外&#xff0c;要知道只有中序前/后序可以唯一确定一棵二叉树&…

手写nms

手写nms 计算宽高的时候加1是为什么&#xff1f; 本文总结自互联网的多种nms实现&#xff0c;供参考&#xff0c;非博主原创&#xff0c;各原文链接如下&#xff0c;也建议大家动手写一写。 Ref&#xff1a; 浅谈NMS的多种实现 目标窗口检测算法-NMS非极大值抑制 一、fas…

目标检测综述

目标检测综述 转自&#xff1a;https://zhuanlan.zhihu.com/p/383616728 论文参考&#xff1a;[Object Detection in 20 Years: A Survey][https://arxiv.org/abs/1905.05055] 引言 目标检测领域发展至今已有二十余载&#xff0c;从早期的传统方法到如今的深度学习方法&#x…

Nvidia CUDA初级教程7 CUDA编程二

Nvidia CUDA初级教程7 CUDA编程二 视频&#xff1a;https://www.bilibili.com/video/BV1kx411m7Fk?p8 讲师&#xff1a;周斌 本节内容&#xff1a; 内置类型和函数 Built-ins and functions线程同步 Synchronizing线程调度 Scheduling threads存储模型 Memory model重访 Matr…

详解优酷视频质量评价体系

万字长文 | 详解优酷视频质量评价体系 分享嘉宾&#xff5c;李静博士&#xff0c;阿里巴巴文娱集团资深算法专家&#xff0c;阿里巴巴大文娱摩酷实验室视频体验与质量团队负责人 整理出品&#xff5c;AICUG人工智能社区 本文地址&#xff1a;https://www.6aiq.com/article/1617…

视频质量评价:挑战与机遇

视频质量评价&#xff1a;挑战与机遇 转自&#xff1a;https://zhuanlan.zhihu.com/p/384603663 本文整理自鹏城实验室助理研究员王海强在LiveVideoStack线上分享上的演讲。他通过自身的实践经验&#xff0c;详细讲解了视频质量评价的挑战与机遇。 文 / 王海强 整理 / LiveVi…

关于二分法的边界问题及两种写法

关于二分法的边界问题及两种写法 二分查找法大家很熟悉了&#xff0c;对于一个有序序列&#xff0c;我们可以通过二分查找法在 O(logN)O(logN)O(logN) 的时间内找到想要的元素。但是&#xff0c;在代码实现的过程中&#xff0c;如果没有仔细理解清楚&#xff0c;二分法的边界条…

LeetCode上的各种股票最大收益

LeetCode上的各种股票最大收益 对于力扣平台上的股票类型的题目&#xff1a; 121 买卖股票的最佳时机 122 买卖股票的最佳时机 II 123 买卖股票的最佳时机 III 124 买卖股票的最佳时机 IV 309 最佳买卖股票时机含冷冻期 714 买卖股票的最佳时机含手续费 剑指 Offer 63. …

建设专业化运维服务团队必要性

信息系统的生命周期涵盖&#xff1a;设计、开发、测试、部署上线、运行维护。其中&#xff0c;运行维护阶段是信息系统生命周期中的关键环节&#xff0c;其执行效果直接影响系统是否能达到预期的运行目标。为了实现这个目标&#xff0c;我们必须建立一个以业务服务为导向的专业…

docker初探

docker初探 本文旨在介绍 docker 基本的安装、常用命令和常见概念的辨析&#xff0c;方便新手入门和笔者日后查阅&#xff0c;大部分内容整理自互联网&#xff0c;原出处在文中注明。 文章目录docker初探docker安装&#xff08;mac&#xff09;版本、信息相关命令version/info…

ubuntu安装zsh、oh-my-zsh及常用配置

ubuntu安装zsh、oh-my-zsh及常用配置 目前&#xff0c;ubuntu默认的shell是bash&#xff0c;但还有一种shell&#xff0c;叫做zsh它比bash更加强大&#xff0c;功能也更加完善&#xff0c;zsh虽说功能强大&#xff0c;但是配置比较复杂导致流行度不是很高 但是好东西终究是好…

Segmentaion标签的三种表示:poly、mask、rle

Segmentaion标签的三种表示&#xff1a;poly、mask、rle 不同于图像分类这样比较简单直接的计算机视觉任务&#xff0c;图像分割任务&#xff08;又分为语义分割、实例分割、全景分割&#xff09;的标签形式稍为复杂。在分割任务中&#xff0c;我们需要在像素级上表达的是一张…