pytorch开发基础 从张量到梯度运算和反向传播

75 阅读11分钟

pytorch开发基础 从张量到梯度运算和反向传播

一、PyTorch简介

PyTorch是当前深度学习研究与应用开发的主流框架之一,自2017年发布以来,凭借使用简单、动态计算图等特性快速崛起,成为TensorFlow的核心竞争对手。

需要提前下载好cuda(nvida支持)和pytorch cuda官网:developer.nvidia.com/cuda-downlo… pytorch官网: pytorch.org/ 或者用pip

1、核心定位

PyTorch主要面向两类用户:

  1. 希望替代NumPy、利用GPU加速数值计算的开发者;
  2. 需要灵活、快速的深度学习研究平台的研究者。

2、主要特点

  1. 动态计算图(define-by-run) 与TensorFlow等框架的“静态计算图”不同,PyTorch的计算图在每次前向传播时动态构建,更灵活,便于复杂模型的调试与迭代。
  2. Pythonic设计 充分贴合Python特性,例如Tensor对象可直接用Python切片、索引操作,使用更直观易用。
  3. GPU加速 支持NVIDIA的CUDA技术,能在GPU上高效完成数值计算,大幅提升模型训练与推理速度。
  4. 丰富的API与工具 内置自动求导、优化器、数据加载/预处理等功能;子项目 TorchVision 提供图像处理工具与预训练模型。
  5. 活跃的社区支持 社区资源丰富(教程、项目、问题解答),多数顶会论文也提供PyTorch实现代码。
  6. 生产环境集成 通过 TorchServe 可快速部署模型到生产环境;支持导出为ONNX格式,兼容Caffe2、TensorRT等框架。

二、环境检查

1、 检查PyTorch版本

通过以下代码可查看当前PyTorch及配套组件版本:

import torch
#导入torch模块
print(torch.__version__)               # PyTorch版本
print(torch.version.cuda)              # 对应CUDA版本
print(torch.backends.cudnn.version())  # 对应cuDNN版本
print(torch.cuda.get_device_name(0))   # GPU型号(若有)

2. 判断CUDA支持

检查当前环境是否可使用GPU加速:

print(torch.cuda.is_available())  # 返回True/False表示是否支持CUDA

3. 指定运行的GPU

  • 命令行指定:
CUDA_VISIBLE_DEVICES=0,1 python train.py  # 指定使用第0、1块GPU
  • 代码中指定:
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'  # 运行时指定GPU

三、张量创建(Tensor)

张量是PyTorch的核心数据结构,等价于NumPy的多维数组,但支持GPU加速计算。

1、张量创建基础

以全0张量为例,说明张量的定义方式:

#创建形状为(3,4)的全0张量
torch.zeros((3, 4))  # 或简写为torch.zeros(3, 4)
#输出:
#tensor([[0., 0., 0., 0.],
#[0., 0., 0., 0..],
#[0., 0., 0., 0.]])

可指定数据类型和运行设备(如GPU):

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
#创建形状(2,3)、数据类型为整型的全0张量,并放到GPU上
torch.zeros((2, 3), dtype=torch.long, device=device)
#输出(若有GPU):
#tensor([[0, 0, 0],
#[0, 0, 0]], device='cuda:0')

PyTorch支持的常用数据类型: torch.float64 、 torch.float32 、 torch.int64 、 torch.int32 、 torch.bool 等。

2、张量创建函数清单

PyTorch提供多种张量初始化函数,按类型分类如下:

  1. 直接赋值
torch.tensor()#从数据(如列表、NumPy数组)创建张量
  1. 按指定规则赋值
函数功能描述
torch.eye()生成指定大小的单位矩阵
torch.empty()创建给定大小的未初始化张量
torch.empty_like()基于已有张量,创建同形状的未初始化张量
torch.zeros()生成指定大小的全0张量
torch.zeros_like()基于已有张量,创建同形状的全0张量
torch.ones()生成指定大小的全1张量
torch.ones_like()基于已有张量,创建同形状的全1张量
torch.full()生成指定大小、填充为指定值的张量
torch.full_like()基于已有张量,创建同形状且填充指定值的张量

3、随机赋值

函数功能描述
torch.rand()生成形状指定、元素值在 [0,1) 区间的随机张量
torch.rand_like()生成与给定张量同形状、元素值在 [0,1) 区间的随机张量
torch.randn()生成给定形状、元素符合标准正态分布(均值0、标准差1)的随机张量
torch.randn_like()生成与给定张量同形状、元素符合标准正态分布的随机张量
torch.randint()生成给定形状、元素值在 [low, high) 区间内的随机整数张量
torch.randint_like()生成与给定张量同形状、元素为指定区间内随机整数的张量

4、计算赋值函数

用于生成按特定规则排列的张量:

函数功能描述
torch.arange()生成从 start 到 end (不包含)、步长为 step 的张量(类似Python的 range )
torch.range()生成从 start 到 end (不包含)的整数张量,可指定步长
torch.linspace()生成指定区间内均匀分布的张量,需指定元素个数
torch.logspace()生成指定区间内对数刻度均匀分布的张量,可指定元素个数和底数

5、Tensor与NumPy数组的转换

两者在CPU上共享内存(修改一个会同步改变另一个):

import torch
import numpy as np
#Tensor转NumPy数组
tensor = torch.tensor([1, 2, 3])
numpy_arr = tensor.numpy()

#NumPy数组转Tensor
numpy_arr2 = np.array([4, 5, 6])
tensor2 = torch.from_numpy(numpy_arr2)

6、重点:torch.randn() 详解

torch.randn() 是生成标准正态分布随机张量的核心函数,特点如下:

  1. 核心优势
  • 便捷性:直接传入形状参数即可生成符合标准正态分布的张量
  • 多维支持:可指定任意维度,适配深度学习中的张量操作
  • 可重复性:通过设置随机种子( torch.manual_seed() ),能固定随机序列,便于调试
  • GPU支持:可直接在GPU上生成张量,支持CUDA加速计算
  1. 调用格式

torch.randn( *size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False )

7、实战示例:生成+验证正态分布

import torch
import matplotlib.pyplot as plt
#不需要导入 FontProperties!

#1. 生成10x5的标准正态分布张量
x = torch.randn((10, 5))
#设置打印选项(不压缩、保留4位小数)
torch.set_printoptions(precision=4)
print(x)

#2. 关键:设置全局中文字体(解决乱码,无需本地字体文件)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS', 'WenQuanYi Zen Hei']  # 系统默认中文字体列表
plt.rcParams['axes.unicode_minus'] = False  # 解决负号 '-' 显示为方块的问题

#3. 绘制直方图(直接用中文,无需 fontproperties 参数)
plt.hist(x.flatten().numpy(), bins=30)
plt.xlabel('数值')  # 不用加 fontproperties!
plt.ylabel('频率')
plt.title('正态分布图')
plt.show()

运行后可得到类似下图的正态分布直方图:

在这里插入图片描述

8、计算均值和方差

#计算张量的均值
mean = torch.mean(x)
print("均值:",mean)
#计算张量的方差
variance = torch.var(x)
print("方差",variance)

9、数学运算

  • 基础运算: torch.add() (加)、 torch.sub() (减)、 torch.mul() (乘)、 torch.div() (除)
  • 逐元素函数: torch.sin() 、 torch.cos() 、 torch.exp() 、 torch.log() 等
  • 线性代数运算: torch.mm() (矩阵乘)、 torch.matmul() (矩阵/向量乘)、 torch.dot() (点积)
  1. 索引和切片
  • 整数索引: tensor[0, 1] (访问特定元素)
  • 范围切片: tensor[:, 1:4] (取指定范围元素)
  • 布尔索引: tensor[tensor > 0] (筛选满足条件的元素)
  1. 变形操作
  • tensor.view(2, -1) :改变张量形状(共享内存)
  • tensor.reshape(2, -1) :改变张量形状(可能创建新张量)
  • tensor.permute() :交换张量的维度顺序
  1. 广播

当两个张量形状不一致时,PyTorch会自动广播,对齐形状后执行逐元素运算。

  1. 合并和拆分
  • torch.cat() :按指定维度合并多个张量
  • torch.stack() :在新维度上堆叠多个张量
  • torch.split() :将张量拆分为多个子张量
  1. 统计操作
  • 统计量: torch.sum() (总和)、 torch.mean() (均值)、 torch.std() (标准差)
  • 极值/排序: torch.max() (最大值)、 torch.min() (最小值)、 torch.sort() (排序)
  1. 共享内存

PyTorch中部分张量操作(如 view )会与原张量共享内存,修改一个会同步改变另一个。 张量可通过 .to() 方法在CPU/GPU间切换,实现硬件加速:

10、单GPU设备迁移

import torch
if torch.cuda.is_available():
    device = torch.device("cuda")  # 定义CUDA设备
    x = torch.tensor([1, 2, 3])
    y = torch.ones_like(x, device=device)  # 直接在GPU创建张量
    x = x.to(device)  # 迁移张量到GPU(也可写x.to("cuda"))
    z = x + y
    print(z)  # 输出带device="cuda:0"标识
    print(z.to("cpu", torch.double))  # 迁移回CPU并修改数据类型
#多GPU指定设备

指定不同GPU卡

device0 = torch.device("cuda:0")
device1 = torch.device("cuda:1")
device2 = torch.device("cuda:2")
x = torch.tensor([1, 2, 3]).to(device0)
y = torch.tensor([4, 5, 6]).to(device1)
z = torch.tensor([7, 8, 9]).to(device2)

四、梯度计算(自动微分)

梯度计算是深度学习优化的核心,PyTorch通过自动微分(Autograd)系统实现梯度的自动计算,以下是核心内容整理:

1、导数与偏导数

  • 导数:一元函数中,函数在某点的切线斜率(输入微小变化时的函数变化率)。
  • 偏导数:多元函数中,固定其他变量时,某一变量的变化对函数值的影响率。
  • 全导数:多元函数中,所有变量的变化对函数值的总影响(各偏导数与对应变量微小变化的乘积和)。

2、导数规则

计算复杂函数导数的基础规则:

  1. 常数规则:常数的导数为0(如 f(x)=C ,则 f'(x)=0 )。
  2. 幂规则: f(x)=x^n ,则 f'(x)=n \cdot x^{n-1} 。
  3. 和差规则: f(x)=g(x)\pm h(x) ,则 f'(x)=g'(x)\pm h'(x) 。
  4. 积规则: f(x)=g(x)\cdot h(x) ,则 f'(x)=g'(x)\cdot h(x)+g(x)\cdot h'(x) 。
  5. 商规则: f(x)=g(x)/h(x) ,则 f'(x)=\frac{g'(x)\cdot h(x)-g(x)\cdot h'(x)}{[h(x)]^2} 。
  6. 链式法则:复合函数 f(g(x)) 的导数为 f'(g(x))\cdot g'(x) (深度学习反向传播的核心)。

3、梯度

梯度是多元函数的导数推广,是一个向量,每个元素对应函数对输入向量中某一元素的偏导数。

若函数 f 的输入是 n 维向量 x=[x1,x2,,xn],则f的梯度为:f=[fx1,fx2,,fxn]\boldsymbol{x}=[x_1,x_2,\dots,x_n] ,则 f 的梯度为: \nabla f = \left[ \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, \dots, \frac{\partial f}{\partial x_n} \right]

梯度公式推导示例

x=[1234]为例,计算out=z.mean()(其中z=3y2y=x+2 x=\begin{bmatrix}1&2\\3&4\end{bmatrix} 为例,计算 \text{out}=z.mean() (其中 z=3y^2 、 y=x+2 ) 对 x 的梯度:

  1. dydxy=x+2是线性函数,斜率为1,故dydx=1\frac{dy}{dx} y=x+2 是线性函数,斜率为1,故 \frac{dy}{dx}=1
  2. dzdy由幂规则,z=3y2的导数为6y \frac{dz}{dy} 由幂规则, z=3y^2 的导数为 6y 。
  3. d(out)dzout=z.mean()=z1+z2+z3+z44,故每个元素的导数为14\frac{d(\text{out})}{dz} \text{out}=z.mean()=\frac{z_1+z_2+z_3+z_4}{4} ,故每个元素的导数为 \frac{1}{4}
  4. 链式法则:d(out)dx=d(out)dzdzdydydx=14×6y×1=1.5(x+2)\frac{d(\text{out})}{dx} = \frac{d(\text{out})}{dz} \cdot \frac{dz}{dy} \cdot \frac{dy}{dx} = \frac{1}{4} \times 6y \times 1 = 1.5(x+2)

最终 x 的梯度为: 1.5×[3456]=[4.56.07.59.0]1.5 \times \begin{bmatrix}3&4\\5&6\end{bmatrix} = \begin{bmatrix}4.5&6.0\\7.5&9.0\end{bmatrix}

4、PyTorch自动梯度计算(Autograd)

PyTorch的Autograd系统是“define-by-run”模式(反向传播逻辑由代码运行方式决定),核心逻辑:

  1. 跟踪张量操作:给张量设置 requires_grad=True ,PyTorch会跟踪该张量的所有运算。
  2. 计算梯度:调用张量的 .backward() 方法,自动计算所有依赖张量的梯度(梯度会存到 .grad 属性中)。
  3. 标量限制: backward() 默认仅支持对标量调用,若为向量需先转标量(如求均值、求和)。
  4. 梯度管理:
  • torch.no_grad() :上下文管理器,阻止梯度跟踪(用于模型评估,节省内存)。
  • .zero_grad() :清空梯度(避免梯度累积,训练时需在每次反向传播前调用)。

自动梯度计算代码解析

  
import torch

# 1. 创建需计算梯度的张量
x = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True)
# 2. 定义运算流程
y = x + 2
z = y * y * 3
out = z.mean()  # 转标量,方便backward()
# 3. 反向传播计算梯度
out.backward()

# 查看x的梯度(与公式推导结果一致)
print(x.grad)
# 输出:
# tensor([[4.5000, 6.0000],
#         [7.5000, 9.0000]])

五、PyTorch反向传播

反向传播是训练神经网络的核心步骤,其目标是计算损失函数关于模型参数的梯度,再通过梯度下降更新参数以最小化损失。

1、反向传播的基本流程

  1. 前向传播:从输入层到输出层,依次计算各层的输出(预测结果),并记录每一步的运算过程。
  2. 计算损失:用预测结果与真实标签计算损失(如MSE、交叉熵)。
  3. 反向传播:从输出层反向计算每一层参数的梯度(基于链式法则)。
  4. 参数更新:利用梯度(结合优化器)更新网络的权重和偏置,降低损失。

2、PyTorch中反向传播的关键细节

  1. 叶节点张量(Leaf Tensor)
  • 定义:直接创建的张量(如 torch.tensor() )是叶节点张量;通过运算生成的张量(非直接创建)是非叶节点张量。
  • 梯度存储:反向传播时,只有叶节点张量的 grad 属性会存储梯度,非叶节点张量的梯度默认会被清空(节省内存)。
  • 示例:
import torch
x = torch.tensor([1., 2.], requires_grad=True)  #叶节点张量
y = x + 2  #非叶节点张量
z = y.mean()#变成标量
z.backward()
print(x.is_leaf)  #输出:True(叶节点)
print(y.is_leaf)  #输出:False(非叶节点)
print(x.grad)     #输出梯度(叶节点存储梯度)
print(y.grad)     #输出:None(非叶节点不存储梯度)
  1. 非叶节点梯度的保存

若需查看非叶节点的梯度,可在反向传播前用 retain_grad() 方法:

y.retain_grad()  #保存非叶节点y的梯度
z.backward()
print(y.grad)    #输出y的梯度
  1. 自动微分的核心逻辑

PyTorch的Autograd系统会跟踪张量的运算,构建计算图;反向传播时,从损失张量出发,沿计算图反向计算各参数的梯度(依赖链式法则)。

下次会带来用torch.nn构建神经网络的教程,觉得有帮助的话别忘了点赞喵