张量作为有序的序列,也是具备数值索引的功能,并且基本索引方法和Python原生的列表、NumPy中的数组基本一致,当然,所有不同的是,PyTorch中还定义了一种采用函数来进行索引的方式。而作为PyTorch中基本数据类型,张量即具备了列表、数组的基本功能,同时还充当着向量、矩阵、甚至是数据框等重要数据结构,因此PyTorch中也设置了非常完备的张量合并与变换的操作。
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
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])
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])
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]])
'''
“视图”的作用就是节省空间,而值得注意的是,在接下来介绍的很多切分张量的方法中,返回结果都是“视图”,而不是新生成一个对象。
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]]])
'''
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]]))
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]]))
'''
张量的合并操作类似与列表的追加元素,可以拼接、也可以堆叠。
注意理解,拼接的本质是实现元素的堆积,也就是构成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.]])
'''
和拼接不同,堆叠不是将元素拆分重装,而是简单的将各参与堆叠的对象分装到一个更高维度的张量里。
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函数。
#将一维张量转变为二维张量
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])
注意理解维度和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])