如果你想在五分钟内编写Pytorch模型,需要完成四个步骤:
1.导入和预处理(数据集)数据,并对其进行批处理(数据加载器)
2.使用神经网络建立模型。
3.编写一个训练循环并运行它。
4.验证集上的验证。
由于MNIST已经做得非常彻底,我们将介绍如何导入torchvision数据集,并在五分钟内编写一些代码。出于这个原因,它不会很漂亮,但会起作用。
下载和导入数据
因为MNIST已经做得很死了,我们将搜索标准的torchvision数据集,看看是否还有其他我们想要尝试和预测的东西。
让我们来看Kuzushiji MNIST,它是由70000张图像组成的MNIST数据集的平假名替代品。
首先,我们找到每个通道的平均值和标准偏差。这背后的原因是,我们希望对训练数据进行归一化,但Pytorch变换需要提前给出归一化平均值和标准偏差。因此,我们将使用数据集找到这些值,然后重新导入它,并用预定义的值传递一个归一化转换。
kmnist = datasets.KMNIST(data_path, train=True, download=True,
transform = transforms.Compose([
transforms.ToTensor()]))
kmnist_val = datasets.KMNIST(data_path, train=False, download=True,
transform = transforms.ToTensor())
请注意,kmnist是一个数据集,因此循环它会为我们提供每一个图像和每一个标签。因此,如果我们在数据集中的每个图像上循环,并沿着额外的第四维叠加它们,我们将得到所有图像的张量。
imgs = torch.stack([img_t for img_t, _ in kmnist], dim=3)
imgs.shape
>>> torch.Size([1, 28, 28, 60000])
现在我们计算每个通道的平均值。请注意,调用imgs.view(1,-1)将把所有的张量压缩到第二维度。我们在这个第二维度上取像素值的平均值(因此dim=1)和标准差。
# compute mean and std per channel
mean = imgs.view(1,-1).mean(dim=1)
std = imgs.view(1,-1).std(dim=1)
mean, std
>>> (tensor([0.1918]), tensor([0.3483]))
我们现在可以重新导入数据,使用Normalize转换以及将数组转换为张量。请注意,Normalize将像素值的平均值和标准偏差作为参数。
# normalised data
t_kmnist = datasets.KMNIST(data_path, train=True,
download=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307),
(0.3081))
]))
t_kmnist_val = datasets.KMNIST(data_path, train=False,
download=False, transform=transforms.Compose([
transforms.ToTensor()]))
现在我们有了数据集,我们需要将这些数据输入数据加载器进行批处理。如果你使用的是CPU,请确保设置较小的批处理大小,并将num_workers=1(这是一个GPU问题,不要太担心)。
train_loader = torch.utils.data.DataLoader(kmnist, batch_size=128,
shuffle=True,
num_workers=1)
val_loader = torch.utils.data.DataLoader(kmnist_val, batch_size=128,
shuffle=False,
num_workers=1)
我们可以从数据集中查看一些样本。
figure = plt.figure(figsize=(10, 8))
cols, rows = 5, 5
for i in range(1, cols * rows + 1):
sample_idx = torch.randint(len(t_kmnist), size=(1,)).item()
img, label = t_kmnist[sample_idx]
figure.add_subplot(rows, cols, i)
plt.title(label)
plt.axis("off")
plt.imshow(img.squeeze(), cmap="gray")
plt.show()
构建模型
这不是一篇关于如何从理论上构建深度学习模型的教程。因此,我们将在这里介绍模型实现。
首先,需要将模型实例化为nn.Module的实例。其次,你需要使用常用的Python方法初始化类。最后,你需要一个模型初始化,在这里我们定义了所有的模型层,然后是一个forward方法,在这里我们告诉模型如何获取输入并将其传递给这些层。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5,
stride=1, padding=2)
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, stride=1,
padding=2)
self.layer1 = nn.Sequential(self.conv1, nn.ReLU(), nn.MaxPool2d(kernel_size=2))
self.layer2 = nn.Sequential(self.conv2, nn.ReLU(), nn.MaxPool2d(kernel_size=2))
self.out = nn.Linear(32*7*7, 10) # output 10 classes
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
# flatten the output of conv2 to (batch_size, 32*7*7)
x = x.view(x.size(0), -1)
output = self.out(x)
return output
在这个阶段,通过从dataloader中给出一个示例来调试模型总是很重要的。然后,我们将该图像传递给模型,并检查它是否输出了正确大小的内容。
img, _ = next(iter(train_loader))
img = img[0]
model = Net()
model(img.unsqueeze(0)).shape
>>> torch.Size([1, 10])
完美的我们构建了一个模型,该模型采用K-MNIST图像,并输出10个类,代表每个可能的数字0到9的10种不同概率。
编写和运行训练循环像
往常一样,我们的训练步骤是相似的。前向传播,计算损失,重置梯度(Pytorch)。反向传播以计算有关损失的梯度。用这些梯度更新我们的权重。
def training_loop(n_epochs, model, loss_fn, optimiser, train_loader):
model.train()
total_step = len(train_loader)
for n in range(1, n_epochs+1):
for i, (imgs, labels) in enumerate(train_loader):
output = model(Variable(imgs)) # forward pass
loss = loss_fn(output, Variable(labels)) # compute loss
optimiser.zero_grad() # reset gradients
loss.backward() # backpropagation
optimiser.step() # update the weights
if (i+1) % (n_epochs/10) == 0:
print ('{} Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
.format(datetime.datetime.now(), n, n_epochs,
i + 1, total_step, loss.item()))
然后,我们实例化我们的模型,设置Adam优化器,并使用交叉熵损失(因为这是一个多类分类问题)。
model = Net()
optimiser = torch.optim.Adam(model.parameters(), lr=1e-2)
loss_fn = nn.CrossEntropyLoss()
然后把这些参数传递给训练循环。
training_loop(
n_epochs = 10,
model = model,
loss_fn = loss_fn,
optimiser = optimiser,
train_loader = train_loader
)
验证模型
迭代验证数据加载器中的图像和标签,前向传播,通过在输出张量中找到值最高的索引(记住,我们输出了10个概率的向量)得到预测。data.squeeze()以获取实际的标量本身。最后,统计预测值与标签相等的样本数量,除以标签总数。
def validate(model, train_loader, val_loader):
model.eval() # set to eval mode to avoid batchnorm
with torch.no_grad(): # avoid calculating gradients
correct, total = 0, 0
for images, labels in val_loader:
test_output = model(images)
pred_y = torch.max(test_output, 1)[1].data.squeeze()
accuracy = (pred_y == labels).sum().item() / float(labels.size(0))
print('VALIDATION SET ACCURACY: %.2f' % accuracy)
validate(model, train_loader, val_loader)
>>> VALIDATION SET ACCURACY: 0.95
验证集准确率为95%。
结论
让模型变得更好:
· 在模型训练时打印验证集指标:显然,很高兴看到训练损失随着每个epoch而减少。但是,直到我们在训练后对模型进行验证,我们才真正了解模型的性能。如果在运行过程中打印验证准确性,你将更好地了解模型的成功。
· 早停:一旦验证的准确性在一定时期内(称为耐心)没有提高,就回到表现最好的epoch并使用这些权重。看看其他指标:例如曲线下面积(AUC)。
· 实现其他网络架构:自CNN引入以来,计算机视觉已经取得了长足的进步。你可以尝试其他体系结构来提高性能。
参与评论
登录后参与讨论 0/1000