pytorch|pytorch_lesson2 张量的索引+torch.index_select+torch.view+张量的分片函数+张量的合并操作+张量的维度变换


文章目录

  • 前言
  • 一、张量的符号索引
    • 1、一维张量索引
    • 2、二维张量索引
    • 3、三维张量索引
  • 二、张量的函数索引torch.index_select()方法
  • 三、tensor.view()方法
  • 四、张量的分片函数
    • 1、分块:chunk函数
    • 2、拆分:split函数
  • 四、张量的合并操作
    • 1、cat
    • 2、stack堆叠
  • 六、张量维度变换
    • 1、squeeze函数:删除不必要的维度
    • 2、unsqueeze函数:手动升维
【pytorch|pytorch_lesson2 张量的索引+torch.index_select+torch.view+张量的分片函数+张量的合并操作+张量的维度变换】
前言 张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和Python原生的列表、NumPy中的数组基本一致,当然,所有不同的是,PyTorch中还定义了一种采用函数来进行索引的方式。而作为PyTorch中基本数据类型,张量即具备了列表、数组的基本功能,同时还充当着向量、矩阵、甚至是数据框等重要数据结构,因此PyTorch中也设置了非常完备的张量合并与变换的操作。
一、张量的符号索引 1、一维张量索引
import torch t1 = torch.arange(1, 11) print(t1) #tensor([ 1,2,3,4,5,6,7,8,9, 10])#从左到右,从零开始 print(t1[0]) #tensor(1)#冒号分割,表示对某个区域进行索引,也是切片 print(t1[1: 8]) #tensor([2, 3, 4, 5, 6, 7, 8]) #第二个冒号,表示索引的间隔 print(t1[1: 8: 2]) #索引其中第2-9个元素,左闭右开,每隔2个取一个值 #tensor([2, 4, 6, 8])

注:在张量的索引中,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]) #提取第一行,第二列的数值 #tensor(2)print(t2[0, ::2])#在第一行中每隔一个数取一个值 #tensor([1, 3])print(t2[0, [0, 2]])#提取第一行中第一列和第三列的数值 #tensor([1, 3])print(t2[::2, ::2]) #每隔1行取一行,取出来的数值里面每隔一列取一个数值 ''' tensor([[1, 3], [7, 9]]) '''print(t2[[0, 2], 1]) #提取第2列里面,第一行、第三行的数值 #tensor([2, 8])

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) #torch.Size([3, 3, 3])print(t3[1, 1, 1]) #提取第二堆里第二行,第三列的数值 #tensor(14)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.Size([3, 3, 3])

二、张量的函数索引torch.index_select()方法
print(t1) #tensor([ 1,2,3,4,5,6,7,8,9, 10]) print(t1.ndim) #1 indices = torch.tensor([1, 2])#设置了一个索引标,索引是一维数组,由两个数值组成 print(indices) #tensor([1, 2]) print(t1[1: 3]) #tensor([2, 3]) print(t1[[1, 2]]) #tensor([2, 3]) print(torch.index_select(t1, 0, indices)) #torch.index_select 里面第一个参数是张量,第二个参数是维度即从第几个维度上提取数据,indices是我们的索引号 #tensor([2, 3])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) #torch.Size([4, 3]) print(torch.index_select(t2, 0, indices))#维度是从外到里依次排列的,在第一个维度上取索引值为1和2的数值,即取第二行和第三行的数值 ''' tensor([[3, 4, 5], [6, 7, 8]]) '''print(torch.index_select(t2, 1, indices)) #在第二个维度上取索引值为1和2的数值,即取第二、三列值 ''' 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) #其作用类似于reshape print(te) ''' tensor([[0, 1], [2, 3], [4, 5]]) '''#view是浅拷贝的,改变te中的某个值,原tensor值也会发生变化 print(te[0][0]) #tensor(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函数能够按照某维度,对张量进行均匀切分,并且返回结果是原张量的视图。
#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]])) '''#注意:chunk返回的结果是一个视图,不是新生成了一个对象,可以验证一下 print(te[0][0][0]) #tensor(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) #对行进行操作,将4行数据分割成3组数据,由于无法均等分为3组,所以会自动分成2组数据 print(t3) ''' (tensor([[1, 1, 2], [3, 4, 5]]), tensor([[ 6,7,8], [ 9, 10, 11]])) '''print(len(t3)) #2t4 = torch.chunk(t2, 5,dim=0) print(t4) #(tensor([[1, 1, 2]]), tensor([[3, 4, 5]]), tensor([[6, 7, 8]]), tensor([[ 9, 10, 11]]))

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) #第2个参数同样表示需要拆分成几块,第三个参数代表在第几个维度上进行操作 print(t) ''' (tensor([[0, 1, 2], [3, 4, 5]]), tensor([[ 6,7,8], [ 9, 10, 11]])) '''tr = torch.split(t2, [1, 3], 0) #第二个参数是一个序列的时候,表明需要按照序列中的数值进行切分,也就是对行进行操作,按照1行和3行的数量进行查费 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) #(tensor([[0, 1, 2]]), tensor([[3, 4, 5]]), tensor([[6, 7, 8]]), tensor([[ 9, 10, 11]])) #切分为了四个二维数据,每个二维数据都是由4个数组成的一维张量构成的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]) #对行进行操作,扩展行数,dim默认取值为0,前提条件是列数要一致 print(d) ''' tensor([[0., 0., 0.], [0., 0., 0.], [1., 1., 1.], [1., 1., 1.]]) '''e = torch.cat([a, b], 1)#对列进行操作,扩展列数,将dim设置为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) #torch.Size([2, 2, 3])

注意对比二者区别,拼接之后维度不变,堆叠之后维度升高。拼接是把一个个元素单独提取出来之后再放到二维张量中,而堆叠则是直接将两个二维张量封装到一个三维张量中,因此,堆叠的要求更高,参与堆叠的张量必须形状完全相同。
六、张量维度变换 此前我们介绍过,通过reshape方法,能够灵活调整张量的形状。而在实际操作张量进行计算时,往往需要另外进行降维和升维的操作,当我们需要除去不必要的维度时,可以使用squeeze函数,而需要手动升维时,则可采用unsqueeze函数。
1、squeeze函数:删除不必要的维度
#将一维张量转变为二维张量 a2 = a.reshape(1, 4) print(a2) #tensor([[0, 1, 2, 3]])#很明显,最外层的维度没什么用,所以我们将这一个维度压缩 a3 = torch.squeeze(a2) print(a3) #tensor([0, 1, 2, 3]) print(a3.shape) #torch.Size([4]) 由4个数组成的一维张量t = torch.zeros(1, 1, 3, 1) print(t) ''' tensor([[[[0.], [0.], [0.]]]]) ''' print(torch.squeeze(t)) #tensor([0., 0., 0.]) print(torch.squeeze(t).shape) #torch.Size([3])t1 = torch.zeros(1, 1, 3, 2, 1, 2) print(t1.shape) #torch.Size([1, 1, 3, 2, 1, 2]) print(torch.squeeze(t1).shape) #torch.Size([3, 2, 2])

2、unsqueeze函数:手动升维 注意理解维度和shape返回结果一一对应的关系,shape返回的序列有几个元素,张量就有多少维度。
t1 = torch.zeros(1, 1, 3, 2, 1, 2) print(t1.shape) #torch.Size([1, 1, 3, 2, 1, 2]) print(torch.squeeze(t1).shape) #torch.Size([3, 2, 2])t = torch.zeros(1, 2, 1, 2) print(t.shape) #torch.Size([1, 2, 1, 2]) t1 = torch.unsqueeze(t, dim=0) #在维度索引值为0的地方增加一个维度 print(t1.shape) #torch.Size([1, 1, 2, 1, 2])t2 = torch.unsqueeze(t, dim=1) #在维度索引值为1的地方增加一个维度 print(t2.shape) #torch.Size([1, 1, 2, 1, 2])t3 = torch.unsqueeze(t, dim=4) print(t3.shape) #torch.Size([1, 2, 1, 2, 1])

    推荐阅读