张量
1.1 创建张量(Tensor)
张量(Tensor)是 PyTorch 的核心数据结构,类似于 NumPy 的 ndarray,但支持 GPU 加速和自动求导。
1. 从数据直接创建
1 | import torch |
2. 创建特定值的张量
1 | torch.empty(2, 3) # 未初始化的张量(含随机垃圾值) |
3. 创建数值序列
1 | torch.arange(0, 10, 2) # [0, 2, 4, 6, 8],步长为 2 |
4. 随机张量
1 | torch.rand(2, 3) # [0,1) 均匀分布 |
5. 指定数据类型和设备
1 | x = torch.zeros(2, 3, dtype=torch.float32, device='cpu') # 可指定 device='cuda'(若可用) |
6. 从 NumPy 数组创建(共享内存)
1 | import numpy as np |
可以用numpy方法从Tensor得到numpy数组,也可以用torch.from_numpy从numpy数组得到 Tensor。
这两种方法关联的Tensor和numpy数组是共享数据内存的。
如果改变其中一个,另外一个的值也会发生改变。
如果有需要,可以用张量的clone方法拷贝张量,中断这种关联。
此外,还可以使用item方法从标量张量得到对应的Python数值。
使用tolist方法从张量得到对应的Python数值列表。
1 | #例1-1-4 张量和numpy数组 |
7. 张量属性
1 | x.shape # 形状 |
8. 创建与另一个张量相同属性的新张量
1 | y = torch.zeros_like(x) # 形状、dtype、device 与 x 相同,值为 0 |
💡 小贴士:
- 使用
torch.tensor()会拷贝数据,而torch.from_numpy()是共享内存。- 在训练模型时,通常使用
requires_grad=True来启用自动求导(后续章节)。
当然可以,祺!以下是与前一节格式一致的 1.2 张量的数据类型、维度与尺寸 学习笔记:
1.2 张量的数据类型、维度与尺寸
张量的数据类型(dtype)、维度(dimension) 和尺寸(shape) 是理解和操作张量的基础,直接影响计算效率、内存占用和模型兼容性。
1. 数据类型(dtype)
PyTorch 支持多种数据类型,常见如下:
| 类型名(Python) | 对应 dtype | 说明 |
|---|---|---|
| 32 位浮点数 | torch.float32 或 torch.float |
默认浮点类型,常用 |
| 64 位浮点数 | torch.float64 或 torch.double |
高精度计算 |
| 16 位浮点数 | torch.float16 或 torch.half |
节省内存,用于训练加速 |
| 32 位整数 | torch.int32 |
较少用 |
| 64 位整数 | torch.int64 或 torch.long |
常用于分类标签(如 CrossEntropyLoss) |
| 8 位无符号整数 | torch.uint8 |
图像像素常用(0–255) |
| 布尔类型 | torch.bool |
用于掩码(mask)操作 |
⚠️ 注意:
- 某些操作(如
torch.nn.CrossEntropyLoss)要求标签为torch.long。- GPU 上不支持所有 dtype(如
torch.double在部分操作中受限)。
2. 查看与转换数据类型
1 | x = torch.tensor([1, 2, 3]) |
3. 维度(Dimension)与形状(Shape)
- 维度(dim):张量的“轴”数。例如:
- 标量:0 维(
torch.tensor(5)) - 向量:1 维(
[1, 2, 3]→ shape(3,)) - 矩阵:2 维(
[[1,2],[3,4]]→ shape(2, 2)) - 批量图像:4 维(
[batch, channel, height, width])
- 标量:0 维(
- 形状(shape):每个维度的大小,用元组表示。
1 | x = torch.randn(2, 3, 4) |
可以使用view方法改变张量的尺寸。
如果view方法改变尺寸失败,可以使用reshape方法
1 | # 使用view可以改变张量尺寸 |
4. 张量的内存布局与连续性(进阶)
is_contiguous():判断张量在内存中是否连续存储。- 某些操作(如
view())要求张量是连续的,否则需先调用.contiguous()。
1 | x = torch.randn(2, 3) |
💡 小贴士:
- 使用
x.shape而不是x.size()可读性更好(两者等价)。- 在模型输入前,务必检查张量的
dtype和shape是否符合预期。
5. 实用技巧
- 统一类型以避免错误
不同 dtype 的张量不能直接运算:
1 | a = torch.tensor([1, 2], dtype=torch.float32) |
- 检查张量是否在 GPU 上
1 | if x.is_cuda: |
- 创建与某张量相同属性的新张量
1 | y = torch.zeros_like(x) # 自动继承 x 的 dtype、device、shape |
1.3 张量的基本操作
1. 算术运算
PyTorch 支持逐元素(element-wise)的数学运算,支持广播(broadcasting)。
1 | x = torch.tensor([1., 2., 3.]) |
⚠️ 注意:原地操作(如
add_)会覆盖原数据,可能影响自动求导,训练中慎用。
2. 索引与切片
与 Python 列表和 NumPy 类似,支持负索引、切片、高级索引。
1 | x = torch.randn(3, 4) |
💡 提示:索引操作返回的张量通常与原张量共享内存,修改可能影响原张量。
3. 形状变换(Reshape)
改变张量的形状而不改变数据。
1 | x = torch.arange(12) # shape: (12,) |
4. 拼接与拆分
1 | a = torch.tensor([[1, 2]]) |
5. 其他常用操作
1 | x = torch.randn(3, 4) |
💡 小贴士:
view()和reshape()功能相似,但reshape()更安全(可处理非连续张量)。- 拼接用
cat,堆叠用stack—— 后者会新增一个维度。- 索引和切片操作在调试和数据采样中极为常用,务必熟练掌握。
torch.cat
torch.cat(inputs, dimension=0) → Tensor
在给定维度上对输入的张量序列seq 进行连接操作。
参数:
inputs (sequence of Tensors) – 可以是任意相同Tensor 类型的python 序列
dimension (int, optional) – 沿着此维连接张量序列。
1 | x = torch.randn(2, 3) |
torch.squeeze
torch.squeeze(input, dim=None, out=None)
将输入张量形状中的1 去除并返回。 如果输入是形如(A×1×B×1×C×1×D)
,那么输出形状就为: (A×B×C×D)
当给定dim时,那么挤压操作只在给定维度上。例如,输入形状为: (A×1×B)
, squeeze(input, 0) 将会保持张量不变,只有用 squeeze(input, 1),形状会变成 (A×B)
。
注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
参数:
input (Tensor) – 输入张量
dim (int, optional) – 如果给定,则input只会在给定维度挤压
out (Tensor, optional) – 输出张量
1 | x = torch.zeros(2,1,2,1,2) |
torch.unsqueeze
torch.unsqueeze(input, dim, out=None)
返回一个新的张量,对输入的制定位置插入维度 1
注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。
参数:
- tensor (Tensor) – 输入张量
- dim (int) – 插入维度的索引
- out (Tensor, optional) – 结果张量
1 | x = torch.Tensor([1, 2, 3, 4]) |
当然可以,祺!以下是格式统一、结构清晰的 第二章 自动求导(Autograd)与自动微分机制 学习笔记:
自动微分
2.1 自动求导(Autograd)与自动微分机制
1. 基本原理
- PyTorch 使用动态计算图(Dynamic Computation Graph):计算过程即构建图,每次前向传播都会重新构建。
- 只有当张量的
requires_grad=True时,PyTorch 才会追踪其计算历史,用于后续求导。
1 | x = torch.tensor(2.0, requires_grad=True) |
✅ 关键点:
requires_grad=True是开启自动求导的前提。backward()触发反向传播,梯度累积到.grad属性中。
2. 多变量与向量求导
对于向量或矩阵输出,backward() 需要传入与输出同形的“梯度权重”(gradient tensor)。
1 | x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) |
💡 说明:
若y是标量(如 loss),可直接调用y.backward();
若y是向量,则需提供gradient参数,通常用于加权求和。
3. 计算图与梯度累积
- 每次调用
backward()会累加梯度到.grad中,而非覆盖。 - 训练时需在每次迭代前清零梯度:
1 | optimizer.zero_grad() # 等价于 model.zero_grad() 或 x.grad.zero_() |
手动清零示例:
1 | if x.grad is not None: |
4. 停止梯度追踪(常用场景)
有时希望某些操作不参与求导(如冻结部分网络、推理阶段):
1 | # 方法1:设置 requires_grad=False |
⚠️ 注意:
detach()返回的张量与原张量共享数据,但断开计算图。no_grad()是上下文管理器,适用于批量操作。
5. 梯度检查与调试
- 使用
retain_graph=True可多次调用backward()(默认会释放计算图):
1 | y = x ** 2 |
- 检查是否需要梯度:
1 | print(x.requires_grad) # True/False |
6. 自定义梯度(进阶)
可通过 torch.autograd.Function 自定义前向和反向传播逻辑(如实现新算子):
1 | class MyReLU(torch.autograd.Function): |
数据加载
Pytorch通常使用Dataset和DataLoader这两个工具类来构建数据管道。
Dataset:定义了数据集的内容,它相当于一个类似列表的数据结构,具有确定的长度,能够用索 引获取数据集中的元素。
DataLoader:定义了按batch加载数据集的方法,它是一个实现了 iter 方法的可迭代对 象,每次迭代输出一个batch的数据。能够控制batch的大小,batch中元素的采样方法,以及将batch结果整理成模型所需 输入形式的方法,并且能够使用多进程读取数据。
在绝大部分情况下,用户只需实现Dataset的 len 方法和 getitem 方法,就可以轻松构 建自己的数据集,并用默认数据管道进行加载
Dataset 与 DataLoader 基础
Dataset:定义数据集
Dataset 是一个抽象类,需继承并实现两个方法:
__len__(self):返回数据集大小__getitem__(self, idx):返回第idx个样本(通常为(data, label)元组)
1 | from torch.utils.data import Dataset |
✅ 提示:
- 可在
__init__中加载文件路径、进行初步预处理;- 实际数据读取(如图像解码)通常放在
__getitem__中,实现按需加载。
PyTorch 还提供内置数据集(如 torchvision.datasets.MNIST),用法类似。
DataLoader:批量加载与并行
DataLoader 将 Dataset 封装为可迭代对象,支持:
- 批量(batching)
- 打乱(shuffling)
- 多进程加载(num_workers)
- 自定义采样(sampler)
1 | from torch.utils.data import DataLoader |
关键参数说明
| 参数 | 作用 |
|---|---|
batch_size |
每批样本数 |
shuffle |
是否打乱顺序(训练集设为 True,验证集设为 False) |
num_workers |
加载数据的子进程数(0 表示主进程加载) |
pin_memory |
若为 True,数据加载到 CUDA 锁页内存,加快 GPU 传输 |
drop_last |
若最后一批不足 batch_size,是否丢弃(默认 False) |
⚠️ 注意事项:
- 在 Windows 或 Jupyter 中使用
num_workers > 0时,需将数据加载代码包裹在if __name__ == '__main__':中,避免多进程 fork 错误。pin_memory=True仅在使用 GPU 时有意义,且会增加 CPU 内存占用。
示例:完整训练数据流
1 | # 1. 定义数据集 |
💡 小贴士:
- 自定义
Dataset是处理私有数据的标准方式;- 对于大型数据集(如图像),建议在
__getitem__中动态读取文件,避免内存溢出;- 使用
torchvision.transforms可方便地集成图像预处理(见后续小节)。
torchvision.transforms 数据增强与预处理
在计算机视觉任务中,原始数据(如图像)通常需要进行标准化、裁剪、翻转、归一化等操作。torchvision.transforms 提供了一系列可组合的变换工具,无缝集成到 Dataset 中,支持高效、灵活的数据预处理和增强。
基本用法:组合变换(Compose)
多个变换可通过 transforms.Compose 串联,按顺序执行:
1 | from torchvision import transforms |
✅ 说明:
ToTensor()必须在Normalize之前,因为后者要求输入为[0,1]的 Tensor;Normalize的mean和std通常使用 ImageNet 统计量(迁移学习时保持一致)。
常用变换分类
图像格式转换
| 变换 | 作用 |
|---|---|
ToTensor() |
PIL Image 或 NumPy → torch.Tensor(HWC → CHW,值域 [0,1]) |
ToPILImage() |
Tensor → PIL Image(用于可视化) |
几何变换(数据增强)
| 变换 | 说明 |
|---|---|
RandomHorizontalFlip(p) |
随机水平翻转 |
RandomVerticalFlip(p) |
随机垂直翻转 |
RandomRotation(degrees) |
随机旋转(如 degrees=15) |
RandomCrop(size) |
随机裁剪(常用于训练) |
CenterCrop(size) |
中心裁剪(常用于验证) |
Resize(size) |
调整尺寸(如 224 或 (224, 224)) |
颜色与亮度调整
| 变换 | 说明 |
|---|---|
ColorJitter(brightness, contrast, saturation, hue) |
随机改变颜色属性 |
RandomGrayscale(p) |
以概率 p 转为灰度图 |
张量级操作
| 变换 | 说明 |
|---|---|
Normalize(mean, std) |
逐通道标准化:output = (input - mean) / std |
RandomErasing(p, scale, ratio) |
随机遮挡图像区域(正则化手段) |
在自定义 Dataset 中使用
1 | class ImageDataset(Dataset): |
⚠️ 注意:
- 训练集使用随机增强(如
RandomHorizontalFlip),- 验证/测试集应使用确定性变换(如
Resize+CenterCrop),保证结果可复现。
高级技巧
- 条件变换:通过自定义函数实现复杂逻辑(如仅对特定类别增强)。
- Lambda 表达式:
1 | transforms.Lambda(lambda x: x.unsqueeze(0)) # 添加维度 |
- MixUp / CutMix:需在
DataLoader之后、送入模型前实现(不属于transforms内置)。
可视化验证变换效果(调试用)
1 | import matplotlib.pyplot as plt |
💡 小贴士:
- 数据增强只在训练阶段使用,避免验证集“失真”;
- 过度增强可能损害模型性能(如旋转对数字识别不利);
- 对非图像数据(如表格、文本),需自行实现预处理逻辑,不依赖
torchvision.transforms。
神经网络
神经网络模块(torch.nn)
torch.nn 是 PyTorch 构建神经网络的核心模块,提供了层(Layer)、损失函数(Loss)、容器(Container) 等组件,配合 torch.optim 可快速搭建和训练模型。
基本结构:nn.Module
所有神经网络模型都继承自 nn.Module,必须实现 __init__ 和 forward 方法。如果Pytorch的内置模型层不能够满足需求,我们也可以通过继承nn.Module基类构建自定义的模型层。 实际上,pytorch不区分模型和模型层,都是通过继承nn.Module进行构建。 因此,我们只要继承nn.Module基类并实现forward方法即可自定义模型层。
1 | import torch |
✅ 关键点:
__init__中定义网络层(作为属性)。forward中定义数据流向(PyTorch 自动构建计算图)。
常用网络层(Layers)
| 层 | 说明 |
|---|---|
nn.Linear(in_features, out_features) |
全连接层(仿射变换)。 参数数量 = in × out(权重) + out(偏置) |
nn.Flatten(start_dim=1) |
将多维张量从 start_dim 开始压平为一维,常用于 CNN 后接全连接层前 |
nn.BatchNorm1d(num_features) |
一维批归一化。对 batch 维度做标准化,通常置于激活函数之前。 可通过 affine=False 禁用可学习参数 |
nn.BatchNorm2d/3d |
二维/三维批归一化,分别用于图像和视频数据 |
nn.Dropout(p=0.5) |
随机将输入张量的部分元素置零(概率 p),用于正则化 Dropout2d/3d 按通道/体素整体丢弃,适用于卷积特征图 |
nn.Threshold(threshold, value) |
限幅函数:若 x <= threshold,输出 value;否则输出 x |
nn.ConstantPad2d(padding, value) |
二维常数填充(如 padding=(1,1,1,1) 表示上下左右各填1) |
nn.ReplicationPad1d/2d |
边缘复制填充(重复边界值) |
nn.ZeroPad2d(padding) |
二维零填充 |
nn.GroupNorm(num_groups, num_channels) |
组归一化:将通道分为 num_groups 组分别归一化,不受 batch size 限制,在小 batch 场景优于 BatchNorm |
nn.LayerNorm(normalized_shape) |
层归一化:对单个样本的所有特征做归一化,常用于 Transformer |
nn.InstanceNorm2d(num_features) |
实例归一化:对每个样本的每个通道单独归一化,常用于风格迁移 |
卷积与池化层
| 层 | 说明 |
|---|---|
nn.Conv1d(in_ch, out_ch, kernel_size) |
一维卷积,常用于文本或时序数据 参数数量 = in_ch × kernel_size × out_ch + out_ch |
nn.Conv2d(in_ch, out_ch, kernel_size, ...) |
二维卷积,图像任务核心 参数数量 = in_ch × k_h × k_w × out_ch + out_ch • dilation > 1 → 空洞卷积(扩大感受野) • groups > 1 → 分组卷积 • groups = in_ch → 深度卷积(Depthwise Conv) • 深度卷积 + 1×1 卷积 = 深度可分离卷积(Separable Conv) |
nn.Conv3d |
三维卷积,用于视频或体数据 |
nn.MaxPool2d(kernel_size, stride, ...) |
二维最大池化,无参数,常用下采样手段 |
nn.AvgPool2d |
二维平均池化 |
nn.AdaptiveMaxPool2d(output_size) |
自适应池化:自动计算 stride/padding,使输出尺寸恒为 output_size(如 (1,1) 用于全局池化) |
nn.AdaptiveAvgPool2d |
自适应平均池化 |
nn.FractionalMaxPool2d |
分数最大池化:支持非整数缩放比,具随机性,有一定正则效果 |
nn.ConvTranspose2d |
转置卷积(“反卷积”),常用于上采样(如语义分割) ⚠️ 并非数学逆运算,但可恢复空间尺寸 |
nn.Upsample(size/scale_factor, mode='nearest') |
插值上采样,mode 可选 'nearest'、'bilinear' 等 |
nn.Unfold / nn.Fold |
滑动窗口提取与重构。卷积操作可等价表示为:Unfold → Linear → Fold |
循环网络层
| 层 | 说明 |
|---|---|
nn.Embedding(num_embeddings, embedding_dim) |
词嵌入层:将离散索引(如单词 ID)映射为稠密向量,参数可学习 |
nn.LSTM(input_size, hidden_size, num_layers, ...) |
长短时记忆网络,缓解梯度消失,支持长期依赖 • bidirectional=True → 双向 LSTM • 默认输入形状:(seq_len, batch, feature) • 设 batch_first=True 可改为 (batch, seq_len, feature) |
nn.GRU |
门控循环单元,结构比 LSTM 简单,参数更少,训练更快 |
nn.RNN |
基础循环网络,易梯度消失,较少使用 |
nn.LSTMCell / GRUCell / RNNCell |
单步循环单元,适用于自定义循环逻辑(一般不直接使用) |
Transformer 相关层
| 层 | 说明 |
|---|---|
nn.MultiheadAttention(embed_dim, num_heads) |
多头注意力机制,Transformer 核心组件 |
nn.TransformerEncoderLayer |
编码器层:包含多头注意力 + 前馈网络 + 残差连接 + LayerNorm |
nn.TransformerDecoderLayer |
解码器层:含自注意力、编码器-解码器注意力、前馈网络 |
nn.TransformerEncoder |
由多个 TransformerEncoderLayer 堆叠而成 |
nn.TransformerDecoder |
由多个 TransformerDecoderLayer 堆叠而成 |
nn.Transformer |
完整 Transformer 模型(含编码器+解码器),适用于序列到序列任务 |
💡 小贴士:
- BatchNorm 在小 batch(<8)时不稳定,可改用 GroupNorm 或 LayerNorm;
- Depthwise Separable Conv(深度可分离卷积)大幅减少参数量,是 MobileNet 等轻量模型的核心;
- Transformer 默认不包含位置编码,需手动添加(如
nn.Embedding或正弦编码);- 所有层的参数(如权重、偏置)会自动注册到模型的
.parameters()中,无需手动管理。
激活函数(非线性)
激活函数通常作为独立层使用:
1 | nn.ReLU() |
⚠️ 注意:
Softmax通常不与CrossEntropyLoss一起使用(因后者内部已包含 LogSoftmax)。
容器(Containers)
用于组织多层结构:
1 | # Sequential:按顺序执行 |
参数管理
- 所有
nn.Parameter(如权重、偏置)会自动注册为模型参数。 - 可通过
.parameters()获取所有可训练参数:
1 | for name, param in model.named_parameters(): |
设备与数据类型一致性
模型和输入张量必须在同一设备(CPU/GPU)和兼容 dtype 上:
1 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
模型保存与加载
1 | # 保存整个模型(不推荐) |
✅ 最佳实践:
- 训练时用
model.train(),推理时用model.eval()。- 保存/加载使用
state_dict,更灵活、安全。
优化器与训练循环(torch.optim)
训练神经网络的核心流程包括:前向传播 → 计算损失 → 反向传播 → 参数更新。torch.optim 提供了多种优化算法,简化了参数更新过程。
基本训练循环结构
1 | import torch |
✅ 关键三步:
zero_grad():防止梯度累积(除非故意累积)backward():自动求导step():执行优化算法更新权重
常用优化器(Optimizers)
| 优化器 | 说明 | 典型用法 |
|---|---|---|
SGD |
随机梯度下降,可加动量 | optim.SGD(params, lr=0.01, momentum=0.9) |
Adam |
自适应学习率,收敛快 | optim.Adam(params, lr=0.001, betas=(0.9, 0.999)) |
RMSprop |
适合非平稳目标 | optim.RMSprop(params, lr=0.01, alpha=0.99) |
AdamW |
Adam + 权重衰减解耦(推荐) | optim.AdamW(params, lr=3e-4, weight_decay=0.01) |
💡 建议:
- 初学者可首选 Adam 或 AdamW;
- 微调大模型时,AdamW + warmup + linear decay 是常见组合。
学习率调度(Learning Rate Scheduler)
动态调整学习率可提升收敛性和泛化能力:
1 | scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1) |
其他常用调度器:
ReduceLROnPlateau:当 loss 不下降时降低 lr(需传入metrics)CosineAnnealingLR:余弦退火LinearWarmup + CosineDecay:常用于 Transformer 类模型
损失函数(Loss Functions)
torch.nn 提供常用损失函数,需根据任务选择:
| 任务类型 | 损失函数 | 注意事项 |
|---|---|---|
| 多分类 | nn.CrossEntropyLoss() |
输入未归一化 logits,标签为 long 类型 |
| 二分类 | nn.BCEWithLogitsLoss() |
输入为 logits,内部含 sigmoid |
| 回归 | nn.MSELoss() / nn.L1Loss() |
注意输出维度匹配 |
| 对比学习 | nn.CosineEmbeddingLoss() |
用于向量相似性 |
⚠️ 重要区别:
CrossEntropyLoss=LogSoftmax + NLLLoss,不要在模型最后加Softmax!BCEWithLogitsLoss比BCELoss更数值稳定(推荐使用前者)。
训练 vs 评估模式
- 训练模式:
model.train()→ 启用 Dropout、BatchNorm 更新统计量 - 评估模式:
model.eval()→ 关闭 Dropout,使用固定统计量(如均值/方差)
1 | # 验证阶段示例 |
✅ 最佳实践:
- 验证/测试时务必使用
model.eval()+torch.no_grad()。
梯度裁剪(Gradient Clipping)
防止梯度爆炸(尤其在 RNN 或大模型中):
1 | torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) |
💡 小贴士:
- 每次迭代必须调用
optimizer.zero_grad(),否则梯度会累加。- 使用
loss.item()获取标量损失值(避免内存泄漏)。- 训练日志建议记录:epoch、loss、lr、评估指标(如准确率)。