PyTorch的基本概念
一、什么是Pytorch,为什么选择Pytroch?
1. 什么是Pytorch
pytorch是一个基于Python的科学计算软件包,针对两组受众:
- NumPy的替代品,可以使用GPU的强大功能
- 深入学习研究平台,提供最大的灵活性和速度
PyTorch 是一个以Python 优先的深度学习框架,不仅能够实现强大的GPU 加速,同时还支持动态神经网络,这是现在很多主流框架比如Tensorflow 等都不支持的。
PyTorch 既可以看做加入了GPU 支持的numpy,同时也可以看成一个拥有自动求导功能的强大的深度神经网络,除了Facebook 之外,它还已经被Twitter、CMU 和Salesforce 等机构采用。
2. 为什么选择pytorch
面对如此多的深度学习框架,我们为何要选择PyTorch 呢?Tensorflow 不是深度学习框架默认的老大吗,为什么不直接选择Tensorflow 而是要选择PyTorch 呢?下面分4个方面来介绍为何要使用PyTorch。
(1)掌握一个框架并不能一劳永逸,现在深度学习并没有谁拥有绝对的垄断地位,就算是Google 也没有,所以只学习Tensorflow 并不够。同时现在的研究者使用各个框架的都有,如果你要去看他们实现的代码,至少也需要了解他们使用的框架,所以多学一个框架,以备不时之需。
(2)Tensorflow 与Caffe 都是命令式的编程语言,而且是静态的,首先必须构建一个神经网络,然后一次又一次使用同样的结构,如果想要改变网络的结构,就必须从头开始。但是对于PyTorch,通过一种反向自动求导的技术,可以让你零延迟地任意改变神经网络的行为,尽管这项技术不是PyTorch 独有,但目前为止它实现是最快的,能够为你任何疯狂想法的实现获得最高的速度和最佳的灵活性,这也是PyTorch 对比Tensorflow 最大的优势。
(3)PyTorch 的设计思路是线性、直观且易于使用的,当你执行一行代码时,它会忠实地执行,并没有异步的世界观,所以当你的代码出现Bug 的时候,可以通过这些信息轻松快捷地找到出错的代码,不会让你在Debug 的时候因为错误的指向或者异步和不透明的引擎浪费太多的时间。
(4)PyTorch 的代码相对于Tensorflow 而言,更加简洁直观,同时对于Tensorflow高度工业化的很难看懂的底层代码,PyTorch 的源代码就要友好得多,更容易看懂。深入API,理解PyTorch 底层肯定是一件令人高兴的事。一个底层架构能够看懂的框架,你对其的理解会更深。
最后,我们简要总结一下PyTorch 的特点:
- 支持GPU;
- 动态神经网络;
- Python 优先;
- 命令式体验;
- 轻松扩展。
二、Pytroch的安装
到这个网站根据硬件情况查看所需要安装的pytorch版本,然后用命令行安装
https://pytorch.org/get-started/locally/
三、配置Python环境
安装python解释器
四、准备Python管理器
安装anaconda,安装一些必要的包
五、通过命令行安装PyTorch
找到对应的pytorch安装命令,在命令行中输入进行安装
六、PyTorch基础概念
根据此网址学习pytorch基础知识
使用PYTORCH深度学习:60分钟闪电战
https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
- tensor
tensor与NumPy的ndarray类似,另外还有Tensors也可用于GPU以加速计算。
from __future__ import print_function
import torch
#构造一个未初始化的5x3矩阵:
x = torch.empty(5, 3)
#构造一个随机初始化的矩阵:
x = torch.rand(5, 3)
#构造一个矩阵填充的零和dtype long:
x = torch.zeros(5, 3, dtype=torch.long)
#直接从数据构造张量tensor
x = torch.tensor([5.5, 3])
#或者根据现有的张量创建张量。除非用户提供新值,否则这些方法将重用输入张量的属性,例如dtype
x = x.new_ones(5, 3, dtype=torch.double)
print(x)
x = torch.randn_like(x, dtype=torch.float)
print(x)
#得到它的大小:
print(x.size()) #torch.Size 实际上是一个元组,因此它支持所有元组操作
- 各种操作
(1)增加
#增加:语法1
y = torch.rand(5, 3)
print(x + y)
#增加;语法2
print(torch.add(x, y))
#增加:提供输出张量作为参数
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)
#增加:就地(in-place)
#add x to y
y.add(x)
print(y)
注意:任何使原位张量变形的操作都是用_后固定的。例如:x.copy_(y),x.t_(),将改变x。
#切片,输出x的第一列
print(x[:, 1])
(2)调整大小
如果要调整张量/重塑张量,可以使用torch.view
#调整大小:如果要调整张量/重塑张量,可以使用torch.view:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) #大小-1是从其他维度推断出来的
print(x.size(), y.size(), z.size())
#如果你有一个元素张量,用于.item()获取值作为Python数字
x = torch.randn(1)
print(x)
print(x.item())
- NumPy Bridge
将Torch Tensor转换为NumPy阵列(反之亦然)是一件轻而易举的事。
Torch Tensor和NumPy阵列将共享其底层内存位置(如果Torch Tensor在CPU上),更改一个将改变另一个。
(1)将Torch Tensor转换为NumPy数组
#将Torch Tensor转换为NumPy数组
a = torch.ones(5)
print(a)
b = a.numpy()
print(b)
#输出结果
tensor([1., 1., 1., 1., 1.])
[1. 1. 1. 1. 1.]
#了解numpy数组的值如何变化
a.add_(1)
print(a)
print(b)
#输出
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
(2)将NumPy数组转换为Torch Tensor
#将NumPy数组转换为Torch Tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
#输出
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
除了CharTensor之外,CPU上的所有Tensors都支持转换为NumPy并返回。
-
AUTOGRAD:AUTOMATIC DIFFERENTIATION(自动微分)
autograd包中是PyTorch中所有神经网络的核心。该autograd软件包为Tensors上的所有操作提供自动区分。它是一个逐个运行的框架,这意味着您的backprop由您的代码运行方式定义,并且每个迭代都可以不同。
torch.Tensor是包的核心类。如果将其属性设置 .requires_grad为True,则会开始跟踪其上的所有操作。完成计算后,您可以调用**.backward()并自动计算所有渐变。该张量的梯度将累积到.grad**属性中。
要阻止tensor跟踪历史记录,您可以调用**.detach()**它将其从计算历史记录中分离出来,并防止将来的计算被跟踪。
要防止跟踪历史记录(和使用内存),您还可以将代码块包装在其中。这在评估模型时尤其有用,因为模型可能具有可训练的参数 ,但我们不需要梯度。
with torch.no_grad():requires_grad=True还有一个类对于autograd实现非常重要 - a Function
Tensor与Function互相连接并构建一个非循环图,它编码完整的计算历史。每个张量都有一个
.grad_fn属性,该属性引用Function已创建的属性Tensor(除了用户创建的张量 - grad_fn is None )如果你想计算导数,你可以调用 .backward() a Tensor。如果Tensor是标量(即它包含一个元素数据),则不需要指定任何参数backward(),但是如果它有更多元素,则需要指定一个gradient 匹配形状的张量的参数。
#自动微分
import torch
#创建一个张量并设置requires_grad=True为跟踪计算
x = torch.ones(2, 2, requires_grad=True)
print(x)
#做一个张量操作:
y = x + 2
print(y)
#y是作为一个操作的结果创建的,所以它有一个grad_fn
print(y.grad_fn)
#做更多的操作 y
z = y * y * 3
out = z.mean()
print(z, out)
#.requires_grad_( ... ):requires_grad 就地改变现有的Tensor标志。False如果没有给出,输入标志默认为False
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad = True
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
#输出
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x000001FBB78C96A0>
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
False
True
<SumBackward0 object at 0x000001FBB78C9A90>
- Gradients
因为out包含单个标量,out.backward()相当于out.backward(torch.tensor(1.))
#求导
out.backward()
#打印导数 d(out)/ dx
print(x.grad)
#
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)
print()
print()
#还可以.requires_grad=True通过包装代码块来 停止在Tensors上跟踪历史记录的autogradwith torch.no_grad():
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
#输出
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
tensor([ -100.9193, 255.7541, -1418.0422], grad_fn=<MulBackward0>)
tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])
True
True
False
七、通用代码实现流程(实现一个深度学习的代码流程)
- 神经网络
可以使用torch.nn包构造神经网络
现在你已经看到了autograd,nn取决于 autograd定义模型并区分它们。一个nn.Module包含层,和一种方法forward(input),它返回output
神经网络的典型训练程序如下:
- 定义具有一些可学习参数(或权重)的神经网络
- 迭代输入数据集
- 通过网络处理输入
- 计算损失(输出距离正确多远)
- 将gradient传播回网络参数
- 通常使用简单的更新规则更新网络权重: weight = weight - learning_rate * gradient
(1)定义网络
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 3x3 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
(2)损失函数
损失函数采用(输出,目标)输入对,并计算估计输出距目标的距离的值。
nn包下有几种不同的 损失函数。一个简单的损失是:nn.MSELoss它计算输入和目标之间的均方误差。
例如:
output = net(input)
target = torch.randn(10) # a dummy target, for example
target = target.view(1, -1) # make it the same shape as output
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
(3)反向传播
要反向传播,我们所要做的就是loss.backward()。您需要清除现有渐变,否则渐变将累积到现有渐变。
现在我们call loss.backward(),看一下conv1在向后之前和之后的偏差梯度。
net.zero_grad() # zeroes the gradient buffers of all parameters
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
(4)更新权重
实践中使用的最简单的更新规则是随机梯度下降(SGD):
weight = weight - learning_rate * gradient
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
但是,当使用神经网络时,希望使用各种不同的更新规则,例如SGD,Nesterov-SGD,Adam,RMSProp等。为了实现这一点,torch.optim实现了所有这些方法。使用它非常简单:
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update
- 数据的处理
通常,当您必须处理图像,文本,音频或视频数据时,您可以使用标准的python包将数据加载到numpy数组中。然后你可以将这个数组转换成一个torch.*Tensor
- 对于图像,Pillow,OpenCV等软件包很有用
- 对于音频,包括scipy和librosa
- 对于文本,无论是原始Python还是基于Cython的加载,还是NLTK和SpaCy都很有用
特别是对于视觉,我们创建了一个名为的包 torchvision,其中包含用于常见数据集的数据加载器,如Imagenet,CIFAR10,MNIST等,以及用于图像的数据转换器,即 torchvision.datasets和torch.utils.data.DataLoader。
这提供了极大的便利并避免编写样板代码。
- 例子:训练图像分类器
我们将按顺序执行以下步骤:
- 使用加载和标准化CIFAR10训练和测试数据集 torchvision
- 定义卷积神经网络
- 定义损失函数
- 在训练数据上训练网络
- 在测试数据上测试网络
(1)加载和标准化CIFAR10
import torch
import torchvision
import torchvision.transforms as transforms
torchvision数据集的输出是范围[0,1]的PILImage图像。我们将它们转换为归一化范围的张量[-1,1]
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')
显示一些训练图像
import matplotlib.pyplot as plt
import numpy as np
# functions to show an image
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()
# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
(2)定义卷积神经网络
从神经网络部分复制神经网络并修改它以获取3通道图像(而不是定义的1通道图像)
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函数和优化器
让我们使用分类交叉熵损失和SGD动量
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
(4)训练网络
for epoch in range(2): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
(5)在测试数据上测试网络
1)第一步,让我们从测试集中显示一个图像以熟悉
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)))
2)现在让我们看看神经网络认为上面这些例子是什么
outputs = net(images)
3)输出是10分类的能量。一个分类的能量越高,网络认为图像是特定分类的越多。那么,让我们得到最高能量的指数
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
for j in range(4)))
4)让我们看看网络如何在整个数据集上执行
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))
5)表现良好的分类,以及表现不佳的分类
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]))
6)在GPU上进行训练
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# Assuming that we are on a CUDA machine, this should print a CUDA device:
print(device)
net.to(device)
#将每一步的输入和目标发送到GPU
inputs, labels = data[0].to(device), data[1].to(device)
八、参考教程
CSDN博主「博文视点」https://blog.csdn.net/broadview2006/article/details/80133047
使用PYTORCH深度学习:60分钟闪电战
https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html