欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

PyTorch初识:数据载体张量与线性回归 - 3.1 张量基本操作指南

最编程 2024-08-08 16:07:04
...
  1. 张量的拼接 这是啥意思,stack 会新创建一个维度,然后完成拼接。还是看代码:
    • 「torch.cat(tensors, dim=0, out=None):将张量按维度 dim 进行拼接, tensors 表示张量序列, dim 要拼接的维度」
    • 「torch.stack(tensors, dim=0, out=None):在新创建的维度 dim 上进行拼接, tensors 表示张量序列, dim 要拼接的维度」
# 张量的拼接
t = torch.ones((2, 3))
print(t)

t_0 = torch.cat([t, t], dim=0)       # 行拼接
t_1 = torch.cat([t, t], dim=1)    # 列拼接
print(t_0, t_0.shape)
print(t_1, t_1.shape)

# 结果:
tensor([[1., 1., 1.],
     [1., 1., 1.]])
tensor([[1., 1., 1.],
     [1., 1., 1.],
     [1., 1., 1.],
     [1., 1., 1.]]) torch.Size([4, 3])
tensor([[1., 1., 1., 1., 1., 1.],
     [1., 1., 1., 1., 1., 1.]]) torch.Size([2, 6])

.cat 是在原来的基础上根据行和列,进行拼接,我发现一个问题,就是浮点数类型拼接才可以,long 类型拼接会报错。

下面我们看看 .stack 方法:

t_stack = torch.stack([t,t,t], dim=0)
print(t_stack)
print(t_stack.shape)

t_stack1 = torch.stack([t, t, t], dim=1)
print(t_stack1)
print(t_stack1.shape)

## 结果:
tensor([[[1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.]]])
torch.Size([3, 2, 3])
tensor([[[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]],

       [[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]]])
torch.Size([2, 3, 3])

.stack 是根据给定的维度新增了一个新的维度,在这个新维度上进行拼接,这个 .stack 与其说是从新维度上拼接,不太好理解,其实是新加了一个维度 Z 轴,只不过 dim=0 和 dim=1 的视角不同罢了。dim=0 是横向看,dim=1 是纵向看。所以这两个使用的时候要小心,看好了究竟是在原来的维度上拼接到一块,还是从新维度上拼接到一块。

  1. 张量的切分 **torch.chunk(input, chunks, dim=0):将张量按维度 dim 进行平均切分,返回值是张量列表,注意,如果不能整除, 最后一份张量小于其他张量。chunks 代表要切分的维度。**下面看一下代码实现:
a = torch.ones((2, 7))  # 7
list_of_tensors = torch.chunk(a, dim=1, chunks=3)   # 第一个维度切成三块, 那么应该是(2,3), (2,3), (2,1)  因为7不能整除3,所以每一份应该向上取整,最后不够的有多少算多少
print(list_of_tensors)
for idx, t in enumerate(list_of_tensors):
   print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))

## 结果:
(tensor([[1., 1., 1.],
       [1., 1., 1.]]), tensor([[1., 1., 1.],
       [1., 1., 1.]]), tensor([[1.],
       [1.]]))
第1个张量:tensor([[1., 1., 1.],
       [1., 1., 1.]]), shape is torch.Size([2, 3])
第2个张量:tensor([[1., 1., 1.],
       [1., 1., 1.]]), shape is torch.Size([2, 3])
第3个张量:tensor([[1.],
       [1.]]), shape is torch.Size([2, 1])

「torch.split(tensor, split_size_or_sections, dim=0):这个也是将张量按维度 dim 切分,但是这个更加强大,可以指定切分的长度,split_size_or_sections 为 int 时表示每一份的长度, 为 list 时,按 list 元素切分」

# split
t = torch.ones((2, 5))

list_of_tensors = torch.split(t, [2, 1, 2], dim=1)  # [2 , 1, 2], 这个要保证这个list的大小正好是那个维度的总大小,这样才能切
for idx, t in enumerate(list_of_tensors):
   print("第{}个张量:{}, shape is {}".format(idx+1, t, t.shape))

## 结果
第1个张量:tensor([[1., 1.],
       [1., 1.]]), shape is torch.Size([2, 2])
第2个张量:tensor([[1.],
       [1.]]), shape is torch.Size([2, 1])
第3个张量:tensor([[1., 1.],
       [1., 1.]]), shape is torch.Size([2, 2])

所以切分,也有两个函数,.chunk 和 .split。.chunk 切分的规则就是提供张量,切分的维度和几份, 比如三份,先计算每一份的大小,也就是这个维度的长度除以三,然后上取整,就开始沿着这个维度切,最后不够一份大小的,也就那样了。所以长度为 7 的这个维度,3 块,每块 7/3 上取整是 3,然后第一块 3,第二块是 3,第三块 1。这样切 .split 这个函数的功能更加强大,它可以指定每一份的长度,只要传入一个列表即可,或者也有一个整数,表示每一份的长度,这个就根据每一份的长度先切着,看看能切几块算几块。不过列表的那个好使,可以自己指定每一块的长度,但是注意一下,这个长度的总和必须是维度的那个总长度才用办法切。

  1. 张量的索引「torch.index_select(input, dim, index, out=None):在维度 dim 上,按 index 索引数据,返回值,以 index 索引数据拼接的张量。」
t = torch.randint(0, 9, size=(3, 3))     #  从0-8随机产生数组成3*3的矩阵
print(t)
idx = torch.tensor([0, 2], dtype=torch.long)   # 这里的类型注意一下,要是long类型
t_select = torch.index_select(t, dim=1, index=idx)  #第0列和第2列拼接返回
print(t_select)

## 结果:
tensor([[3, 7, 3],
     [4, 3, 7],
     [5, 8, 0]])
tensor([[3, 3],
     [4, 7],
     [5, 0]])

「torch.masked_select(input, mask, out=None):按 mask 中的 True 进行索引,返回值:一维张量。input 表示要索引的张量,mask 表示与 input 同形状的布尔类型的张量。这种情况在选择符合某些特定条件的元素的时候非常好使」,注意这个是返回一维的张量。下面看代码:

mask = t.ge(5)   # le表示<=5, ge表示>=5 gt >5  lt <5
print("mask:n", mask)
t_select1 = torch.masked_select(t, mask)   # 选出t中大于5的元素
print(t_select1)

## 结果:
mask:
tensor([[False,  True, False],
     [False, False,  True],
     [ True,  True, False]])
tensor([7, 7, 5, 8])

所以张量的索引,有两种方式:.index_select 和 .masked_select

  • .index_select:按照索引查找 需要先指定一个 Tensor 的索引量,然后指定类型是 long 的
  • .masked_select:就是按照值的条件进行查找,需要先指定条件作为 mask
  1. 张量的变换「torch.reshape(input, shape):变换张量的形状,这个很常用,input 表示要变换的张量,shape表示新张量的形状。但注意,当张量在内存中是连续时,新张量与input共享数据内存」
# torch.reshape
t = torch.randperm(8)       # randperm是随机排列的一个函数
print(t)

t_reshape = torch.reshape(t, (-1, 2, 2))    # -1的话就是根据后面那两个参数,计算出-1这个值,然后再转
print("t:{}nt_reshape:n{}".format(t, t_reshape))

t[0] = 1024
print("t:{}nt_reshape:n{}".format(t, t_reshape))
print("t.data 内存地址:{}".format(id(t.data)))
print("t_reshape.data 内存地址:{}".format(id(t_reshape.data))) # 这个注意一下,两个是共内存的

## 结果:
tensor([2, 4, 3, 1, 5, 6, 7, 0])
t:tensor([2, 4, 3, 1, 5, 6, 7, 0])
t_reshape:
tensor([[[2, 4],
        [3, 1]],

       [[5, 6],
        [7, 0]]])
t:tensor([1024,    4,    3,    1,    5,    6,    7,    0])
t_reshape:
tensor([[[1024,    4],
        [   3,    1]],

       [[   5,    6],
        [   7,    0]]])
t.data 内存地址:1556953167336
t_reshape.data 内存地址:1556953167336

上面这两个是共内存的, 一个改变另一个也会改变。这个要注意一下。「torch.transpose(input, dim0, dim1):交换张量的两个维度, 矩阵的转置常用, 在图像的预处理中常用, dim0 要交换的维度, dim1 表示要交换的问题」

# torch.transpose
t = torch.rand((2, 3, 4))      # 产生0-1之间的随机数
print(t)
t_transpose = torch.transpose(t, dim0=0, dim1=2)    # c*h*w     h*w*c, 这表示第0维和第2维进行交换
print("t shape:{}nt_transpose shape:{}".format(t.shape, t_transpose.shape))

## 结果:
tensor([[[0.7480, 0.5601, 0.1674, 0.3333],
        [0.4648, 0.6332, 0.7692, 0.2147],
        [0.7815, 0.8644, 0.6052, 0.3650]],

       [[0.2536, 0.1642, 0.2833, 0.3858],
        [0.8337, 0.6173, 0.3923, 0.1878],
        [0.8375, 0.2109, 0.4282, 0.4974]]])
t shape:torch.Size([2, 3, 4])
t_transpose shape:torch.Size([4, 3, 2])
tensor([[[0.7480, 0.2536],
        [0.4648, 0.8337],
        [0.7815, 0.8375]],

       [[0.5601, 0.1642],
        [0.6332, 0.6173],
        [0.8644, 0.2109]],

       [[0.1674, 0.2833],
        [0.7692, 0.3923],
        [0.6052, 0.4282]],

       [[0.3333, 0.3858],
        [0.2147, 0.1878],
        [0.3650, 0.4974]]])

「torch.t(input):2 维张量的转置, 对矩阵而言,相当于 torch.transpose(inpuot, 0,1)」

「torch.squeeze(input, dim=None, out=None):压缩长度为 1 的维度, dim 若为 None,移除所有长度为 1 的轴,若指定维度,当且仅当该轴长度为 1 时可以被移除」

# torch.squeeze
t = torch.rand((1, 2, 3, 1))
t_sq = torch.squeeze(t)
t_0 = torch.squeeze(t, dim=0)
t_1 = torch.squeeze(t, dim=1)
print(t.shape)        # torch.Size([1, 2, 3, 1])
print(t_sq.shape)     # torch.Size([2, 3])
print(t_0.shape)     # torch.Size([2, 3, 1])
print(t_1.shape)     # torch.Size([1, 2, 3, 1])

「torch.unsqueeze(input, dim, out=None):依据 dim 扩展维度」