索引与切片

1.Pytorch风格的索引
根据Tensor的shape,从前往后索引,依次在每个维度上做索引

import torch
 
a = torch.rand(4, 3, 28, 28)
print(a[0].shape) #取到第一个维度  
torch.Size([3, 28, 28])
print(a[0, 0].shape) # 取到二个维度  
torch.Size([28, 28])
print(a[1, 2, 2, 4])  # 具体到某个元素
tensor(0.1076)

上述代码创建了一个shape=[4, 3, 28, 28]的Tensor,我们可以理解为4张图片,每张图片有3个通道,每个通道是28x28的图像数据。a代表这个Tensor,a后面跟着的列表[]表示对Tensor进行索引,a的维度dim = 4,决定了[]中的元素个数不能超过4个,[]中的值表示对应维度上的哪一个元素,比如 a[0]表示取第一个维度上的第一个元素,可以理解为第一张图片,a[1]表示取第一个维度上的第二个元素,可以理解为第二张图片。a[0, 0]表示取第一个维度上第一个元素与第二个维度上的第一个元素,也就是第一张图片第一个通道的元素。a[1, 2, 2, 4]表示取第一个维度上的第二个元素与第二个维度上的第三个元素与第三个维度上的第三个元素与第四个维度上的第5个元素,也就是第二张图片第三个通道第三行第四列的像素值是一个标量值。

2.python风格的索引

import torch
# 譬如:4张图片,每张三个通道,每个通道28行28列的像素
a = torch.rand(4, 3, 28, 28)  
 
# 在第一个维度上取后0和1,等同于取第一、第二张图片
print(a[:2].shape)  #
torch.Size([2, 3, 28, 28])
 
# 在第一个维度上取0和1,在第二个维度上取0,
# 等同于取第一、第二张图片中的第一个通道
print(a[:2, :1, :, :].shape)  #
torch.Size([2, 1, 28, 28])
 
# 在第一个维度上取0和1,在第二个维度上取1,2,
# 等同于取第一、第二张图片中的第二个通道与第三个通道
print(a[:2, 1:, :, :].shape)  #torch.Size([2, 2, 28, 28])
 
# 在第一个维度上取0和1,在第二个维度上取1,2,
# 等同于取第一、第二张图片中的第二个通道与第三个通道
print(a[:2, -2:, :, :].shape)  #
torch.Size([2, 2, 28, 28])
 
# 使用step隔行采样
# 在第一、第二维度取所有元素,在第三、第四维度隔行采样
# 等同于所有图片所有通道的行列每隔一行或者一列采样
# 注意:下面的代码不包括28
print(a[:, :, 0:28:2, 0:28:2].shape) 
print(a[:, :, ::2, ::2].shape)  # 等同于上面语句

注意:负值的索引即表示倒数第几个元素,-2就是倒数第二个元素

3.index_select()选择特定索引

index_select(dim,index)

功能:从张量的某个维度的指定位置选取数据。
参数:
dim:表示从第几维挑选数据,类型为int值;
index:表示从第一个参数维度中的哪个位置挑选数据,类型为torch.Tensor类的实例;

选择特定下标有时候很有用,比如上面的a这个Tensor可以看作4张RGB(3通道)的MNIST图像,长宽都是28px。那么在第一维度上可以选择特定的图片,在第二维度上选择特定的通道,在第三维度上选择特定的行等:

# 选择第一张和第三张图
print(a.index_select(0, torch.tensor([0, 2])).shape)
#torch.Size([2, 3, 28, 28])

# 选择R通道和B通道
print(a.index_select(1, torch.tensor([0, 2])).shape) 
#torch.Size([4, 2, 28, 28])

# 选择图像的0~8行
print(a.index_select(2, torch.arange(8)).shape)  
#torch.Size([4, 3, 8, 28])

注意:index_select()的第二个索引参数必须是Tensor类型

4.使用 … 索引任意多的维度

import torch

a = torch.rand(4, 3, 28, 28)
# 等于a
print(a[...].shape)  #
torch.Size([4, 3, 28, 28])
 
# 第一张图片的所有维度
print(a[0, ...].shape) #
torch.Size([3, 28, 28])
 
# 所有图片第二通道的所有维度
print(a[:, 1, ...].shape) #
torch.Size([4, 28, 28])
 
# 所有图像所有通道所有行的第一、第二列
print(a[..., :2].shape)#
torch.Size([4, 3, 28, 2])

5.使用mask索引

import torch
 
a = torch.randn(3, 4)
print(a)
 
# 生成a这个Tensor中大于0.5的元素的掩码
mask = a.ge(0.5)
print(mask)
 
# 取出a这个Tensor中大于0.5的元素
val = torch.masked_select(a, mask)
print(val)
print(val.shape)
torch.ge(input, other, out=None) → Tensor  #大于等于
torch.gt(input, other, out=None) → Tensor  #大于
torch.le(input, other, out=None) → Tensor  #小于等于
torch.lt(input, other, out=None) → Tensor  #小于
tensor([[ 0.2055, -0.7070, 1.1201, 1.3325],
 [-1.6459, 0.9635, -0.2741, 0.0765],
 [ 0.2943, 0.1206, 1.6662, 1.5721]])
 tensor([[0, 0, 1, 1],
 [0, 1, 0, 0],
 [0, 0, 1, 1]], dtype=torch.uint8)
 tensor([1.1201, 1.3325, 0.9635, 1.6662, 1.5721])
 torch.Size([5])


注意:张量 mask须跟input张量有相同数量的元素数目,但形状或维度不需要相同;最后取出的大于0.5的Tensor的shape已经被打平

维度操作

1.squezee & unsqueeze

x = torch.rand(5,1,2,1)
x = torch.squeeze(x)#x.squeeze()去掉大小为1的维度,x.shape =(5,2)
x = torch.unsqueeze(x,2)#x.unsqueeze(2)和squeeze相反在第三维上扩展,x.shape = (5,2,1)

2.转置 transpose & permute

x = torch.transpose(x, 1, 2) # 交换1和2维度
x = x.permute(1, 2, 3, 0) # 进行维度重组

torch.transpose 只能交换两个维度 permute没有限制

3.改变形状,view&reshape
两者作用一样,区别在于是当从多的维度变到少的维度时,如果张量不是在连续内存存放,则view无法变成合并维度,会报错

x = x.view(1, 2, -1)#把原先tensor中的数据按照行优先的顺序排成一个一维的数据(这里应该是因为要求地址是连续存储的),然后按照参数组合成其他维度的tensor
x = x.reshape(1, 2, -1)

4.张量拼接 cat & stack

torch.cat()

torch.cat(tensors, dim=0, out=None) → Tensor

功能:将张量按维度dim进行拼接【在已有维度】
参数:
tensors 为待拼接张量的序列,通常为 tuple
dim 指定张量拼接的所在维度,即在第几维对张量进行拼接,除该拼接维度外,其余维度上待拼接张量的尺寸必须相同
out 表示在拼接张量的输出,也可直接使用函数返回值

x = torch.randn(2,2,3)
tensor([[[-0.6042,  0.1972, -0.4371],
         [-0.4953,  0.4525, -0.2239]],

        [[ 0.0161, -0.4663, -0.7115],
         [ 0.7699,  0.7269,  0.7691]]])
y = torch.randn(2,2,3)
tensor([[[-0.0362, -1.3889, -0.9299],
         [-0.5749, -0.2899, -0.1523]],

        [[ 0.3843,  0.1168, -0.9871],
         [ 0.2703,  2.2756, -1.2757]]])
c=torch.cat((x,y),1)   #(2,4,3)
tensor([[[-0.6042,  0.1972, -0.4371],
         [-0.4953,  0.4525, -0.2239],
         [-0.0362, -1.3889, -0.9299],
         [-0.5749, -0.2899, -0.1523]],

        [[ 0.0161, -0.4663, -0.7115],
         [ 0.7699,  0.7269,  0.7691],
         [ 0.3843,  0.1168, -0.9871],
         [ 0.2703,  2.2756, -1.2757]]])
d=torch.cat((x,y),2) #(2,2,6)
tensor([[[-0.6042,  0.1972, -0.4371, -0.0362, -1.3889, -0.9299],
         [-0.4953,  0.4525, -0.2239, -0.5749, -0.2899, -0.1523]],

        [[ 0.0161, -0.4663, -0.7115,  0.3843,  0.1168, -0.9871],
         [ 0.7699,  0.7269,  0.7691,  0.2703,  2.2756, -1.2757]]])

torch.stack()

torch.stack(tensors, dim=0, out=None) → Tensor

作用:在新维度拼接张量
参数:
tensors 为待拼接张量的序列,通常为 tuple
dim 指定张量拼接的新维度对应已有维度的插入索引,即在原来第几维的位置上插入新维度对张量进行拼接,待拼接张量在所有已有维度上的尺寸必须完全相同
out 表示在拼接张量的输出,也可直接使用函数返回值

x = torch.randn(2, 3)
tensor([[-0.0288,  0.6936, -0.6222],
        [ 0.8786, -1.1464, -0.6486]])
a = torch.stack((x, x, x), dim = 0)
tensor([[[-0.0288,  0.6936, -0.6222],
         [ 0.8786, -1.1464, -0.6486]],

        [[-0.0288,  0.6936, -0.6222],
         [ 0.8786, -1.1464, -0.6486]],

        [[-0.0288,  0.6936, -0.6222],
         [ 0.8786, -1.1464, -0.6486]]])
# torch.Size([3, 2, 3])
b = torch.stack((x, x, x), dim = 1)
tensor([[[-0.0288,  0.6936, -0.6222],
         [-0.0288,  0.6936, -0.6222],
         [-0.0288,  0.6936, -0.6222]],

        [[ 0.8786, -1.1464, -0.6486],
         [ 0.8786, -1.1464, -0.6486],
         [ 0.8786, -1.1464, -0.6486]]])
# torch.Size([2, 3, 3])

注意:cat()不会扩展张量的维度,而stack()会拓展张量的维度

5.where()
这个函数返回一个新的张量,其值在每个索引处都根据给定条件改变。这个函数的参数有:条件,第一个张量和第二个张量。在每个张量的值上检查条件(在条件中使用),如果为真,就用第一个张量中相同位置的值代替,如果为假,就用第二个张量中相同位置的值代替。

a=torch.tensor([[[1,2,3],[4,5,6]]]).to(torch.float32)
b=torch.zeros(1,2,3)
torch.where(a%2==0,b,a)
>>tensor([[[1., 0., 3.],
         [0., 5., 0.]]])

这里,它检查张量a的值是否是偶数。如果是,则用张量b中的值替换,b中的值都是0,否则还是和原来一样。此函数可用于设定阈值。如果张量中的值大于或小于某一数值,它们可以很容易地被替换。

6.expand() & expand_as()
expand()
功能: 扩展张量中某维数据的尺寸,它返回输入张量在某维扩展为更大尺寸后的张量;expand()函数括号中的输入参数为指定经过维度尺寸扩展后的张量的size

import torch
a = torch.tensor([1, 2, 3])
c = a.expand(2, 3)
print(a)
print(c)
# 输出信息:
tensor([1, 2, 3])
tensor([[1, 2, 3],
        [1, 2, 3]]
        
a = torch.tensor([[1], [2], [3]])
print(a.size())
c = a.expand(3, 3)
print(a)
print(c)
# 输出信息:
torch.Size([3, 1])
tensor([[1],
        [2],
        [3]])
tensor([[1, 1, 1],
        [2, 2, 2],
        [3, 3, 3]])

注意:
1.expand()函数只能将size=1的维度扩展到更大的尺寸,如果扩展其他size的维度会报错。
2.使用expand()函数的时候,x自身不会改变,因此需要将结果重新赋值

expand_as()
函数功能:expand_as()函数与expand()函数类似,功能都是用来扩展张量中某维数据的尺寸,区别是它括号内的输入参数是另一个张量,作用是将输入tensor的维度扩展为与指定tensor相同的size。

import torch
a = torch.tensor([[2], [3], [4]])
print(a)
b = torch.tensor([[2, 2], [3, 3], [5, 5]])
print(b.size())
c = a.expand_as(b)
print(c)
print(c.size()) 
# 输出信息:
tensor([[2],
        [3],
        [4]])
torch.Size([3, 2])
tensor([[2, 2],
        [3, 3],
        [4, 4]])
torch.Size([3, 2])