文章目录
- [PyTorch 深度学习笔记(上):框架认知与张量基础](#PyTorch 深度学习笔记(上):框架认知与张量基础)
- [1. PyTorch 概述](#1. PyTorch 概述)
-
- [1.1 什么是 PyTorch](#1.1 什么是 PyTorch)
- [1.2 PyTorch vs TensorFlow](#1.2 PyTorch vs TensorFlow)
- [1.3 PyTorch 的核心特点](#1.3 PyTorch 的核心特点)
- [2. 张量概述](#2. 张量概述)
-
- [2.1 什么是张量](#2.1 什么是张量)
- [2.2 张量与 PyTorch 的关系](#2.2 张量与 PyTorch 的关系)
- [2.3 PyTorch 张量 vs NumPy 数组](#2.3 PyTorch 张量 vs NumPy 数组)
- [2.4 张量的数据类型](#2.4 张量的数据类型)
- [2.5 张量的核心属性](#2.5 张量的核心属性)
- [3. 张量的创建](#3. 张量的创建)
-
- [3.1 根据已有数据创建:torch.tensor()](#3.1 根据已有数据创建:torch.tensor())
- [3.2 根据形状创建:torch.Tensor()](#3.2 根据形状创建:torch.Tensor())
- [3.3 创建指定类型的张量:torch.XXXTensor()](#3.3 创建指定类型的张量:torch.XXXTensor())
- [3.4 创建线性张量](#3.4 创建线性张量)
-
- [3.4.1 torch.arange():按步长生成](#3.4.1 torch.arange():按步长生成)
- [3.4.2 torch.linspace():按元素个数生成](#3.4.2 torch.linspace():按元素个数生成)
- [3.5 创建随机张量](#3.5 创建随机张量)
-
- [3.5.1 torch.rand():均匀分布](#3.5.1 torch.rand():均匀分布)
- [3.5.2 torch.randn():标准正态分布](#3.5.2 torch.randn():标准正态分布)
- [3.5.3 torch.randint():离散整数随机](#3.5.3 torch.randint():离散整数随机)
- [3.5.4 随机种子](#3.5.4 随机种子)
- [3.6 创建指定值张量](#3.6 创建指定值张量)
-
- [3.6.1 全零张量](#3.6.1 全零张量)
- [3.6.2 全一张量](#3.6.2 全一张量)
- [3.6.3 全指定值张量](#3.6.3 全指定值张量)
- [4. 张量与 NumPy 互转](#4. 张量与 NumPy 互转)
-
-
- [4.1 张量 → NumPy](#4.1 张量 → NumPy)
- [4.2 NumPy → 张量](#4.2 NumPy → 张量)
- [4.3 为什么带梯度的张量不能直接 .numpy()?](#4.3 为什么带梯度的张量不能直接 .numpy()?)
-
PyTorch 深度学习笔记(上):框架认知与张量基础
1. PyTorch 概述
1.1 什么是 PyTorch
PyTorch 是一个基于 Python 的开源深度学习框架,由 Facebook AI Research(FAIR)团队主导开发。它的核心设计思路可以概括为一句话:将数据封装为张量(Tensor),在动态构建的计算图上执行自动微分,从而高效完成神经网络模型的训练与推理。
理解这个定义,可以拆成三层:
| 层次 | 做什么 | 对应模块 |
|---|---|---|
| 数据层 | 将数值数据统一封装为张量,支持 CPU 和 GPU 两种设备 | torch.Tensor |
| 计算层 | 基于动态计算图自动追踪每一步运算,实现自动求导 | torch.autograd |
| 模型层 | 提供预置的神经网络层、损失函数、优化器,快速搭建模型 | torch.nn、torch.optim |
PyTorch 的主要应用场景包括计算机视觉(CV)、自然语言处理(NLP)、语音识别、强化学习等几乎全部深度学习领域。
1.2 PyTorch vs TensorFlow
两大主流框架的对比如下:
| 对比维度 | PyTorch | TensorFlow |
|---|---|---|
| 计算图模式 | 动态图(Define-by-Run),代码执行即建图 | 早期为静态图(先定义后运行),2.x 后引入 Eager Execution 支持动态 |
| 代码风格 | Pythonic,贴近原生 Python 编程习惯 | Keras 为高级接口,底层仍偏声明式 |
| 调试体验 | 可直接使用 print、pdb 等 Python 调试工具 |
2.x 改善很多,1.x 时期调试困难 |
| 学术/工业倾向 | 学术论文首选,近年来工业界快速普及,论文复现多基于 PyTorch | 工业部署生态成熟(TF Serving、TF Lite) |
动态图是核心差异点
- 动态图 = 计算图随着代码的执行,逐行即时构建。定义即执行,你写代码的那一刻,计算就发生了。指框架构建计算图这个动作与你的Python代码执行是同步的、实时的、可随代码逻辑变化的,而非预先定义好整个图再喂数据。这带来的直接好处是:你可以像调试普通 Python 程序一样使用 print、breakpoint,极大提升了开发和排错效率。
- 静态图先构建后执行。先把整个计算图定义好,再启动会话执行。图和代码执行是分离的,图定义阶段没法打印,因为没数据。
1.3 PyTorch 的核心特点
① 动态图计算
是什么:动态图(Define-by-Run)是指计算图在代码实际执行时才被逐步构建,而不是像早期 TensorFlow 1.x 那样先完整定义图结构再喂入数据。
具体到代码层面,动态图意味着:
python
import torch
# 每一个运算步骤都在执行时立即构建对应的计算图节点
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
y = x * 2 # 图节点1:乘法
z = y.mean() # 图节点2:求均值
z.backward() # 沿动态构建的图反向传播
print(x.grad) # tensor([0.6667, 0.6667, 0.6667])
为什么重要:
- 调试直观:可以用
print在任何位置查看中间变量的值,而不需要执行整个图 - 研究友好:模型结构可以灵活实验,不需要预先声明全部输入输出维度
- 控制流自然:可以使用 Python 原生的条件判断、循环来控制网络结构。
if、for、while直接作用于张量,每次迭代可以产生不同形状的图(这对处理变长序列的 RNN 至关重要)
一个体现动态图优势的典型场景:变长输入的处理:
python
# 对于变长句子,每个样本长度不同,动态图每次构建不同的计算路径
for sentence in variable_length_sentences:
emb = embedding(sentence) # 不同长度的句子产生不同形状的张量
output = rnn_model(emb) # 计算图随输入长度动态调整
② 强大的 GPU 加速能力
是什么:PyTorch 中的张量可以存放在两种设备上:CPU(主机内存)和 GPU(显存)。当张量位于 GPU 上时,所有运算都会在 GPU 的数千个计算核心上并行执行,速度远超 CPU。
这里澄清一个概念:不是"张量具有 GPU 加速能力",而是 PyTorch 提供了将张量无缝迁移到 GPU 的能力,并利用 NVIDIA 的 CUDA 平台在 GPU 上执行高效的并行计算。 张量只是一个数据容器,GPU 加速是 PyTorch 框架底层为你做的事情。
为什么需要 GPU 加速 :深度学习模型的训练本质上是海量的矩阵乘法和加法运算。以一个简单的全连接层 y = Wx + b 为例,当 W 是 1000×1000 的矩阵,一次前向传播就需要 100 万次乘法。整个训练过程需要重复数十万甚至百万次这样的运算,CPU 的少数几个核心串行处理根本跑不动,而 GPU 拥有数千个核心可以同时处理。
python
import torch
# 创建一个张量,默认在 CPU 上
x_cpu = torch.randn(1000, 1000)
# 检查 CUDA 是否可用,将张量移到 GPU
if torch.cuda.is_available():
x_gpu = x_cpu.to('cuda') # 或 x_cpu.cuda()
# 此时对 x_gpu 的所有运算都在 GPU 上执行
y = x_gpu @ x_gpu.T # GPU 上做矩阵乘法,极快
设备切换的核心 API:
| 方法 | 说明 |
|---|---|
tensor.to('cuda') / tensor.cuda() |
将张量迁移到 GPU |
tensor.to('cpu') / tensor.cpu() |
将张量迁回 CPU |
torch.cuda.is_available() |
检查当前环境是否有可用 GPU |
tensor.device |
查看张量当前所在的设备 |
torch.cuda.device_count() |
获取可用 GPU 数量 |
③ 完备的神经网络工具箱(torch.nn)
PyTorch 通过 torch.nn 子模块提供了构建神经网络的完整积木集合:
- 预置网络层 :全连接层(
nn.Linear)、卷积层(nn.Conv2d)、循环层(nn.LSTM、nn.GRU)、池化层(nn.MaxPool2d)、Dropout(nn.Dropout)、批归一化(nn.BatchNorm2d)等 - 损失函数 :交叉熵损失(
nn.CrossEntropyLoss)、均方误差(nn.MSELoss)、二元交叉熵(nn.BCELoss)等 - 激活函数:ReLU、Sigmoid、Tanh、GELU、Softmax 等
这意味着你不需要从零实现反向传播或参数更新公式,只需定义好网络结构,前向计算、损失评估、反向传播由框架自动衔接。
python
import torch.nn as nn
# 一个简单的三层全连接网络
model = nn.Sequential(
nn.Linear(784, 256), # 输入层 → 隐藏层1
nn.ReLU(),
nn.Linear(256, 128), # 隐藏层1 → 隐藏层2
nn.ReLU(),
nn.Linear(128, 10), # 隐藏层2 → 输出层(10分类)
)
④ 自动微分系统(torch.autograd)
这是 PyTorch 最核心的特性,也是深度学习框架区别于 NumPy 等数值计算库的本质所在。
核心能力 :当你设置 requires_grad=True 后,PyTorch 会自动追踪该张量参与的每一次运算,构建出完整的计算图 。当调用 .backward() 时,框架沿计算图自动计算出损失函数对每个参数的偏导数(梯度),无需手动推导和编码反向传播公式。
后续详细展开自动微分的完整工作流程
⑤ 跨平台与分布式支持
PyTorch 支持 CPU、GPU(CUDA)、TPU,可在 Linux、Windows、macOS 上运行,并提供 torch.distributed 模块支持多 GPU 乃至多机分布式训练。
2. 张量概述
2.1 什么是张量
张量(Tensor)是 PyTorch 中最基本的数据结构,它是一个可以包含任意维度的、元素类型相同的多维数组。
简单来说:张量是一个所有元素数据类型相同 的多维数组。
两个关键约束:
- 元素类型必须相同 :一个张量中所有元素的数据类型是统一的,不能同时混合
int和float - 维度不设上限:可以构建 0 维(标量)到任意 N 维的张量
关于数据类型:张量主要支持数值类型(包括整数、浮点数、布尔值),字符串类型在深度学习中极少使用,因为模型参数和输入必须是数值才能进行数学运算。
从数学和计算机科学的角度来看,张量是对标量、向量、矩阵的统称与推广:
标量(Scalar) 0 维张量 torch.tensor(3.14) 形状: ()
向量(Vector) 1 维张量 torch.tensor([1, 2, 3]) 形状: (3,)
矩阵(Matrix) 2 维张量 torch.tensor([[1,2],[3,4]]) 形状: (2, 2)
三维张量 3 维张量 形状如 (batch, rows, cols)
N 维张量 N 维张量 任意维度,不设上限

2.2 张量与 PyTorch 的关系
这是一个容易混淆的点,需要明确:张量是 PyTorch 框架内定义的一个类(torch.Tensor),它是 PyTorch 提供的数据容器 ,正如 NumPy 数组(numpy.ndarray)是 NumPy 库的核心数据结构。
PyTorch 框架
├── torch.Tensor ← 数据容器(张量)
├── torch.autograd ← 自动微分(依赖 Tensor 的梯度追踪)
├── torch.nn ← 神经网络模块(各层接收和返回 Tensor)
└── torch.optim ← 优化器(更新 Tensor 中的参数值)
这里有一个重要的观念需要理清:优秀的是 PyTorch 这个框架,而非张量本身。 张量只是 PyTorch 提供的众多工具中的"数据载体"。真正厉害的是 PyTorch 围绕张量构建的一整套基础设施,自动微分引擎、GPU 计算后端、神经网络抽象、优化器体系。张量本身就是一个存数据的容器,但因为它是 PyTorch 生态的一环,所以天然享受了这些能力的加持。
换句话说:
- 同样一个矩阵,在 NumPy 中就是普通的多维数组,只能做常规数值计算
- 同样的数据放在 PyTorch 张量中,就可以被自动追踪梯度、搬到 GPU 上并行加速、作为神经网络层的输入输出
2.3 PyTorch 张量 vs NumPy 数组
| 对比维度 | PyTorch Tensor | NumPy ndarray |
|---|---|---|
| GPU 支持 | 支持(.to('cuda')) |
不支持(仅 CPU) |
| 自动微分 | 支持(requires_grad=True 后自动追踪) |
不支持 |
| 默认数据类型 | float32(torch.float32) |
float64(numpy.float64) |
| 深度学习集成 | 无缝对接 nn 模块、DataLoader 等 |
需手动转换为张量 |
| 数学运算 | 基本运算齐全,专为 DL 优化 | 科学计算生态更广泛(线性代数、FFT 等) |
| 内存布局 | 行优先(row-major),与 NumPy 一致 | 行优先(C 风格) |
使用场景:
- 用 PyTorch Tensor:构建和训练深度学习模型时(前向传播、反向传播、参数更新全流程)
- 用 NumPy ndarray:数据预处理阶段(读取 CSV、图像归一化、特征变换等),处理完再转为张量送入模型
实际工作流往往是:NumPy 预处理 → 转为 PyTorch 张量 → 送入模型训练 → 转回 NumPy 做结果分析/可视化。
2.4 张量的数据类型
PyTorch 支持的数据类型及选择建议:
| 数据类型 | dtype 简写 | 说明 | 典型场景 |
|---|---|---|---|
torch.float32 |
torch.float / torch.float32 |
32 位浮点,默认类型 | 绝大多数模型训练与推理的首选 |
torch.float64 |
torch.double / torch.float64 |
64 位双精度浮点 | 高精度科学计算、对梯度精度要求极高的场景 |
torch.float16 |
torch.half / torch.float16 |
16 位半精度浮点 | 混合精度训练,节省显存和加速计算 |
torch.int32 |
torch.int / torch.int32 |
32 位有符号整数 | 标签、索引等离散值 |
torch.int64 |
torch.long / torch.int64 |
64 位有符号整数 | 通常作为类别标签的默认类型 |
torch.bool |
torch.bool |
布尔类型 | 掩码、条件筛选 |
torch.uint8 |
torch.uint8 |
8 位无符号整数 | 图像像素值(0--255) |
torch.complex64 |
torch.complex64 |
64 位复数 | 信号处理、量子计算等特殊场景 |
关于默认类型的说明 :PyTorch 创建张量时默认使用 torch.float32(32 位浮点),而不是 float64。这是深度学习领域的共识选择,原因有二:
- 精度足够:32 位浮点提供约 7 位有效数字,对于神经网络的梯度计算和参数更新完全够用
- 速度快、省内存:float32 占 4 字节,是 float64(8 字节)的一半。在大规模模型和大批量训练时,内存占用和计算速度的优势非常显著
2.5 张量的核心属性
python
# 常用属性
import torch
t = torch.randn(2, 3, dtype=torch.float32)
print(t.shape) # torch.Size([2, 3]) ------ 每个维度的大小
print(t.dtype) # torch.float32 ------ 数据类型
print(t.device) # cpu ------ 存储设备
print(t.requires_grad) # False ------ 是否需要自动微分
| 属性 | 含义 | 示例值 | 说明 |
|---|---|---|---|
x.shape / x.size() |
张量的形状(各维度大小) | torch.Size([2, 3]) |
shape 是属性访问;size() 是方法调用,效果相同,传入维度索引可获取该维大小 |
x.dtype |
张量中元素的数据类型 | torch.float32 |
可通过 .to(dtype=...) 修改 |
x.device |
张量所在的设备 | device(type='cpu') |
可通过 .to('cuda') 迁移 |
x.requires_grad |
是否需要计算梯度 | False(默认) |
设为 True 后 autograd 会追踪该张量的所有运算 |
x.grad |
存储反向传播后的梯度值 | None / 同形状张量 |
初始为 None,调用 .backward() 后被赋值为梯度张量 |
x.data |
返回一个与当前张量共享底层数据但剥离了计算图的张量 | 与 x 同值、同形状 |
用于需要修改张量值但不希望被 autograd 追踪的场景 |
x.ndim |
张量的维度数(秩) | 2 |
等价于 len(x.shape) |
x.numel() |
张量中元素的总个数 | 6(= 2×3) |
numel = number of elements |
关于 grad 属性:
grad存储在张量自身,但这个梯度值是由backward()方法计算并回填的,不是张量主动拥有的功能- 只有调用了
.backward()且该张量的requires_grad=True,grad才有实际值 grad是一个和原张量同形状的张量,存储了损失函数对该张量每个元素的偏导数
关于 data 属性:
- 返回一个与原张量共享底层数据 但
requires_grad=False的新张量 - 主要用途:在需要修改张量的数值、但不想让这次修改变动被 autograd 记录时使用。例如在手动梯度更新中:
x.data = x.data - lr * x.grad,这样可以更新x的值但不会被 autograd 视为一次运算从而追加到计算图中 - 注意:
data是比较早期的用法,现代代码中更推荐使用torch.no_grad()上下文管理器或.detach()方法,安全性更好。data的隐患在于它绕过了 autograd 的追踪,可能导致梯度计算错误而不报错(静默 bug)
3. 张量的创建
PyTorch 提供了丰富的张量创建方式,可以根据已有数据创建,也可以根据形状、规则或概率分布生成。
3.1 根据已有数据创建:torch.tensor()
python
torch.tensor(data, dtype=None, device=None, requires_grad=False)
参数说明:
| 参数 | 含义 | 默认值 | 说明 |
|---|---|---|---|
data |
用于创建张量的数据 | 必填 | 支持 Python 列表、元组、NumPy 数组等可迭代容器 |
dtype |
指定张量数据类型 | None(自动推断) |
不指定则根据 data 的值自动推断(整数→int64,浮点→float32) |
device |
指定设备 | None(CPU) |
可设为 'cuda'、'cuda:0' 等 |
requires_grad |
是否追踪梯度 | False |
训练参数通常设为 True |
python
import torch
import numpy as np
# 从 Python 列表创建
t1 = torch.tensor([1, 2, 3]) # 1维,自动推断为 int64
t2 = torch.tensor([[1.0, 2.0], [3.0, 4.0]]) # 2维,自动推断为 float32
# 从 NumPy 数组创建(注意:不共享内存,是拷贝)
arr = np.array([1.0, 2.0, 3.0])
t3 = torch.tensor(arr) # 等价于 torch.from_numpy(arr) 的拷贝版本
# 指定数据类型
t4 = torch.tensor([1, 2, 3], dtype=torch.float32) # 强制转为 float32
# 指定设备和梯度追踪
t5 = torch.tensor([1.0, 2.0], device='cuda', requires_grad=True)
注意 :
torch.tensor()总是拷贝数据 ,不共享内存。如果想共享内存(零拷贝),用torch.from_numpy()或torch.as_tensor()。
data 参数的可接受类型 :不仅限于字面量列表,支持一切实现了 Python 序列协议的可迭代对象(list、tuple、numpy.ndarray、甚至另一个张量)。但它不能直接指定形状,形状完全由传入的数据结构决定。
3.2 根据形状创建:torch.Tensor()
python
# 注意大写 T!这是类构造函数,行为和小写版本不同
torch.Tensor(*sizes)
torch.Tensor() 是张量类的构造函数,接收形状参数,创建指定形状的张量,元素为未初始化的随机值(内存中的残留数据)。也可以传入已有的可迭代数据来创建张量。
python
# 通过形状创建(不推荐直接使用,因为值是未初始化的垃圾数据)
t1 = torch.Tensor(2, 3) # 形状 (2, 3),内容是随机残留值
# 也可以传入数据(但不常用,torch.tensor() 更常用)
t2 = torch.Tensor([1, 2, 3]) # 从数据创建
3.3 创建指定类型的张量:torch.XXXTensor()
python
# 直接通过类型命名的构造函数创建
torch.FloatTensor([1, 2, 3]) # 等同于 torch.tensor([1., 2., 3.]),创建 float32
torch.DoubleTensor([1, 2, 3]) # float64
torch.IntTensor([1, 2, 3]) # int32
torch.LongTensor([1, 2, 3]) # int64
torch.BoolTensor([True, False]) # bool
这些是历史遗留的 API,功能可以用 torch.tensor(data, dtype=torch.float32) 完全覆盖。推荐统一使用 torch.tensor() 加 dtype 参数 ,代码意图更明确,API 更统一。
python
# 两种写法等价,推荐第二种
a = torch.FloatTensor([1, 2, 3])
b = torch.tensor([1, 2, 3], dtype=torch.float32)
3.4 创建线性张量
当需要快速生成有规律的一维数值序列时使用。
3.4.1 torch.arange():按步长生成
python
torch.arange(start=0, end, step=1, dtype=None)
按步长 在 [start, end) 区间(左闭右开)内生成等差数列。
| 参数 | 含义 | 默认值 |
|---|---|---|
start |
起始值 | 0 |
end |
终止值(不包含) | 必填 |
step |
步长(相邻元素间隔) | 1 |
python
torch.arange(0, 10, 2) # tensor([0, 2, 4, 6, 8]) → 左闭右开,不包含 10
torch.arange(3) # tensor([0, 1, 2]) → start 默认 0
3.4.2 torch.linspace():按元素个数生成
python
torch.linspace(start, end, steps, dtype=None)
按元素个数 在 [start, end] 区间(左闭右闭,包含两端)内生成等间隔数列。
| 参数 | 含义 | 说明 |
|---|---|---|
start |
起始值 | 必填,包含在结果中 |
end |
终止值 | 必填,包含在结果中 |
steps |
生成元素个数 | 必填,决定结果张量的长度 |
python
torch.linspace(0, 10, 5) # tensor([0.0, 2.5, 5.0, 7.5, 10.0]) → 包含10
需要精确控制元素数量时用
linspace(如生成画图用的 x 轴坐标点);步长明确时用arange(如生成卷积核的偏移量)。
3.5 创建随机张量
3.5.1 torch.rand():均匀分布
python
torch.rand(*size, dtype=None)
在 [0, 1) 区间内按均匀分布随机生成浮点数。
python
torch.rand(2, 3) # 形状 (2,3),值均匀分布在 [0, 1) 之间,默认 float32
3.5.2 torch.randn():标准正态分布
python
torch.randn(*size, dtype=None)
按标准正态分布(均值 μ=0,标准差 σ=1)随机生成浮点数。
注意:它不是 0-1 之间!值可以出现负数,约有 95% 的值落在 -2, 2 之间,但也可能出现绝对值更大的值。
python
torch.randn(3, 4) # 形状 (3,4),值从 N(0,1) 分布中采样
3.5.3 torch.randint():离散整数随机
python
torch.randint(low=0, high, size, dtype=torch.int64)
在 [low, high) 区间(左闭右开)随机生成整数。返回类型默认是 int64。
python
torch.randint(0, 10, (2, 3)) # 在 [0, 10) 范围内随机整数,形状 (2,3)
torch.randint(0, 2, (100,)) # 随机生成 100 个 0 或 1,用于随机标签/掩码
3.5.4 随机种子
为什么需要随机种子:深度学习实验中,权重初始化、数据打乱、Dropout 等步骤都依赖随机数。如果不固定种子,每次运行结果都不同,无法复现实验结果,无法调试,写论文时也无法让审稿人复现你的实验。
随机种子的原理:
- 计算机生成的"随机数"本质上是伪随机数,由一个确定的算法(伪随机数生成器,PRNG)根据一个初始种子值生成
- 相同的种子 + 相同的生成次序列 → 完全相同的随机数序列
- 种子本身只是一个整数,设什么值都可以(42 是流行的"梗值",源于《银河系漫游指南》)
关键理解 :设置了 manual_seed(42) 后,连续调用两次 torch.randn(3):
- 第一次调用返回随机序列 A
- 第二次调用返回随机序列 B(A ≠ B,序列是往下推进的)
- 但如果重新运行整个脚本,两次调用的结果分别和上次运行时相同(A 和 B 都重现了)
python
torch.manual_seed(42)
print(torch.randn(3)) # 每次运行输出: tensor([0.3367, 0.1288, 0.2345]) ← 固定
print(torch.randn(3)) # 每次运行输出: tensor([0.2303, -1.1229, -0.1863]) ← 也固定
常用种子 API:
| API | 作用 | 使用场景 |
|---|---|---|
torch.manual_seed(seed) |
设置 CPU 随机种子 | 必须调用的基础设置 |
torch.cuda.manual_seed(seed) |
设置当前 GPU 随机种子 | 使用 GPU 时配合使用 |
torch.cuda.manual_seed_all(seed) |
设置所有 GPU 随机种子 | 多 GPU 训练 |
torch.initial_seed() |
返回当前的种子值(不设置) | 查看当前种子,调试用 |
torch.initial_seed()返回的是当前生成器使用的种子值,它的设计目的是让你检查当前种子,而非设置种子。在一次运行中,它的返回值固定不变。
3.6 创建指定值张量
3.6.1 全零张量
python
torch.zeros(*size, dtype=None) # 根据形状创建
torch.zeros_like(input, dtype=None) # 根据已有张量的形状创建
典型场景:
- 初始化偏置(bias)为零
- 创建注意力掩码(mask)时,先建全零再填充
- 占位:知道形状和 dtype,不知道具体值时先占位
3.6.2 全一张量
python
torch.ones(*size, dtype=None)
torch.ones_like(input, dtype=None)
典型场景:
- 初始化某些缩放因子为 1
- 创建全一掩码
- 在门控机制(gate)中初始化遗忘门偏置为 1(LSTM 中常用技巧)
3.6.3 全指定值张量
python
torch.full(size, fill_value, dtype=None) # 根据形状创建
torch.full_like(input, fill_value, dtype=None) # 根据已有张量的形状创建
典型场景:
- 创建全为
-inf的注意力掩码 - 创建全为某个填充值(如 padding value)的张量
*_like系列函数(zeros_like、ones_like、full_like)会继承输入张量的 dtype、device、requires_grad 等属性,除非在参数中显式覆盖。这是设计上的便利,当你需要创建一个形状和属性都和已有张量相同的模板张量时,不需要再手动传一遍。
pythonx = torch.randn(3, 4, dtype=torch.float64, device='cuda') y = torch.zeros_like(x) # 形状 (3,4), float64, 在 cuda 上 --- 全部自动继承
关于创建张量的默认 dtype:
torch.rand()、torch.randn()、torch.zeros()等未指定dtype时,默认使用全局默认类型torch.float32。可通过 dtype 参数修改。torch.tensor()在未指定dtype时会根据输入数据自动推断
4. 张量与 NumPy 互转
这是实际项目中最频繁的操作之一,核心要理解共享内存 vs 拷贝的区别。
4.1 张量 → NumPy
python
# 方式1: .numpy() --- 共享内存
x = torch.tensor([1.0, 2.0, 3.0])
arr = x.numpy() # arr 和 x 指向同一块内存!
arr[0] = 999
print(x) # tensor([999., 2., 3.]) ← 张量也变了!
# 方式2: .numpy().copy() --- 不共享
arr_safe = x.numpy().copy()
# 注意:requires_grad=True 的张量不能直接 .numpy()
x_grad = torch.tensor([1.0, 2.0], requires_grad=True)
# arr = x_grad.numpy() # RuntimeError! 需要先 detach
arr = x_grad.detach().numpy() # 正确做法
4.2 NumPy → 张量
python
import numpy as np
arr = np.array([1.0, 2.0, 3.0])
# 方式1: torch.from_numpy() --- 共享内存
t1 = torch.from_numpy(arr) # t1 和 arr 共享底层数据
arr[0] = 999
print(t1) # tensor([999., 2., 3.]) ← 张量也变了
# 方式2: torch.tensor() --- 拷贝(不共享内存)
t2 = torch.tensor(arr) # 拷贝数据,各自独立
arr[0] = 0
print(t2) # tensor([999., 2., 3.]) ← 不受影响
# 避免共享内存的写法
t_safe = torch.from_numpy(arr).copy()
4.3 为什么带梯度的张量不能直接 .numpy()?
python
x = torch.tensor([1.0, 2.0], requires_grad=True)
# x.numpy() # RuntimeError: Can't call numpy() on Tensor that requires grad.
原因:NumPy 没有自动微分机制。如果一个张量还在计算图中(requires_grad=True),直接转为 NumPy 会丢失梯度追踪信息,造成数据一致性问题。PyTorch 选择直接报错,强制你必须显式调用 .detach():
python
x_np = x.detach().numpy() # detach 断开计算图,返回 requires_grad=False 的张量
detach() 的作用与原理:
- 返回一个与原张量共享底层数据 但
requires_grad=False的新张量 - 它从当前计算图中"脱离"出来,之后对它的一切操作都不会被 autograd 追踪
- 常用于:获取模型中间结果做可视化、保存 checkpoint 时去掉梯度信息节省存储、将梯度的张量转为 NumPy
detach() vs data:
data是老式写法,存在安全隐患(可能绕过 autograd 检查导致错误梯度)detach()更安全,是现代 PyTorch 的推荐做法- 结论:优先用
detach(),尽量不用data
以上为个人学习总结,旨在梳理个人理解。如有疏漏或不当之处,欢迎指正与交流。如果文章对你有帮助,别忘了点个赞、留个言,让更多的小伙伴看到~ 我们下篇再见!
下篇将深入张量的计算、索引、形状操作、拼接以及自动微分的完整工作流程~