近几年,人工智能的深度学习大热,相关从业者的薪资水平明显高于不少行业,这主要是因为深度学习领域对从业者的要求较高,相关人才有些供不应求——从业者要具备扎实的数学功底,还要熟悉所使用的硬件平台特性,掌握一门扎实的编程语言,尽可能高效的把理论付诸实践。
同样的任务,可能水平欠佳的程序员设计的算法需要几天才能完成,而较高水准的算法几小时就完成了。
好在,愈发成熟的各种深度学习框架大大方便了从业者,从业者甚至无需再关心硬件平台,也无需再把大量精力花费在算法设计上,就能轻易的以不错的性能实现自己的深度学习网络。
关于深度学习框架,我较深入的使用过 Caffe、tensorflow,但是最终还是选择了 PyTorch。
PyTorch 是什么
我认为,与其说 PyTorch 是一个深度学习框架,倒不如说它是一个深度学习库,这一点可以在以后的使用中得出。按照官网上的说法,PyTorch 是一个 Python 科学计算包,有两类受众:
- 替换 NumPy 库,更方便的使用 GPUs 高效计算
- 充当深度学习研究平台,最大限度的提供灵活性和效率
关于 PyTorch 的安装,可以参考官网,已经非常傻瓜式了,本文就不赘述了。
开始
Tensors(张量)
PyTorch 中的 tensor 类似于 NumPy 中的 ndarray,只不过 tensor 能够更加方便的使用 GPU 加速运算,在使用 tensor 之前,需要导入 PyTorch 包:
import torch
创建一个没有初始化的 5x3 的矩阵:
x = torch.empty(5, 3)
print(x)
输出如下:
tensor([[-7.1077e+12, 4.5558e-41, 4.2319e-11],
[ 3.0942e-41, 1.0177e+31, 2.2017e+12],
[ 2.8488e-14, 9.6809e+24, 1.0880e-19],
[ 1.0874e-19, 2.5443e+30, 9.4727e+21],
[ 2.4835e+27, 2.5428e+30, 1.0877e-19]])
创建一个随机初始化的矩阵:
x = torch.rand(5, 3)
print(x)
输出如下:
tensor([[0.6363, 0.5365, 0.6489],
[0.8479, 0.3439, 0.3546],
[0.8024, 0.7198, 0.8319],
[0.6590, 0.1750, 0.7466],
[0.8664, 0.7331, 0.1298]])
创建一个数据类型为 torch.long 的全零矩阵:
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
输出如下:
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
直接从现有数据构造矩阵:
x = torch.tensor([5.5, 3])
print(x)
输出如下:
tensor([5.5000, 3.0000])
或者基于一个现有 tensor 创建新矩阵,下面这些方法将会重用输入 tensor 的属性,比如数据结构等,除非用户指定的新值:
x = x.new_ones(5, 3, dtype=torch.double)
# new_* 方法使用不同的 size
print(x)
x = torch.randn_like(x, dtype=torch.float)
# 覆盖数据类型,但是 size 保持不变
print(x)
输出如下:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 0.2295, 0.2996, 0.0104],
[ 0.5513, 0.0489, 0.3046],
[-0.0529, 0.8116, -0.2754],
[ 2.3672, 0.1456, -1.3679],
[ 0.4731, 0.0102, 0.9750]])
获取它的 size:
print(x.size())
输出如下:
torch.Size([5, 3])
torch.Size 实际上是一个 tuple,所以它支持所有的 tuple 操作。
Operations(操作)
PyTorch 中的操作有多个语法,在接下来的例子中,我们将通过加法操作了解这一点:
加法:语法 1
y = torch.rand(5, 3)
print(x + y)
输出:
tensor([[ 0.7623, 0.4833, 0.7200],
[ 0.8453, 0.4220, 1.2202],
[ 0.8108, 1.6514, 0.3858],
[ 2.7137, 0.1767, -1.2774],
[ 0.7659, 0.4483, 1.2968]])
加法:语法2
print(torch.add(x, y))
输出:
tensor([[ 0.7623, 0.4833, 0.7200],
[ 0.8453, 0.4220, 1.2202],
[ 0.8108, 1.6514, 0.3858],
[ 2.7137, 0.1767, -1.2774],
[ 0.7659, 0.4483, 1.2968]])
加法:通过参数的形式把结果输出给指定张量
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)
输出:
tensor([[ 0.7623, 0.4833, 0.7200],
[ 0.8453, 0.4220, 1.2202],
[ 0.8108, 1.6514, 0.3858],
[ 2.7137, 0.1767, -1.2774],
[ 0.7659, 0.4483, 1.2968]])
加法:in-place 操作
# adds x to y
y.add_(x)
print(y)
输出:
tensor([[ 0.7623, 0.4833, 0.7200],
[ 0.8453, 0.4220, 1.2202],
[ 0.8108, 1.6514, 0.3858],
[ 2.7137, 0.1767, -1.2774],
[ 0.7659, 0.4483, 1.2968]])
通过
_
符号,PyTorch 中的操作将变为 in-place 操作,举个例子:x.copy(y),x.t(),这两个操作的最终结果会作用在 x 上。
可以像标准的 NumPy 库那样对 tensor 使用切片操作:
print(x[:, 1])
输出:
tensor([0.2996, 0.0489, 0.8116, 0.1456, 0.0102])
resize:如果希望对 tensor 实现 resize/reshape 操作,可以使用 torch.view
:
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # the size -1 is inferred from other dimensions
print(x.size(), y.size(), z.size())
输出:
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
如果 tensor 中只有一个元素,可以使用 .item()
把这个元素以 Python 标准 number 的形式取出:
x = torch.randn(1)
print(x)
print(x.item())
输出:
tensor([-1.4181])
-1.4181468486785889
更多的操作可以参考官方文档。
与 NumPy 互相转换
PyTorch 中的 Tensor 和 NumPy 中的 array 可以共享一块内存去区域(当 tensor 位于 CPU 中时),改变其中一个,另一个也会随之改变。
将 Tensor 转换为 Numpy 中的 Array
a = torch.ones(5)
print(a)
输出:
tensor([1., 1., 1., 1., 1.])
将张量 a 转换为 numpy 是非常简单的:
b = a.numpy()
print(b)
输出:
[1. 1. 1. 1. 1.]
正如刚才所说,此时 a、b 共享一块数据内存,我们改变 a,b也会随之改变:
a.add_(1)
print(a)
print(b)
输出:
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
将 NumPy 中的 Array 转换为 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 类型的 Tensor,CPU 中的其他所有 Tensor 都支持与 NumPy 互相转换。
CUDA Tensor
PyTorch 中的 Tensor 可以简单的通过 .to()
方法移到任意的 device 中:
# 下面的代码只有当 CUDA 可用时才运行
if torch.cuda.is_available():
device = torch.device("cuda")
# 直接在 GPU 上创建张量
y = torch.ones_like(x, device=device)
# 或者使用 .to 方法
x = x.to(device)
z = x + y
print(z)
# .to 方法同时也可以改变数据类型
print(z.to("cpu", torch.double))
输出:
tensor([-0.4181], device='cuda:0')
tensor([-0.4181], dtype=torch.float64)
本文主要参考 PyTorch 官方文档: https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py