赞
踩
torch.utils.data.DataLoader和torch.utils.data.Dataset, Dataset存储样本及其相应的标签,DataLoader在Dataset周围包装了一个可迭代对象。
- import torch
- from torch import nn
PyTorch 提供特定领域的库,例如TorchText、 TorchVision和TorchAudio,所有这些库都包含数据集。
Dataset包含两个参数:transform和 target_transform分别修改样本和标签。
- # Download training data from open datasets.
- training_data = datasets.FashionMNIST(
- root="data",
- train=True,
- download=True,
- transform=ToTensor(),
- )
-
- # Download test data from open datasets.
- test_data = datasets.FashionMNIST(
- root="data",
- train=False,
- download=True,
- transform=ToTensor(),
- )
将Dataset作为参数传递给DataLoader,支持自动批处理、采样、混洗和多进程数据加载。
- batch_size = 64
-
- # Create data loaders.N 代表 Batch,C 代表Channel,H 代表Height,W 代表Width
- 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函数中指定数据如何通过网络。
- # Get cpu or gpu device for training.
- device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
- print(f"Using {device} device")
-
- # Define model
- 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
-
- model = NeuralNetwork().to(device)
- print(model)
-
- ########################################################################
- Using cuda device
- NeuralNetwork(
- (flatten): Flatten(start_dim=1, end_dim=-1)
- (linear_relu_stack): Sequential(
- (0): Linear(in_features=784, out_features=512, bias=True)
- (1): ReLU()
- (2): Linear(in_features=512, out_features=512, bias=True)
- (3): ReLU()
- (4): Linear(in_features=512, out_features=10, bias=True)
- )
- )
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
损失函数和优化器
- 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)
-
- # Compute prediction error
- pred = model(X)
- loss = loss_fn(pred, y)
-
- # Backpropagation
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- if batch % 100 == 0:
- loss, current = loss.item(), batch * 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.1f}%, Avg loss: {test_loss:>8f} \n")
'运行
训练过程进行了几次迭代(epochs)。在每个时期,模型学习参数以做出更好的预测。我们在每个时期打印模型的准确性和损失;我们希望看到每个时期的准确性都在提高,损失在减少。
- 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!")
- ############################################
- Epoch 1
- -------------------------------
- loss: 2.306185 [ 0/60000]
- loss: 2.298008 [ 6400/60000]
- loss: 2.280848 [12800/60000]
- loss: 2.273232 [19200/60000]
- loss: 2.248048 [25600/60000]
- loss: 2.229527 [32000/60000]
- loss: 2.229292 [38400/60000]
- loss: 2.199877 [44800/60000]
- loss: 2.196356 [51200/60000]
- loss: 2.163561 [57600/60000]
- Test Error:
- Accuracy: 48.9%, Avg loss: 2.162945
-
- Epoch 2
- -------------------------------
- loss: 2.173212 [ 0/60000]
- loss: 2.172224 [ 6400/60000]
- loss: 2.117870 [12800/60000]
- loss: 2.126270 [19200/60000]
- loss: 2.074794 [25600/60000]
- loss: 2.025475 [32000/60000]
- loss: 2.045674 [38400/60000]
- loss: 1.976126 [44800/60000]
- loss: 1.975976 [51200/60000]
- loss: 1.904687 [57600/60000]
- Test Error:
- Accuracy: 57.0%, Avg loss: 1.905852
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
序列化内部状态字典(包含模型参数)
- torch.save(model.state_dict(), "model.pth")
- print("Saved PyTorch Model State to model.pth")
加载模型的过程包括重新创建模型结构并将状态字典加载到其中
- model = NeuralNetwork()
- model.load_state_dict(torch.load("model.pth"))
-
- <All keys matched successfully>
张量是一种特殊的数据结构,与数组和矩阵非常相似。在 PyTorch 中,我们使用张量对模型的输入和输出以及模型的参数进行编码。
张量可以直接从数据中创建。数据类型是自动推断的。
可以从 NumPy 数组创建张量
除非明确覆盖,否则新张量保留参数张量的属性(形状、数据类型)。
- import torch
- import numpy as np
-
- data = [[1, 2],[3, 4]]
- x_data = torch.tensor(data)
- np_array = np.array(data)
- x_np = torch.from_numpy(np_array)
-
-
- x_ones = torch.ones_like(x_data) # retains the properties of x_data
- print(f"Ones Tensor: \n {x_ones} \n")
- x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
- print(f"Random Tensor: \n {x_rand} \n")
-
- Ones Tensor:
- tensor([[1, 1],
- [1, 1]])
-
- Random Tensor:
- tensor([[0.6368, 0.0069],
- [0.7914, 0.9197]])
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
shape是张量维度的元组
- shape = (2,3,)
- rand_tensor = torch.rand(shape)
- ones_tensor = torch.ones(shape)
- zeros_tensor = torch.zeros(shape)
-
- print(f"Random Tensor: \n {rand_tensor} \n")
- print(f"Ones Tensor: \n {ones_tensor} \n")
- print(f"Zeros Tensor: \n {zeros_tensor}")
-
- Random Tensor:
- tensor([[0.1977, 0.4749, 0.8820],
- [0.9729, 0.3019, 0.4694]])
-
- Ones Tensor:
- tensor([[1., 1., 1.],
- [1., 1., 1.]])
-
- Zeros Tensor:
- tensor([[0., 0., 0.],
- [0., 0., 0.]])
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
张量属性描述了它们的形状、数据类型和存储它们的设备。
- tensor = torch.rand(3,4)
-
- print(f"Shape of tensor: {tensor.shape}")
- print(f"Datatype of tensor: {tensor.dtype}")
- print(f"Device tensor is stored on: {tensor.device}")
-
- Shape of tensor: torch.Size([3, 4])
- Datatype of tensor: torch.float32
- Device tensor is stored on: cpu
- # We move our tensor to the GPU if available
- if torch.cuda.is_available():
- tensor = tensor.to("cuda")
连接张量您可以使用它torch.cat沿给定维度连接一系列张量。
# ``tensor.T`` returns the transpose of a tensor
就地操作 将结果存储到操作数中的操作称为就地操作。它们由_后缀表示。例如:x.copy_(y), x.t_(), 将改变x。
我们希望数据集代码与模型训练代码解耦,以获得更好的可读性和模块化。PyTorch提供了两个数据原语:torch.utils.data.DataLoader和torch.utils.data.Dataset,它们允许你使用预加载的数据集以及你自己的数据。Dataset存储样本及其对应的标签,DataLoader在Dataset周围包装了一个可迭代对象,以方便访问样本。
下面是如何从TorchVision加载Fashion-MNIST数据集的示例。Fashion-MNIST是Zalando的文章图像数据集,由6万个训练示例和1万个测试示例组成。每个示例包括28×28灰度图像和来自10个类别之一的关联标签。
我们用以下参数加载FashionMNIST数据集:
root是存储训练/测试数据的路径,
train指定训练或测试数据集,
download=True如果不可用,则从 Internet 下载数据root。
transform target_transform指定特征和标签转换
- import torch
- from torch.utils.data import Dataset
- from torchvision import datasets
- from torchvision.transforms import ToTensor
- import matplotlib.pyplot as plt
-
-
- training_data = datasets.FashionMNIST(
- root="data",
- train=True,
- download=True,
- transform=ToTensor()
- )
-
- test_data = datasets.FashionMNIST(
- root="data",
- train=False,
- download=True,
- transform=ToTensor()
- )
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
手动索引:training_data[index]。我们用来matplotlib可视化训练数据中的一些样本。
- labels_map = {
- 0: "T-Shirt",
- 1: "Trouser",
- 2: "Pullover",
- 3: "Dress",
- 4: "Coat",
- 5: "Sandal",
- 6: "Shirt",
- 7: "Sneaker",
- 8: "Bag",
- 9: "Ankle Boot",
- }
- figure = plt.figure(figsize=(8, 8))
- cols, rows = 3, 3
- for i in range(1, cols * rows + 1):
- sample_idx = torch.randint(len(training_data), size=(1,)).item()
- img, label = training_data[sample_idx]
- figure.add_subplot(rows, cols, i)
- plt.title(labels_map[label])
- plt.axis("off")
- plt.imshow(img.squeeze(), cmap="gray")
- plt.show()
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
自定义数据集类必须实现三个函数:__init__、__len__和__getitem_
__getitem__ 函数从给定索引处的数据集中加载并返回样本idx。根据索引,它识别图像在磁盘上的位置,使用 将其转换为张量read_image,从中的 csv 数据中检索相应的标签self.img_labels,调用它们的转换函数(如果适用),并返回张量图像和相应的标签一个元组。
- import os
- import pandas as pd
- from torchvision.io import read_image
-
- class 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
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
Dataset一次检索我们数据集的特征并标记一个样本。在训练模型时,我们通常希望以“小批量”传递样本,在每个时期重新调整数据以减少模型过度拟合
- from torch.utils.data import DataLoader
-
- train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
- test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)
-
- # Display image and label.
- train_features, train_labels = next(iter(train_dataloader))
- print(f"Feature batch shape: {train_features.size()}")
- print(f"Labels batch shape: {train_labels.size()}")
- img = train_features[0].squeeze()
- label = train_labels[0]
- plt.imshow(img, cmap="gray")
- plt.show()
- print(f"Label: {label}")
将该数据集加载到 中DataLoader,并可以根据需要迭代数据集。下面的每次迭代都会返回一批train_features和train_labels(batch_size=64分别包含特征和标签)。因为我们指定了shuffle=True,在我们遍历所有批次之后,数据被打乱
-transform修改特征和 target_transform修改标签
ToTensor 将 PIL 图像或 NumPy 转换ndarray为FloatTensor. 并在 [0., 1.] 范围内缩放图像的像素强度值
Lambda 转换应用任何用户定义的 lambda 函数
- target_transform = Lambda(lambda y: torch.zeros(
- 10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
神经网络由对数据执行操作的层/模块组成。torch.nn命名空间提供了构建自己的神经网络所需的所有构建块。神经网络本身就是一个由其他模块(层)组成的模块。这种嵌套结构允许轻松构建和管理复杂的体系结构。
我们希望能够在 GPU 等硬件加速器上训练我们的模型
- device = "cuda" if torch.cuda.is_available() else "cpu"
- print(f"Using {device} device")
-
- Using cuda device
通过子类化来定义我们的神经网络nn.Module,并在__init__中初始化神经网络层。模块子类在forward方法中实现对输入数据的操作。
- class NeuralNetwork(nn.Module):
- def __init__(self):
- super(NeuralNetwork, self).__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,并打印其结构。
- model = NeuralNetwork().to(device)
- print(model)
-
- NeuralNetwork(
- (flatten): Flatten(start_dim=1, end_dim=-1)
- (linear_relu_stack): Sequential(
- (0): Linear(in_features=784, out_features=512, bias=True)
- (1): ReLU()
- (2): Linear(in_features=512, out_features=512, bias=True)
- (3): ReLU()
- (4): Linear(in_features=512, out_features=10, bias=True)
- )
- )
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
神经网络的最后一个线性层返回logits ([-infty, infty]中的原始值),这些值被传递给nn.Softmax模块。被缩放为值[0,1],表示模型对每个类的预测概率。Dim参数表示数值之和必须为1的尺寸。
- softmax = nn.Softmax(dim=1)
- pred_probab = softmax(logits)
在训练神经网络时,最常用的算法是 反向传播。在该算法中,参数(模型权重)根据损失函数相对于给定参数的梯度进行调整。为了计算这些梯度,PyTorch 有一个名为torch.autograd. 它支持自动计算任何计算图的梯度。
考虑最简单的一层神经网络,具有输入x、参数w和b,以及一些损失函数。它可以通过以下方式在 PyTorch 中定义:
- import torch
-
- x = torch.ones(5) # input tensor
- y = torch.zeros(3) # expected output
- w = torch.randn(5, 3, requires_grad=True)
- b = torch.randn(3, requires_grad=True)
- z = torch.matmul(x, w)+b
- loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
-
- #计算梯度
- loss.backward()
- print(w.grad)
- print(b.grad)
您可以在创建张量时设置 的值requires_grad,也可以稍后使用x.requires_grad_(True)方法设置。
默认情况下,所有具有的张量requires_grad=True都在跟踪它们的计算历史并支持梯度计算。
您可能想要禁用梯度跟踪的原因有:
将神经网络中的某些参数标记为冻结参数。这是 微调预训练网络的一种非常常见的场景
在只进行前向传递时加快计算速度,因为对不跟踪梯度的张量进行计算会更有效率
通过优化我们的数据参数来训练、验证和测试
训练模型是一个迭代过程;在每次迭代中,模型对输出进行猜测,计算其猜测中的误差(损失),收集关于其参数的误差的导数,并使用梯度下降优化这些参数
超参数是可调整的参数,可控制模型优化过程。不同的超参数值会影响模型训练和收敛速度.
定义了以下用于训练的超参数:
Number of Epochs - 迭代数据集的次数
Batch Size——参数更新前通过网络传播的数据样本数量
学习率——每个批次/时期更新模型参数的量。较小的值会产生较慢的学习速度,而较大的值可能会导致训练期间出现不可预测的行为。
- learning_rate = 1e-3
- batch_size = 64
- epochs = 5
'运行
一旦我们设置了超参数,我们就可以使用优化循环来训练和优化我们的模型。
Train Loop - 迭代训练数据集并尝试收敛到最佳参数。
验证/测试循环- 遍历测试数据集以检查模型性能是否正在提高。
损失函数衡量的是得到的结果与目标值的相异程度,是我们在训练时想要最小化的损失函数。为了计算损失,我们使用给定数据样本的输入进行预测,并将其与真实数据标签值进行比较。
常见的损失函数包括用于回归任务的nn.MSELoss(均方误差)和 用于分类的nn.NLLLoss(负对数似然)。 nn.CrossEntropyLoss结合了nn.LogSoftmax和nn.NLLLoss。
- # Initialize the loss function
- loss_fn = nn.CrossEntropyLoss()
优化是在每个训练步骤中调整模型参数以减少模型误差的过程。优化算法定义了这个过程是如何执行的
使用随机梯度下降,所有优化逻辑都封装在optimizer对象中。在这里,我们使用 SGD 优化器;此外,PyTorch 中有许多不同的优化器 可用,例如 ADAM 和 RMSProp,它们可以更好地处理不同类型的模型和数据。
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
在训练循环中,优化分三步进行:
调用optimizer.zero_grad()以重置模型参数的梯度。
通过调用反向传播预测损失loss.backward()。PyTorch 存储每个参数的损失梯度。
一旦我们有了梯度,我们就会optimizer.step()调用通过在反向传递中收集的梯度来调整参数。
- def train_loop(dataloader, model, loss_fn, optimizer):
- size = len(dataloader.dataset)
- for batch, (X, y) in enumerate(dataloader):
- # Compute prediction and loss
- pred = model(X)
- loss = loss_fn(pred, y)
-
- # Backpropagation
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- if batch % 100 == 0:
- loss, current = loss.item(), (batch + 1) * len(X)
- print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
-
-
- def test_loop(dataloader, model, loss_fn):
- size = len(dataloader.dataset)
- num_batches = len(dataloader)
- test_loss, correct = 0, 0
-
- with torch.no_grad():
- for X, y in dataloader:
- 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")
-
- loss_fn = nn.CrossEntropyLoss()
- optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
-
- epochs = 10
- for t in range(epochs):
- print(f"Epoch {t+1}\n-------------------------------")
- train_loop(train_dataloader, model, loss_fn, optimizer)
- test_loop(test_dataloader, model, loss_fn)
- print("Done!")
-
- Epoch 1
- -------------------------------
- loss: 2.304189 [ 64/60000]
- loss: 2.294250 [ 6464/60000]
- loss: 2.275085 [12864/60000]
- loss: 2.270736 [19264/60000]
- loss: 2.252864 [25664/60000]
- loss: 2.217214 [32064/60000]
- loss: 2.232735 [38464/60000]
- loss: 2.195049 [44864/60000]
- loss: 2.199419 [51264/60000]
- loss: 2.167980 [57664/60000]
- Test Error:
- Accuracy: 31.8%, Avg loss: 2.161290
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
我们初始化损失函数和优化器,并将其传递给train_loop和test_loop。随意增加 epoch 的数量以跟踪模型的改进性能。
- model = models.vgg16(pretrained=True)
- torch.save(model.state_dict(), 'model_weights.pth')
- #要加载模型权重,您需要先创建相同模型的实例,然后使用load_state_dict()方法加载参数。
- model = models.vgg16() # we do not specify pretrained=True, i.e. do not load default weights
- model.load_state_dict(torch.load('model_weights.pth'))
- model.eval()
一定要 model.eval() 在推理之前调用方法,将 dropout 和 batch normalization 层设置为评估模式。
在加载模型权重时,我们需要先实例化模型类,因为该类定义了网络的结构。我们可能希望将此类的结构与模型一起保存,在这种情况下,我们可以将model(而不是model.state_dict())传递给保存函数:
- torch.save(model, 'model.pth')
-
- model = torch.load('model.pth')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。