PyTorch

什么是 PyTorch

它是一个基于 Python 的科学计算包,主要有两大特色:

  • 替代 NumPy,用以利用 GPU 的强大计算功能
  • 拥有最大灵活性和速度的深度学习研究平台

    PyTorch 的特点/亮点

  1. 对 Python 的原生支持及其库的使用
  2. 深度结合于 Facebook 的开发,以满足平台中的所有深度学习要求
  3. PyTorch 确保提供易于使用的 API,这有助于更换的使用和理解 API
  4. 动态图计算是 PyTorch 的一个主要亮点,可以确保在代码执行的每个点动态构建图形,并且可以在运行时进行操作
  5. PyTorch 速度快,因此可以确保轻松编码和快速处理
  6. 对 CUDA 的支持确保代码可以在 GPU 上运行,从而减少运行代码所需的时间并提高系统的整体性能

    安装 PyTorch

    在机器上安装 PyTorch 还是非常简单的
    PyTorch 入门之旅 - 图1
    基于操作系统或包管理器等系统属性,可以从命令提示符或在 IDE(如 PyCharm 等)中直接安装

    张量(Tensors)

    Tensors 类似于 NumPy 的 n 维数组,此外 Tensors 也可以在 GPU 上进行加速计算
    构造一个简单的 Tensors 并检查输出,首先看看如何构建一个 5×3 的未初始化矩阵:
    1. x = torch.empty(5, 3)
    2. print(x)
    Output:
    1. tensor([[8.3665e+22, 4.5580e-41, 1.6025e-03],
    2. [3.0763e-41, 0.0000e+00, 0.0000e+00],
    3. [0.0000e+00, 0.0000e+00, 3.4438e-41],
    4. [0.0000e+00, 4.8901e-36, 2.8026e-45],
    5. [6.6121e+31, 0.0000e+00, 9.1084e-44]])
    现在构造一个随机初始化的矩阵:
    1. x = torch.rand(5, 3)
    2. print(x)
    Output:
    1. tensor([[0.1607, 0.0298, 0.7555],
    2. [0.8887, 0.1625, 0.6643],
    3. [0.7328, 0.5419, 0.6686],
    4. [0.0793, 0.1133, 0.5956],
    5. [0.3149, 0.9995, 0.6372]])
    直接从数据构造张量:
    1. x = torch.tensor([5.5, 3])
    2. print(x)
    Output:
    1. tensor([5.5000, 3.0000])

    张量运算

    张量运算操作有多种语法,在下面的例子中,看看加法运算:
    1. y = torch.rand(5, 3)
    2. print(x + y)
    Output:
    1. tensor([[ 0.2349, -0.0427, -0.5053],
    2. [ 0.6455, 0.1199, 0.4239],
    3. [ 0.1279, 0.1105, 1.4637],
    4. [ 0.4259, -0.0763, -0.9671],
    5. [ 0.6856, 0.5047, 0.4250]])
    调整大小:如果想重塑/调整张量的大小,可以使用“torch.view”:
    1. x = torch.randn(4, 4)
    2. y = x.view(16)
    3. z = x.view(-1, 8) # the size -1 is inferred from other dimensions
    4. print(x.size(), y.size(), z.size())
    Output:
    1. torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

    NumPy

    NumPy 是 Python 编程语言的库,增加了对大型、多维数组和矩阵的支持,以及用于对这些数组进行操作的大量高级数学函数集合
    Numpy 还有以下特定
  • 提供用于集成 C/C++ 和 FORTRAN 代码的工具
  • 具有线性代数、傅立叶变换和随机数功能的运算能力

除了明显的科学用途外,NumPy 还可以用作通用数据的高效多维容器,也可以定义任意的数据类型
这使得 NumPy 可以无缝快速地与各种数据库集成!

连接 Array 和 Tensors 的桥梁

将 Torch Tensor 转换为 NumPy 数组,反之亦然是轻而易举
PyTorch 入门之旅 - 图2
Torch Tensor 和 NumPy 数组将共享它们的底层内存位置,改变一个将同时改变另一个
将 Torch 张量转换为 NumPy 数组:

  1. a = torch.ones(5)
  2. print(a)
  3. b = a.numpy()
  4. print(b)

Output:

  1. tensor([1., 1., 1., 1., 1.])
  2. [1. 1. 1. 1. 1.]

下面执行求和运算并检查值的变化:

  1. a.add_(1)
  2. print(a)
  3. print(b)

Output:

  1. tensor([2., 2., 2., 2., 2.]) [2. 2. 2. 2. 2.]

将 NumPy 数组转换为 Torch 张量:

  1. import numpy as no
  2. a = np.ones(5)
  3. b = torch.from_numpy(a)
  4. np.add(a, 1, out=a)
  5. print(a)
  6. print(b)

Output:

  1. [2. 2. 2. 2. 2.]
  2. tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

可以看到,Numpy 与 Torch 之间的互转还是非常方便的

实战—训练图像分类器

数据集选择

通常,处理图像、文本、音频或视频数据时,可以使用标准的 Python 包将数据加载到 Numpy 数组中,然后就可以把这个数组转换成一个 torch.*Tensor

  • 对于图像,可以 Pillow 和 OpenCV
  • 对于音频,使用 Scipy 和 Librosa
  • 对于文本,原始 Python、基于 Cython 的加载或 NLTK 和 SpaCy 都可以

专门针对视觉,有一个名为 torchvision 的包,它实现了 Imagenet、CIFAR10、MNIST 等常见数据集的数据加载器和用于图像的数据转换器,这样就可以很方便的使用已有数据集进行学习
在这里,将使用 CIFAR10 数据集
它有类别:“飞机”、“汽车”、“鸟”、“猫”、“鹿”、“狗”、“青蛙”、“马”、“船”、“卡车”。CIFAR-10 中的图像大小为3x32x32,即32×32像素大小的3通道彩色图像,如下图:
PyTorch 入门之旅 - 图3

训练 CIFAR10 分类器

PyTorch 入门之旅 - 图4
首先加载和归一化 CIFAR10 使用 torchvision 加载

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

torchvision 数据集的输出是范围 [0, 1] 的 PILImage 图像,将它们转换为归一化范围 [-1, 1] 的张量

  1. transform = transforms.Compose(
  2. [transforms.ToTensor(),
  3. transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
  4. trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
  5. download=True, transform=transform)
  6. trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
  7. shuffle=True, num_workers=2)
  8. testset = torchvision.datasets.CIFAR10(root='./data', train=False,
  9. download=True, transform=transform)
  10. testloader = torch.utils.data.DataLoader(testset, batch_size=4,
  11. shuffle=False, num_workers=2)
  12. classes = ('plane', 'car', 'bird', 'cat',
  13. 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Output:

  1. Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz Files already downloaded and verified

接下来,从数据集中打印一些训练图像

  1. import matplotlib.pyplot as plt
  2. import numpy as np
  3. # functions to show an image
  4. def imshow(img):
  5. img = img / 2 + 0.5 # unnormalize
  6. npimg = img.numpy()
  7. plt.imshow(np.transpose(npimg, (1, 2, 0)))
  8. # get some random training images
  9. dataiter = iter(trainloader)
  10. images, labels = dataiter.next()
  11. # show images
  12. imshow(torchvision.utils.make_grid(images))
  13. # print labels
  14. print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

PyTorch 入门之旅 - 图5
Output:

  1. dog bird horse horse

下面定义卷积神经网络 考虑使用 3 通道图像(红色、绿色和蓝色)的情况,定义 CNN 架构

  1. import torch.nn as nn
  2. import torch.nn.functional as F
  3. class Net(nn.Module):
  4. def __init__(self):
  5. super(Net, self).__init__()
  6. self.conv1 = nn.Conv2d(3, 6, 5)
  7. self.pool = nn.MaxPool2d(2, 2)
  8. self.conv2 = nn.Conv2d(6, 16, 5)
  9. self.fc1 = nn.Linear(16 * 5 * 5, 120)
  10. self.fc2 = nn.Linear(120, 84)
  11. self.fc3 = nn.Linear(84, 10)
  12. def forward(self, x):
  13. x = self.pool(F.relu(self.conv1(x)))
  14. x = self.pool(F.relu(self.conv2(x)))
  15. x = x.view(-1, 16 * 5 * 5)
  16. x = F.relu(self.fc1(x))
  17. x = F.relu(self.fc2(x))
  18. x = self.fc3(x)
  19. return x
  20. net = Net()

接下来定义损失函数和优化器
使用 Cross-Entropy loss 和 SGD
事实上,Cross-Entropy Loss 是一个介于 0-1 之间的概率值,完美模型的交叉熵损失为 0,但也可能发生预期值为 0.2 但最终却得到 2 的情况,这将导致非常高的损失并且根本没有任何作用

  1. import torch.optim as optim
  2. criterion = nn.CrossEntropyLoss()
  3. optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

下面就可以训练神经网络了
只需要遍历数据迭代器,并将输入提供给网络并进行优化

  1. for epoch in range(2): # loop over the dataset multiple times
  2. running_loss = 0.0
  3. for i, data in enumerate(trainloader, 0):
  4. # get the inputs
  5. inputs, labels = data
  6. # zero the parameter gradients
  7. optimizer.zero_grad()
  8. # forward + backward + optimize
  9. outputs = net(inputs)
  10. loss = criterion(outputs, labels)
  11. loss.backward()
  12. optimizer.step()
  13. # print statistics
  14. running_loss += loss.item()
  15. if i % 2000 == 1999: # print every 2000 mini-batches
  16. print('[%d, %5d] loss: %.3f' %
  17. (epoch + 1, i + 1, running_loss / 2000))
  18. running_loss = 0.0
  19. print('Finished Training')

Output:

  1. [1, 2000] loss: 2.236
  2. [1, 4000] loss: 1.880
  3. [1, 6000] loss: 1.676
  4. [1, 8000] loss: 1.586
  5. [1, 10000] loss: 1.515
  6. [1, 12000] loss: 1.464
  7. [2, 2000] loss: 1.410
  8. [2, 4000] loss: 1.360
  9. [2, 6000] loss: 1.360
  10. [2, 8000] loss: 1.325
  11. [2, 10000] loss: 1.312
  12. [2, 12000] loss: 1.302
  13. Finished Training

最后,在测试集上测试神经网络
已经在训练数据集上训练了 2 遍网络,但是还需要检查网络是否学到了什么东西
通过预测神经网络输出的类标签来检查这一点,并根据真实情况进行检查,如果预测正确,将样本添加到正确预测列表中
第一步,展示一张来自测试集的图片

  1. dataiter = iter(testloader)
  2. images, labels = dataiter.next()
  3. # print images
  4. imshow(torchvision.utils.make_grid(images))
  5. print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

PyTorch 入门之旅 - 图6
Output:

  1. GroundTruth: cat ship ship plane

第二步,使用神经网络进行预测

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

outputs 输出是 10 个类别的权重,一个类别的权重越高,神经网络就越认为该图像属于该类别 Output:

  1. Predicted: cat car car plane

效果似乎还不错
再接下来看看神经网络在整个数据集上的表现

  1. correct = 0
  2. total = 0
  3. with torch.no_grad():
  4. for data in testloader:
  5. images, labels = data
  6. outputs = net(images)
  7. _, predicted = torch.max(outputs.data, 1)
  8. total += labels.size(0)
  9. correct += (predicted == labels).sum().item()
  10. print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

Output:

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

准确率还可以,比随机偶然的10%(从 10 个类中随机选择一个类)还是高出不少的
最后再看下不同类别的准确率

  1. class_correct = list(0. for i in range(10))
  2. class_total = list(0. for i in range(10))
  3. with torch.no_grad():
  4. for data in testloader:
  5. images, labels = data
  6. outputs = net(images)
  7. _, predicted = torch.max(outputs, 1)
  8. c = (predicted == labels).squeeze()
  9. for i in range(4):
  10. label = labels[i]
  11. class_correct[label] += c[i].item()
  12. class_total[label] += 1
  13. for i in range(10):
  14. print('Accuracy of %5s : %2d %%' % (
  15. classes[i], 100 * class_correct[i] / class_total[i]))

Output:

  1. Accuracy of plane : 61 %
  2. Accuracy of car : 85 %
  3. Accuracy of bird : 46 %
  4. Accuracy of cat : 23 %
  5. Accuracy of deer : 40 %
  6. Accuracy of dog : 36 %
  7. Accuracy of frog : 80 %
  8. Accuracy of horse : 59 %
  9. Accuracy of ship : 65 %
  10. Accuracy of truck : 46 %