在前面的理论讲解和网络实现中,我们断断续续的学习了 Tensorflow 和 keras 两个著名的深度学习框架。当然主要还是 Tensorflow,keras 的底层计算都是以 Tensorflow 为后端的。在正式进入下一环节的学习前,笔者先给 pytorch 入个门,至于系统的学习,还是需要依靠各种项目实战来锻炼。
pytorch 是一款可以媲美于 Tensorflow 优秀的深度学习计算框架,但又相比于 Tensorflow 在语法上更具备灵活性。pytorch 原生于一款小众语言 lua,而后基于 python 版本后具备了强大的生命力。作为一款基于 python 的深度学习计算库,pytorch 提供了高于 numpy 的强大的张量计算能力和兼具灵活度和速度的深度学习研究功能。
下面笔者就以 pytorch 基本张量运算、自动求导机制和基于 LeNet-5 的训练实例对 pytorch 进行一个快速的入门和上手。
torch 的张量运算
和学习 Tensorflow 中的张量 tensor 一样,torch 的张量运算也可以理解为 numpy 科学计算的加强版。底层的计算逻辑基本一致,torch 张量的强大之处可以利用 GPU 来加速运算。
x = torch.Tensor(2, 3)
print(x)
获取矩阵的大小:
print(x.size())
torch.Size([2, 3])
执行张量运算:
y = torch.rand(2, 3)
print(x + y)
或者是提供一种指定输出张量的运算语法:
result = torch.Tensor(2, 3)
torch.add(x, y, out = result)
print(result)
当然 torch 也可以方便的与 numpy 数组进行转换。
a = torch.ones(5).numpy()
print(a)
[1. 1. 1. 1. 1.]
numpy 数组转为 torch 张量:
import numpy as np
print(torch.from_numpy(np.ones(5)))
使用 .cuda 方法将 tensor 在 GPU 上运行:
if torch.cuda.is_available():
x = x.cuda()
y = y.cuda()
x + y
由上述操作可见,torch 的张量运算和 numpy 一样非常简单,相较于 tensorflow 的张量运算要更加灵活。
自动求导
在神经网络的反向传播中涉及了大量的求导运算,pytorch 中求导的核心计算模块 autograd 可以帮助我们快速实现复杂的求导运算。而求导运算又是建立在 torch 变量 Variable 基础之上的,Variable 对 torch 的 Tensor 进行了包装,当神经网络的结构和前向计算完成后,可以方便的对变量调用 backward 方法执行反向计算。
from torch.autograd import Variable
x = Variable(torch.ones(2, 3), requires_grad = True)
print(x)
执行梯度计算:
y = x + 2z = y * y * 5out = z.mean()
out.backward()
print(x.grad)
需要注意的是 torch 计算梯度时关于目标变量的梯度计算的表达方式为 Variable.grad
。
基于 torch 的 LeNet-5 训练 cifar10
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')
torchvision 是用来服务于 torch 包的,用于生成、转换和准备预训练模型。cifar10 数据集:
定义 LeNet-5 网络结构:
from torch.autograd import Variable
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()
定义损失函数和优化器:
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
训练 LeNet-5:
for epoch in range(5):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get input data
inputs, labels = data
# variable the data
inputs, labels = Variable(inputs), Variable(labels)
# gradients zeros
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print model train info
running_loss += loss.data[0]
if i % 2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
在测试集上展示训练效果:
import matplotlib.pyplot as plt
def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
# test the net on test datasets# ground truth
dataiter = iter(testloader)
images, labels = dataiter.next()
# print image
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
看看 LeNet-5 的预测结果:
# the net predict result
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
(‘Predicted: ‘, ‘ cat ship ship plane’)
貌似训练效果很好。再来看一下模型在全部测试集上的表现:
# test on all test data
correct = 0
total = 0
for data in testloader:
images, labels = data
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))
Accuracy of the network on the 10000 test images: 61 %
再看看模型在每一类别上的分类准确率的表现:
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
for data in testloader:
images, labels = data
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i]
class_total[label] += 1
for i in range(10):
print('Accuracy of %5s : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))
可见模型在猫和鸟等小型动物上分类效果较差,在车船飞机等大型物体上效果较好。该实例来自于 pytorch 的官方 tutorial 60 分钟快速上手文档,能够让大家非常快速的入门学习 pytorch 。 在对神经网络的基本原理有深刻理解的基础上,对比之前的 tensorflow 和 keras,相信大家都能快速掌握 pytorch 。
参考资料:
http://pytorch.org/tutorials/
http://pytorch.apachecn.org/cn/tutorials/
往期精彩:
一个数据科学从业者的学习历程