计算机视觉重要框架总结剖析
第一章 Pytorch框架
前言
PyTorch 是由 Facebook 主导开发的深度学习框架,因其高效的计算过程以及良好的易用性被诸多大公司和科研人员所喜爱。本次实验中,我们将学习 PyTorch 的基础语法,了解 Autograd 自动求导机制,并最终利用 PyTorch 构建可用于图像分类任务的人工神经网络。
一、什么是PyTorch?
PyTorch 是 Facebook 主导开发的,基于 Python 的科学计算包,其主要有以下两个特点:
- 比 NumPy 更灵活,可以使用 GPU 的强大计算能力。
- 开源高效的深度学习研究平台。
二、PyTorch 基础语法
(1) 张量
Tensors(张量)与 NumPy 中的 Ndarrays 多维数组类似,但是在 PyTorch 中 Tensors 可以使用 GPU 进行计算。
下面,我们创建一个 5×3 矩阵。使用 torch.empty 可以返回填充了未初始化数据的张量。张量的形状由可变参数大小定义。
代码如下(示例):
import torch
torch.empty(5, 3)
创建一个随机初始化的矩阵:
torch.rand(5, 3)
创建一个 0 填充的矩阵,指定数据类型为 long:
torch.zeros(5, 3, dtype=torch.long)
创建 Tensor 并使用现有数据初始化:
x = torch.tensor([5.5, 3])
根据现有张量创建新张量。这些方法将重用输入张量的属性,除非设置新的值进行覆盖。
x = x.new_ones(5, 3, dtype=torch.double) # new_* 方法来创建对象
覆盖 dtype,对象的 size 是相同的,只是值和类型发生了变化:
x = torch.randn_like(x, dtype=torch.float)
获取张量的 size:
x.size()
操作
针对 Tensor 的操作语法很多,我们先看一下加法运算。其中,加法运算方法就有好多种:
第一种加法运算:
y = torch.rand(5, 3)
print(x + y)
第二种加法运算:
torch.add(x, y)
提供输出 Tensor 作为参数:
result = torch.empty(5, 3)
torch.add(x, y, out=result)
替换:
y.add_(x) # 将 x 加到 y
小贴士
任何以下划线结尾的操作都会用结果替换原变量。例如:x.copy_(y), x.t_(), 都会改变 x。
你可以使用与 NumPy 索引方式相同的操作来进行对张量的操作:
x[:, 1]
torch.view 可以改变张量的维度和大小:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # size -1 从其他维度推断
print(x.size(), y.size(), z.size())
如果张量只有一个元素,使用 .item() 来得到 Python 数据类型的数值:
x = torch.randn(1)
print(x, x.item())
更多关于 Tensor 操作,包括:转置,索引,切片,数学运算,线性代数,随机数等,可以进一步阅读官方文档。
(2) NumPy 转换
将 PyTorch 张量转换为 NumPy 数组(反之亦然)是一件轻而易举的事。PyTorch 张量和 NumPy 数组将共享其底层内存位置,改变一个也将改变另一个。
将 PyTorch 张量转换为 NumPy 数组:
a = torch.ones(5)
b = a.numpy()
了解 NumPy 数组的值如何变化:
a.add_(1)
print(a, b)
NumPy 数组转换成 PyTorch 张量时,可以使用 from_numpy 完成:
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a, b)
所有的 Tensor 类型默认都是基于 CPU, CharTensor 类型不支持到 NumPy 的转换。
(3) CUDA 张量
CUDA 张量是能够在 GPU 设备中运算的张量。使用 .to 方法可以将 Tensor 移动到 GPU 设备中:
# is_available 函数判断是否有 GPU 可以使用
if torch.cuda.is_available():
device = torch.device("cuda") # torch.device 将张量移动到指定的设备中
y = torch.ones_like(x, device=device) # 直接从 GPU 创建张量
x = x.to(device) # 或者直接使用 .to("cuda") 将张量移动到 cuda 中
z = x + y
print(z)
print(z.to("cpu", torch.double)) # .to 也会对变量的类型做更改
三、Autograd 自动求导
PyTorch 中所有神经网络的核心是 autograd。我们先简单介绍一下这个包,然后训练一个神经网络。
autograd为张量上的所有操作提供了自动求导。它是一个在运行时定义的框架,这意味着反向传播是根据你的代码来确定如何运行。torch.Tensor 是这个包的核心类。如果设置 .requires_grad 为 True,那么将会追踪所有对于该张量的操作。当完成计算后通过调用 .backward() 会自动计算所有的梯度,这个张量的所有梯度将会自动积累到 .grad 属性。这也就完成了自动求导的过程。
要阻止张量跟踪历史记录,可以调用 .detach() 方法将其与计算历史记录分离。为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad(): 语句中。这一点在评估模型时特别有用,因为模型可能具有 requires_grad=True 的可训练参数,但是我们并不需要计算梯度。
自动求导中还有另外一个重要的类 Function。Tensor 和 Function 互相连接并生成一个非循环图,其存储了完整的计算历史。
如果需要计算导数,你可以在 Tensor 上调用 .backward()。 如果 Tensor 是一个标量(即它包含一个元素数据)则不需要为 backward() 指定任何参数。但是,如果它有多个元素,你需要指定一个 gradient 参数来匹配张量的形状。
接下来,我们创建一个张量并设置 requires_grad=True 用来追踪他的计算历史:
x = torch.ones(2, 2, requires_grad=True)
对张量进行操作,也就是计算过程:
y = x + 2
结果 y
已经被计算出来了,所以,grad_fn
已经被自动生成了。
y.grad_fn
然后,再对 y
进行操作:
z = y * y * 3
out = z.mean()
print(z, out)
.requires_grad_( … ) 可以改变现有张量的 requires_grad 属性。 如果没有指定的话,默认输入的 flag 是 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)
梯度
上面只是完成了梯度的自动追踪,下面通过反向传播打印对应节点的梯度。因为 out 是一个纯量 Scalar,out.backward() 等于 out.backward(torch.tensor(1))。
out.backward()
打印其梯度:
x.grad
print(b.grad_fn)
我们可以通过数学公式来验算上面的结果:
𝑜𝑢𝑡=1/4∑𝑧𝑖
𝑧𝑖=3(𝑥𝑖+2)2
𝑧𝑖|𝑥𝑖=1=27
因此,
∂𝑜𝑢𝑡/∂𝑥𝑖=32(𝑥𝑖+2)
∂𝑜𝑢𝑡/∂𝑥𝑖|𝑥𝑖=1=92=4.5
下面,演示 Autograd 更多的操作:
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
# gradients 张量大小表示重要程度
y.backward(gradients)
print(x.grad)
如果 .requires_grad=True 但是你又不希望进行 Autograd 的计算,那么可以将变量包裹在 with torch.no_grad() 中:
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文介绍了pytorch的基本使用和几个重要概念,而pytorch还提供了训练一个ai分类模型和计算loss损失的方法,将会在后文详细介绍,请大家阅读Pytorch框架学习总结(中)继续阅读学习。
(在Pytorch框架学习总结(下)将会附带所有源码文件)