在本文中,我们将通过示例代码,深入探讨PyTorch的几个核心概念:自动微分、数据加载、自定义数据集、预训练模型的使用、模型保存与加载。同时了解关于pytorch的基础语法以及神经网络构建与训练流程。
一、环境设置
首先,确保安装了PyTorch和torchvision。如果未安装,可以通过以下命令进行安装:
pip install torch torchvision
二、基础语法
PyTorch是一个流行的开源机器学习库,用于计算机视觉和自然语言处理等应用。以下是PyTorch的一些基础语法,它们是开始使用PyTorch时需要了解的关键概念:
1. 导入PyTorch
import torch
2. 创建张量(Tensors)
张量是PyTorch中的基本数据结构。
# 创建一个标量(0维张量)
scalar = torch.tensor(3.5)
# 创建一个向量(1维张量)
vector = torch.tensor([1.0, 2.0, 3.0])
# 创建一个矩阵(2维张量)
matrix = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
# 创建一个3维张量
tensor = torch.tensor([[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]])
3. 张量操作
# 形状
print(tensor.shape)
# 逐元素加法
add_result = tensor + vector
# 矩阵乘法
product = torch.matmul(matrix, matrix)
# 转置
transposed = matrix.t()
# 索引
element = matrix[0, 1] # 获取第一个矩阵的第二列元素
4. 自动微分(Autograd)
PyTorch使用自动微分机制来计算梯度。
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x ** 2
y.backward() # 计算梯度
print(x.grad) # 输出梯度
5. 定义模型
使用torch.nn
模块可以定义神经网络模型。
import torch.nn as nn
model = nn.Sequential(
nn.Linear(2, 3),
nn.ReLU(),
nn.Linear(3, 1)
)
6. 损失函数
使用torch.nn.functional
模块可以定义损失函数。
import torch.nn.functional as F
loss_fn = nn.MSELoss()
output = model(input_tensor)
loss = loss_fn(output, target_tensor)
7. 优化器
优化器用于更新模型的权重。
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
8. 训练循环
训练模型通常涉及前向传播、损失计算、反向传播和参数更新。
for epoch in range(num_epochs):
optimizer.zero_grad() # 清除之前的梯度
output = model(input_tensor)
loss = loss_fn(output, target_tensor)
loss.backward() # 反向传播
optimizer.step() # 更新参数
9. 数据加载
使用DataLoader
可以方便地加载数据集。
from torch.utils.data import DataLoader, TensorDataset
data = TensorDataset(tensor_a, tensor_b)
data_loader = DataLoader(data, batch_size=32, shuffle=True)
10. 保存和加载模型
保存和加载模型通常使用torch.save
和torch.load
。
# 保存整个模型
torch.save(model.state_dict(), 'model.pth')
# 加载模型参数
model.load_state_dict(torch.load('model.pth'))
这些基础语法为使用PyTorch提供了一个起点。随着对PyTorch的进一步学习,你将能够构建更复杂的模型和训练流程。
三、一般深度学习模型的构建和训练步骤
使用PyTorch进行深度学习模型的训练和测试通常遵循以下步骤:
1. 定义模型
首先,你需要定义你的模型。这通常涉及创建一个继承自nn.Module
的类,并在其中初始化层和定义前向传播逻辑。
import torch.nn as nn
class MyModel(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
self.fc1 = nn.Linear(in_features, hidden_features)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_features, out_features)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
model = MyModel()
2. 定义损失函数和优化器
选择一个损失函数来评估模型的预测与真实标签之间的差异,并选择一个优化器来更新模型的权重。
loss_fn = nn.CrossEntropyLoss() # 用于多分类问题
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 使用Adam优化器
3. 数据加载
使用DataLoader
来加载训练数据和测试数据,它可以帮助进行批量加载、打乱数据等。
from torch.utils.data import DataLoader, TensorDataset
train_data = TensorDataset(torch.randn(batch_size, features), torch.randint(0, classes, (batch_size,)))
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_data = TensorDataset(torch.randn(batch_size, features), torch.randint(0, classes, (batch_size,)))
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False)
4. 训练模型
训练模型通常涉及多个训练周期(epochs),在每个epoch中遍历整个数据集,执行前向传播、损失计算、反向传播和参数更新。
num_epochs = 10
for epoch in range(num_epochs):
for inputs, labels in train_loader:
optimizer.zero_grad() # 清除之前的梯度
outputs = model(inputs) # 前向传播
loss = loss_fn(outputs, labels) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新参数
print(f'Epoch {epoch+1}, Loss: {loss.item()}')
5. 测试模型
在测试阶段,你需要评估模型在测试集上的性能。测试集应该从未在训练过程中使用过。
model.eval() # 将模型设置为评估模式
with torch.no_grad(): # 关闭梯度计算
correct = 0
total = 0
for inputs, labels in test_loader:
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = 100 * correct / total
print(f'Accuracy: {accuracy}%')
6. 保存和加载模型
训练完成后,你可以保存模型的参数,并在以后重新加载它们。
# 保存模型参数
torch.save(model.state_dict(), 'model_parameters.pth')
# 加载模型参数
model.load_state_dict(torch.load('model_parameters.pth'))
7. 微调和迁移学习
如果你需要微调模型或使用迁移学习,你可以冻结基础层的权重并只训练顶层。
for param in model.base_model.parameters():
param.requires_grad = False
# 替换或添加新的层进行微调
model.new_fc_layer = nn.Linear(modified_features, out_features)
这些步骤提供了一个从训练到测试的完整流程,涵盖了使用PyTorch进行深度学习的基本方面。根据具体的应用和数据集,可能需要对这些步骤进行调整和优化。
四、实战示例
1. 自动微分基础
自动微分是深度学习框架的基石,它允许我们自动计算导数,是训练神经网络的关键。PyTorch中的autograd
功能正是为此设计。
1.1 基础示例1
在第一个示例中,我们创建了三个需要梯度的张量x
、w
和b
,并构建了一个简单的计算图y = w * x + b
。通过调用.backward()
方法,我们计算了y
相对于这些张量的梯度,并打印出来。
import torch
import torch.nn as nn
x = torch.tensor(1., requires_grad=True)
w = torch.tensor(2., requires_grad=True)
b = torch.tensor(3., requires_grad=True)
y = w * x + b
y.backward()
print(x.grad) # x.grad = 2
print(w.grad) # w.grad = 1
print(b.grad) # b.grad = 1
1.2 基础示例2
第二个示例展示了如何在一个全连接层中使用自动微分。我们定义了一个损失函数和优化器,进行了前向传播、损失计算、反向传播,并执行了一个梯度下降步骤来更新模型权重。
x = torch.randn(10, 3)
y = torch.randn(10, 2)
linear = nn.Linear(3, 2)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(linear.parameters(), lr=0.01)
pred = linear(x)
loss = criterion(pred, y)
loss.backward()
optimizer.step()
print('loss after 1 step optimization: ', loss.item())
2. NumPy与PyTorch的互操作性
PyTorch与NumPy之间的无缝互操作性使得数据在两者之间的转换变得非常容易。
import numpy as np
x = np.array([[1, 2], [3, 4]])
y = torch.from_numpy(x)
z = y.numpy()
3. 数据加载与处理
3.1 数据加载
PyTorch提供了强大的数据加载工具,可以轻松地下载、加载和预处理数据。
import torchvision
import torchvision.transforms as transforms
train_dataset = torchvision.datasets.CIFAR10(root='../../data/',
train=True,
transform=transforms.ToTensor(),
download=True)
image, label = train_dataset[0]
print(image.size())
print(label)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=64,
shuffle=True)
3.2 自定义数据集
为了使用自己的数据集,我们需要定义一个继承自torch.utils.data.Dataset
的类。
class CustomDataset(torch.utils.data.Dataset):
def __init__(self):
# TODO: Initialize file paths or a list of file names.
pass
def __getitem__(self, index):
# TODO: Read one data from file and preprocess.
pass
def __len__(self):
return 0
custom_dataset = CustomDataset()
train_loader = torch.utils.data.DataLoader(dataset=custom_dataset,
batch_size=64,
shuffle=True)
4. 预训练模型
预训练模型是迁移学习的基础,允许我们将在大型数据集上学到的特征应用到新的任务上。
resnet = torchvision.models.resnet18(pretrained=True)
for param in resnet.parameters():
param.requires_grad = False
resnet.fc = nn.Linear(resnet.fc.in_features, 100)
images = torch.randn(64, 3, 224, 224)
outputs = resnet(images)
print(outputs.size())
5. 模型保存与加载
在训练过程中,保存和加载模型是一个常见需求。
torch.save(resnet, 'model.ckpt')
model = torch.load('model.ckpt')
# Or save and load only the model parameters
torch.save(resnet.state_dict(), 'params.ckpt')
resnet.load_state_dict(torch.load('params.ckpt'))
6.完整代码
import torch
import torchvision
import torch.nn as nn
import numpy as np
import torchvision.transforms as transforms
# ================================================================== #
# Table of Contents #
# ================================================================== #
# 1. Basic autograd example 1 (Line 25 to 39)
# 2. Basic autograd example 2 (Line 46 to 83)
# 3. Loading data from numpy (Line 90 to 97)
# 4. Input pipline (Line 104 to 129)
# 5. Input pipline for custom dataset (Line 136 to 156)
# 6. Pretrained model (Line 163 to 176)
# 7. Save and load model (Line 183 to 189)
# ================================================================== #
# 1. Basic autograd example 1 #
# ================================================================== #
# Create tensors.
x = torch.tensor(1., requires_grad=True)
w = torch.tensor(2., requires_grad=True)
b = torch.tensor(3., requires_grad=True)
# Build a computational graph.
y = w * x + b # y = 2 * x + 3
# Compute gradients.
y.backward()
# Print out the gradients.
print(x.grad) # x.grad = 2
print(w.grad) # w.grad = 1
print(b.grad) # b.grad = 1
# ================================================================== #
# 2. Basic autograd example 2 #
# ================================================================== #
# Create tensors of shape (10, 3) and (10, 2).
x = torch.randn(10, 3)
y = torch.randn(10, 2)
# Build a fully connected layer.
linear = nn.Linear(3, 2)
print ('w: ', linear.weight)
print ('b: ', linear.bias)
# Build loss function and optimizer.
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(linear.parameters(), lr=0.01)
# Forward pass.
pred = linear(x)
# Compute loss.
loss = criterion(pred, y)
print('loss: ', loss.item())
# Backward pass.
loss.backward()
# Print out the gradients.
print ('dL/dw: ', linear.weight.grad)
print ('dL/db: ', linear.bias.grad)
# 1-step gradient descent.
optimizer.step()
# You can also perform gradient descent at the low level.
# linear.weight.data.sub_(0.01 * linear.weight.grad.data)
# linear.bias.data.sub_(0.01 * linear.bias.grad.data)
# Print out the loss after 1-step gradient descent.
pred = linear(x)
loss = criterion(pred, y)
print('loss after 1 step optimization: ', loss.item())
# ================================================================== #
# 3. Loading data from numpy #
# ================================================================== #
# Create a numpy array.
x = np.array([[1, 2], [3, 4]])
# Convert the numpy array to a torch tensor.
y = torch.from_numpy(x)
# Convert the torch tensor to a numpy array.
z = y.numpy()
# ================================================================== #
# 4. Input pipeline #
# ================================================================== #
# Download and construct CIFAR-10 dataset.
train_dataset = torchvision.datasets.CIFAR10(root='../../data/',
train=True,
transform=transforms.ToTensor(),
download=True)
# Fetch one data pair (read data from disk).
image, label = train_dataset[0]
print (image.size())
print (label)
# Data loader (this provides queues and threads in a very simple way).
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=64,
shuffle=True)
# When iteration starts, queue and thread start to load data from files.
data_iter = iter(train_loader)
# Mini-batch images and labels.
images, labels = data_iter.next()
# Actual usage of the data loader is as below.
for images, labels in train_loader:
# Training code should be written here.
pass
# ================================================================== #
# 5. Input pipeline for custom dataset #
# ================================================================== #
# You should build your custom dataset as below.
class CustomDataset(torch.utils.data.Dataset):
def __init__(self):
# TODO
# 1. Initialize file paths or a list of file names.
pass
def __getitem__(self, index):
# TODO
# 1. Read one data from file (e.g. using numpy.fromfile, PIL.Image.open).
# 2. Preprocess the data (e.g. torchvision.Transform).
# 3. Return a data pair (e.g. image and label).
pass
def __len__(self):
# You should change 0 to the total size of your dataset.
return 0
# You can then use the prebuilt data loader.
custom_dataset = CustomDataset()
train_loader = torch.utils.data.DataLoader(dataset=custom_dataset,
batch_size=64,
shuffle=True)
# ================================================================== #
# 6. Pretrained model #
# ================================================================== #
# Download and load the pretrained ResNet-18.
resnet = torchvision.models.resnet18(pretrained=True)
# If you want to finetune only the top layer of the model, set as below.
for param in resnet.parameters():
param.requires_grad = False
# Replace the top layer for finetuning.
resnet.fc = nn.Linear(resnet.fc.in_features, 100) # 100 is an example.
# Forward pass.
images = torch.randn(64, 3, 224, 224)
outputs = resnet(images)
print (outputs.size()) # (64, 100)
# ================================================================== #
# 7. Save and load the model #
# ================================================================== #
# Save and load the entire model.
torch.save(resnet, 'model.ckpt')
model = torch.load('model.ckpt')
# Save and load only the model parameters (recommended).
torch.save(resnet.state_dict(), 'params.ckpt')
resnet.load_state_dict(torch.load('params.ckpt'))
结论
通过这段示例代码,我们可以看到PyTorch在自动微分、数据处理、模型加载与保存方面的强大能力。这些功能不仅使得PyTorch成为一个灵活且功能丰富的深度学习工具,也极大地简化了机器学习研究和开发的工作流程。随着技术的不断发展,PyTorch将继续作为深度学习领域的一个关键驱动力。