Tensor

Tensor(张量)是深度学习框架中极为基础的概念,也是PyTroch、TensorFlow中最重要的知识点之一,它是一种数据的存储和处理结构

从标量、向量和矩阵的关系来看,我们可以认为它们是不同"维度"的Tensor,更准确的说:应该叫Rank(秩)而不是"维度"

Tensor和NumPy都可以看作科学计算的通用工具,但是NumPy和Tensor的用途是不一样的,NumPy不能用于GPU加速,Tensor则可以

以下是一些Tensor操作的简单示例,很多操作和NumPy一致或有细微差别,这里不再赘述

Tensor的类型

一般来说,torch.float32、torch.float64、torch.uint8和torch.int64用得相对较多一些,但也不是绝对,还是要根据实际情况进行选择

Tensor的创建

创建Tensor的时候注意大小写,torch.Tensor()是Tensor类的构造方法,通过构造方法创建Tensor对象的实例;torch.tensor()则是Tensor类内部的一个方法,方法的返回值是Tensor类型

可以按照下面的习惯使用

  • torch.tensor(值)
  • torch.Tensor(2,3) # 2行3列,也可以传值(不能是标量,至少是list,不建议这样用)
import torch
import numpy as np

# 直接创建
data = 1
'''
data: list、tuple、ndarray、scalar等
dtype: torch.float32、torch.float64、torch.uint8、torch.int64等
device: 指定了数据要返回到的设备
requires_grad: 用于说明当前量是否需要在计算中保留对应的梯度信息。在PyTorch中,只有当一个Tensor设置requires_grad为True的情况下
            才会对这个Tensor以及由这个Tensor计算出来的其他Tensor进行求导,然后将导数值存在Tensor的grad属性中,便于优化器来更新参数
            把requires_grad设置成true或者false要灵活处理。如果是训练过程就要设置为true,目的是方便求导、更新参数
            而到了验证或者测试过程,我们的目的是检查当前模型的泛化能力,那就要把requires_grad设置成Fasle,避免这个参数根据loss自动更新
'''
a = torch.tensor(data, dtype=None, device=None, requires_grad=False)

# 设置requires_grad为True(默认)或False
a.requires_grad_()

# 从numpy数组创建
torch.from_numpy(np.zeros((3, 3)))

# torch中也有封装零矩阵、单位阵等
torch.zeros((3, 3), dtype=torch.int64)
torch.eye(3)

# 假设要生成一个全7的矩阵(非常用的全0、全1等),可以使用full
torch.full((2, 3), 7)

# 随机矩阵
torch.rand((3, 2))                  # 0-1区间均匀分布
torch.randn((3, 3))                 # 均值0,方差1的标准正态分布
torch.normal(0, 1, (2, 5))          # 可以指定均值和标准差,均值,标准差,形状
torch.randint(0, 100, (10, 10))     # [low, high)区间内均匀分布的整数

Tensor的转换

import torch
import numpy as np

# int与Tensor的转换
t = torch.tensor(123)
n = t.item()

# list与Tensor的转换
t = torch.tensor([1, 2, 3])
l = t.numpy().tolist()

# numpy与Tensor的转换
t = torch.tensor(np.array([1, 2, 3]))
nd = t.numpy()

# CPU与GPU的Tensor之间的转换:
# CPU->GPU
data.cuda()
# GPU->CPU:
data.cpu()

Tensor的形状

import torch
import numpy as np

# 获取Tensor的形状,shape和size()等效,区别是一个是属性、一个是方法
a = torch.zeros(2, 3, 5)
# torch.Size([2, 3, 5])
print(a.shape)
print(a.size())

# 获取Tensor的元素数量
print(a.numel())

# 转置:permute()(对任意高维矩阵进行转置)、transpose()(每次只能转换两个维度)
x = torch.rand(2, 3, 5)
# 0轴和2轴交换,1轴不变
x = x.permute(2, 1, 0)
# 0轴和1轴交换
x = x.transpose(0, 1)

# 需要注意的是,经过了transpose或者permute处理之后的数据,变得不再连续了
y = torch.randn(4, 4)
# 正常
y.view(2, 8)
y = y.transpose(0, 1)
# 报错,view不能处理内存不连续Tensor
y.view(2, 8)
# 正常。reshape相当于进行了两步操作,先把Tensor在内存中捋顺了,然后再进行view操作
y.reshape(2, 8)

tensor = torch.arange(12)
a = torch.reshape(tensor, (-1, 1, 3, 4))
'''
reshape可以在任意轴填-1(最多一个轴)表示自动推算
举例:在一个channel的3×4的矩阵的基础上,推断出batchsize为1
tensor([
            [
                [
                    [ 0,  1,  2,  3],
                    [ 4,  5,  6,  7],
                    [ 8,  9, 10, 11]
                ]
            ]
        ])
'''
print(a)
b = torch.flatten(a)
# 摊平,tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
print(b)

增减维度

  • unsqueeze: 增加一个维度(反压缩)
  • squeeze: 减少一个维度(压缩),前提:当前维度只有一个元素(否则无法压缩,返回原来的Tensor)
  • expand:只能把维度为1的拓展成指定维度。如果哪个维度为-1,就是该维度不变
  • repeat:里面参数代表是重复多少次(就是复制多少次),它不允许使用参数-1

unsqueeze、squeeze示例

import torch
import numpy as np

a = torch.Tensor(np.arange(12)).reshape(3, 4)  # type: torch.Tensor

'''
tensor([
            [ 0.,  1.,  2.,  3.],
            [ 4.,  5.,  6.,  7.],
            [ 8.,  9., 10., 11.]
    ])
'''
print(a)

'''
第0维扩充维度
tensor([
        [
            [ 0.,  1.,  2.,  3.],
            [ 4.,  5.,  6.,  7.],
            [ 8.,  9., 10., 11.]
        ]
    ])
'''
print(a.unsqueeze(0))

'''
第1维扩充维度
tensor([
        [
            [ 0.,  1.,  2.,  3.]
        ],
        [
            [ 4.,  5.,  6.,  7.]
        ],
        [
            [ 8.,  9., 10., 11.]
        ]
    ])
'''
print(a.unsqueeze(1))

b = a.unsqueeze(0)
# 缩减维度
b = b.squeeze(0)
print(b)
# 再次调用,当前维度不止一个元素,保持原样
b = b.squeeze(0)
print(b)

expand、repeat示例

import torch

# 维度扩展
x = torch.rand((2, 1, 3, 1))
x_expand = x.expand(2, 3, 3, 2)
x_expand_1 = x.expand(-1, -1, -1, 4)

# 维度重复
x_rep = x.repeat(2, 3, 1, 6)

'''
torch.Size([2, 1, 3, 1])
torch.Size([2, 3, 3, 2])
torch.Size([2, 1, 3, 4])
torch.Size([4, 3, 3, 6])
'''
print(x.shape)
print(x_expand.shape)
print(x_expand_1.shape)
print(x_rep.shape)

获取可变维度

...可变长度

data = torch.ones((4, 3, 28, 28))
# torch.Size([4, 3, 28, 28])
print(data.shape)
data1 = data[...]
# torch.Size([4, 3, 28, 28])
print(data1.shape)
data2 = data[0, ...]
# torch.Size([3, 28, 28])
print(data2.shape)
data3 = data[0, ..., 2, 3]
# torch.Size([3])
print(data3.shape)

Tensor拼接

import torch

a = torch.zeros(3, 3)
b = torch.ones(3, 3)

'''
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
'''
print(torch.cat((a, b), dim=0, out=None))
'''
tensor([[0., 0., 0., 1., 1., 1.],
        [0., 0., 0., 1., 1., 1.],
        [0., 0., 0., 1., 1., 1.]])
'''
print(torch.cat((a, b), dim=1, out=None))

Tensor堆叠(stack)

import torch

a = torch.arange(0, 4)
b = torch.arange(5, 9)
c = torch.stack((a, b), 0)
d = torch.stack((a, b), 1)

# tensor([0, 1, 2, 3])
print(a)
# tensor([5, 6, 7, 8])
print(b)

'''
tensor([
        [0, 1, 2, 3],
        [5, 6, 7, 8]
    ])
'''
print(c)

'''
tensor([
        [0, 5],
        [1, 6],
        [2, 7],
        [3, 8]
    ])
'''
print(d)

Tensor的切分

chunk:是按照"切分成确定的份数"来进行切分的

import torch

# chunks必须为整数
# torch.chunk(input, chunks, dim=0)

a = torch.tensor(list(range(1, 18)))
'''
chunk会向上取整,然后进行分配,17 / 4 = 4.25
所以前面几块的长度都是5,不够的在最后一块,长度为2
(tensor([1, 2, 3, 4, 5]), tensor([ 6,  7,  8,  9, 10]), tensor([11, 12, 13, 14, 15]), tensor([16, 17]))
'''
print(torch.chunk(a, 4, dim=0))

split:split_size_or_sections参数为整数时,表示将tensor按照每块大小为这个整数的数值来切割;当这个参数为列表时,则表示将此tensor切成和列表中元素一样大小的块

import torch

a = torch.tensor(list(range(1, 18)))

# (tensor([1, 2, 3, 4]), tensor([5, 6, 7, 8]), tensor([ 9, 10, 11, 12]), tensor([13, 14, 15, 16]), tensor([17]))
print(torch.split(a, 4, dim=0))
# (tensor([1, 2]), tensor([3, 4, 5]), tensor([ 6,  7,  8,  9, 10]), tensor([11, 12, 13, 14, 15, 16, 17]))
print(torch.split(a, (2, 3, 5, 7), dim=0))

unbind:是一种降维切分的方式,相当于删除一个维度之后的结果

import torch

a = torch.tensor(list(range(1, 5)))

# (tensor(1), tensor(2), tensor(3), tensor(4))
print(torch.unbind(a, dim=0))

Tensor的索引操作

index_select:基于给定的索引来进行数据提取

import torch

a = torch.arange(0, 16).view(4, 4)
print(a)

'''
在0轴上取1,3行
tensor([[ 4,  5,  6,  7],
        [12, 13, 14, 15]])
'''
b = a.index_select(0, torch.tensor([1, 3]))
print(b)

'''
在1轴上去0,3列
tensor([[ 0,  3],
        [ 4,  7],
        [ 8, 11],
        [12, 15]])
'''
c = torch.index_select(a, 1, torch.tensor([0, 3]))
print(c)

masked_select:通过一些判断条件来进行选择

import torch

a = torch.arange(5)
# tensor([3, 4])
print(a.masked_select(a > 2))
# tensor([2, 3, 4])
print(torch.masked_select(a, a > 1))

常用函数

```

### one-hot编码

```python
import torch
import torch.nn.functional as F

tensor = torch.tensor([[0, 1], [2, 3]])

'''
tensor([[0, 1],
        [2, 3]])
tensor([[[1, 0, 0, 0],
         [0, 1, 0, 0]],

        [[0, 0, 1, 0],
         [0, 0, 0, 1]]])
'''
one_hot_tensor = F.one_hot(tensor)

results matching ""

    No results matching ""