张量也是有序序列,我们可以根据每个元素在系统内的顺序“编号”,来找出特定的元素,也就是索引。
一维张量的索引过程和Python原生对象类型的索引一致,基本格式遵循`[start: end: step]
import torch t1 = torch.arange(1, 11) # 张量索引出来的结果还是零维张量, 而不是单独的数。要转化成单独的数,需要使用item()方法。 t1[0] # tensor(1) # 冒号分隔,表示对某个区域进行索引,也就是所谓的切片 t1[1: 8] # tensor([2, 3, 4, 5, 6, 7, 8]) # 第二个冒号,表示索引的间隔,在张量的索引中,step位必须大于0 t1[1: 8: 2] # tensor([2, 4, 6, 8]) # 冒号前后没有值,表示索引这个区域 t1[1: : 2] # 从第二个元素开始索引,一直到结尾,并且每隔两个数取一个 t1[: 8: 2] # 从第一个元素开始索引到第9个元素(不包含),并且每隔两个数取一个
二维张量的索引逻辑和一维张量的索引逻辑基本相同,二维张量可以视为两个一维张量组合而成,而在实际的索引过程中,需要用逗号进行分隔,分别表示对哪个一维张量进行索引、以及具体的一维张量的索引。
import torch t2 = torch.arange(1, 10).reshape(3, 3) t2[0, 1] # 表示索引第一行、第二个(第二列的)元素 t2[0, ::2] # 表示索引第一行、每隔两个元素取一个 t2[::2, ::2] # 表示每隔两行取一行、并且每一行中每隔两个元素取一个 t2[[0, 2], 1] # 索引第一行、第三行、第二列的元素
PyTorch中的.view()方法。该方法会返回一个类似视图的结果,该结果和原张量对象共享一块数据存储空间,并且通过.view()方法,还可以改变对象结构,生成一个不同结构,但共享一个存储空间的张量。当然,共享一个存储空间,也就代表二者是“浅拷贝”的关系,修改其中一个,另一个也会同步进行更改。
t = torch.arange(6).reshape(2, 3) te = t.view(3, 2) # 构建一个数据相同,但形状不同的“视图” tr = t.view(1, 2, 3) # 维度也可以修改
“视图”的作用就是节省空间,而值得注意的是,很多切分张量的方法中,返回结果都是“视图”,而不是新生成一个对象。
chunk函数能够按照某维度,对张量进行均匀切分,并且返回结果是原张量的视图。
t2 = torch.arange(12).reshape(4, 3) tc = torch.chunk(t2, 4, dim=0) # 在第零个维度上(按行),进行四等分 # 当原张量不能均分时,chunk不会报错,但会返回其他均分的结果 torch.chunk(t2, 3, dim=0) # 次一级均分结果
split既能进行均分,也能进行自定义切分。当然,需要注意的是,和chunk函数一样,split返回结果也是view。
t2 = torch.arange(12).reshape(4, 3) torch.split(t2, 2, 0) # 第二个参数只输入一个数值时表示均分,第三个参数表示切分的维度 torch.split(t2, [1, 3], 0) # 第二个参数输入一个序列时,表示按照序列数值进行切分,也就是1/3分 # 注意,当第二个参数位输入一个序列时,序列的各数值的和必须等于对应维度下形状分量的取值。 torch.split(t2, [1, 1, 2], 0) ts = torch.split(t2, [1, 2], 1)
张量的合并操作类似与列表的追加元素,可以拼接、也可以堆叠。
PyTorch中,可以使用cat函数实现张量的拼接。
注意理解,拼接的本质是实现元素的堆积,也就是构成a、b两个二维张量的各一维张量的堆积,最终还是构成二维向量。
a = torch.zeros(2, 3) b = torch.ones(2, 3) torch.cat([a, b]) # 按照行进行拼接,dim默认取值为0 torch.cat([a, b], 1) # 按照列进行拼接
和拼接不同,堆叠不是将元素拆分重装,而是简单的将各参与堆叠的对象分装到一个更高维度的张量里。
a = torch.zeros(2, 3) b = torch.ones(2, 3) torch.stack([a, b]) # 堆叠之后,生成一个三维张量 torch.stack([a, b]).shape
注意对比二者区别,拼接之后维度不变,堆叠之后维度升高。拼接是把一个个元素单独提取出来之后再放到二维张量中,而堆叠则是直接将两个二维张量封装到一个三维张量中,因此,堆叠的要求更高,参与堆叠的张量必须形状完全相同。