【PyTorch基础教程2】自动求导机制(学不会来打我啊)

简介: 回顾我们在完成一项机器学习任务时的步骤:(1)首先需要对数据进行预处理,其中重要的步骤包括数据格式的统一和必要的数据变换,同时划分训练集和测试集。

第一部分:深度学习和机器学习

一、机器学习任务

回顾我们在完成一项机器学习任务时的步骤:

(1)首先需要对数据进行预处理,其中重要的步骤包括数据格式的统一和必要的数据变换,同时划分训练集和测试集。

(2)接下来选择模型,并设定损失函数和优化函数,以及对应的超参数(当然可以使用sklearn这样的机器学习库中模型自带的损失函数和优化器)。

(3)最后用模型去拟合训练集数据,并在验证集/测试集上计算模型表现。

二、ML和DL区别

(1)数据加载

深度学习和机器学习在流程上类似,但在代码实现上有较大的差异。

(1)首先,由于深度学习所需的样本量很大,一次加载全部数据运行可能会超出内存容量而无法实现;

(2)同时还有批(batch)训练等提高模型表现的策略,需要每次训练读取固定数量的样本送入模型中训练,因此深度学习在数据加载上需要有专门的设计。

(2)模型实现

在模型实现上,深度学习和机器学习也有很大差异:

由于深度神经网络层数往往较多,同时会有一些用于实现特定功能的层(如卷积层、池化层、批正则化层、LSTM层等),因此深度神经网络往往需要“逐层”搭建,或者预先定义好可以实现特定功能的模块,再把这些模块组装起来。这种“定制化”的模型构建方式能够充分保证模型的灵活性,也对代码实现提出了新的要求。

接下来是损失函数和优化器的设定。这部分和经典机器学习的实现是类似的。但由于模型设定的灵活性,因此损失函数和优化器要能够保证反向传播能够在用户自行定义的模型结构上实现。

(3)训练过程

上述步骤完成后就可以开始训练了。

(1)GPU的概念和GPU用于并行计算加速的功能,不过程序默认是在CPU上运行的,因此在代码实现中,需要把模型和数据“放到”GPU上去做运算,同时还需要保证损失函数和优化器能够在GPU上工作。

(2)如果使用多张GPU进行训练,还需要考虑模型和数据分配、整合的问题。

(3)后续计算一些指标还需要把数据“放回”CPU。这里涉及到了一系列有关于GPU的配置和操作。

(4)深度学习中训练和验证过程最大的特点在于读入数据是按批的,每次读入一个批次的数据,放入GPU中训练,然后将损失函数反向传播回网络最前面的层,同时使用优化器调整网络参数。这里会涉及到各个模块配合的问题。训练/验证后还需要根据设定好的指标计算模型表现。

经过以上步骤,一个深度学习任务就完成了。

第二部分:Pytorch部分

一、学习资源

(1) Awesome-pytorch-list:目前已获12K Star,包含了NLP,CV,常见库,论文实现以及Pytorch的其他项目。

(2)PyTorch官方文档:官方发布的文档,十分丰富。

(3)Pytorch-handbook:GitHub上已经收获14.8K,pytorch手中书。

(4)PyTorch官方社区:在这里你可以和开发pytorch的人们进行交流。

除此之外,还有很多学习pytorch的资源,b站,stackoverflow,知乎等多多利用。

二、自动求导机制

PyTorch 中,所有神经网络的核心是 autograd包。autograd包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义 ( define-by-run )的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。

2.1 torch.Tensor类

torch.Tensor是这个包的核心类。如果设置它的属性.requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用.backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性。

注意:在 y.backward() 时,如果 y 是标量,则不需要为 backward() 传入任何参数;否则,即如果 y 不是标量,需要传入一个与 y 同形的Tensor。

要阻止一个张量被跟踪历史,可以调用.detach()方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad():中。在评估模型时特别有用,因为模型可能具有 requires_grad = True 的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算。

2.2 Function类

还有一个类对于autograd的实现非常重要:Function。Tensor和Function 互相连接生成了一个无环图 (acyclic graph),它编码了完整的计算历史。每个张量都有一个.grad_fn属性,该属性引用了创建 Tensor自身的Function(除非这个张量是用户手动创建的,即这个张量的grad_fn是 None )。

如果需要计算导数,可以在 Tensor 上调用 .backward()。如果Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward()指定任何参数,但是如果它有更多的元素,则需要指定一个gradient参数,该参数是形状匹配的张量。

2.3 雅克比矩阵

数学上,若有向量函数y ⃗ = f ( x ⃗ ) \vec{y}=f(\vec{x})

y

=f(

x

),那么 y ⃗ \vec{y}

y

 关于 x ⃗ \vec{x}

x

 的梯度就是一个雅可比矩阵:

image.png

而 torch.autograd 这个包就是用来计算一些雅可比矩阵的乘积的。例如,如果 v vv 是一个标量函数 l = g ( y ⃗ ) l = g(\vec{y})l=g(

y

) 的梯度:

image.png

由链式法则,我们可以得到:

image.png

注意:grad在反向传播过程中是累加的(accumulated),这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以一般在反向传播之前需把梯度清零。

2.4 代码栗子

# -*- coding: utf-8 -*-
"""
Created on Fri Oct 15 21:07:32 2021
@author: 86493
"""
import torch
# require_grad=True用来追踪计算历史
x = torch.ones(2, 2, requires_grad = True)
print(x)
print('-' * 50)
# 对张量作指数运算
y = x ** 2
print(y) 
# y是计算的结果,所以又grad_fn属性
print(y.grad_fn)
print('-' * 50)
z = y * y * 3
out = z.mean() # 计算所有元素的平均值
print("z:", z)
print("out:", out)
print('-' * 50)
# requires_grad默认为False
a = torch.randn(2, 2)
print("初始的a值为:\n", a)
a = ((a * 3) / (a - 1))
print("运算后的a值为:\n", a)
print(a.requires_grad) # 默认为False
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn) # b是计算的结果,所有它有grad_fn属性
print('-' * 50)
# ==================================
# 求梯度
out.backward() # out是一个标量
print(x.grad) # 输入导数d(out)/dx
print('-' *50)
# 再来反向传播,注意grad是累加的(加多一次梯度)
# out2.backward()
# print(x.grad)
out3 = x.sum()
# 一般在反向传播前把梯度清零(以防累加)
x.grad.data.zero_() 
out3.backward()
print(x.grad)
print('-' *50)
# 雅克比向量积
x = torch.randn(3, requires_grad = True)
print(x)
y = x * 2
i = 0 
# norm是求该tensor的L2范数
while y.data.norm() < 1000:
    y = y * 2
    i = i + 1
print("y:\n", y, '\n')
print("i:", i)
v = torch.tensor([0.1, 1.0, 0.0001],
                 dtype = torch.float)
y.backward(v)
print("x.grad:\n", x.grad)
# 可以通过将代码块包装在with torch.no_grad()中
# 来阻止autograd跟踪设置了requires_grad=True
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
    print((x ** 2).requires_grad)
print('-' *50)
# 想修改tensor的数值,又不希望被autograd记录
# 即不会影响反向传播,可以对tensor.data操作
x = torch.ones(1, requires_grad = True)
print("x: ", x)
print(x.data) # 还是一个tensor
# 但是已经独立于计算图之外
print(x.data.requires_grad)
y = 2 * x
# 只改变了值,不会记录在计算图,所以不会影响梯度传播
x.data *= 100
y.backward()
# 更改data值也会影响tensor的值
print(x)
print(x.grad)

结果为:

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
--------------------------------------------------
tensor([[1., 1.],
        [1., 1.]], grad_fn=<PowBackward0>)
<PowBackward0 object at 0x000001D74AEFBE50>
--------------------------------------------------
z: tensor([[3., 3.],
        [3., 3.]], grad_fn=<MulBackward0>)
out: tensor(3., grad_fn=<MeanBackward0>)
--------------------------------------------------
初始的a值为:
 tensor([[-0.5364, -0.5926],
        [-0.5702, -0.7497]])
运算后的a值为:
 tensor([[1.0474, 1.1163],
        [1.0894, 1.2855]])
False
True
<SumBackward0 object at 0x000001D745FEDF70>
--------------------------------------------------
tensor([[3., 3.],
        [3., 3.]])
--------------------------------------------------
tensor([[1., 1.],
        [1., 1.]])
--------------------------------------------------
tensor([ 0.4216,  0.1233, -0.3729], requires_grad=True)
y:
 tensor([ 863.4903,  252.5478, -763.7181], grad_fn=<MulBackward0>) 
i: 10
x.grad:
 tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])
True
True
False
--------------------------------------------------
x:  tensor([1.], requires_grad=True)
tensor([1.])
False
runfile('D:/桌面文件/matrix/code/Torch/grad.py', wdir='D:/桌面文件/matrix/code/Torch')
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)
--------------------------------------------------
tensor([[1., 1.],
        [1., 1.]], grad_fn=<PowBackward0>)
<PowBackward0 object at 0x000001D74AEFBA30>
--------------------------------------------------
z: tensor([[3., 3.],
        [3., 3.]], grad_fn=<MulBackward0>)
out: tensor(3., grad_fn=<MeanBackward0>)
--------------------------------------------------
初始的a值为:
 tensor([[ 0.1064, -1.0084],
        [-0.2516, -0.4749]])
运算后的a值为:
 tensor([[-0.3570,  1.5063],
        [ 0.6030,  0.9660]])
False
True
<SumBackward0 object at 0x000001D745593FD0>
--------------------------------------------------
tensor([[3., 3.],
        [3., 3.]])
--------------------------------------------------
tensor([[1., 1.],
        [1., 1.]])
--------------------------------------------------
tensor([-0.8706, -1.1828, -0.8192], requires_grad=True)
y:
 tensor([ -891.5447, -1211.1826,  -838.8481], grad_fn=<MulBackward0>) 
i: 9
x.grad:
 tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
True
True
False
--------------------------------------------------
x:  tensor([1.], requires_grad=True)
tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])

三、基本配置

3.1 导包

常见的有torch、torch.nn、torch.utils.data.Dataset、torch.utils.data.DataLoader、torch.optimizer、numpy、os等,具体可以在官网查对应的API,另外还有比如:

涉及到表格信息的读入很可能用到pandas;

对于不同的项目可能还需要导入一些更上层的包如cv2等。

如果涉及可视化还会用到matplotlib、seaborn等。

涉及到下游分析和指标计算也常用到sklearn

import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer

3.2 超参数设置

  • batch size
  • 初始学习率(初始)
  • 训练次数(max_epochs)
  • GPU配置
batch_size = 16
lr = 1e-4
max_epochs = 100

3.3 GPU的设置

记得提前import os

# GPU设置
import os
# 方案一:使用os.environ,如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0.1'
# 方案二:使用"device"
# 后续要使用GPU的变量用.to(device)
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

Reference

(1)2.5.4 PyTorch中的非标量反向传播

(2)datawhale的pytorch笔记

相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
相关文章
|
存储 物联网 PyTorch
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
**Torchtune**是由PyTorch团队开发的一个专门用于LLM微调的库。它旨在简化LLM的微调流程,提供了一系列高级API和预置的最佳实践
737 59
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
|
10月前
|
缓存 并行计算 PyTorch
PyTorch CUDA内存管理优化:深度理解GPU资源分配与缓存机制
本文深入探讨了PyTorch中GPU内存管理的核心机制,特别是CUDA缓存分配器的作用与优化策略。文章分析了常见的“CUDA out of memory”问题及其成因,并通过实际案例(如Llama 1B模型训练)展示了内存分配模式。PyTorch的缓存分配器通过内存池化、延迟释放和碎片化优化等技术,显著提升了内存使用效率,减少了系统调用开销。此外,文章还介绍了高级优化方法,包括混合精度训练、梯度检查点技术及自定义内存分配器配置。这些策略有助于开发者在有限硬件资源下实现更高性能的深度学习模型训练与推理。
1928 0
|
并行计算 监控 搜索推荐
使用 PyTorch-BigGraph 构建和部署大规模图嵌入的完整教程
当处理大规模图数据时,复杂性难以避免。PyTorch-BigGraph (PBG) 是一款专为此设计的工具,能够高效处理数十亿节点和边的图数据。PBG通过多GPU或节点无缝扩展,利用高效的分区技术,生成准确的嵌入表示,适用于社交网络、推荐系统和知识图谱等领域。本文详细介绍PBG的设置、训练和优化方法,涵盖环境配置、数据准备、模型训练、性能优化和实际应用案例,帮助读者高效处理大规模图数据。
339 5
|
并行计算 Ubuntu PyTorch
Ubuntu下CUDA、Conda、Pytorch联合教程
本文是一份Ubuntu系统下安装和配置CUDA、Conda和Pytorch的教程,涵盖了查看显卡驱动、下载安装CUDA、添加环境变量、卸载CUDA、Anaconda的下载安装、环境管理以及Pytorch的安装和验证等步骤。
4056 1
Ubuntu下CUDA、Conda、Pytorch联合教程
|
机器学习/深度学习 监控 PyTorch
以pytorch的forward hook为例探究hook机制
【10月更文挑战第9天】PyTorch中的Hook机制允许在不修改模型代码的情况下,获取和修改模型中间层的信息,如输入和输出等,适用于模型可视化、特征提取及梯度计算。Forward Hook在前向传播后触发,可用于特征提取和模型监控。实现上,需定义接收模块、输入和输出参数的Hook函数,并将其注册到目标层。与Backward Hook相比,前者关注前向传播,后者侧重反向传播和梯度处理,两者共同增强了对模型内部运行情况的理解和控制。
570 3
|
机器学习/深度学习 存储 数据可视化
以pytorch的forward hook为例探究hook机制
【10月更文挑战第10天】PyTorch 的 Hook 机制允许用户在不修改模型代码的情况下介入前向和反向传播过程,适用于模型可视化、特征提取及梯度分析等任务。通过注册 `forward hook`,可以在模型前向传播过程中插入自定义操作,如记录中间层输出。使用时需注意输入输出格式及计算资源占用。
456 1
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十八)(1)
PyTorch 2.2 中文官方教程(十八)
682 2
PyTorch 2.2 中文官方教程(十八)(1)
|
并行计算 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十七)(4)
PyTorch 2.2 中文官方教程(十七)
614 2
PyTorch 2.2 中文官方教程(十七)(4)
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十九)(1)
PyTorch 2.2 中文官方教程(十九)
325 1
PyTorch 2.2 中文官方教程(十九)(1)
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十八)(3)
PyTorch 2.2 中文官方教程(十八)
268 1
PyTorch 2.2 中文官方教程(十八)(3)

推荐镜像

更多