上一篇博客讲述了如何根据自己的实际需要在pytorch中创建tensor,这一篇主要来探讨关于tensor的基本数据变换,是pytorch处理数据的基本方法。


文章目录

  • 1 tensor数据查看与提取
  • 2 tensor数据变换
  • 2.1 重置tensor形状:pytorch.view()
  • 2.2 增加/减少tensor维度:torch.unsqueeze()/torch.squeeze()
  • 2.3 tensor扩充:torch.expand()/torch.repeat()
  • 2.4 tensor维度交换/重新排序:torch.transpose()/torch.permute()
  • 3 总结


1 tensor数据查看与提取

tensor数据的查看与提取主要是通过索引和切片实现的。主要方法有:

  • 直接索引:tensor[index]
  • 选定数据范围切片:tensor[s:d]
  • 步长切片:tensor[s:d:step]
  • 数据筛选:torch.masked_select(tensor,mask),其中mask是和tensor等shape的Bool类型矩阵,函数将以一维矩阵的形式返回mask中值为1对应位置tensor矩阵的值
################################################
#1、直接索引
a = torch.rand(4,1,16,16)
b = a[0]
print(b.size())
"""
输出结果:
torch.Size([1, 16, 16])
"""
################################################
#2、选定索引的维度范围
c = a[1:3,:,:,:]
print(c.size())
"""
输出结果:
torch.Size([2, 1, 16, 16])
"""
################################################
#3、按步长索引
c = a[0:3:2,:,:,:]
print(c.size())
"""
输出结果:
torch.Size([2, 1, 16, 16])
"""
################################################
#4、按mask筛选索引
d = torch.rand(2,3)
mask = torch.BoolTensor([[0,1,0],
                        [1,0,1]])
res = torch.masked_select(d,mask)
print(d)
print(res)
"""
输出结果:
tensor([[0.2507, 0.8419, 0.6681],
        [0.0940, 0.8476, 0.5883]])
tensor([0.8419, 0.0940, 0.5883])
"""

2 tensor数据变换

本部分主要介绍的是将已有的tensor数据改变shape的方法,这是pytorch变换数据的基本操作。

2.1 重置tensor形状:pytorch.view()

torch.view()主要实现重新定义tensor的shape,对tensor中的元素进行重排列,在view过程中tensor的总元素个数保持不变。该方法的缺点是会丢失维度信息。

#############################################
a = torch.rand(4,4,16,16)
b = a.view(4,32,32)
print(a.shape)
print(b.shape)
"""
输出结果:
torch.Size([4, 4, 16, 16])
torch.Size([4, 32, 32])
#注:在view前后必须保证tensor的总元素个数不变
#   在本例中:4*4*16*16 = 4*32*32
"""

2.2 增加/减少tensor维度:torch.unsqueeze()/torch.squeeze()

  • torch.unsqueeze(index):在原tensor的index索引位置对应的维度前增加一个维度,
  • torch.squeeze(index):去掉index索引位置对应的维度,注意:这里index对应的维度必须是1维的,也就是压缩(删除)此维度并不会改变数据的元素个数,当不指定index时,函数会将tensor中所有数值为1的维度压缩。例如:现有tensor(1,4,1,16,16),tensor.squeeze(0)的值为:(4,1,16,16),函数压缩了0这个维度;当不指定index时,tensor.squeeze()的返回值为:(4,16,16),这时函数将tensor上数值为1的维度全部压缩
#############################################
a = torch.rand(4,4,16,16)
b = a.unsqueeze(0)
c = torch.rand(1,4,1,16,16).squeeze()
print(a.shape)
print(b.shape)
print(c.shape)
"""
输出结果:
torch.Size([4, 4, 16, 16])
torch.Size([1, 4, 4, 16, 16])
torch.Size([4, 16, 16])
"""

2.3 tensor扩充:torch.expand()/torch.repeat()

  • torch.expand():接收参数是将对应元素扩充的指定维度,函数只把原来维度为1的拓展成指定的维度,其它原来维度不是1的接收参数要与其原来维度数保持一致,否则会报错
  • torch.repeat():接收参数是将对应元素复制的次数,函数会将所有维度复制对应的次数,形成新的tensor
    示例及讲解:
#############################################
a = torch.rand(4,1,16,16)
#a中只有a[1]是1维度,这里要将它扩充至4,因此expand中第二个参数为4,其它维度不是1,所以要与a保持一致(否则会报错)
b = a.expand(4,4,16,16)
#repeat中的参数都是,代表这要将a中对应维度的元素都复制两次
c = a.repeat(2,2,2,2)
print(a.shape)
print(b.shape)
print(c.shape)
"""
输出结果:
#这是原来a变量的shape
torch.Size([4, 1, 16, 16])
#这是经过expand后的a变量(b)的shape
#可以看出,expand成功将原理index=1位置处的1维度扩充成了对应的4维度
torch.Size([4, 4, 16, 16])
#这是经过repeat后的a变量的shape
#可以看出,repeat将每个维度都复制了2次,即:(4*2,1*2,16*2,16*2)
torch.Size([8, 2, 32, 32])
"""

2.4 tensor维度交换/重新排序:torch.transpose()/torch.permute()

  • torch.tanspose():接收参数为指定对换的两个维度索引a,b,函数将返回维度互换后的新tensor
  • torch.permute():接收参数为新排序的维度索引(原来的索引值),函数将返回维度重新排列的新tensor
#############################################
a = torch.rand(4,1,12,16)
b = a.transpose(0,2)        #第0个维度和第2个维度互换,即:4和12互换
c = a.permute(3,2,0,1)  #将原有的3, 2, 0,1维度作为新tensor的0,1,2,3维度
print(a.shape)
print(b.shape)
print(c.shape)
"""
输出结果:
torch.Size([4, 1, 12, 16])  #a.shape
torch.Size([12, 1, 4, 16])  #b.shape  (4,1,12,16)-互换4,12->(12,1,4,16) 
torch.Size([16, 12, 4, 1])  #c.shape  重新对a进行排列(a[3],a[2],a[0],a[1])->(16,12,4,1)
"""

3 总结

tensor变换的核心理念是为了更好的服务于高维向量运算,这部分的变换技巧和相关的方法有很多。关于这部分的内容我的建议是:不需要花费大量的时间去刻意记忆,只要知道有这些方法,在编程需要的时候能想起来用这些方法可以解决即可。