决定好好整理下学习的有关 Pytorch 的内容。系统性的梳理和笔记对我这种只有七秒记忆的来说真的太重要,撸起袖子加油干!!!
我一直看的文档是这个 Pytorch官方教程中文版(提取码:pyto) ,以及参考百度来的各种博客。如果本文的某些内容存在错误或其他问题,希望各位大佬能够帮我指出来,互相学习共同进步!
共勉!
————————————————————————————————————
0. Tensors 简介
Tensors 是一种特殊的数据结构,类似于数组(array)和矩阵(matrices),可以将 Tensors 想象成 NumPy 的 ndarray,两者的不同点在于 Tensors 可以在GPU或其他硬件加速器上运行。
那么Tensors 与 PyTorch 的关联是什么?PyTorch 是一个深度学习框架,通常用来搭建网络模型,其中模型的输入和输出以及模型的参数都是使用 Tensors 进行的编码,所以 Tensors 是学好 PyTorch 必备的基础知识。
正文开始前先把 torch 定义的10 种数据类型 放在这里。
1. Tensor的创建
1.1 torch.tensor() 构造函数
torch.tensor() 本质是一个构造函数,能够利用已经存在的数据创建一个 tensor,这里的数据可以是 Python 列表或序列。
>>> import torch
>>> data=[[1,2],[3,4]]
>>> x_data=torch.tensor(data)
>>> x_data
tensor([[1, 2],
[3, 4]])
1.2 torch.* ( * 代表 tensor creation ops)
创建具有特定大小或数据的 tensor,例如:
>>> torch.rand((2,3))
tensor([[0.4003, 0.6610, 0.7162],
[0.2321, 0.3320, 0.1750]])
>>> torch.randn((2,3))
tensor([[-0.4969, -0.9932, 0.6773],
[-2.4680, -0.1342, 1.2729]])
>>> torch.randint(0,10,(2,3))
tensor([[0, 4, 3],
[1, 6, 9]])
>>> torch.zeros((2,3))
tensor([[0., 0., 0.],
[0., 0., 0.]])
>>> torch.ones((2,3))
tensor([[1., 1., 1.],
[1., 1., 1.]])
>>> torch.empty((2,3))
tensor([[0.0000e+00, 0.0000e+00, 1.4013e-45],
[0.0000e+00, 1.4013e-45, 0.0000e+00]])
>>> torch.full((2,3), 3.14)
tensor([[3.1400, 3.1400, 3.1400],
[3.1400, 3.1400, 3.1400]])
1.3 torch.*_like ( * 代表 tensor creation ops)
创建与另一个 tensor 具有相同大小 (和相似类型) 的 tensor,例如:
>>> x_data = torch.tensor([[1,2],[3,4]])
>>> torch.rand_like(x_data,dtype=torch.float)
tensor([[0.9007, 0.4228],
[0.7481, 0.3723]])
>>> torch.randn_like(x_data,dtype=torch.float)
tensor([[-0.3384, 0.8430],
[-0.7049, 0.8661]])
>>> torch.randint_like(x_data,0,10)
tensor([[8, 0],
[8, 8]])
>>> torch.zeros_like(x_data)
tensor([[0, 0],
[0, 0]])
>>> torch.ones_like(x_data)
tensor([[1, 1],
[1, 1]])
>>> torch.empty_like(x_data)
tensor([[0, 0],
[0, 0]])
>>> torch.full_like(x_data,3.14)
tensor([[3, 3],
[3, 3]])
# 生成的tensor之所以是由3填充而不是3.14的原因在于:
# x_data的数据类型是torch.int64
# 而torch.full_like()在不给定参数dtype时会生成与x_data大小和类型都相同的tensor
1.4 tensor.new_* ( * 代表 tensor creation ops)
创建与其他 tensor 具有相似类型但大小不同的 tensors,例如:
new_tensor(data, dtype=None, device=None, requires_grad=False)
>>> tensor = torch.ones((2,), dtype=torch.int8)
>>> data = [[0, 1], [2, 3]]
>>> tensor.new_tensor(data)
tensor([[ 0, 1],
[ 2, 3]], dtype=torch.int8)
new_full(size, fill_value, dtype=None, device=None, requires_grad=False)
>>> tensor = torch.ones((2,), dtype=torch.float64)
>>> tensor.new_full((3, 4), 3.141592)
tensor([[ 3.1416, 3.1416, 3.1416, 3.1416],
[ 3.1416, 3.1416, 3.1416, 3.1416],
[ 3.1416, 3.1416, 3.1416, 3.1416]], dtype=torch.float64)
new_empty(size, dtype=None, device=None, requires_grad=False)
>>> tensor = torch.ones(())
>>> tensor.new_empty((2, 3))
tensor([[ 5.8182e-18, 4.5765e-41, -1.0545e+30],
[ 3.0949e-41, 4.4842e-44, 0.0000e+00]])
new_ones(size, dtype=None, device=None, requires_grad=False)
>>> tensor = torch.tensor((), dtype=torch.int32)
>>> tensor.new_ones((2, 3))
tensor([[ 1, 1, 1],
[ 1, 1, 1]], dtype=torch.int32)
new_zeros(size, dtype=None, device=None, requires_grad=False)
>>> tensor = torch.tensor((), dtype=torch.float64)
>>> tensor.new_zeros((2, 3))
tensor([[ 0., 0., 0.],
[ 0., 0., 0.]], dtype=torch.float64)
2. Tensor的属性
2.1 tensor.shape
返回 tensor 的形状。
>>> import torch
>>> tensor=torch.randn((3,4))
>>> tensor.shape
torch.Size([3, 4])
2.2 tensor.dtype
返回 tensor 的数据类型。
>>> tensor.dtype
torch.float32
2.3 tensor.device
返回 tensor 存储的设备。
>>> tensor.device
device(type='cpu')
3. 在 GPU 上创建 tensor
一般情况下 tensors 默认是在 CPU 上创建的,如果想要在 GPU 上创建 tensor,有两种方式可选:
3.1 在 CPU 上创建后再复制到 GPU 上
>>> import torch
>>> cpu_tensor = torch.ones((2,2))
>>> cpu_tensor.device
device(type='cpu')
# 法一:利用 tensor.cuda()
>>> gpu_tensor = cpu_tensor.cuda()
>>> gpu_tensor.device
device(type='cuda', index=0)
# 法二:在确认 GPU 可用后,利用 tensor.to('cuda')
>>> gpu_tensor2 = cpu_tensor.to('cuda')
>>> gpu_tensor2.device
device(type='cuda', index=0)
3.2 使用 device 参数直接在 GPU 上创建:
>>> gpu_tensor = torch.ones((2,2), device='cuda')
>>> gpu_tensor.device
device(type='cuda', index=0)
实验证明跨设备复制 tensors 会非常的消耗时间与内存,所以~ 第二种方式速度会更快噢~ 这种方式使用的 RAM 更少,且不存在意外留下引用到CPU张量的参考的风险。
3.3 tensor.is_cuda
如果不清楚自己的 tensors 是存储在哪里,可以用 tensor.is_cuda
来判断:
# 接上面代码
>>> cpu_tensor.is_cuda
False
>>> gpu_tensor.is_cuda
True
4. Tensor 的运算
Pytorch 中有100多种 tensors 间的运算,包括 算术运算、线性代数运算、矩阵处理(如转置、索引、切片…)、采样等更复杂的运算,这些操作中的每一个都可以在GPU上运行(通常以比CPU更高的速度运行)。
太多了也不能一一列举出来,就记录下几个常用的操作。
4.1 加法操作
x+y
>>> import torch
>>> x = torch.randint(0,10,(2,2))
>>> y = torch.randint(0,10,(2,2))
>>> x + y
tensor([[17, 9],
[ 6, 6]])
torch.add(x, y)
torch.add(input, other, *, alpha=1, out=None):
out = input + alpha * other,这里的 other 可以是 tensor 或者数字,如果输入的类型为 Float Tensor 或 DoubleTensor,则 other 必须为实数,否则应为整数。
>>> torch.add(x,y)
tensor([[17, 9],
[ 6, 6]])
>>> torch.add(x,2)
tensor([[10, 7],
[ 6, 5]])
>>> torch.add(x,y,alpha=1)
tensor([[17, 9],
[ 6, 6]])
>>> torch.add(x,2,alpha=1)
tensor([[10, 7],
[ 6, 5]])
y.add_(x)
add_(other, *, alpha=1) → Tensor:
它是 torch.add()
的 in-place 版操作,这种操作会改变 tensor1 的值。
# 相当于将 x 的值加到 y 上,也就改变了 y 的值
# 但 x 的值不发生变换
# 如果操作为 x.add_(y)那么改变的就是 x 的值
# 因此这里要注意前后顺序
>>> y.add_(x)
tensor([[17, 9],
[ 6, 6]])
>>> y
tensor([[17, 9],
[ 6, 6]])
# 原本 x 的值
>>> x
tensor([[8, 5],
[4, 3]])
# 将上述改变后的 y 加到 x 上
>>> x.add_(y)
tensor([[25, 14],
[10, 9]])
# 可看到 x 发生了变化
>>> x
tensor([[25, 14],
[10, 9]])
4.2 索引与切片
import torch
tensor = torch.ones(4, 4)
print('First row: ',tensor[0])
print('First column: ', tensor[:, 0])
print('Last column: ', tensor[..., -1])
tensor[:,1] = 0
print('Changed second colunm:', tensor)
"""
Out:
First row: tensor([1., 1., 1., 1.])
First column: tensor([1., 1., 1., 1.])
Last column: tensor([1., 1., 1., 1.])
Changed second colunm: tensor([[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.],
[1., 0., 1., 1.]])
"""
4.3 Tensor 拼接
上述两个操作均能将一系列 tensors 按照给定的维度拼接在一起,除拼接的维度之外要求被拼接的 tensors 具有相同的形状或者为空。
>>> import torch
>>> tensor = torch.randint(1,3,(2,2))
>>> tensor
tensor([[1, 2],
[2, 1]])
# dim=1 表示拼接维度为 column
>>> tensor1 = torch.cat([tensor, tensor, tensor], dim=1)
>>> tensor1
tensor([[1, 2, 1, 2, 1, 2],
[2, 1, 2, 1, 2, 1]])
>>> tensor2 = torch.cat([tensor, tensor, tensor], dim=0)
# dim=0 表示拼接维度为 row
>>> tensor2
tensor([[1, 2],
[2, 1],
[1, 2],
[2, 1],
[1, 2],
[2, 1]])
4.4 单元素 Tensor
如果有一个单元素 tensor,则可以使用 tensor.item()
将其转换为 Python数值:
>>> import torch
>>> tensor = torch.randint(1,3,(2,2))
>>> tensor
tensor([[1, 2],
[2, 1]])
>>> t = tensor.sum()
>>> t
tensor(6)
>>> t.item()
6
4.5 矩阵乘法
x @ x.T
>>> import torch
>>> x = torch.randint(1,3,(2,2))
>>> x
tensor([[2, 2],
[1, 2]])
>>> y1 = x @ x.T
>>> y1
tensor([[8, 6],
[6, 5]])
x.matmul(x.T)
>>> y2 = x.matmul(x.T)
>>> y2
tensor([[8, 6],
[6, 5]])
torch.matmul(x, x.T)
>>> y3 = torch.matmul(x, x.T)
>>> y3
tensor([[8, 6],
[6, 5]])
操作结果都是相同的。
4.6 element-wise product
x * x
>>> import torch
>>> x = torch.randint(1,3,(2,2))
>>> x
tensor([[2, 2],
[1, 2]])
>>> z1 = x * x
>>> z1
tensor([[4, 4],
[1, 4]])
x.mul(x)
>>> z2 = x.mul(x)
>>> z2
tensor([[4, 4],
[1, 4]])
torch.mul(x, x)
>>> z3 = torch.mul(x, x)
>>> z3
tensor([[4, 4],
[1, 4]])
5. Numpy 与 Tensor
CPU 上的 tensors 和NumPy 数组 可以共享其基础内存位置,更改一个将更改另一个。
tensor.numpy()
将 tensor 转换为 NumPy 数组:
>>> t = torch.ones(5)
>>> t
tensor([1., 1., 1., 1., 1.])
>>> n = t.numpy()
>>> n
array([1., 1., 1., 1., 1.], dtype=float32)
- tensor 的变化会影响 NumPy 数组。
>>> t.add_(1)
tensor([2., 2., 2., 2., 2.])
>>> n
array([2., 2., 2., 2., 2.], dtype=float32)
6. 总结
关于 Pytorch 中 torch.Tensor 类还有很多知识点,一口吃不成大胖子啦,一步一个脚印的去学习会掌握的更好,冲鸭!