pytorch开发基础 从张量到梯度运算和反向传播
一、PyTorch简介
PyTorch是当前深度学习研究与应用开发的主流框架之一,自2017年发布以来,凭借使用简单、动态计算图等特性快速崛起,成为TensorFlow的核心竞争对手。
需要提前下载好cuda(nvida支持)和pytorch cuda官网:developer.nvidia.com/cuda-downlo… pytorch官网: pytorch.org/ 或者用pip
1、核心定位
PyTorch主要面向两类用户:
- 希望替代NumPy、利用GPU加速数值计算的开发者;
- 需要灵活、快速的深度学习研究平台的研究者。
2、主要特点
- 动态计算图(define-by-run) 与TensorFlow等框架的“静态计算图”不同,PyTorch的计算图在每次前向传播时动态构建,更灵活,便于复杂模型的调试与迭代。
- Pythonic设计 充分贴合Python特性,例如Tensor对象可直接用Python切片、索引操作,使用更直观易用。
- GPU加速 支持NVIDIA的CUDA技术,能在GPU上高效完成数值计算,大幅提升模型训练与推理速度。
- 丰富的API与工具 内置自动求导、优化器、数据加载/预处理等功能;子项目 TorchVision 提供图像处理工具与预训练模型。
- 活跃的社区支持 社区资源丰富(教程、项目、问题解答),多数顶会论文也提供PyTorch实现代码。
- 生产环境集成 通过 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提供多种张量初始化函数,按类型分类如下:
- 直接赋值
torch.tensor()#从数据(如列表、NumPy数组)创建张量
- 按指定规则赋值
| 函数 | 功能描述 |
|---|---|
| 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() 是生成标准正态分布随机张量的核心函数,特点如下:
- 核心优势
- 便捷性:直接传入形状参数即可生成符合标准正态分布的张量
- 多维支持:可指定任意维度,适配深度学习中的张量操作
- 可重复性:通过设置随机种子( torch.manual_seed() ),能固定随机序列,便于调试
- GPU支持:可直接在GPU上生成张量,支持CUDA加速计算
- 调用格式
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() (点积)
- 索引和切片
- 整数索引: tensor[0, 1] (访问特定元素)
- 范围切片: tensor[:, 1:4] (取指定范围元素)
- 布尔索引: tensor[tensor > 0] (筛选满足条件的元素)
- 变形操作
- tensor.view(2, -1) :改变张量形状(共享内存)
- tensor.reshape(2, -1) :改变张量形状(可能创建新张量)
- tensor.permute() :交换张量的维度顺序
- 广播
当两个张量形状不一致时,PyTorch会自动广播,对齐形状后执行逐元素运算。
- 合并和拆分
- torch.cat() :按指定维度合并多个张量
- torch.stack() :在新维度上堆叠多个张量
- torch.split() :将张量拆分为多个子张量
- 统计操作
- 统计量: torch.sum() (总和)、 torch.mean() (均值)、 torch.std() (标准差)
- 极值/排序: torch.max() (最大值)、 torch.min() (最小值)、 torch.sort() (排序)
- 共享内存
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、导数规则
计算复杂函数导数的基础规则:
- 常数规则:常数的导数为0(如 f(x)=C ,则 f'(x)=0 )。
- 幂规则: f(x)=x^n ,则 f'(x)=n \cdot x^{n-1} 。
- 和差规则: f(x)=g(x)\pm h(x) ,则 f'(x)=g'(x)\pm h'(x) 。
- 积规则: f(x)=g(x)\cdot h(x) ,则 f'(x)=g'(x)\cdot h(x)+g(x)\cdot h'(x) 。
- 商规则: f(x)=g(x)/h(x) ,则 f'(x)=\frac{g'(x)\cdot h(x)-g(x)\cdot h'(x)}{[h(x)]^2} 。
- 链式法则:复合函数 f(g(x)) 的导数为 f'(g(x))\cdot g'(x) (深度学习反向传播的核心)。
3、梯度
梯度是多元函数的导数推广,是一个向量,每个元素对应函数对输入向量中某一元素的偏导数。
若函数 f 的输入是 n 维向量
梯度公式推导示例
以 对 x 的梯度:
- 求
- 求
- 求
- 链式法则:
最终 x 的梯度为:
4、PyTorch自动梯度计算(Autograd)
PyTorch的Autograd系统是“define-by-run”模式(反向传播逻辑由代码运行方式决定),核心逻辑:
- 跟踪张量操作:给张量设置 requires_grad=True ,PyTorch会跟踪该张量的所有运算。
- 计算梯度:调用张量的 .backward() 方法,自动计算所有依赖张量的梯度(梯度会存到 .grad 属性中)。
- 标量限制: backward() 默认仅支持对标量调用,若为向量需先转标量(如求均值、求和)。
- 梯度管理:
- 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、反向传播的基本流程
- 前向传播:从输入层到输出层,依次计算各层的输出(预测结果),并记录每一步的运算过程。
- 计算损失:用预测结果与真实标签计算损失(如MSE、交叉熵)。
- 反向传播:从输出层反向计算每一层参数的梯度(基于链式法则)。
- 参数更新:利用梯度(结合优化器)更新网络的权重和偏置,降低损失。
2、PyTorch中反向传播的关键细节
- 叶节点张量(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(非叶节点不存储梯度)
- 非叶节点梯度的保存
若需查看非叶节点的梯度,可在反向传播前用 retain_grad() 方法:
y.retain_grad() #保存非叶节点y的梯度
z.backward()
print(y.grad) #输出y的梯度
- 自动微分的核心逻辑
PyTorch的Autograd系统会跟踪张量的运算,构建计算图;反向传播时,从损失张量出发,沿计算图反向计算各参数的梯度(依赖链式法则)。