张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和Python原生的列表、NumPy中的数组基本一致,当然,所有不同的是,PyTorch中还定义了一种采用函数来进行索引的方式。

而作为PyTorch中基本数据类型,张量即具备了列表、数组的基本功能,同时还充当着向量、矩阵、甚至是数据框等重要数据结构,因此PyTorch中也设置了非常完备的张量合并与变换的操作。

一、张量的符号索引

张量也是有序序列,我们可以根据每个元素在系统内的顺序“编号”,来找出特定的元素,也就是索引。

1.一维张量索引

一维张量的索引过程和Python原生对象类型的索引一致,基本格式遵循[start: end: step],索引的基本要点回顾如下。

t1 = torch.arange(1, 11)
t1
#tensor([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])
'''从左到右,从零开始'''t1[0]
#tensor(1)
'''注:张量索引出来的结果还是零维张量,而不是单独的数。要转化成单独的数,需要使用item()方法。'''
  • 冒号分隔,表示对某个区域进行索引,也就是所谓的切片
t1[1: 8]             '''索引其中2-9号元素,并且左包含右不包含'''
#tensor([2, 3, 4, 5, 6, 7, 8])
  • 第二个冒号,表示索引的间隔
t1[1: 8: 2]          '''索引其中2-9号元素,左包含右不包含,且隔两个数取一个'''
#tensor([2, 4, 6, 8])
  • 冒号前后没有值,表示索引这个区域
t1[: 8: 2]           '''从第一个元素开始索引到第9个元素(不包含),并且每隔两个数取一个'''
#tensor([1, 3, 5, 7])
  • 在张量的索引中,step位必须大于0
t1[9: 1: -1]
#报错

2.二维张量索引

二维张量的索引逻辑和一维张量的索引逻辑基本相同,二维张量可以视为两个一维张量组合而成,而在实际的索引过程中,需要用逗号进行分隔,分别表示对哪个一维张量进行索引、以及具体的一维张量的索引。

t2 = torch.arange(1, 10).reshape(3, 3)
t2
#tensor([[1, 2, 3],
#        [4, 5, 6],
#        [7, 8, 9]])t2[0, 1]                  '''表示索引第一行、第二个(第二列的)元素'''
#tensor(2)t2[0, ::2]                '''表示索引第一行、每隔两个元素取一个'''
#tensor([1, 3])t2[0, [0, 2]]             '''索引结果同上'''
#tensor([1, 3])t2[::2, ::2]              '''表示每隔两行取一行、并且每一行中每隔两个元素取一个'''
#tensor([[1, 3],
#        [7, 9]])t2[[0, 2], 1]              '''索引第一行、第三行、第二列的元素'''
#tensor([2, 8])

理解:对二维张量来说,基本可以视为是对矩阵的索引,并且行、列的索引遵照相同的索引规范,并用逗号进行分隔。

3.三维张量的索引

在二维张量索引的基础上,三维张量拥有三个索引的维度。我们将三维张量视作矩阵组成的序列,则在实际索引过程中拥有三个维度,分别是索引矩阵、索引矩阵的行、索引矩阵的列。

t3 = torch.arange(1, 28).reshape(3, 3, 3)
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]]])t3.shape
#torch.Size([3, 3, 3])t3[1, 1, 1]          '''索引第二个矩阵中,第二行、第二个元素'''
#tensor(14)t3[1, ::2, ::2]      '''索引第二个矩阵,行和列都是每隔两个取一个'''
#tensor([[10, 12],
#        [16, 18]])t3[:: 2, :: 2, :: 2]      '''每隔两个取一个矩阵,对于每个矩阵来说,行和列都是每隔两个取一个'''
#tensor([[[ 1,  3],
#         [ 7,  9]],#        [[19, 21],
#         [25, 27]]])

理解:更为本质的角度去理解高维张量的索引,其实就是围绕张量的“形状”进行索引

二、张量的函数索引

在PyTorch中,我们还可以使用index_select函数,通过指定index来对张量进行索引。

t1
#tensor([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])t1.ndim
#1indices = torch.tensor([1, 2])
indices
#tensor([1, 2])t1[1: 3]
#tensor([2, 3])t1[[1, 2]]
#tensor([2, 3])torch.index_select(t1, 0, indices)
#tensor([2, 3])

在index_select函数中,第二个参数实际上代表的是索引的维度。对于t1这个一维向量来说,由于只有一个维度,因此第二个参数取值为0,就代表在第一个维度上进行索引

t2 = torch.arange(12).reshape(4, 3)
t2
#tensor([[ 0,  1,  2],
#        [ 3,  4,  5],
#        [ 6,  7,  8],
#        [ 9, 10, 11]])t2.shape
#torch.Size([4, 3])indices
#tensor([1, 2])torch.index_select(t2, 0, indices)   '''dim参数取值为0,代表在shape的第一个维度上索引'''
#tensor([[3, 4, 5],
#        [6, 7, 8]])torch.index_select(t2, 1, indices)  '''dim参数取值为1,代表在shape的第一个维度上索引'''
#tensor([[ 1,  2],
#        [ 4,  5],
#        [ 7,  8],
#        [10, 11]])

三、tensor.view()方法

在正式介绍张量的切分方法之前,需要首先介绍PyTorch中的.view()方法。该方法会返回一个类似视图的结果,该结果和原张量对象共享一块数据存储空间,并且通过.view()方法,还可以改变对象结构,生成一个不同结构,但共享一个存储空间的张量。当然,共享一个存储空间,也就代表二者是“浅拷贝”的关系,修改其中一个,另一个也会同步进行更改。

t = torch.arange(6).reshape(2, 3)
t
#tensor([[0, 1, 2],
#        [3, 4, 5]])te = t.view(3, 2)             '''构建一个数据相同,但形状不同的“视图”'''
te
#tensor([[0, 1],
#        [2, 3],
#        [4, 5]])t
#tensor([[0, 1, 2],
#        [3, 4, 5]])t[0] = 1                     '''对t进行修改'''t
#tensor([[1, 1, 1],
#        [3, 4, 5]])te                             '''te同步变化'''
#tensor([[1, 1],
#        [1, 3],
#        [4, 5]])tr = t.view(1, 2, 3)           # 维度也可以修改
tr
#tensor([[[1, 1, 1],
#         [3, 4, 5]]])

“视图”的作用就是节省空间,而值得注意的是,在接下来介绍的很多切分张量的方法中,返回结果都是“视图”,而不是新生成一个对象。

四、张量的分片函数

1.分块:chunk函数

chunk函数能够按照某维度,对张量进行均匀切分,并且返回结果是原张量的视图。

t2 = torch.arange(12).reshape(4, 3)
t2
#tensor([[ 0,  1,  2],
#        [ 3,  4,  5],
#        [ 6,  7,  8],
#        [ 9, 10, 11]])tc = torch.chunk(t2, 4, dim=0)          '''在第零个维度上(按行),进行四等分'''
tc
#(tensor([[0, 1, 2]]),
#tensor([[3, 4, 5]]),
#tensor([[6, 7, 8]]),
#tensor([[ 9, 10, 11]]))'''注意:chunk返回结果是一个视图,不是新生成了一个对象'''
tc[0]
#tensor([[0, 1, 2]])tc[0][0]
#tensor([0, 1, 2])tc[0][0][0] = 1           '''修改tc中的值'''tc
#(tensor([[1, 1, 2]]),
# tensor([[3, 4, 5]]),
# tensor([[6, 7, 8]]),
# tensor([[ 9, 10, 11]]))t2                        '''原张量也会对应发生变化'''
#tensor([[ 1,  1,  2],
#        [ 3,  4,  5],
#        [ 6,  7,  8],
#        [ 9, 10, 11]])"""当原张量不能均分时,chunk不会报错,但会返回其他均分的结果"""
torch.chunk(t2, 3, dim=0)            '''次一级均分结果'''
#(tensor([[1, 1, 2],
#         [3, 4, 5]]),
# tensor([[ 6,  7,  8],
#         [ 9, 10, 11]]))len(torch.chunk(t2, 3, dim=0))
#2torch.chunk(t2, 5, dim=0)            '''次一级均分结果'''
#(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)
t2
#tensor([[ 0,  1,  2],
#        [ 3,  4,  5],
#        [ 6,  7,  8],
#        [ 9, 10, 11]])torch.split(t2, 2, 0)     '''第二个参数只输入一个数值时表示均分,第三个参数表示切分的维度'''
#(tensor([[0, 1, 2],      '''按照第二个参数的数值均分'''
#         [3, 4, 5]]),
# tensor([[ 6,  7,  8],
#         [ 9, 10, 11]]))torch.split(t2, [1, 3], 0)   '''第二个参数输入一个序列时,表示按照序列数值进行切分,也就是1/3分'''
#(tensor([[0, 1, 2]]),
# tensor([[ 3,  4,  5],
#         [ 6,  7,  8],
#         [ 9, 10, 11]]))

注意,当第二个参数位输入一个序列时,序列的各数值的和必须等于对应维度下形状分量的取值。例如,上述代码中,是按照第一个维度进行切分,而t2总共有4行,因此序列的求和必须等于4,也就是1+3=4,而序列中每个分量的取值,则代表切块大小。

torch.split(t2, [1, 1, 1, 1], 0)
#(tensor([[0, 1, 2]]),
# tensor([[3, 4, 5]]),
# tensor([[6, 7, 8]]),
# tensor([[ 9, 10, 11]]))torch.split(t2, [1, 1, 2], 0)
#(tensor([[0, 1, 2]]),
# tensor([[3, 4, 5]]),
# tensor([[ 6,  7,  8],
#         [ 9, 10, 11]]))ts = torch.split(t2, [1, 2], 1)
ts
#(tensor([[0],
#         [3],
#         [6],
#         [9]]),
# tensor([[ 1,  2],
#         [ 4,  5],
#         [ 7,  8],
#         [10, 11]]))ts[0][0]
#tensor([0])ts[0][0] = 1              '''view进行修改'''
ts
#(tensor([[1],
#         [3],
#         [6],
#         [9]]),
# tensor([[ 1,  2],
#         [ 4,  5],
#         [ 7,  8],
#         [10, 11]]))t2                         '''原对象同步改变'''
#tensor([[ 1,  1,  2],
#        [ 3,  4,  5],
#        [ 6,  7,  8],
#        [ 9, 10, 11]])

tensor的split方法和array的split方法有很大的区别,array的split方法是根据索引进行切分。

五、张量的合并操作

张量的合并操作类似与列表的追加元素,可以拼接、也可以堆叠。

  • 拼接函数:cat

PyTorch中,可以使用cat函数实现张量的拼接。

a = torch.zeros(2, 3)
a
#tensor([[0., 0., 0.],
#        [0., 0., 0.]])b = torch.ones(2, 3)
b
#tensor([[1., 1., 1.],
#        [1., 1., 1.]])c = torch.zeros(3, 3)
c
#tensor([[0., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.]])torch.cat([a, b])                 '''按照行进行拼接,dim默认取值为0'''
#tensor([[0., 0., 0.],
#        [0., 0., 0.],
#        [1., 1., 1.],
#        [1., 1., 1.]])torch.cat([a, b], 1)              '''按照列进行拼接'''
#tensor([[0., 0., 0., 1., 1., 1.],
#        [0., 0., 0., 1., 1., 1.]])torch.cat([a, c], 1)              '''形状不匹配时将报错'''

注意理解,拼接的本质是实现元素的堆积,也就是构成a、b两个二维张量的各一维张量的堆积,最终还是构成二维向量。

  • 堆叠函数:stack

和拼接不同,堆叠不是将元素拆分重装,而是简单的将各参与堆叠的对象分装到一个更高维度的张量里。

a
#tensor([[0., 0., 0.],
#        [0., 0., 0.]])b
#tensor([[1., 1., 1.],
#        [1., 1., 1.]])torch.stack([a, b])                 #堆叠之后,生成一个三维张量
#tensor([[[0., 0., 0.],
#         [0., 0., 0.]],#        [[1., 1., 1.],
#         [1., 1., 1.]]])torch.stack([a, b]).shape
#torch.Size([2, 2, 3])torch.cat([a, b])
#tensor([[0., 0., 0.],
#        [0., 0., 0.],
#        [1., 1., 1.],
#        [1., 1., 1.]])

注意对比二者区别,拼接之后维度不变,堆叠之后维度升高。拼接是把一个个元素单独提取出来之后再放到二维张量中,而堆叠则是直接将两个二维张量封装到一个三维张量中,因此,堆叠的要求更高,参与堆叠的张量必须形状完全相同。

a
#tensor([[0., 0., 0.],
#        [0., 0., 0.]])c
#tensor([[0., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.]])torch.cat([a, c])                '''横向拼接时,对行数没有一致性要求'''
#tensor([[0., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.],
#        [0., 0., 0.]])torch.stack([a, c])              '''维度不匹配时也会报错'''

六、张量维度变换

此前我们介绍过,通过reshape方法,能够灵活调整张量的形状。而在实际操作张量进行计算时,往往需要另外进行降维和升维的操作,当我们需要除去不必要的维度时,可以使用squeeze函数,而需要手动升维时,则可采用unsqueeze函数。

a = torch.arange(4)
a
#tensor([0, 1, 2, 3])a2 = a.reshape(1, 4)
a2
#tensor([[0, 1, 2, 3]])torch.squeeze(a2).ndim
#1'''squeeze函数:删除不必要的维度'''
t = torch.zeros(1, 1, 3, 1)
t
#tensor([[[[0.],
#          [0.],
#          [0.]]]])t.shape
#torch.Size([1, 1, 3, 1])'''t张量解释:一个包含一个三维的四维张量,三维张量只包含一个三行一列的二维张量。'''
torch.squeeze(t)
#tensor([0., 0., 0.])torch.squeeze(t).shape
#torch.Size([3])
'''转化后生成了一个一维张量'''t1 = torch.zeros(1, 1, 3, 2, 1, 2)
t1.shape
#torch.Size([1, 1, 3, 2, 1, 2])torch.squeeze(t1)
#tensor([[[0., 0.],
#         [0., 0.]],#        [[0., 0.],
#         [0., 0.]],#        [[0., 0.],
#         [0., 0.]]])torch.squeeze(t1).shape
#torch.Size([3, 2, 2])
'''简单理解,squeeze就相当于提出了shape返回结果中的1'''
  • unsqeeze函数:手动升维
t = torch.zeros(1, 2, 1, 2)
t.shape
#torch.Size([1, 2, 1, 2])torch.unsqueeze(t, dim = 0)             '''在第1个维度索引上升高1个维度'''
#tensor([[[[[0., 0.]],
#          [[0., 0.]]]]])torch.unsqueeze(t, dim = 0).shape
#torch.Size([1, 1, 2, 1, 2])torch.unsqueeze(t, dim = 2).shape         # 在第3个维度索引上升高1个维度
#torch.Size([1, 2, 1, 1, 2])torch.unsqueeze(t, dim = 4).shape          # 在第5个维度索引上升高1个维度
#torch.Size([1, 2, 1, 2, 1])

注意理解维度和shape返回结果一一对应的关系,shape返回的序列有几个元素,张量就有多少维度。

Lesson 2.张量的索引、分片、合并以及维度调整相关推荐

  1. Elasticsearch之如何合理分配索引分片

    大多数ElasticSearch用户在创建索引时通用会问的一个重要问题是:我需要创建多少个分片? 在本文中, 我将介绍在分片分配时的一些权衡以及不同设置带来的性能影响. 如果想搞清晰你的分片策略以及如 ...

  2. 优化Elasticsearch之如何合理分配索引分片

    大多数ElasticSearch用户在创建索引时通用会问的一个重要问题是:我需要创建多少个分片? 在本文中, 我将介绍在分片分配时的一些权衡以及不同设置带来的性能影响. 如果想搞清晰你的分片策略以及如 ...

  3. Elasticsearch 之 commit point | Segment | refresh | flush 索引分片内部原理

    转载自: http://www.6aiq.com/article/1539308290695 基本概念 Segments in Lucene 众所周知,Elasticsearch 存储的基本单元是sh ...

  4. plupload分片上传php,plupload 大文件分片上传与PHP分片合并探索

    最近老大分给我了做一个电影cms系统,其中涉及到一个功能,使用七牛云的文件上传功能.七牛javascript skd,使用起来很方便,屏蔽了许多的技术细节.如果只满足与调用sdk,那么可能工作中也就没 ...

  5. 设置文件副本数_ElasticSearch平滑批量缩减索引分片数实践

    一.背景    Elasticsearch作为开源分布式检索分析引擎,具有近实时的检索能力.海量数据的分布式存储能力.海量数据的近实时分析能力,目前广泛应用于国内外互联网公司.站内检索.订单类数据的多 ...

  6. Lesson 3.张量的广播和科学运算

    数学运算与算子   作为PyTorch中执行深度学习的基本数据类型,张量(Tensor)也拥有非常多的数学运算函数和方法,以及对应的一系列计算规则.在PyTorch中,能够作用与Tensor的运算,被 ...

  7. MySQL-8.0.x 新特性之索引页合并

    [背景] 索引的重要是在些不表.在这里我想说的另一个问题:索引和数据一样在innodb中都是以page的形式来组织的,那么问题就来了. 比如果说索引 ix_person_name 的内容只要8个页面就 ...

  8. 【Elasticsearch】索引 强制合并 缓存 refresh flush 等操作

    文章目录 1.概述 1.1 索引强制合并 1.1.1 实战 1.2 缓存清理 1.3.flush操作 1.4.refresh操作 1.概述 1.1 索引强制合并 强制合并的功能为强制合并一个或多个索引 ...

  9. es动态分配分片_解决ES索引分片均衡问题

    运行了两年的ES集群,今天索引节点突然不均衡,新建索引分片都集中到一个节点上了,如下图: 同时还有部分节点分片在恢复,如上图,有432个待恢复分片,而且只有两个分片在同时恢复,网络吞吐也很小,这要等到 ...

最新文章

  1. iOS-直播开发(开发从底层做起)
  2. Android判断当前的android设备是否处于联网状态
  3. 《UNIX环境编程》第十六章--网络IPC:套接字
  4. iOS原生地图与高德地图的使用
  5. 移动端 项目开发经验集锦
  6. 远程桌面端口修改批处理
  7. 【bzoj2929】[Poi1999]洞穴攀行 网络流最大流
  8. attempt to write a readonly database 错误
  9. IDEA2019.3 双击启动无反应解决方法
  10. mysql建表影响效率_关于MySQL建表对DML的影响【转】
  11. resiprocate之message
  12. emmet工具使用和技巧
  13. Audio播放流程(六)---数据进入AudioTrack
  14. java在线反编译class文件工具地址
  15. xlsxwriter去掉网格线_xlsxwriter图表网格间距
  16. 未群发消息接腾讯服务器吗,腾讯企点如何群发消息 腾讯企点群发消息方法教程...
  17. 【基于MapReduce的成绩分析系统】——计算每门课程的平均成绩、最高成绩、最低成绩
  18. 你认为的CISP这个证书是怎么样的
  19. 南宁职业技术学院计算机专业宿舍,南宁职业技术学院星级文明宿舍评比办法(试行)...
  20. 宁波银行金融科技面试

热门文章

  1. linux内核时间second,闰秒(leap second)和linux/unix时间
  2. 互斥信号量解决任务间资源共享问题
  3. 弹出硬盘时应用程序阻碍停用设备_7GB硬盘空间被Win 10“偷走”?微软系统的这些“潜规则”你掌握了吗?...
  4. java多数做增删改查_Java 实现一个 能够 进行简单的增删改查的 超市管理系统
  5. php判断ip跳转城市,PHP判断IP并转跳到相应城市分站的方法
  6. linux:安装ubuntu18-04
  7. Python中if __name__ == ‘__main__‘:的作用和原理(自用笔记)
  8. Pytorch学习-Task1
  9. mysql8 允许外网访问
  10. R语言实战应用精讲50篇(十四)-R语言构建层次分析模型