这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
我们可以通过在现有的 Tensor 基础上通过选择,或者更形象地去说在某一个维度上切分(slice)一下,从而保留下想要数据 tensor,从而类似创建得到了一个新的 Tensor。今天理论少一些,coding 多一些,通过实例帮助大家理解如何选择合适 torch 提供的方法来 slice tensor。
import numpy as np
import torch
复制代码
# indexing
a = torch.rand(4,3,28,28)
复制代码
首先我们创建一个矩阵 , 这里使用了 torch.rand
来创建,返回一个由区间 上的均匀分布的随机数组成的 tensor。改矩阵实际意义是输入到模型一个小批次图片,4 表示一次输入图片数量,3 表示图像的通道数, 表示图像宽和高。
如何获取 Tensor 中某一个值
a[0,0].shape
复制代码
a[0,0]
表示从 1 张图片去除其第 1 个通道,也就是 表示图像一个通道图片
torch.Size([28, 28])
复制代码
a[0,0,2,4]
复制代码
这里表示从表示从 1 张图片去除其第 1 个通道去取其第 3 列第 5 行对应像素值。
tensor(0.4215)
复制代码
Tensor 切片方式创建新的 Tensor
Tensor 就像一个大蛋糕,我们对其按某一个轴向进行切分,最终从一个数据中获取到想要的那一块数据。
# 从 0 开始 1 这里不包含 2
a[:2].shape
复制代码
torch.Size([2, 3, 28, 28])
复制代码
:
怎么理解这个符号,如果这个符号出现 Tensor 某一个维度(axis) 上,:
表示截取的区间符号,说到区间我们先复习一下,在计算机关于区间范围定义通常是左闭右开 这条规则在 tensor 上同样适用,:
符号左右都可以有值,符号左边表示区间起始值,右边则表示结束值。
start:end
: 表示从 start 包括 start 位置对应值到 end 前一个位置的值start:
: 表示 start 位置的值取到数组末尾,这里说数组不算严谨,不过相信大家都能够理解:end
: 表示 end 前一个位置对应值一直取到数组最开始:
: 取数组所有的值
a[:2,:1].shape
复制代码
看看这个我们可以想象一下,因为这里 4 个维度,可能超出大家想象,如图
我们把通道数、宽度和长度理解立方体的高、宽和长 也就是 而再高一个维度就是可以理解 2 两个立方体,那么如果再高一个维度呢?假设是 , 其实很简单,可以其前一个维度想象为一个整体,放在 box 里面,然后有 2 个这样 box ,依次类推,
结合图再解释 一下,这是 5 维的数据,用立方体表示前 3 维任何,然后 4 维是 2(我们是从后向前来计算维度数),也就是 2 个立方体,然后在 5 维,2 表示不同两种颜色,这个时候假设我们已经相同颜色立方体放入到一个 box 这时候也就是有两个这样 box。其实只要大家将这个理解了,也就是不难理解深度学习 tensor 中高维,这里高维空间,并不是物理上的高维空间。
torch.Size([2, 1, 28, 28])
复制代码
# 取从 1 开始到最末尾
# [0,1,2][1,2]
a[:2,1:,:,:].shape
复制代码
torch.Size([2, 2, 28, 28])
复制代码
# [0,1,2] [0,1,2] ,[-3,-2,-1]
a[:2,-1:,:,:].shape
复制代码
如果出现负数就表示不再是从前向后,而是从后向前,-1 表示从后向前开始第 2 位,也就是数组倒数第 2 位置值开始
torch.Size([2, 1, 28, 28])
复制代码
# 对于图片进行隔行采样,隔列采样
a[:,:,0:28:2,0:28:2].shape
复制代码
start:end:step
, 这里 step 取 2 表示没间隔 1 取一个值,也就是取值指针每次移动 2 个元素的位置
torch.Size([4, 3, 14, 14])
复制代码
a[:,:,::2,::2].shape
复制代码
torch.Size([4, 3, 14, 14])
复制代码
准确选择索引创建新的 Tensor
从张量的某个维度的指定位置选取数据。
a.index_select(2,torch.arange(14)).shape
复制代码
torch.Size([4, 3, 14, 28])
复制代码
a.index_select(0,torch.tensor([0,2])).shape
复制代码
从 tensor 第 1 维度选择 0 和 2 位置的图片组成得到结果为
torch.Size([2, 3, 28, 28])
复制代码
a.index_select(1,torch.tensor([1,2])).shape
复制代码
从 tensor 第 2 维度选择 1 和 2 位置,也就是对于所有图片选择其 1 和 2 通道得到 tensor 为
torch.Size([4, 2, 28, 28])
复制代码
a.index_select(2,torch.arange(8)).shape
复制代码
torch.Size([4, 3, 8, 28])
复制代码
来看一看这个省略号选择符号
...
这个符号表示在既有选择基础接受全部,这符号目的就是减少我们在选择,或者设定空间时的工作量
a[...].shape
复制代码
...
表示并没有做任何选择,将 a tensor 所有元素选取
torch.Size([4, 3, 28, 28])
复制代码
a[:1,...].shape
复制代码
这里表示只考虑第一个 1 维度,选取第 1 张图片的 tensor 数据。好处就是不用在分别每一个
torch.Size([1, 3, 28, 28])
复制代码
a[0,...].shape
复制代码
其实这个选择操作并不能看出效果,因为我们a[0,]
也有同样效果,所以现在还看不出省略号...
选择的好处
torch.Size([3, 28, 28])
复制代码
a[:,1,...].shape
复制代码
torch.Size([4, 28, 28])
复制代码
如果我们 tensor 中间维度随意间隔一定维度进行选择的情况,该省略符号就发挥作用了
a[0,...,::2].shape
复制代码
torch.Size([3, 28, 14])
复制代码
还有就是在我们从后向前通过:
在进行选择之后,我们就发现这时候...
也就派上了用场,如下
a[...,:2].shape
复制代码
torch.Size([4, 3, 28, 2])
复制代码
通过筛选的方式来创建新的 Tensor
通过条件对 tensor 进行筛选,满足条件的元素会被保留下来,而那些不满条件元素将被过滤掉,保留下来元素最终会一个 1 维数组形状 tensor 返回。
x = torch.randn(3,4)
x
复制代码
tensor([[-0.1850, 1.3697, -1.0597, -0.5774],
[-0.2925, 0.9569, 0.1281, 0.0644],
[ 0.9894, -0.0659, -0.7232, -0.4943]])
复制代码
import numpy as np
a_arr = np.random.randint(0,10,size=(3,4))
a_mask = a_arr >5
a_mask
复制代码
a_mask = a_arr >5
通过返回 True 和 False 所组成 mask 随后我们可以利用这个 mask 对 tensor 进行过滤,True 对应位置值会被保留而 False 所对应位置并不会被保留。
array([[False, False, True, True],
[False, False, False, False],
[ True, True, False, False]])
复制代码
# np.random.randint?
复制代码
mask = x.ge(0.5)
复制代码
mask
复制代码
tensor([[False, True, False, False],
[False, True, False, False],
[ True, False, False, False]])
复制代码
torch.masked_select(x,mask)
复制代码
经过 mask 筛选保留实现元素维 1 维 tensor 返回,这个不难想象,讲过筛选保留的元素无法保留原有的形状。
tensor([1.3697, 0.9569, 0.9894])
复制代码
torch.masked_select(x,mask).shape
torch.masked_select(x,mask).dim()
复制代码
1
复制代码
也可以通过 tensor 操作运算获取新的 Tensor
tensor_1 = torch.randint(low=0,high=10,size=(3,3))
tensor_2 = torch.randint(low=0,high=10,size=(3,3))
复制代码
tensor_1
复制代码
tensor([[0, 6, 5],
[7, 0, 2],
[8, 7, 6]])
复制代码
tensor_2
复制代码
tensor([[0, 4, 7],
[5, 2, 8],
[2, 9, 0]])
复制代码
tensor_add_res = tensor_1 + tensor_2
复制代码
tensor_add_res
复制代码
tensor([[ 0, 10, 12],
[12, 2, 10],
[10, 16, 6]])
复制代码
复制代码