pytorch入门笔记04

  1. 1. 训练一个分类器
    1. 1.1. 数据是什么?
    2. 1.2. 训练一个图像分类器
      1. 1.2.1. 加载和标准化CIFAR10
      2. 1.2.2. 定义一个卷积神经网络
      3. 1.2.3. 定义损失函数和优化器
      4. 1.2.4. 训练网络
      5. 1.2.5. 在测试集上测试网络
    3. 1.3. 在GPU上训练

训练一个分类器

在这里,你已经了解了如何定义神经网络,计算损失和更新网络的权重。现在你可能会想

数据是什么?

总的来说,当你解决图像,文字,音频或视频数据,你能用标准的python包加载数据到numpy数组。然后你可以将这个数组转为torch.*Tensor.

  • 对于图像,可以用Pillow,OpenCV
  • 对于音频,可以用scipy和librosa
  • 对于文字,用要么是raw Python 或者 Cython 或NLTK和SpaCy

尤其是视觉,我们创建了一个包名为 torchvision, 它有公共数据集的数据加载器,比如,ImageNet, CIRFAR10, MNIST等等。图像转数据。torchvision.datasets 和 torch.utils.data.DataLoader.

这提供了极大的便利,并避免了编写样板代码。

对于这个教程,我们用CIFAR10数据集。他有的分类:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’.CIFAR10的图像是3 32 32

训练一个图像分类器

我们会做如下几步:

  1. 使用torchvision加载和标准化CIFAR10训练和测试数据集
  2. 定义一个卷积神经网络
  3. 定义一个损失函数
  4. 在训练集上训练网络
  5. 在测试集测试网络

加载和标准化CIFAR10

使用torchvision,加载CIFAR10是非常简单的

1
2
3
import torch
import torchvision
import torchvision.transforms as transforms

torchvision的输出数据集是在[0,1]范围的PILImage。我们把他们转换为Tensors的标准范围[-1,1].

注意:如果在windows平台,有BrokenPipeError,设定torch.utils.data.DataLoader()中的num_worker为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import matplotlib.pyplot as plt
import numpy as np

transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]
)

batch_size = 4
trainset = torchvision.datasets.CIFAR10(root='./cifar10',
train=True,
download=True,
transform=transform)
trainloader = Data.DataLoader(trainset,
batch_size=batch_size,
shuffle=True,
num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

out:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./cifar10\cifar-10-python.tar.gz
170500096it [02:27, 1156941.89it/s]

展示一些训练的图

out:

Files already downloaded and verified
dog ship plane ship

定义一个卷积神经网络

从神经网络章节复制神经网络,修改它为3通道图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 单通道图像输入, 输出6通道, 5x5 卷积核
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6, 16, 5)
# 一个仿射变换操作(Affine) : y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5 来自图像的维度
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max池化,窗口为(2,2)
x = self.pool(F.relu(self.conv1(x)))
# 如果尺寸是正方形, 你可以用一个数字来指定
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # 批处理除外,所有数据降维展平,意思就是二维图像转成一行数组。
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net()

定义损失函数和优化器

我们使用交叉熵误差和有动量的SGD

1
2
3
4
import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

训练网络

到这里就变得有趣了,我们循环遍历数据迭代器,将数据传入网络并优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for epoch in range(2):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 获取输入;数据是一个list类型的[inputs, labels]
inputs, labels = data

# 梯度参数设为0
optimizer.zero_grad()

# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()

# 打印统计数据
running_loss += loss.item()
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
print('Finished Training')

out:

[1, 2000] loss: 2.211
[1, 4000] loss: 1.825
[1, 6000] loss: 1.648
[1, 8000] loss: 1.562
[1, 10000] loss: 1.504
[1, 12000] loss: 1.448
[2, 2000] loss: 1.397
[2, 4000] loss: 1.353
[2, 6000] loss: 1.341
[2, 8000] loss: 1.313
[2, 10000] loss: 1.270
[2, 12000] loss: 1.280
Finished Training

让我们快速保存我们的训练模型

1
2
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

在测试集上测试网络

我们在训练数据集上训练了网络2次。但是我们需要检查网络是否学习到了所有东西。我们将通过预测神经网络输出的类标签来验证这一点,并根据ground-truth来验证它。如果预测是正确的,我们将该样本添加到正确预测列表中。

第一步,展示一个测试集图片

1
2
3
4
5
6
dataiter = iter(testloader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

out:

GroundTruth: cat ship ship plane

下一步,我们加载会之前保存的模型(note:这里没有必要保存并重新加载模型,我们这样做只是为了说明如何这样做)

1
2
net = Net()
net.load_state_dict(torch.load(PATH))

现在让我们看看神经网络是怎么看待上面这些例子的:

1
outputs = net(images)

输出是10个类的能量。一个类的能量越高,网络就越认为这个图像属于这个类。那么,让我们得到最高能量的指数:

1
2
3
4
_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))

out:

1
Predicted:   frog  ship  ship  ship

看一下网络对于整个数据集的表现怎么样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
correct = 0
total = 0
# 由于我们没有训练,我们不需要为我们的输出计算梯度
with torch.no_grad():
for data in testloader:
images, labels = data
# 图像通过网络计算输出
outputs = net(images)
# 我们选择能量最高的类型作为预测
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))

out:

Accuracy of the network on the 10000 test images: 54 %

这看起来比概率(10%的准确率)要好得多(从10个类型中随机挑选一个类型)。看来网络学到了一些东西。

Hmmm, 那些类型表现好,那些类型表现不好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 对每个类型准备计数
correct_pred = {classname: 0 for classname in classes}
total_pred = {classname: 0 for classname in classes}

# 不需要梯度
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predictions = torch.max(outputs, 1)
# 对每一个类型预测正确解收集
for label, prediction in zip(labels, predictions):
if label == prediction:
correct_pred[classes[label]] += 1
total_pred[classes[label]] += 1


# 打印每个类型的准确率
for classname, correct_count in correct_pred.items():
accuracy = 100 * float(correct_count) / total_pred[classname]
print("Accuracy for class {:5s} is: {:.1f} %".format(classname,
accuracy))

out:

Accuracy for class plane is: 60.1 %
Accuracy for class car is: 69.8 %
Accuracy for class bird is: 45.2 %
Accuracy for class cat is: 26.4 %
Accuracy for class deer is: 30.5 %
Accuracy for class dog is: 60.4 %
Accuracy for class frog is: 70.7 %
Accuracy for class horse is: 69.5 %
Accuracy for class ship is: 53.3 %
Accuracy for class truck is: 61.0 %

在GPU上训练

就像你把张量放在GPU上一样,将神经网络放在GPU上。

首先定义我们的设备作为cuda第一可见,如果cuda可用:

1
2
3
4
5
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 假设我们在CUDA机器,这里应该打印CUDA设备

print(device)

out:

cuda:0

然后这些方法将递归遍历所有模块,并将它们的参数和缓冲区转换为CUDA张量:

1
net.to(device)

记住你每步也要把输入和目标送到GPU中

1
inputs, labels = data[0].to(device), data[1].to(device)

为什么没有强调与CPU相比MASSIVE的加速?因为你的网络太小。