Pytorch 入门与提高(2)—通过 slice 来创建 Tensor

4,290 阅读6分钟

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

我们可以通过在现有的 Tensor 基础上通过选择,或者更形象地去说在某一个维度上切分(slice)一下,从而保留下想要数据 tensor,从而类似创建得到了一个新的 Tensor。今天理论少一些,coding 多一些,通过实例帮助大家理解如何选择合适 torch 提供的方法来 slice tensor。

tensor_003.jpg

import numpy as np
import torch
# indexing
a = torch.rand(4,3,28,28)

首先我们创建一个矩阵 4×3×28×284 \times 3 \times 28 \times 28, 这里使用了 torch.rand 来创建,返回一个由区间 [0,1)[0,1) 上的均匀分布的随机数组成的 tensor。改矩阵实际意义是输入到模型一个小批次图片,4 表示一次输入图片数量,3 表示图像的通道数,28×2828 \times 28 表示图像宽和高。

如何获取 Tensor 中某一个值

a[0,0].shape

a[0,0] 表示从 1 张图片去除其第 1 个通道,也就是 28×2828 \times 28 表示图像一个通道图片

    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 个维度,可能超出大家想象,如图

我们把通道数、宽度和长度理解立方体的高、宽和长 channle×hight×weihgtchannle \times hight \times weihgt 也就是 (3,28,28)(3, 28, 28) 而再高一个维度就是可以理解 2 两个立方体,那么如果再高一个维度呢?假设是 2×2×3×28×282 \times 2 \times 3 \times 28 \times 28, 其实很简单,可以其前一个维度想象为一个整体,放在 box 里面,然后有 2 个这样 box ,依次类推,

结合图再解释 2×2×3×28×282 \times 2 \times 3 \times 28 \times 28一下,这是 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 位置的图片组成得到结果为 2×3×28×282 \times 3 \times 28 \times 28

torch.Size([2, 3, 28, 28])
a.index_select(1,torch.tensor([1,2])).shape

从 tensor 第 2 维度选择 1 和 2 位置,也就是对于所有图片选择其 1 和 2 通道得到 tensor 为 4×2×28×284 \times 2 \times 28 \times 28

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]])