一、数据的加载 1.加载互联网数据集 PyTorch 提供了两个数据原语:torch.utils.data.DataLoader 和 torch.utils.data.Dataset ,允许您使用预加载的数据集以及您自己的数据。
Dataset 存储样本及其对应的标签
DataLoader 将可迭代对象包装在 Dataset周围,以便于访问样本。
PyTorch 领域库提供了一些预加载的数据集,它们是 torch.utils.data.Dataset 的子类,并实现了特定于特定数据的函数。
以下是如何从 TorchVision 加载 Fashion-MNIST 数据集的示例
使用如下参数:
root
是存储训练/测试数据的路径,
train
指定训练或测试数据集,
download=True
如果 root
中没有数据,则从互联网下载数据。
transform
和 target_transform
指定特征和标签转换
代码演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from torch.utils.data import Datasetfrom torchvision import datasetsfrom torchvision.transforms import ToTensorimport matplotlib.pyplot as plttraining_data = datasets.FashionMNIST( root="data" , train=True , download=True , transform=ToTensor() ) test_data = datasets.FashionMNIST( root="data" , train=False , download=True , transform=ToTensor() )
2,为你的文件创建自定义数据集 自定义Dataset类必须实现三个函数:__ init__ 、__ len __ 和 __ getitem__。
代码实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import osimport pandas as pdfrom torchvision.io import read_imageclass CustomImageDataset (Dataset ): def __init__ (self, annotations_file, img_dir, transform=None , target_transform=None ): self.img_labels = pd.read_csv(annotations_file) self.img_dir = img_dir self.transform = transform self.target_transform = target_transform def __len__ (self ): return len (self.img_labels) def __getitem__ (self, idx ): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0 ]) image = read_image(img_path) label = self.img_labels.iloc[idx, 1 ] if self.transform: image = self.transform(image) if self.target_transform: label = self.target_transform(label) return image, label
(1)__ init__ :函数在实例化 Dataset 对象时运行一次。我们初始化包含图像的目录、注释文件和两个转换。
1 2 3 4 5 tshirt1.jpg, 0 tshirt2.jpg, 0 ...... ankleboot999.jpg, 9
(2)__ len __:函数返回数据集中样本的数量
(3)__ getitem __ 函数加载并返回给定索引 idx 处的数据集样本。根据索引,它识别磁盘上图像的位置,使用 read_image 将其转换为张量,从 self.img_labels 中的 csv 数据中检索相应的标签,对它们调用转换函数(如果适用),并以元组的形式返回张量图像和相应的标签。
3.使用DataLoaders准备训练数据 Dataset 一次检索一个数据集特征和标签样本。在训练模型时,我们通常希望以“小批量”的形式传递样本,并在每个时期重新排列数据以减少模型过拟合,并使用 Python 的 multiprocessing 来加速数据检索。
DataLoader 是一个可迭代对象,它在一个简单的 API 中为我们抽象了这种复杂性。
示例:
1 2 3 4 from torch.utils.data import DataLoadertrain_dataloader = DataLoader(training_data, batch_size=64 , shuffle=True ) test_dataloader = DataLoader(test_data, batch_size=64 , shuffle=True )
二、创建模型 神经网络由对数据执行操作的层/模块组成。 torch.nn 命名空间提供了构建您自己的神经网络所需的所有构建块。PyTorch 中的每个模块都是 nn.Module 的子类。神经网络本身就是一个模块,它由其他模块(层)组成。这种嵌套结构允许轻松构建和管理复杂的架构。
1.获取训练设备 创建模型的第一步是获取训练设备
1 2 3 4 5 6 7 8 device = ( "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" ) print (f"Using {device} device" )
2.定义类 我们通过继承 nn.Module来定义我们的神经网络,并在 __ init __ 中初始化神经网络层。每个 nn.Module子类都在 forward 方法中实现对输入数据的操作。
为了使用模型,我们将输入数据传递给它。这将执行模型的 forward
方法,以及一些 后台操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class NeuralNetwork (nn.Module ): def __init__ (self ): super ().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28 *28 , 512 ), nn.ReLU(), nn.Linear(512 , 512 ), nn.ReLU(), nn.Linear(512 , 10 ), ) def forward (self, x ): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits
我们创建NeuralNetwork的实例,并将其移动到device。
1 model = NeuralNetwork().to(device)
在输入上调用模型会返回一个二维张量,其中 dim=0 对应于每个类的 10 个原始预测值的每个输出,dim=1 对应于每个输出的各个值。我们通过将其传递给 nn.Softmax 模块的实例来获得预测概率
1 2 3 4 5 获取 X = torch.rand(1 , 28 , 28 , device=device) logits = model(X) pred_probab = nn.Softmax(dim=1 )(logits) y_pred = pred_probab.argmax(1 )
3.模型层 获取一个大小为28x28的三张图像样本,并传递给网络
1 2 input_image = torch.rand(3 ,28 ,28 ) print (input_image.size())
nn.Flatten:初始化nn.Flatten层,将每个 2D 28x28 图像转换为 784 个像素值的连续数组(minibatch 维度(在 dim=0 处)保持不变)。
1 2 3 flatten = nn.Flatten() flat_image = flatten(input_image) print (flat_image.size())
nn.Linear:线性层是一个模块,它使用其存储的权重和偏差对输入应用线性变换。
1 2 3 layer1 = nn.Linear(in_features=28 *28 , out_features=20 ) hidden1 = layer1(flat_image) print (hidden1.size())
nn.ReLU:非线性激活函数是在模型的输入和输出之间创建复杂映射的原因。它们应用于线性变换之后以引入_非线性_,帮助神经网络学习各种现象。
1 2 3 print (f"Before ReLU: {hidden1} \n\n" )hidden1 = nn.ReLU()(hidden1) print (f"After ReLU: {hidden1} " )
nn.Sequential:是一个有序的模块容器。数据按定义的相同顺序传递给所有模块。你可以使用顺序容器来组合一个像 seq_modules 这样的快速网络。
1 2 3 4 5 6 7 8 seq_modules = nn.Sequential( flatten, layer1, nn.ReLU(), nn.Linear(20 , 10 ) ) input_image = torch.rand(3 ,28 ,28 ) logits = seq_modules(input_image)
nn.Softmax:神经网络的最后一个线性层返回_logits_ - [-infty, infty] 中的原始值 - 这些值被传递给 nn.Softmax模块。logits 被缩放到 [0, 1] 之间的值,表示模型对每个类的预测概率。 dim 参数指示值必须沿其求和为 1 的维度。
1 2 softmax = nn.Softmax(dim=1 ) pred_probab = softmax(logits)
三、优化模型参数 训练模型是一个迭代过程;在每次迭代中,模型都会猜测输出,计算猜测中的误差(损失),收集误差相对于其参数的导数,并使用梯度下降法优化 这些参数。
1.超参数 超参数是可调整的参数,允许您控制模型优化过程。不同的超参数值会影响模型训练和收敛速度。
我们为训练定义了以下超参数:
时期数 - 迭代数据集的次数
批量大小 - 在更新参数之前通过网络传播的数据样本数
学习率 - 在每个批次/时期更新模型参数的程度。较小的值会导致学习速度慢,而较大的值可能会导致训练期间出现不可预测的行为。
1 2 3 learning_rate = 1e-3 batch_size = 64 epochs = 5
2.优化循环 设置超参数后,我们就可以使用优化循环来训练和优化模型。优化循环的每次迭代称为一个时期 。
每个时期都包含两个主要部分
训练循环 - 遍历训练数据集并尝试收敛到最佳参数。验证/测试循环 - 遍历测试数据集以检查模型性能是否有所提高。
让我们简单熟悉一下训练循环中使用的一些概念。跳转到完整实现 部分查看优化循环。
3.损失函数 当提供一些训练数据时,我们未经训练的网络很可能无法给出正确的答案。损失函数 衡量获得的结果与目标值的差异程度,并且是我们希望在训练期间最小化的函数。为了计算损失,我们使用给定数据样本的输入进行预测,并将其与真实数据标签值进行比较。
我们将模型的输出 logits 传递给 nn.CrossEntropyLoss,它将对 logits 进行归一化并计算预测误差。
1 2 # Initialize the loss function loss_fn = nn.CrossEntropyLoss()
4.优化器 优化是在每个训练步骤中调整模型参数以减少模型误差的过程。优化算法 定义了此过程的执行方式(在本例中,我们使用随机梯度下降)。所有优化逻辑都封装在 optimizer
对象中。在这里,我们使用 SGD 优化器;此外,PyTorch 中还有许多不同的优化器 可用,例如 ADAM 和 RMSProp,它们适用于不同类型的模型和数据。
我们通过注册需要训练的模型参数并传递学习率超参数来初始化优化器。
1 optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
在训练循环中,优化分三个步骤进行
调用 optimizer.zero_grad()
重置模型参数的梯度。默认情况下,梯度会累加;为了防止重复计算,我们在每次迭代时将其显式归零。通过调用 loss.backward()
反向传播预测损失。PyTorch 将损失相对于每个参数的梯度存储起来。
获得梯度后,我们调用 optimizer.step()
根据反向传播中收集的梯度调整参数。
代码实现以上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 import torchfrom torch import nnfrom torch.utils.data import DataLoaderfrom torchvision import datasetsfrom torchvision.transforms import ToTensortraining_data = datasets.FashionMNIST( root="data" , train=True , download=True , transform=ToTensor() ) test_data = datasets.FashionMNIST( root="data" , train=False , download=True , transform=ToTensor() ) device = ( "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" ) print (f"Using {device} device" )batch_size = 64 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 class createModule (nn.Module ): def __init__ (self ): super ().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28 *28 , 512 ), nn.ReLU(), nn.Linear(512 , 512 ), nn.ReLU(), nn.Linear(512 , 10 ) ) def forward (self, x ): x = self.flatten(x) logits = self.linear_relu_stack(x) return logits model = createModule().to(device) X = torch.rand(1 , 28 , 28 , device=device) logits = model(X) pred_probab = nn.Softmax(dim=1 )(logits) y_pred = pred_probab.argmax(1 ) print (f"Predicted class: {y_pred} " )loss_fn = nn.CrossEntropyLoss() optimizer = torch.optim.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): X, y = X.to(device), y.to(device) pred = model(X) loss = loss_fn(pred, y) loss.backward() optimizer.step() optimizer.zero_grad() if batch % 100 == 0 : 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 () test_loss, correct = 0 , 0 with torch.no_grad(): for X, y in dataloader: X, y = X.to(device), y.to(device) 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.1 f} %, Avg loss: {test_loss:>8f} \n" ) epochs = 5 for t in range (epochs): print (f"Epoch {t+1 } \n-------------------------------" ) train(train_dataloader, model, loss_fn, optimizer) test(test_dataloader, model, loss_fn) print ("Done!" )