PyTorch作为当前最受欢迎的深度学习框架之一,其最大的特点就是采用了动态计算图(Dynamic Computational Graph)机制。与静态图框架相比,PyTorch允许我们在代码执行过程中动态构建和修改计算图,这种特性为研究和开发带来了极大的灵活性。
动态图的工作机制可以类比为"即兴演讲"与"背诵演讲稿"的区别。在PyTorch中,计算图的构建是随着代码执行实时发生的,就像即兴演讲者可以根据现场反应随时调整内容;而静态图框架则需要预先定义完整的计算流程,类似于必须严格遵循预先写好的演讲稿。
这种设计带来的直接好处体现在以下几个方面:
提示:动态图特别适合需要频繁修改模型结构的场景,如学术研究和模型调优。但在生产部署时,通常需要将动态图转换为静态图以获得更好的性能。
PyTorch中的Tensor是其核心数据结构,可以简单理解为Numpy数组的加强版,但具有GPU加速和自动求导能力。创建Tensor的几种典型方式:
python复制import torch
# 从Python列表创建
data_tensor = torch.tensor([[1, 2], [3, 4]])
# 创建特定形状的全0张量
zeros_tensor = torch.zeros(3, 4)
# 随机初始化张量
rand_tensor = torch.rand(2, 3)
Tensor支持丰富的数学运算,包括:
注意:PyTorch中的Tensor运算会尽量利用GPU加速,但需要显式将Tensor移动到GPU设备:
python复制if torch.cuda.is_available(): tensor = tensor.to('cuda')
PyTorch的自动微分系统是其深度学习能力的核心。每个Tensor都有一个requires_grad属性,当设置为True时,PyTorch会跟踪所有对该Tensor的操作,构建计算图用于反向传播。
python复制x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward() # 自动计算梯度
print(x.grad) # 输出dy/dx的值
在实际训练中,我们通常会在每个batch处理完成后调用zero_grad()清除累积的梯度:
python复制optimizer.zero_grad() # 清除历史梯度
loss.backward() # 反向传播计算梯度
optimizer.step() # 更新参数
PyTorch通过nn.Module类提供了构建神经网络的标准化方式。自定义网络通常继承nn.Module并实现两个关键方法:
python复制class SimpleNet(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 128) # 全连接层
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = torch.relu(self.fc1(x))
return self.fc2(x)
PyTorch提供了丰富的预定义层,包括:
PyTorch通过Dataset和DataLoader抽象数据加载过程。自定义数据集需要实现__len__和__getitem__方法:
python复制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(train_data, train_labels)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
PyTorch还提供了一些内置数据集(如torchvision.datasets),可以直接使用:
python复制from torchvision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
train_set = datasets.MNIST('data/', download=True, train=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=64, shuffle=True)
一个典型的训练循环包含以下步骤:
python复制model = SimpleNet()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(10):
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
在实际工程中,我们通常还会:
PyTorch提供了多种模型保存方式:
python复制# 保存整个模型
torch.save(model, 'model.pth')
# 仅保存模型参数(推荐方式)
torch.save(model.state_dict(), 'model_params.pth')
# 加载模型
model = torch.load('model.pth') # 方式1
model = SimpleNet() # 方式2
model.load_state_dict(torch.load('model_params.pth'))
提示:在生产环境中,通常会将PyTorch模型转换为TorchScript格式,以获得更好的部署性能:
python复制traced_model = torch.jit.trace(model, example_input) traced_model.save('traced_model.pt')
PyTorch允许我们通过继承torch.autograd.Function来创建自定义的自动微分操作:
python复制class MyReLU(torch.autograd.Function):
@staticmethod
def forward(ctx, input):
ctx.save_for_backward(input)
return input.clamp(min=0)
@staticmethod
def backward(ctx, grad_output):
input, = ctx.saved_tensors
grad_input = grad_output.clone()
grad_input[input < 0] = 0
return grad_input
# 使用自定义函数
relu = MyReLU.apply
PyTorch支持自动混合精度训练,可以显著减少显存占用并加速训练:
python复制from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for inputs, labels in train_loader:
optimizer.zero_grad()
with autocast():
outputs = model(inputs)
loss = criterion(outputs, labels)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
PyTorch提供了多种分布式训练方式,最简单的DataParallel方式:
python复制model = nn.DataParallel(model) # 单机多卡
对于大规模分布式训练,可以使用DistributedDataParallel:
python复制import torch.distributed as dist
dist.init_process_group(backend='nccl')
model = nn.parallel.DistributedDataParallel(model)
现象:模型无法学习或损失值变为NaN
解决方案:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)nn.init.xavier_uniform_(layer.weight)调试步骤:
torch.cuda.empty_cache()释放缓存python复制accumulation_steps = 4
for i, (inputs, labels) in enumerate(train_loader):
outputs = model(inputs)
loss = criterion(outputs, labels)
loss = loss / accumulation_steps
loss.backward()
if (i+1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
检查清单:
PyTorch拥有丰富的生态系统,包括:
以TorchVision为例,加载预训练模型非常简单:
python复制from torchvision import models
resnet = models.resnet50(pretrained=True)
# 修改最后一层适应新任务
resnet.fc = nn.Linear(resnet.fc.in_features, num_classes)
在实际项目中,我通常会先使用预训练模型进行迁移学习,这可以显著提高模型性能并减少训练时间。特别是在数据量不足的情况下,迁移学习往往能带来意想不到的效果提升。