PyTorch 训练一个分类神经网络

现在知道怎么定义一个网络,计算loss更新权重了。

What about data?

通常来说处理的数据有图片,文本,声音和视频,python有专门的库加载这些数据成numpy的array格式。然后转成torch.Tensor.

  • 对于图片,可以使用 Pillow,OpenCV 等
  • 声音的话,可以用 scipy,librosa
  • 文本信息可以使用 原生的 Python或Cython,或者 NLKT 和 SpyCy

特别是视频,Torch 有专门的包 torchvision,他为像 Imagenet,CIFAR10,MNIST等这样的数据集提供加载器,也提供图片的数据转换,torchvison.datasets 和 torch.utils.data.DataLoader .

这样提供了很大的便利,避免重复造轮子。

这小节使用 CIFAR10 数据集。这是一个包含,飞机,汽车,鸟,猫,鹿,狗,青蛙,马,船,卡车,十种类型图片。 CIFAR-10 图片是统一的 32 x 32 大小,包含 RGB 三个通道。
image

训练一个图片分类器

我们需要做以下几布:

  1. 利用 torchvision 加载格式化 CIFAR-10 测试集和训练集
  2. 定义一个卷积神经网络
  3. 定义一个 loss 函数
  4. 利用训练集训练网络
  5. 利用测试集进行测试

1.加载、标准化 CIFAR-10

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

torchvision 输出的数据是 [0,1] PILImage 图片,我们需要转换成 标准化的 Tensor [-1,1]格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data',train=True,
download=True,
transform=transform)

trainloader = torch.utils.data.DataLoader(trainset,batch_size=4,
shuffle=True,
num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data',train=False,
download=True,
transform=transform)

testloader = torch.utils.data.DataLoader(testset,batch_size=4,
shuffle=False,num_workers=2)

classes = ('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')

OUT:

Files already downloaded and verified
Files already downloaded and verified

这部分数据是从网上下载下来的,但我根本下载不了,所以从kaggle下载放在./data 文件夹下。

先看看图片吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg,(1,2,0)))

dataiter = iter(trainloader)
images, labels = dataiter.next()

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

OUT:

frog   dog   dog truck

image

2.定义一个卷积神经网络

把上一小节那个卷积神经网络复制过来,将输入通道改成三。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(3,6,5)
self.pool = nn.MaxPool2d(2,2)
self.conv2 = nn.Conv2d(6,16,5)
self.fc1 = nn.Linear(16*5*5,120)
self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84,10)

def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1,16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x

net = Net()

3.定义 loss 函数和优化器

利用交叉熵loss 和 SGD.

1
2
3
4
import torch.optim as optim

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

4.训练网络

现在开始好玩了。先简单跑一下数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for epoch in range(2):

running_loss = 0.0
for i,data in enumerate(trainloader,0):
inputs, labels = data
optimizer.zero_grad()

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))
running_loss = 0.0
print('Finished Training')

5.利用测试集测试网络

现在已经利用训练集训练了两次,我们需要测试集检查一下是否学到了什么东西。

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

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

OUT:
GroundTruth: cat ship ship plane
image

看神经网络能不能认出来

1
outputs = net(images)

输出是一个10个类别的权重。哪个权重高,他会认为更可能是那个。

1
2
3
_,predicted = torch.max(outputs,1)
print('Predicted: ',' '.join('%5s' % classes[predicted[j]]
for j in rang(4)))

OUT:
Predicted: dog car truck ship

好吧…

大概看下在每个种类上的表现吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = net(images)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i].item()
class_total[label] += 1


for i in range(10):
print('Accuracy of %5s : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))

OUT:
Accuracy of plane : 73 %
Accuracy of car : 44 %
Accuracy of bird : 45 %
Accuracy of cat : 32 %
Accuracy of deer : 26 %
Accuracy of dog : 42 %
Accuracy of frog : 61 %
Accuracy of horse : 75 %
Accuracy of ship : 43 %
Accuracy of truck : 78 %

6.在GPU上训练

这个嘛,额,不存在的,GT740M啊,不支持啊…