Quickstart的网址:点击进入
以下是内容笔记和相关翻译:
快速开始
本节介绍了机器学习中常见任务的API。请参阅各部分中的链接,以深入了解更多内容。
数据处理
PyTorch提供了两个基本工具来处理数据:torch.utils.data.DataLoader和torch.utils.data.Dataset。Dataset用于存储样本及其对应的标签,而DataLoader则将其包装成可迭代对象。
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor
PyTorch提供了面向特定领域的库,例如TorchText、TorchVision和TorchAudio,其中包括很多数据集。在本教程中,我们将使用TorchVision数据集。
torchvision.datasets模块包含许多真实世界视觉数据集,例如CIFAR、COCO(完整列表在此)。在本教程中,我们将使用FashionMNIST数据集。每个TorchVision数据集都包含两个参数:transform和target_transform,分别用于修改样本和标签。
# Download training data from open datasets.
training_data = datasets.FashionMNIST(
root="data", #指定数据集的本地保存路径为"data"文件夹。
train=True, #表示下载训练数据集。
download=True, #表示从网络上下载数据集
transform=ToTensor(), #将每个图像样本转换为PyTorch张量=
)
# Download test data from open datasets.
test_data = datasets.FashionMNIST(
root="data",
train=False, #与训练数据集不同的是,这里的train参数设置为False,表示下载测试数据集。
download=True,
transform=ToTensor(),
)
在下载完数据后,training_data和test_data变量中存储的是FashionMNIST类型的数据集对象,其中包含图像数据(转换为PyTorch张量)和对应的标签。这样就可以将数据用于后续的模型训练和测试中。
我们将Dataset作为参数传递给DataLoader。DataLoader通过对数据集进行包装,创建了一个可迭代对象,支持自动批处理、采样、洗牌和多进程数据加载。在这里,我们定义了批处理大小为64,也就是说,迭代器中的每个元素将返回一个包含64个特征和标签的批次。
batch_size = 64
# Create data loaders.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
for X, y in test_dataloader:
print(f"Shape of X [N, C, H, W]: {X.shape}")
print(f"Shape of y: {y.shape} {y.dtype}")
break
创建模型
为了在PyTorch中定义一个神经网络,我们创建一个继承自nn.Module的类。我们在_ _init_ _函数中定义网络的层,并在forward函数中指定数据如何通过网络传递。为了加速神经网络的操作,我们可以将其移动到GPU或者MPS(如果可用)上执行。
# Get cpu, gpu or mps device for training.
device = (
"cuda"
if torch.cuda.is_available() # 如果GPU可用,则使用GPU
else "mps"
if torch.backends.mps.is_available() # 如果MPS可用,则使用MPS
else "cpu" # 否则使用CPU
)
print(f"Using {device} device") # 打印使用的设备
# Define model
class NeuralNetwork(nn.Module): # 创建一个继承自nn.Module的神经网络类
def __init__(self): # 实现神经网络的构造函数
super().__init__() # 继承父类的构造函数
self.flatten = nn.Flatten() # 创建一个Flatten层,用于将图像展开成一维向量
self.linear_relu_stack = nn.Sequential( # 创建一个nn.Sequential对象,用于包装一组线性层、ReLU激活函数层和Dropout层
nn.Linear(28*28, 512), # 输入层(28*28维)到隐层(512维)的线性变换层
nn.ReLU(), # 隐层的激活函数,使用ReLU
nn.Linear(512, 512), # 隐层到输出层的线性变换层
nn.ReLU(), # 输出层的激活函数,使用ReLU
nn.Linear(512, 10) # 最后一层是输出层,有10个输出单元,对应10个数字的类别
)
def forward(self, x): # 实现神经网络的正向传播函数,即前向计算过程
x = self.flatten(x) # 将输入张量展开成一维张量
logits = self.linear_relu_stack(x) # 将展开后的向量经过神经网络的所有层,得到最终的输出向量
return logits # 返回输出向量
# Move model to the selected device
model = NeuralNetwork().to(device) # 创建一个神经网络的实例,并将其移动到指定的设备上执行
print(model) # 打印神经网络的结构信息
在代码中,NeuralNetwork类继承自nn.Module,通过重写init方法和forward方法定义了一个简单的神经网络模型。这个模型包含了一个flatten层用于将输入图片展平成一维向量,以及一个由三个线性层和ReLU激活函数组成的linear_relu_stack序列层。这个神经网络模型可以通过调用to(device)方法将模型移动到指定的设备上执行。
优化模型参数
为了训练一个模型,我们需要一个损失函数和一个优化器。
loss_fn = nn.CrossEntropyLoss() #这行代码创建了一个交叉熵(CrossEntropy)损失函数的实例,用于度量模型输出与真实标签之间的差异。交叉熵损失函数通常用于多分类问题的训练,它可以帮助我们评估模型在给定输入上的预测的准确性。
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) #这行代码创建了一个随机梯度下降(Stochastic Gradient Descent,SGD)优化器的实例。model.parameters()表示获取模型中所有需要优化的参数,然后将其传递给优化器。lr=1e-3表示学习率的设置,即优化器在每次更新参数时应该乘以的比例因子。学习率决定了参数更新的速度和方向。
在单个训练循环中,模型对训练数据集进行预测(按批次输入),然后通过反向传播将预测误差反馈给模型的参数以进行调整。
def train(dataloader, model, loss_fn, optimizer): #这段代码是一个训练函数,用于在给定数据集、模型、损失函数和优化器的情况下执行训练过程
size = len(dataloader.dataset) # 获取数据集的大小,用来计算进度
model.train() # 将模型设置为训练模式
for batch, (X, y) in enumerate(dataloader): # 遍历数据集的批次,其中batch是当前批次的索引,X是输入数据,y是对应的真实标签。
X, y = X.to(device), y.to(device) # 将数据和标签转移到设备上(如GPU)
# Compute prediction error
pred = model(X) # 输入数据进行前向传播,得到预测结果
loss = loss_fn(pred, y) # 计算预测结果与真实标签之间的损失
# Backpropagation
loss.backward() # 反向传播,计算损失相对于模型参数的梯度
optimizer.step() # 根据梯度更新模型参数
optimizer.zero_grad() # 清零梯度,为下一次迭代做准备
if batch % 100 == 0: # 每经过100个批次输出一次训练进度和损失
loss, current = loss.item(), (batch + 1) * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
我们还要对模型在测试数据集上的性能进行评估,以确保模型的学习效果。
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset) #这行代码获取了测试数据集的大小,用于计算评估指标时的归一化。
num_batches = len(dataloader) #这行代码获取了测试数据集的批次数量,用来计算平均测试损失。
model.eval() # 这行代码将模型设置为评估模式,以便在评估过程中禁用特定的训练行为(如Dropout)。
test_loss, correct = 0, 0 #这行代码初始化测试损失和正确预测数为0。
with torch.no_grad(): # 这个上下文管理器指定在评估过程中不需要计算梯度,以节省内存和计算资源。
for X, y in dataloader: # 遍历测试数据集
X, y = X.to(device), y.to(device) # 将数据和标签转移到设备上(如GPU)
pred = model(X) # 输入数据进行前向传播,得到预测结果
test_loss += loss_fn(pred, y).item() # 计算预测结果与真实标签之间的损失
correct += (pred.argmax(1) == y).type(torch.float).sum().item() # 统计正确预测的数量,通过将预测结果与真实标签比较,并将结果转换为浮点型后求和。
test_loss /= num_batches # 计算预测结果与真实标签之间的损失,将测试损失除以批次数量。
correct /= size # 计算正确预测的比例,将正确预测的数量除以测试数据集的大小。
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n") #这行代码将测试过程中的评估指标进行打印输出,包括准确率(以百分数形式显示)和平均测试损失。
训练过程进行了多个迭代(epochs)。在每个epoch中,模型学习参数以做出更好的预测。我们在每个epoch打印模型的准确率和损失;我们希望看到准确率增加,损失减少。
epochs = 5 #设置了总共的训练迭代次数为5。
for t in range(epochs): #使用range(epochs)创建了一个从0到(epochs-1)的迭代器,用于遍历每个epoch。
print(f"Epoch {t+1}\n-------------------------------") #这行代码打印当前epoch的编号,并打印分隔线。
train(train_dataloader, model, loss_fn, optimizer) #这行代码调用了train函数来进行模型的训练,使用了训练数据集train_dataloader、模型model、损失函数loss_fn和优化器optimizer。
test(test_dataloader, model, loss_fn) # 这行代码调用了test函数来对模型进行评估,使用了测试数据集test_dataloader、模型model和损失函数loss_fn。
print("Done!") #所有的epochs迭代结束后,打印"Done!"表示训练过程完成。
保存模型
torch.save(model.state_dict(), "model.pth")
print("Saved PyTorch Model State to model.pth")
加载模型
model = NeuralNetwork().to(device)
model.load_state_dict(torch.load("model.pth"))
classes = [
"T-shirt/top",
"Trouser",
"Pullover",
"Dress",
"Coat",
"Sandal",
"Shirt",
"Sneaker",
"Bag",
"Ankle boot",
] #这段代码定义了一个包含了10个类别标签的列表,用于将模型的预测结果转换为具体的类别。
model.eval() #这行代码将模型设置为评估模式。在评估模式下,模型的参数不会被更新,并且会更节省内存。
x, y = test_data[0][0], test_data[0][1] #这行代码从测试数据集中获取第一个样本的输入特征(x)和对应的真实标签(y)。
with torch.no_grad(): #语句块内的代码将在不进行梯度计算的情况下运行。
x = x.to(device)
pred = model(x) #这行代码将输入特征传递给模型进行预测,得到预测结果。
predicted, actual = classes[pred[0].argmax(0)], classes[y]#这行代码将预测结果转换为具体的类别标签。
print(f'Predicted: "{predicted}", Actual: "{actual}"') #这行代码打印预测的类别和真实的类别。使用了字符串插值(f-string)将类别信息放入打印的字符串中。