AI_深度学习-1.PyTorch基础
·
标签: #Python #PyTorch #深度学习 #神经网络
学习周期:3 天 | 核心目标:掌握 PyTorch 张量操作、自动微分、构建神经网络、训练循环及模型保存
6.1 PyTorch 基础
PyTorch 是目前最主流、最易用、最适合科研与工业落地的深度学习框架,由 Facebook AI Research 开发。它提供动态计算图、GPU 加速、自动微分等功能,设计思想贴近 Python,易于调试。
安装与导入
# CPU 版本
pip install torch torchvision
# GPU 版本(需根据 CUDA 版本选择,参考 https://pytorch.org)
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
6.1.1 Tensor 对象
Tensor 是 PyTorch 的核心数据结构,类似于 NumPy 的 ndarray,但支持 GPU 加速和自动微分。
创建张量
# 从列表创建
t1 = torch.tensor([1, 2, 3])
t2 = torch.tensor([[1, 2], [3, 4]])
# 全零/全一/单位矩阵
zeros = torch.zeros(2, 3)
ones = torch.ones(2, 2)
eye = torch.eye(3)
# 随机初始化
rand = torch.rand(2, 3) # 均匀分布 [0,1)
randn = torch.randn(2, 3) # 标准正态分布
randint = torch.randint(0, 10, (2, 3))
# 从 NumPy 转换
np_arr = np.array([1, 2, 3])
t_from_np = torch.from_numpy(np_arr) # 共享内存
back_to_np = t_from_np.numpy()
张量属性
t = torch.randn(2, 3)
print("形状:", t.shape) # torch.Size([2, 3])
print("数据类型:", t.dtype) # torch.float32
print("设备:", t.device) # cpu
print("是否需要梯度:", t.requires_grad) # False
GPU 加速(关键)
if torch.cuda.is_available():
device = torch.device("cuda")
print("GPU 可用:", torch.cuda.get_device_name(0))
elif torch.backends.mps.is_available():
device = torch.device("mps")
print("mps 可用")
else:
device = torch.device("cpu")
# 将张量移到 GPU
t_cpu = torch.randn(2, 3)
t_gpu = t_cpu.to(device)
# 创建时直接指定设备
t_gpu2 = torch.randn(2, 3, device=device)
# 移回 CPU
t_back = t_gpu.cpu()
常用运算(与 NumPy 类似)
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
print(a + b) # 加法
print(a - b) # 减法
print(a * b) # 逐元素乘法
print(a @ b) # 矩阵乘法
print(a.T) # 转置
print(a.sum())
print(a.mean())
# 索引和切片
print(a[0, 1]) # 2
print(a[:, 1]) # [2, 4]
print(a[0, :]) # [1, 2]
# 改变形状
c = torch.arange(12)
print(c.view(3, 4)) # reshape
print(c.reshape(2, 6))
6.1.2 自动微分:torch.autograd
PyTorch 自动计算梯度,核心是 requires_grad 和 backward()。
基本用法
# 创建需要梯度的张量
x = torch.tensor([2.0], requires_grad=True)
y = x ** 2 + 3 * x + 1
# 反向传播
y.backward()
# 查看梯度 dy/dx = 2x+3 = 7
print(x.grad) # tensor([7.])
多变量示例
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x[0] ** 2 + x[1] ** 3
y.backward()
print(x.grad) # tensor([2., 12.])
停止梯度追踪
# 方法1:detach
x = torch.tensor([1.0], requires_grad=True)
y = x.detach() # 不再追踪梯度
# 方法2:with torch.no_grad()
with torch.no_grad():
y = x * 2 # 此操作不会记录梯度
6.1.3 构建神经网络:继承 nn.Module
所有模型必须继承 nn.Module 并实现 forward() 方法。
# 定义一个线性模型类,继承自 PyTorch 的 nn.Module 基类
class LinearModel(nn.Module):
# 构造函数,在创建类的实例时自动调用
# input_dim: 输入特征的维度(例如 10)
# output_dim: 输出特征的维度(例如 1)
def __init__(self, input_dim, output_dim):
# 调用父类 nn.Module 的构造函数,完成必要的初始化
super().__init__()
# 创建一个全连接层(线性层),输入大小为 input_dim,输出大小为 output_dim
# 该层会自动初始化权重和偏置
self.linear = nn.Linear(input_dim, output_dim)
# 定义前向传播函数,当把数据传入模型实例时会自动调用
# x: 输入张量,形状通常是 (batch_size, input_dim)
def forward(self, x):
# 将输入 x 传入线性层,计算输出并返回
# 输出形状为 (batch_size, output_dim)
return self.linear(x)
# 实例化线性模型:输入维度为 10,输出维度为 1
model = LinearModel(10, 1)
# 创建一个随机输入张量,形状为 (5, 10),表示 5 个样本,每个样本有 10 个特征
x = torch.randn(5, 10)
# 将输入 x 传入模型,执行前向传播,得到输出
output = model(x)
# 打印输出的形状,预期为 (5, 1),即 5 个样本,每个样本输出一个标量值
print(output.shape) # torch.Size([5, 1])
多层感知机(MLP)
# 定义一个多层感知机(MLP)类,继承自 PyTorch 的 nn.Module 基类
class MLP(nn.Module):
# 构造函数,在创建类的实例时自动调用
# input_dim: 输入特征的维度(例如 784,对应 MNIST 图像展平)
# hidden_dim: 隐藏层的神经元数量(例如 128)
# output_dim: 输出特征的维度(例如 10,对应 10 个分类)
def __init__(self, input_dim, hidden_dim, output_dim):
# 调用父类 nn.Module 的构造函数,完成必要的初始化
super().__init__()
# 第一个全连接层:输入 input_dim 维,输出 hidden_dim 维
self.fc1 = nn.Linear(input_dim, hidden_dim)
# 第二个全连接层:输入 hidden_dim 维,输出 hidden_dim 维
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
# 第三个全连接层:输入 hidden_dim 维,输出 output_dim 维
self.fc3 = nn.Linear(hidden_dim, output_dim)
# ReLU 激活函数,用于引入非线性(in-place 操作通常不写,这里单独定义以便多次复用)
self.relu = nn.ReLU()
# 定义前向传播函数,当把数据传入模型实例时会自动调用
# x: 输入张量,形状通常是 (batch_size, input_dim)
def forward(self, x):
# 第一层:线性变换后接 ReLU 激活
# 先调用 fc1(x) 得到线性输出,再通过 relu 激活
x = self.relu(self.fc1(x))
# 第二层:线性变换后接 ReLU 激活
x = self.relu(self.fc2(x))
# 第三层:仅线性变换,不再使用激活(对于回归任务或分类的 logits 常用)
x = self.fc3(x)
# 返回最终输出,形状为 (batch_size, output_dim)
return x
6.1.4 常用层
| 层 | 用途 | 示例 |
|---|---|---|
nn.Linear(in, out) |
全连接层 | nn.Linear(256, 128) |
nn.Conv2d(in_ch, out_ch, kernel) |
二维卷积 | nn.Conv2d(3, 64, 3, padding=1) |
nn.MaxPool2d(kernel) |
最大池化 | nn.MaxPool2d(2) |
nn.RNN(input_size, hidden_size) |
简单循环神经网络 | nn.RNN(100, 256) |
nn.LSTM(input_size, hidden_size) |
长短期记忆网络 | nn.LSTM(100, 256, num_layers=2) |
nn.Dropout(p) |
Dropout 正则化 | nn.Dropout(0.5) |
nn.BatchNorm1d/2d(num_features) |
批归一化 | nn.BatchNorm2d(64) |
nn.Embedding(num_embeddings, emb_dim) |
词嵌入 | nn.Embedding(10000, 256) |
激活函数(通常用 F 或 nn)
# 使用 torch.nn.functional(推荐)
x = torch.randn(10)
out = F.relu(x)
out = F.sigmoid(x)
out = F.tanh(x)
out = F.softmax(x, dim=-1)
# 使用 nn 模块(作为层)
layer = nn.ReLU()
out = layer(x)
6.1.5 损失函数
| 损失函数 | 适用场景 |
|---|---|
nn.MSELoss() |
回归任务(均方误差) |
nn.L1Loss() |
回归(平均绝对误差) |
nn.CrossEntropyLoss() |
多分类(含 Softmax) |
nn.BCELoss() |
二分类(需先 Sigmoid) |
nn.BCEWithLogitsLoss() |
二分类(直接输入 logits,更稳定) |
nn.NLLLoss() |
配合 F.log_softmax 使用 |
# 回归
criterion = nn.MSELoss()
pred = torch.randn(3, 1)
target = torch.randn(3, 1)
loss = criterion(pred, target)
# 多分类(最常用)
criterion = nn.CrossEntropyLoss()
pred = torch.randn(3, 5) # logits
target = torch.tensor([0, 2, 1])
loss = criterion(pred, target)
# 二分类(推荐 BCEWithLogitsLoss)
criterion = nn.BCEWithLogitsLoss()
pred = torch.randn(3, 1) # logits
target = torch.tensor([1., 0., 1.]).view(-1, 1)
loss = criterion(pred, target)
6.1.6 优化器
| 优化器 | 特点 |
|---|---|
optim.SGD |
随机梯度下降,可带动量 |
optim.Adam |
自适应学习率,最常用 |
optim.RMSprop |
适合 RNN |
optim.AdamW |
Adam 解耦权重衰减 |
# 创建优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-5)
# 训练时更新参数
optimizer.zero_grad() # 清空梯度
loss.backward() # 反向传播
optimizer.step() # 更新参数
6.1.7 训练循环(标准模板)
线性回归完整示例
# 0. 导入必要的库(原代码未写出,但实际需要)
import torch
import torch.nn as nn
import torch.optim as optim
# ========== 1. 生成数据 ==========
# 生成从0到10的100个等间距点,并转换为列向量(形状为 [100, 1])
x = torch.linspace(0, 10, 100).reshape(-1, 1)
# 生成目标值 y = 2*x + 1,并加上均值为0、标准差为0.5的高斯噪声
# torch.randn(x.size()) 生成与 x 形状相同的随机噪声
y = 2 * x + 1 + torch.randn(x.size()) * 0.5
# ========== 2. 定义模型 ==========
# 创建一个线性回归模型:输入维度1,输出维度1(即 y = w*x + b)
model = nn.Linear(1, 1)
# 定义损失函数:均方误差损失(MSE),用于衡量预测值与真实值的差异
criterion = nn.MSELoss()
# 定义优化器:随机梯度下降(SGD),用于更新模型参数(权重和偏置)
# 学习率 lr = 0.01
optimizer = optim.SGD(model.parameters(), lr=0.01)
# ========== 3. 训练循环 ==========
epochs = 500 # 训练迭代次数
for epoch in range(epochs):
# 清除之前的梯度(因为PyTorch默认会累积梯度)
optimizer.zero_grad()
# 前向传播:输入 x,得到预测值 pred
pred = model(x)
# 计算损失:预测值与真实值 y 之间的 MSE
loss = criterion(pred, y)
# 反向传播:自动计算损失相对于所有可训练参数的梯度
loss.backward()
# 更新参数:根据梯度执行一步SGD优化
optimizer.step()
# 每100个epoch打印一次当前损失值
if (epoch + 1) % 100 == 0:
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
# 训练结束后,打印学习到的权重和偏置(保留两位小数)
print(f"权重: {model.weight.item():.2f}, 偏置: {model.bias.item():.2f}")
分类示例(MNIST 手写数字)
from torchvision import datasets, transforms
# 1. 加载数据
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_dataset = datasets.MNIST('.', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('.', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
# 2. 定义模型
class SimpleCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3, 1)
self.conv2 = nn.Conv2d(32, 64, 3, 1)
self.fc1 = nn.Linear(64*12*12, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2)
x = torch.flatten(x, 1)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
model = SimpleCNN()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 3. 训练
epochs = 5
for epoch in range(epochs):
model.train()
total_loss = 0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
output = model(images)
loss = criterion(output, labels)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.4f}")
# 4. 测试
model.eval()
correct = 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
output = model(images)
pred = output.argmax(dim=1)
correct += (pred == labels).sum().item()
print(f"Test accuracy: {100 * correct / len(test_dataset):.2f}%")
6.1.8 模型保存与加载
# 保存整个模型(不推荐,可能跨版本不兼容)
torch.save(model, 'model.pth')
# 保存 state_dict(推荐,仅参数)
torch.save(model.state_dict(), 'model_weights.pth')
# 加载
model = SimpleCNN()
model.load_state_dict(torch.load('model_weights.pth'))
model.eval() # 重要:切换到评估模式
# 保存检查点(包括优化器状态,用于恢复训练)
checkpoint = {
'epoch': 10,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss
}
torch.save(checkpoint, 'checkpoint.pth')
# 加载检查点
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
start_epoch = checkpoint['epoch']
🎯 完整实战模板(鸢尾花分类)
# 导入 PyTorch 核心库
import torch
# 导入神经网络模块,用于定义层和模型结构
import torch.nn as nn
# 导入优化器模块,用于参数更新算法
import torch.optim as optim
# 从 sklearn 导入鸢尾花数据集
from sklearn.datasets import load_iris
# 导入数据集划分工具
from sklearn.model_selection import train_test_split
# 导入标准化工具,用于特征缩放
from sklearn.preprocessing import StandardScaler
# ========== 1. 数据准备 ==========
# 加载鸢尾花数据集(特征+标签)
iris = load_iris()
# X 为特征数据(150个样本,4个特征),y 为标签(0,1,2 三类)
X, y = iris.data, iris.target
# 划分训练集和测试集:测试集占20%,随机种子42保证可复现
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建标准化器(将特征转换为均值为0、标准差为1的正态分布形式)
scaler = StandardScaler()
# 对训练集进行拟合和转换(计算均值和标准差,然后标准化)
X_train = scaler.fit_transform(X_train)
# 对测试集仅进行转换(使用训练集的均值和标准差,避免数据泄露)
X_test = scaler.transform(X_test)
# 将训练集特征转换为 PyTorch 张量,数据类型为 float32
X_train = torch.tensor(X_train, dtype=torch.float32)
# 将训练集标签转换为长整型(分类问题要求标签为 long 类型)
y_train = torch.tensor(y_train, dtype=torch.long)
# ========== 2. 定义模型 ==========
# 定义一个继承自 nn.Module 的神经网络类
class IrisNet(nn.Module):
# 构造函数,初始化网络的层结构
def __init__(self):
# 调用父类 nn.Module 的构造函数
super().__init__()
# 全连接层1:输入4个特征,输出16个神经元
self.fc1 = nn.Linear(4, 16)
# 全连接层2:输入16个神经元,输出3个类别(对应鸢尾花的3种)
self.fc2 = nn.Linear(16, 3)
# ReLU 激活函数,引入非线性
self.relu = nn.ReLU()
# 定义前向传播过程
def forward(self, x):
# 输入 x 经过第一层线性变换,然后通过 ReLU 激活
x = self.relu(self.fc1(x))
# 输出经过第二层线性变换(得到3个原始分数,不经过 Softmax 因为 CrossEntropyLoss 内部会做)
x = self.fc2(x)
return x
# 实例化模型对象
model = IrisNet()
# ========== 3. 损失函数和优化器 ==========
# 交叉熵损失函数,适用于多分类问题(内部包含 Softmax 运算)
criterion = nn.CrossEntropyLoss()
# Adam 优化器,学习率为0.01,更新模型的所有可训练参数
optimizer = optim.Adam(model.parameters(), lr=0.01)
# ========== 4. 训练循环 ==========
# 总共训练 100 轮(epoch)
for epoch in range(100):
# 清除之前累积的梯度(避免梯度叠加)
optimizer.zero_grad()
# 前向传播:将训练集特征输入模型,得到预测输出(类别分数)
outputs = model(X_train)
# 计算损失:比较预测输出与真实标签
loss = criterion(outputs, y_train)
# 反向传播:自动计算损失对每个参数的梯度
loss.backward()
# 更新参数:根据梯度执行一步优化
optimizer.step()
# 每 20 轮打印一次当前损失值(保留4位小数)
if (epoch + 1) % 20 == 0:
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
# ========== 5. 保存模型 ==========
# 将训练好的模型参数(state_dict)保存到文件 "iris_model.pth" 中
torch.save(model.state_dict(), "iris_model.pth")
# 打印提示信息
print("训练完成,模型已保存")
📚 学习资料(Obsidian 可直接收藏)
-
书籍
《深度学习框架 PyTorch 入门与实践》(陈云)
🎯 学习建议(3 天计划)
- 第 1 天:掌握 Tensor 基本操作、GPU 加速、自动微分(
requires_grad、backward)。 - 第 2 天:学习
nn.Module构建网络、常用层、损失函数和优化器。 - 第 3 天:完整训练循环(线性回归 → MNIST/鸢尾花分类),理解前向传播、反向传播、参数更新,练习模型保存与加载。
✅ 核心要点总结
- Tensor = 支持 GPU 的数组,类似 NumPy。
- 自动微分:设置
requires_grad=True,调用backward()自动计算梯度。 - 构建网络:继承
nn.Module,实现forward(),使用nn.Linear、nn.Conv2d等层。 - 损失函数:回归用
MSELoss,多分类用CrossEntropyLoss,二分类用BCEWithLogitsLoss。 - 优化器:
optim.SGD、optim.Adam,训练时zero_grad()→backward()→step()。 - 训练循环:前向传播 → 计算 loss → 反向传播 → 更新参数。
- 模型保存:推荐保存
state_dict,加载后调用model.eval()。 - 一套模板走天下:所有深度学习训练流程高度一致。
练习题(自测)
- 创建两个随机 3×3 矩阵,计算它们的矩阵乘法,并将结果移到 GPU(如果有)。
- 手动实现一个线性回归(不使用
nn.Linear),用requires_grad自动求导优化参数。 - 构建一个 3 层全连接网络(输入 784,隐藏层 256→128,输出 10),使用 ReLU 激活,计算对随机输入的输出。
- 加载 CIFAR-10 数据集(
torchvision.datasets.CIFAR10),训练一个简单的 CNN 分类器,准确率达到 60% 以上。 - 在训练过程中保存最佳验证集准确率的模型,并在测试集上评估。
建议在 Jupyter Notebook 或本地 Python 环境中运行示例,尝试修改网络结构、学习率等超参数,观察对训练的影响。
更多推荐
所有评论(0)