我们将深入探讨使用 PyTorch 构建自己的神经网络必须了解的 2 个基本概念:张量和梯度。
张量是 PyTorch 中的中央数据单元。它们是类似于数组的数据结构,在功能和属性方面与 Numpy 数组非常相似。它们之间最重要的区别是 PyTorch 张量可以在 GPU 的设备上运行以加速计算。
# 使用Tensor对象创建了一个 3x3 形状的未初始化张量。
import torch
tensor_uninitialized = torch.Tensor(3, 3)
tensor_uninitialized
"""
tensor([[1.7676e-35, 0.0000e+00, 3.9236e-44],
[0.0000e+00, nan, 0.0000e+00],
[1.3733e-14, 1.2102e+25, 1.6992e-07]])
"""
# 我们还可以创建用零、一或随机值填充的张量。
tensor_rand = torch.rand(3, 3)
tensor_rand
"""
tensor([[0.6398, 0.3471, 0.6329],
[0.4517, 0.2253, 0.8022],
[0.9537, 0.1698, 0.5718]])
"""
就像 Numpy 数组一样,PyTorch 允许我们在张量之间执行数学运算,同样的 Numpy 数组中的其他常见操作,如索引和切片,也可以使用 PyTorch 中的张量来实现。
# 数学运算
x = torch.Tensor([[1, 2, 3],
[4, 5, 6]])
tensor_add = torch.add(x, x)
"""
tensor([[ 2., 4., 6.],
[ 8., 10., 12.]])
"""
假设有 2 个参数 a 和 b ,梯度是一个参数相对于另一个参数的偏导数。导数告诉你当你稍微改变其他一些量时,给定量会发生多少变化。在神经网络中,梯度是损失函数相对于模型权重的偏导数。我们只想找到带来损失函数梯度最低的权重。
PyTorch 使用torch库中的Autograd包来跟踪张量上的操作。
# 01. 默认情况下,张量没有关联的梯度。
tensor= torch.Tensor([[1, 2, 3],
[4, 5, 6]])
tensor.requires_grad
"""
False
"""
# 02. 可以通过调用requires_grad_函数在张量上启用跟踪历史记录。
tensor.requires_grad_()
"""
tensor([[1., 2., 3.],
[4., 5., 6.]], requires_grad=True)
"""
# 03. 但是目前该 Tensor 还没有梯度
print(tensor.grad)
"""
None
"""
# 04. 现在,让我们创建一个等于前一个张量中元素均值的新张量,以计算张量相对于新张量的梯度。
mean_tensor = tensor.mean()
mean_tensor
"""
tensor(3.5000, grad_fn=<MeanBackward0>)
"""
# 05. 要计算梯度,我们需要显式执行调用backward()函数的反向传播。
mean_tensor.backward()
print(tensor.grad)
"""
tensor([[0.1667, 0.1667, 0.1667],
[0.1667, 0.1667, 0.1667]])
"""
我们可以将神经网络定义为扩展 torch.nn.Module 类的 Python 类。在这个类中,我们必须定义 2 个基本方法:
init()是类的构造函数。在这里,我们必须定义构成我们网络的层。forward()是我们定义网络结构以及各层连接方式的地方。这个函数接受一个输入,代表模型将被训练的特征。我将向你展示如何构建可用于分类问题的简单卷积神经网络并在 MNIST 数据集上训练它。
首先,我们必须导入torch和我们需要的所有模块。可以创建我们的模型了。
import torch
from torch import nn
import torch.nn.functional as F
import numpy as np
# CNN 由 2 个卷积层组成,后面是一个全局平均池化层。最后,我们有 2 个全连接层和一个softmax来获得最终的输出概率。
class My_CNN(nn.Module):
def __init__(self):
super(My_CNN, self).__init__()
self.conv1 = nn.Conv2d(1, 64, kernel_size=(3, 3), padding=1)
self.conv2 = nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1)
self.avg_pool = nn.AvgPool2d(28)
self.fc1 = nn.Linear(64, 64)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.relu(self.conv2(x))
x = self.avg_pool(x)
x = x.view(-1, 64)
x = F.relu(self.fc1(x))
x = self.fc2(x)
x = F.softmax(x)
return x
其次,加载数据集,直接从 PyTorch 检索 MNIST 数据集,并使用 PyTorch 实用程序将数据集拆分为训练集和验证集。
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader
from torch.utils.data.sampler import SubsetRandomSampler
mnist = MNIST("data", download=True, train=True)
## create training and validation split
split = int(0.8 * len(mnist))
index_list = list(range(len(mnist)))
train_idx, valid_idx = index_list[:split], index_list[split:]
## create sampler objects using SubsetRandomSampler
train = SubsetRandomSampler(train_idx)
valid = SubsetRandomSampler(valid_idx)
# 使用DataLoader创建迭代器对象,它提供了使用多处理 worker 并行批处理、随机播放和加载数据的能力。
train_loader = DataLoader(mnist, batch_size=256, sampler=train)
valid_loader = DataLoader(mnist, batch_size=256, sampler=valid)
现在我们拥有了开始训练模型的所有要素。然后再定义损失函数和优化器,Adam将用作优化器,交叉熵用作损失函数。
model = My_CNN()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
loss_function = nn.CrossEntropyLoss()
最后开始训练,所有 PyTorch 训练循环都将经过每个 epoch 和每个DataPoint(在训练DataLoader 对象中)。
epochs = 10
for epoch in range(epochs):
train_loss, valid_loss = [], []
for data, target in train_loader:
# forward propagation
outputs = model(data)
# loss calculation
loss = loss_function(outputs, target)
# backward propagation
optimizer.zero_grad()
loss.backward()
# weights optimization
optimizer.step()
train_loss.append(loss.item())
for data, target in valid_loader:
outputs = model(data)
loss = los_function(outputs, target)
valid_loss.append(loss.item())
print('Epoch: {}, training loss: {}, validation loss: {}'
.format(epoch, np.mean(train_loss), np.mean(valid_loss)))
在验证阶段,我们必须像在训练阶段所做的那样循环验证集中的数据。不同之处在于我们不需要对梯度进行反向传播。
with torch.no_grad():
correct = 0
total = 0
for data, target in valid_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Validation set Accuracy: {} %'.format(100 * correct / total))
就是这样!现在你已准备好构建自己的神经网络。你可以尝试通过增加模型复杂性向网络添加更多层来获得更好的性能。