前言
张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和Python原生的列表、NumPy中的数组基本一致,当然,所有不同的是,PyTorch中还定义了一种采用函数来进行索引的方式。而作为PyTorch中基本数据类型,张量即具备了列表、数组的基本功能,同时还充当着向量、矩阵、甚至是数据框等重要数据结构,因此PyTorch中也设置了非常完备的张量合并与变换的操作。
一、张量的符号索引
1、一维张量索引
import torch
t1 = torch.arange(1, 11)
print(t1)
print(t1[0])
print(t1[1: 8])
print(t1[1: 8: 2])
注:在张量的索引中,step位必须大于0
2、二维张量索引
t2 = torch.arange(1, 10).reshape(3, 3)
print(t2)
'''
tensor([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
'''
print(t2[0, 1])
print(t2[0, ::2])
print(t2[0, [0, 2]])
print(t2[::2, ::2])
'''
tensor([[1, 3],
[7, 9]])
'''
print(t2[[0, 2], 1])
3、三维张量索引
torch.index_select(张量,维度, 索引值) 这里注意喔,索引值必须是一个tensor值
t3 = torch.arange(1, 28).reshape(3, 3, 3)
print(t3)
'''
tensor([[[ 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]]])
'''
print(t3.shape)
print(t3[1, 1, 1])
print(t3[1, ::2, ::2])
'''
tensor([[10, 12],
[16, 18]])
'''
print(t3[::2, ::2, ::2])
'''
tensor([[[ 1, 3],
[ 7, 9]],
[[19, 21],
[25, 27]]])
'''
print(t3.shape)
二、张量的函数索引torch.index_select()方法
print(t1)
print(t1.ndim)
indices = torch.tensor([1, 2])
print(indices)
print(t1[1: 3])
print(t1[[1, 2]])
print(torch.index_select(t1, 0, indices))
t2 = torch.arange(12).reshape(4, 3)
print(t2)
'''
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
'''
print(t2.shape)
print(torch.index_select(t2, 0, indices))
'''
tensor([[3, 4, 5],
[6, 7, 8]])
'''
print(torch.index_select(t2, 1, indices))
'''
tensor([[ 1, 2],
[ 4, 5],
[ 7, 8],
[10, 11]])
'''
三、tensor.view()方法
“视图”的作用就是节省空间,而值得注意的是,在接下来介绍的很多切分张量的方法中,返回结果都是“视图”,而不是新生成一个对象。
t = torch.arange(6).reshape(2, 3)
print(t)
'''
tensor([[0, 1, 2],
[3, 4, 5]])
'''
te = t.view(3, 2)
print(te)
'''
tensor([[0, 1],
[2, 3],
[4, 5]])
'''
print(te[0][0])
te[0][0] = 1
print(te)
'''
tensor([[1, 1],
[2, 3],
[4, 5]])
'''
print(t)
'''
tensor([[1, 1, 2],
[3, 4, 5]])
'''
tr = t.view(1, 2, 3)
print(tr)
'''
tensor([[[1, 1, 2],
[3, 4, 5]]])
'''
四、张量的分片函数
1、分块:chunk函数
chunk函数能够按照某维度,对张量进行均匀切分,并且返回结果是原张量的视图。
t2 = torch.arange(12).reshape(4, 3)
print(t2)
'''
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
'''
te = torch.chunk(t2, 4, dim=0)
print(te)
'''
(tensor([[0, 1, 2]]), tensor([[3, 4, 5]]), tensor([[6, 7, 8]]), tensor([[ 9, 10, 11]]))
'''
print(te[0][0][0])
te[0][0][0] = 1
print(te)
'''
(tensor([[1, 1, 2]]), tensor([[3, 4, 5]]), tensor([[6, 7, 8]]), tensor([[ 9, 10, 11]]))
'''
print(t2)
'''
tensor([[ 1, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
'''
当原张量不能均分时,chunk不会报错,但会返回其他均分的结果
t3 = torch.chunk(t2, 3, dim=0)
print(t3)
'''
(tensor([[1, 1, 2],
[3, 4, 5]]),
tensor([[ 6, 7, 8],
[ 9, 10, 11]]))
'''
print(len(t3))
t4 = torch.chunk(t2, 5, dim=0)
print(t4)
2、拆分:split函数
split既能进行均分,也能进行自定义切分。当然,需要注意的是,和chunk函数一样,split返回结果也是view。
t2 = torch.arange(12).reshape(4, 3)
print(t2)
'''
tensor([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
'''
t = torch.split(t2, 2, 0)
print(t)
'''
(tensor([[0, 1, 2],
[3, 4, 5]]),
tensor([[ 6, 7, 8],
[ 9, 10, 11]]))
'''
tr = torch.split(t2, [1, 3], 0)
print(tr)
'''
(tensor([[0, 1, 2]]),
tensor([[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]]))
'''
注意,当第二个参数位输入一个序列时,序列的各数值的和必须等于对应维度下形状分量的取值。例如,上述代码中,是按照第一个维度进行切分,而t2总共有4行,因此序列的求和必须等于4,也就是1+3=4,而序列中每个分量的取值,则代表切块大小。
te = torch.split(t2, [1, 1, 1, 1], 0)
print(te)
tq = torch.split(t2, [1, 2, 1], 0)
print(tq)
'''
(tensor([[0, 1, 2]]),
tensor([[3, 4, 5],
[6, 7, 8]]),
tensor([[ 9, 10, 11]]))
'''
tw = torch.split(t2, [1, 2], 1)
print(tw)
'''
(tensor([[0],
[3],
[6],
[9]]),
tensor([[ 1, 2],
[ 4, 5],
[ 7, 8],
[10, 11]]))
'''
四、张量的合并操作
张量的合并操作类似与列表的追加元素,可以拼接、也可以堆叠。
1、cat
注意理解,拼接的本质是实现元素的堆积,也就是构成a、b两个二维张量的各一维张量的堆积,最终还是构成二维向量。
a = torch.zeros(2, 3)
print(a)
'''
tensor([[0., 0., 0.],
[0., 0., 0.]])
'''
b = torch.ones(2, 3)
print(b)
'''
tensor([[1., 1., 1.],
[1., 1., 1.]])
'''
c = torch.zeros(3, 3)
print(c)
'''
tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
'''
d = torch.cat([a, b])
print(d)
'''
tensor([[0., 0., 0.],
[0., 0., 0.],
[1., 1., 1.],
[1., 1., 1.]])
'''
e = torch.cat([a, b], 1)
print(e)
'''
tensor([[0., 0., 0., 1., 1., 1.],
[0., 0., 0., 1., 1., 1.]])
'''
2、stack堆叠
和拼接不同,堆叠不是将元素拆分重装,而是简单的将各参与堆叠的对象分装到一个更高维度的张量里。
print(a)
'''
tensor([[0., 0., 0.],
[0., 0., 0.]])
'''
print(b)
'''
tensor([[1., 1., 1.],
[1., 1., 1.]])
'''
f = torch.stack([a, b])
print(f)
'''
tensor([[[0., 0., 0.],
[0., 0., 0.]],
[[1., 1., 1.],
[1., 1., 1.]]])
'''
print(f.shape)
注意对比二者区别,拼接之后维度不变,堆叠之后维度升高。拼接是把一个个元素单独提取出来之后再放到二维张量中,而堆叠则是直接将两个二维张量封装到一个三维张量中,因此,堆叠的要求更高,参与堆叠的张量必须形状完全相同。
六、张量维度变换
此前我们介绍过,通过reshape方法,能够灵活调整张量的形状。而在实际操作张量进行计算时,往往需要另外进行降维和升维的操作,当我们需要除去不必要的维度时,可以使用squeeze函数,而需要手动升维时,则可采用unsqueeze函数。
1、squeeze函数:删除不必要的维度
a2 = a.reshape(1, 4)
print(a2)
a3 = torch.squeeze(a2)
print(a3)
print(a3.shape)
t = torch.zeros(1, 1, 3, 1)
print(t)
'''
tensor([[[[0.],
[0.],
[0.]]]])
'''
print(torch.squeeze(t))
print(torch.squeeze(t).shape)
t1 = torch.zeros(1, 1, 3, 2, 1, 2)
print(t1.shape)
print(torch.squeeze(t1).shape)
2、unsqueeze函数:手动升维
注意理解维度和shape返回结果一一对应的关系,shape返回的序列有几个元素,张量就有多少维度。
t1 = torch.zeros(1, 1, 3, 2, 1, 2)
print(t1.shape)
print(torch.squeeze(t1).shape)
t = torch.zeros(1, 2, 1, 2)
print(t.shape)
t1 = torch.unsqueeze(t, dim=0)
print(t1.shape)
t2 = torch.unsqueeze(t, dim=1)
print(t2.shape)
t3 = torch.unsqueeze(t, dim=4)
print(t3.shape)
|