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函数:手动升维


前言

张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和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)) #2

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

你可能感兴趣的