python torch基础用法
PyTorch 深度学习入门与全面指南
本文档将全面、系统地介绍 PyTorch 的核心概念、基础用法和完整的工作流程,从张量的基本操作到神经网络的训练与保存,旨在为初学者提供一份详尽的参考手册。
1. PyTorch 简介、作用与安装
1.1 PyTorch 是什么?
PyTorch 是一个开源的机器学习框架,主要用于构建和训练深度学习模型。它以其动态计算图和简洁的 Python 接口而闻名,特别适合研究和快速原型开发。
-
动态计算图:PyTorch 的计算图是动态构建的,这意味着计算图会在每次前向传播时根据输入数据重新构建。这使得 PyTorch 在处理可变长度序列(如自然语言处理任务)或具有复杂控制流的模型时非常灵活和直观。
1.2 PyTorch 的核心作用
PyTorch 提供了高效的多维数组运算能力,并内置了自动微分(Autograd)引擎,这使得它可以:
-
构建和训练神经网络:自动计算梯度,简化了反向传播过程。
-
利用 GPU 加速:高效利用 GPU 进行并行计算,大幅提升训练速度。
-
数据处理:提供强大的数据加载工具,简化数据集管理。
1.3 如何安装 PyTorch
推荐使用官方网站提供的安装命令,它会根据你的操作系统、包管理器和 CUDA 版本生成最合适的安装指令。
-
选择配置:选择你的系统、包管理器(如 Conda 或 pip)、Python 版本和 CUDA 版本。
-
运行命令:
# 以 Conda 和 CUDA 12.1 为例 conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia如果你没有 GPU,选择 "CPU" 版本即可。
2. PyTorch 张量与 NumPy 的核心区别
PyTorch 的核心数据结构是 张量(Tensor),它与 NumPy 的 ndarray 极为相似,但在功能上,PyTorch 张量具有显著的优势。
| 特性 | PyTorch 张量 | NumPy 数组 | 概念解释 |
| GPU 加速 | 支持。是深度学习加速的关键。 | 不支持。只能在 CPU 上运行。 | **GPU(图形处理器)拥有数以千计的核心,擅长并行处理大规模矩阵运算,这正是深度学习的核心。PyTorch 可以将张量无缝地转移到 GPU 上,从而将训练速度提升数十倍甚至数百倍。 |
| 自动微分 | 内置 autograd 引擎,可自动计算梯度。 |
不支持。需要手动实现梯度计算。 | 自动微分是深度学习优化的基石。它能自动追踪所有操作,并计算出每个参数对最终损失的导数(梯度),这是反向传播算法所必需的。NumPy 没有这个功能,你需要手动计算和实现梯度更新。 |
| 动态计算图 | 采用动态计算图**,灵活易于调试。 | 无计算图概念,操作立即执行。 | PyTorch 的计算图在运行时动态构建,这使得你可以使用标准的 Python 控制流(如 if、for 循环)来构建网络。相比之下,TensorFlow 1.x 的静态计算图需要在运行前完全定义好,调试起来更困难。 |
| 互操作性 | 与 NumPy 数组可以方便且高效地转换。 | 不具备此功能。 | 你可以轻松地将 PyTorch 张量转换为 NumPy 数组,反之亦然。这种转换非常高效,因为它们通常共享底层的内存,避免了数据拷贝。 |
3. PyTorch 张量的基础操作
3.1 张量的数据类型与创建
张量可以存储不同类型的数据,其数据类型可以通过 dtype 指定。
import torch
import numpy as np
# 创建一个 32 位浮点数张量(默认)
t1 = torch.tensor([1.0, 2.0])
print(f"默认数据类型: {t1.dtype}") # torch.float32
# 指定为 64 位整数
t2 = torch.tensor([1, 2], dtype=torch.int64)
print(f"指定数据类型: {t2.dtype}") # torch.int64
# 从 NumPy 数组创建,两者共享内存
n_array = np.array([3, 4])
t3 = torch.from_numpy(n_array)
print(f"从 NumPy 创建: {t3}")
# 从 PyTorch 张量转为 NumPy 数组
n_array_new = t3.numpy()
3.2 张量的维度操作与拼接
-
view()和reshape():用于改变张量的形状。-
view():如果可能,返回一个共享底层内存的视图。如果新的形状与原张量内存布局连续,则可以使用。 -
reshape():更通用的方法。如果可以,它会返回一个视图;否则,它会返回一个新的副本。
x = torch.randn(2, 4) y = x.view(8) # 将 2x4 张量展平为一维(1x8) z = x.reshape(4, 2) # 改变形状为 4x2 print(f"原张量形状: {x.shape}") print(f"view 后的形状: {y.shape}") print(f"reshape 后的形状: {z.shape}") -
-
unsqueeze()和squeeze():用于添加或移除单个维度。-
unsqueeze(dim):在指定的dim位置增加一个维度,大小为 1。 -
squeeze():移除所有大小为 1 的维度。
x = torch.randn(5) # 形状: (5,) y = x.unsqueeze(0) # 在第 0 维增加一个维度,形状: (1, 5) z = y.squeeze() # 移除所有大小为 1 的维度,形状: (5,) -
-
cat()和stack():用于拼接张量。-
cat(tensors, dim):沿着指定的dim拼接张量。所有张量在该维度以外的形状必须相同。 -
stack(tensors, dim):沿着指定的dim创建一个新的维度来拼接张量。所有张量的形状必须完全相同。
t1 = torch.randn(2, 3) t2 = torch.randn(2, 3) t_cat = torch.cat([t1, t2], dim=0) # 按行拼接,形状: (4, 3) t_stack = torch.stack([t1, t2], dim=0) # 增加一个新维度来拼接,形状: (2, 2, 3) -
3.3 索引、切片与广播
PyTorch 的索引和切片语法与 NumPy 相同。
-
索引与切片:
t = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(t[0]) # 访问第一行:tensor([1, 2, 3]) print(t[1, 2]) # 访问第二行第三列的元素:tensor(6) print(t[:, 1:]) # 访问所有行的第二列及之后 # tensor([[2, 3], # [5, 6], # [8, 9]]) print(t[:2, 1]) # 访问前两行的第二列 # tensor([2, 5]) -
花式索引(Fancy Indexing):使用张量或列表作为索引,可以访问非连续的元素。
indices = torch.tensor([0, 2]) print(t[indices]) # 访问第0行和第2行 # tensor([[1, 2, 3], # [7, 8, 9]]) -
广播(Broadcasting):
PyTorch 的广播机制允许张量在特定条件下自动扩展其形状,以便进行元素级运算。当两个张量的形状不兼容时,PyTorch 会尝试自动广播以进行运算。
基本规则:
1. 每个张量至少有一个维度。
2. 从尾部维度开始比较,维度大小必须相等,或者其中一个为 1,或者其中一个不存在。
例如:一个 (3, 1) 的张量可以与一个 (1, 4) 的张量相加,因为它们可以被广播为 (3, 4)。
t1 = torch.tensor([1, 2, 3]) # 形状: (3)
t2 = torch.tensor([[10], [20]]) # 形状: (2, 1)
result = t1 + t2 # t1 被广播为 (2, 3), t2 被广播为 (2, 3)
# 结果:
# tensor([[11, 12, 13],
# [21, 22, 23]])
3.4 聚合、克隆与赋值
-
聚合操作:如
sum()、mean()、max()等,可以指定dim参数进行维度聚合。t = torch.tensor([[1, 2], [3, 4]]) print(t.sum()) # 所有元素总和:tensor(10) print(t.mean(dim=0)) # 按列求平均:tensor([2., 3.]) print(t.mean().item()) # 平均值,`.item()`用于取出单个元素:2.5 t = torch.tensor([10, 20, 5, 30]) max_val, max_idx = torch.max(t, dim=0) print(f"最大值: {max_val.item()}, 索引: {max_idx.item()}") # 最大值: 30, 索引: 3 -
按维度聚合:这些操作都可以指定
dim参数,沿着某个维度进行操作。t = torch.tensor([[1, 2], [3, 4]]) print(t.sum(dim=0)) # 沿着第0维(列)求和:tensor([4, 6]) print(t.mean(dim=1)) # 沿着第1维(行)求平均:tensor([1.5000, 3.5000]) -
克隆与赋值:
.clone():创建一个张量的新副本。新副本与原张量拥有独立的内存,改变其中一个不会影响另一个。
原地操作:一些操作会直接在张量上进行修改,而不是返回新的张量。通常,这些操作的名称以 _ 结尾(例如 add_())。
-
t1 = torch.tensor([1, 2]) t2 = t1.clone() # 创建独立副本 t1.add_(5) # 原地加法,t1 被修改 print(t1) # tensor([6, 7]) t2[0] = 30 print(t1) # tensor([6, 7]) print(t2) # tensor([30, 2])
3.5 张量与 GPU 的交互
PyTorch 允许张量在 CPU 和 GPU 之间轻松转换,这对于利用 GPU 加速至关重要。
# 检查 GPU 是否可用
if torch.cuda.is_available():
device = torch.device('cuda')
print("Using GPU.")
else:
device = torch.device('cpu')
print("Using CPU.")
# 将张量移动到 GPU
t_cpu = torch.randn(3, 3)
t_gpu = t_cpu.to(device)
# 将张量移回 CPU
t_cpu_new = t_gpu.to('cpu')
4. PyTorch 深度学习核心组件
4.1 自动微分(Autograd)
这是 PyTorch 的核心功能。通过设置 requires_grad=True,PyTorch 会追踪张量上的所有操作,以便在反向传播时计算梯度。
# 1. 创建需要求梯度的张量
x = torch.tensor(3.0, requires_grad=True)
# 2. 定义函数
y = x**2 + 5 * x
# 3. 反向传播,计算 y 对 x 的梯度
y.backward()
# 4. 访问梯度值
# y' = 2x + 5,当 x=3 时,y' = 2*3 + 5 = 11.0
print(x.grad) # 结果为 tensor(11.)
4.2 数据集与数据加载器(Datasets & DataLoaders)
这两个类是 PyTorch 高效处理数据的关键。
-
Dataset:代表数据集,需要自定义类并实现__len__(返回数据总数)和__getitem__(根据索引返回一个样本)方法。 -
DataLoader:一个迭代器,用于将数据集分批次(batch)加载到内存中,并支持多线程并行加载、数据打乱等功能。
from torch.utils.data import Dataset, DataLoader
# 自定义数据集
class CustomDataset(Dataset):
def __init__(self, data, labels):
self.data = data
self.labels = labels
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx], self.labels[idx]
# 使用 DataLoader
dataset = CustomDataset(torch.randn(100, 10), torch.randint(0, 2, (100,)))
data_loader = DataLoader(dataset, batch_size=16, shuffle=True)
# 遍历数据加载器
for inputs, labels in data_loader:
print(f"批次数据形状: {inputs.shape}, 批次标签形状: {labels.shape}")
break
4.3 模型构建与管理
PyTorch 推荐通过继承 torch.nn.Module 来定义神经网络。
-
模型定义:在
__init__中定义网络层,在forward中定义前向传播过程。import torch.nn as nn class SimpleModel(nn.Module): def __init__(self): super().__init__() # 定义一个线性层(全连接层),输入10个特征,输出5个特征 self.fc1 = nn.Linear(10, 5) # 定义一个激活函数 self.relu = nn.ReLU() # 定义另一个线性层,输入5个特征,输出1个特征 self.fc2 = nn.Linear(5, 1) def forward(self, x): # 定义数据流向,即前向传播的顺序 out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out -
优化器(Optimizer):管理和更新模型参数,如
Adam、SGD等。
model.parameters():返回所有可训练的参数。
torch.optim:提供各种优化算法,如 SGD、Adam,用于根据梯度更新参数。
-
import torch.optim as optim model = SimpleModel() # 将模型的所有参数传递给优化器 optimizer = optim.Adam(model.parameters(), lr=0.001)
5. PyTorch 训练循环
-
清零梯度:在每次反向传播前,将优化器中的梯度清零,
optimizer.zero_grad()。 -
前向传播:将输入数据传递给模型,计算预测值。
-
计算损失:使用损失函数(如
nn.CrossEntropyLoss)计算预测值与真实值之间的差异。 -
反向传播:调用
loss.backward()计算每个参数的梯度。 -
更新参数:调用
optimizer.step(),根据梯度和优化算法更新模型参数。
这是训练神经网络的核心流程,通常在一个 for 循环中完成。
# 假设 model, data_loader, optimizer, loss_fn 已定义
for epoch in range(num_epochs):
# 遍历数据加载器中的所有批次
for inputs, labels in data_loader:
# 1. 清除梯度:在每次反向传播前,将之前计算的梯度清零
optimizer.zero_grad()
# 2. 前向传播:将输入数据传递给模型,计算预测值
outputs = model(inputs)
# 3. 计算损失:使用损失函数(如 nn.MSELoss)计算预测值与真实值之间的差异
loss = loss_fn(outputs, labels)
# 4. 反向传播:计算每个参数对损失的梯度
loss.backward()
# 5. 更新参数:根据梯度和优化算法更新模型参数
optimizer.step()
6. 模型保存与加载
为了在训练后使用或继续训练模型,保存和加载参数是必不可少的。
-
保存:推荐只保存模型的参数字典(
state_dict),因为它更轻量且灵活。# 只保存模型的参数 torch.save(model.state_dict(), 'model_params.pth') -
加载:先实例化模型,再加载参数。
# 实例化一个新的模型 new_model = SimpleModel() # 加载已保存的参数 new_model.load_state_dict(torch.load('model_params.pth'))
更多推荐
所有评论(0)