Tensor基础3——张量的索引与变形

1. 张量的索引操作

  • 在操作张量时,经常要去获取某些元素进行处理或者修改操作,在这里需要了解torch中的索引操作。

准备数据:

1
2
3
4
5
6
7
8
import torch
# 随机生成数据
data = torch.randint(0, 10, [4, 5])
print(data)
>>> tensor([[0, 7, 6, 5, 9],
[6, 8, 3, 1, 0],
[6, 3, 8, 7, 3],
[4, 9, 5, 3, 1]])
1)简单行列索引的使用
1
2
3
4
5
print(data[0])
>>> tensor([0, 7, 6, 5, 9])

print(data[:, 0])
>>> tensor([0, 6, 6, 4])
2)列表索引的使用
1
2
3
4
5
6
7
8
# 返回 (0, 1)、(1, 2) 两个位置的元素
print(data[[0, 1], [1, 2]])
>>> tensor([7, 3])

# 返回 0、1 行的 1、2 列共4个元素
print(data[[[0], [1]], [1, 2]])
>>> tensor([[7, 6],
[8, 3]])
3)范围索引的使用
1
2
3
4
5
6
7
8
9
10
# 前3行的前2列数据
print(data[:3, :2])
>>> tensor([[0, 7],
[6, 8],
[6, 3]])

# 第2行到最后的前2列数据
print(data[2:, :2])
>>> tensor([[6, 3],
[4, 9]])
4)布尔索引的使用
1
2
3
4
5
6
7
8
9
10
11
# 第三列大于5的行数据
print(data[data[:, 2] > 5])
>>> tensor([[0, 7, 6, 5, 9],
[6, 3, 8, 7, 3]])

# 第二行大于5的列数据
print(data[:, data[1] > 5])
>>> tensor([[0, 7],
[6, 8],
[6, 3],
[4, 9]])
5)多维索引的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
data = torch.randint(0, 10, [3, 4, 5])
print(data)
>>> tensor([[[2, 4, 1, 2, 3],
[5, 5, 1, 5, 0],
[1, 4, 5, 3, 8],
[7, 1, 1, 9, 9]],

[[9, 7, 5, 3, 1],
[8, 8, 6, 0, 1],
[6, 9, 0, 2, 1],
[9, 7, 0, 4, 0]],

[[0, 7, 3, 5, 6],
[2, 4, 6, 4, 3],
[2, 0, 3, 7, 9],
[9, 6, 4, 4, 4]]])

# 获取0轴上的第一个数据
print(data[0, :, :])
>>> tensor([[2, 4, 1, 2, 3],
[5, 5, 1, 5, 0],
[1, 4, 5, 3, 8],
[7, 1, 1, 9, 9]])

# 获取1轴上的第一个数据
print(data[:, 0, :])
>>> tensor([[2, 4, 1, 2, 3],
[9, 7, 5, 3, 1],
[0, 7, 3, 5, 6]])

# 获取2轴上的第一个数据
print(data[:, :, 0])
>>> tensor([[2, 5, 1, 7],
[9, 8, 6, 9],
[0, 2, 2, 9]])

2. 张量的形状操作

有重塑、堆叠、挤压和解压:

方法 单行描述
torch.reshape(input, shape) 重塑 input 到 shape (如果兼容),也可以使用 torch.Tensor.reshape()。
tensor.view(shape) 返回不同 shape 中的原始张量视图,但与原始张量共享相同的数据。
tensor.contiguous() 将张量转换到整块内存上
torch.stack(tensors, dim=0) 沿着新的维度(dim)连接 tensors 的序列,所有 tensors 必须具有相同的大小。
torch.squeeze(input) 挤压 input 以移除值为 1 的所有尺寸。
torch.unsqueeze(input, dim) 返回在 dim 处添加了维度值 1 的 input。
torch.transpose(input,dim1,dim2) 实现交换张量形状的指定维度
torch.permute(input, dims) 返回原始 input 的视图,其尺寸被置换(重新排列)为 dims。

深度学习模型(神经网络)都是以某种方式操纵张量。由于矩阵乘法的规则,如果形状不匹配,就会遇到错误。这些方法可帮助您确保张量的正确元素与其他张量的正确元素混合。

举例说明:

1
2
3
4
5
6
7
import torch
x = torch.arange(1., 8.)
print(x)
>>> tensor([1., 2., 3., 4., 5., 6., 7.])

print(x.shape)
>>> torch.Size([7])
1)RESHAPE

reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状。

使用 torch.reshape() 增加一个维度。

1
2
3
4
5
6
7
# 增加一个维度
x_reshaped = x.reshape(1, 7)
print(x_reshaped)
>>> tensor([[1., 2., 3., 4., 5., 6., 7.]])

print(x_reshaped.shape)
>>> torch.Size([1, 7])

使用 torch.reshape() 改变张量的形状。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import torch

data = torch.tensor([[10, 20, 30], [40, 50, 60]])
# 1. 使用 shape 属性或者 size 方法都可以获得张量的形状
print(data.shape, data.shape[0], data.shape[1])
>>> torch.Size([2, 3]) 2 3

print(data.size(), data.size(0), data.size(1))
>>> torch.Size([2, 3]) 2 3

# 2. 使用 reshape 函数修改张量形状
new_data = data.reshape(1, 6)
print(new_data.shape)
>>> torch.Size([1, 6])
2)VIEW / CONTIGUOUS
  • view 函数也可以用于修改张量的形状,只能用于存储在整块内存中的张量
  • 在 PyTorch 中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中,view 函数无法对这样的张量进行变形处理。例如: 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。
  • 此时需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作
# 若要使用view函数, 需要使用contiguous() 变成连续以后再使用view函数
# 2 判断张量是否使用整块内存
data = torch.tensor( [[10, 20, 30],[40, 50, 60]])
print('data--->', data, data.shape)
>>> data---> tensor([[10, 20, 30],
[40, 50, 60]]) torch.Size([2, 3])

# 1 判断是否使用整块内存
print(data.is_contiguous())
>>> True

# 2 view
mydata2 = data.view(3, 2)
print('mydata2--->', mydata2, mydata2.shape)
>>> mydata2---> tensor([[10, 20],
[30, 40],
[50, 60]]) torch.Size([3, 2])

# 3 判断是否使用整块
print('mydata2.is_contiguous()--->', mydata2.is_contiguous())
>>> mydata2.is_contiguous()---> True


# 4 使用 transpose 函数修改形状
mydata3 = torch.transpose(data, 0, 1)
print('mydata3--->', mydata3, mydata3.shape)
>>> mydata3---> tensor([[10, 40],
[20, 50],
[30, 60]]) torch.Size([3, 2])

print('mydata3.is_contiguous()--->', mydata3.is_contiguous())
>>> mydata3.is_contiguous()---> False

# 5 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数
print (mydata3.contiguous().is_contiguous())
>>> True

mydata4 = mydata3.contiguous().view(2, 3)
print('mydata4--->', mydata4.shape, mydata4)
>>> mydata4---> torch.Size([2, 3]) tensor([[10, 40, 20],
[50, 30, 60]])
3)STACK

如果想将新张量堆叠五次,使用 torch.stack() 来实现。

1
2
3
4
5
6
# Stack tensors on top of each other
x_stacked = torch.stack([x, x, x, x], dim=0) # 同pandas的axis,按行堆叠
>>>tensor([[5., 2., 3., 4., 5., 6., 7.],
[5., 2., 3., 4., 5., 6., 7.],
[5., 2., 3., 4., 5., 6., 7.],
[5., 2., 3., 4., 5., 6., 7.]])
4)SQUEEZE / UNSQUEEZE

squeeze 函数删除形状为 1 的维度(降维),unsqueeze 函数添加形状为1的维度(升维)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mydata1 = torch.tensor([1, 2, 3, 4, 5])             
print('mydata1--->', mydata1.shape, mydata1) # 一个普通的数组 1维数据
>>> mydata1---> torch.Size([5]) tensor([1, 2, 3, 4, 5])

mydata2 = mydata1.unsqueeze(dim=0)
print('在0维度上 拓展维度:', mydata2, mydata2.shape) #1*5
>>> 0维度上 拓展维度: tensor([[1, 2, 3, 4, 5]]) torch.Size([1, 5])

mydata3 = mydata1.unsqueeze(dim=1)
print('在1维度上 拓展维度:', mydata3, mydata3.shape) #5*1
>>> 1维度上 拓展维度: tensor([[1],
[2],
[3],
[4],
[5]]) torch.Size([5, 1])


mydata4 = mydata1.unsqueeze(dim=-1)
print('在-1维度上 拓展维度:', mydata4, mydata4.shape) #5*1
>>> 在-1维度上 拓展维度: tensor([[1],
[2],
[3],
[4],
[5]]) torch.Size([5, 1])

mydata5 = mydata4.squeeze()
print('压缩维度:', mydata5, mydata5.shape) #1*5
>>> 压缩维度: tensor([1, 2, 3, 4, 5]) torch.Size([5])
5)TRANSPOSE/ PERMUTE

transpose 函数可以实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) 可以通过 transpose 函数把 3 和 4 进行交换, 将张量的形状变为 (2, 4, 3) 。 permute 函数可以一次交换更多的维度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print('data shape:', data.size())
>>> data shape: torch.Size([3, 4, 5])

# 1 交换1和2维度
mydata2 = torch.transpose(data, 1, 2)
print('mydata2.shape--->', mydata2.shape)
>>> mydata2.shape---> torch.Size([3, 5, 4])

# 2 将data 的形状修改为 (4, 5, 3), 需要变换多次
mydata3 = torch.transpose(data, 0, 1)
mydata4 = torch.transpose(mydata3, 1, 2)
print('mydata4.shape--->', mydata4.shape)
>>> mydata4.shape---> torch.Size([4, 5, 3])

# 3 使用 permute 函数将形状修改为 (4, 5, 3)
# 3-1 方法1
mydata5 = torch.permute(data, [1, 2, 0])
print('mydata5.shape--->', mydata5.shape)
>>> mydata5.shape---> torch.Size([4, 5, 3])

# 3-2 方法2
mydata6 = data.permute([1, 2, 0])
print('mydata6.shape--->', mydata6.shape)
>>> mydata6.shape---> torch.Size([4, 5, 3])
6)总结

<1> reshape 函数可以在保证张量数据不变的前提下改变数据的维度

<2> squeeze 和 unsqueeze 函数可以用来增加或者减少维度

<3> transpose 函数可以实现交换张量形状的指定维度, permute 可以一次交换更多的维度

<4> view 函数也可以用于修改张量的形状, 但是它要求被转换的张量内存必须连续,所以一般配合 contiguous 函数使用

3. 张量的拼接操作

  • torch.cat()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import torch
data1 = torch.randint(0, 10, [1, 2, 3])
data2 = torch.randint(0, 10, [1, 2, 3])
print(data1)
>>> tensor([[[7, 8, 7],
[6, 3, 6]]])



print(data2)
>>> tensor([[[3, 6, 5],
[7, 5, 0]]])


# 1. 按0维度拼接
new_data = torch.cat([data1, data2], dim=0)
print(new_data)
>>> tensor([[[7, 8, 7],
[6, 3, 6]],

[[3, 6, 5],
[7, 5, 0]]])

print(new_data.shape)
>>> torch.Size([2, 2, 3])

# 2. 按1维度拼接
new_data = torch.cat([data1, data2], dim=1)
print(new_data)
>>> tensor([[[7, 8, 7],
[6, 3, 6],
[3, 6, 5],
[7, 5, 0]]])

print(new_data.shape)
>>> torch.Size([1, 4, 3])

# 3. 按2维度拼接
new_data = torch.cat([data1, data2], dim=2)
print(new_data)
>>> tensor([[[7, 8, 7, 3, 6, 5],
[6, 3, 6, 7, 5, 0]]])

print(new_data.shape)
>>> torch.Size([1, 2, 6])

Tensor基础3——张量的索引与变形
https://linxkon.github.io/Tensor基础3--张量的索引与变形.html
作者
linxkon
发布于
2021年2月13日
许可协议