1-Tensor

前言 _ PyTorch主要模块

本专栏主要为张校捷《深入浅出PyTorch:从模型到源码》的读书笔记,兼有笔者自己的思考和补充,更详尽的内容,请支持原作者的大著。

模块名 功能
torch 1. 常见的激活函数,例如torch.relutorch.sigmoid;
2. PyTorch中张量的一些操作,例如torch.add(x,y),注意:这些操作往往也可以通过张量自身的方法实现,这两者是等价的,例如x.add(y),对于一些常用的运算,PyTorch已经实现了运算符的重载,例如x+y
3. 产生一定形状的张量,例如torch.zerostorch.randn
torch.Tensor PyTorch中的张量类型
torch.cuda cuda相关,例如检查cuda是否可用,torch.cuda.is_avalible
torch.nn PyTorch神经网络模块的核心
1. 卷积层nn.Conv2d和全连接层nn.Linear等,实际是调用的nn.functional.Conv2d
2. 构建深度学习模型时,需要继承nn.Module类,并重写forward方法实现一个新的神经网络
3. 损失函数nn.MSELossnn.CrossEntropyLoss
torch.nn.functional 卷积函数,池化函数,不常见的激活函数,例如relu6
torch.optim 优化器;有关学习率衰减的子模块,例如optim.lr_scheduler
torch.autograd 自动微分,包括反向传播autograd.backward函数,autograd.grad实现一个标量(scalar)对一个张量的求导;设置不需要参与求导的模块
torch.distributed PyTorch的分布式计算模块,原理是多进程
torch.distributions 可以解决 强化学习中的Policy Gradient 输出结果离散,不能求导的问题(采样,对数求导)
torch.jit 动态图 → 静态图
torch.utils.data 主要包含了数据集(Dataset)和数据载入器(DataLoader)
torch.utils.tensorboard 可视化

Tensor

张量的数据类型

PyTorch目前支持9种数据类型(torch.float32,torch.int32,torch.uint8,torch.bool,etc.),每种数据类型对应CPU和GPU两个版本,例如,torch.FloatTensortorch.cuda.FloatTensor,但是目前PyTorch并不支持复数类型。

数据类型之间可通过to方法进行转换,例如>>> x.to(torch.FloatTensor)

张量的创建

1. 对于预先有数据的情况,通过Torch.tensor()创建

如果预先已经有数据x(Python list 或 numpy array)了,则可以通过该种方法创建torch.tensor(x),需要注意精度问题:

  1. 对于numpy数组,精度不变(numpy数组和tensor的转换非常迅速,这和tensor的底层实现有关,此处不表)
  2. 对于Python list类型的数据,PyTorch默认把浮点数转换为单精度浮点数(torch.float32)而不是 torch.float64

此外,该方法支持列表的嵌套,但是子列表的大小要一致,否则会报错。

注意:torch.Tensor()也支持这种创建方式,但是不支持直接传入 一个实数,创建标量,例如torch.tensor(1)的结果是一个scalar类型的标量,其shape为torch.Size([]),不同于torch.tensor([1])的shape。

2. 通过torch模块下的内置函数创建特殊形状的张量

>>> import torch  
>>> torch.rand(3,3)  # [0,1)均匀分布,注意,如非特别说明,一般都是前闭后开
tensor([[0.6227, 0.5353, 0.4991],
        [0.9781, 0.3917, 0.0828],
        [0.7478, 0.5403, 0.7267]])
>>> torch.randn(3,3) # 标准正态分布
tensor([[-0.0391,  0.4533,  1.1559],
        [-0.0949,  0.0836,  0.1401],
        [-0.2005,  0.2361, -0.5077]])
>>> torch.zeros(3,3) 
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])
>>> torch.eye(3,3) 
tensor([[1., 0., 0.],
        [0., 1., 0.],
        [0., 0., 1.]])
>>> torch.ones(3,3) 
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]])
>>> torch.randint(0,10,(3,3))  # [0,10)均匀分布的整数
tensor([[2, 9, 4],
        [1, 7, 7],
        [5, 3, 0]])

注意最后一个torch.randint(0,10,(3,3))的用法。

也可以创建形状相同的张量

torch.ones_like(),torch.zeros_like(), torch.rand_like(),torch.randn_like()

或者直接传入张量的shape/形状的元组,会先检查空间是否足够,然后创建,用时才分配空间。

也在创建时,通过.dtype(torch.float32)指定张量的元素类型

注意:创建tensor时共享内存的有 torch.detach(),torch.from_numpy(); 不共享内存的有 torch.clone(),torch.tensor()

张量的存储设备

注意:只有在相同设备上的张量才能进行运算,否则会报错。

将数据转移到其他设备的方法: x.cpu()或者x.gpu(0)x.to("cuda:0")

推荐使用后一种,参数可以是设备名,也可以是torch.device实例。

张量运算

一般有两种等价的运算方法

  1. torch.add(a,b) 或者 torch.add(a,b,out=c),第二种需提前声明c
  2. a.add(b)或者 a.add_(b), 第二种方法后加下划线,表明是就地操作,即修改了a的值

张量维度

可以通过size(x)或者x.shape查看张量形状,两种方法等价,返回的都是PyTorch基于Python Tuple封装的torch.Size类型,通过索引查看每个维度的大小。

改变张量形状:

  1. view方法,指定-1,PyTorch会自动计算,只改变tensor头部中的步长(stride)信息,而不会改变底层数据,注意,一定要保证前后元素个数一致。

    如果想要将内容复制到新的一份内存空间,若前后不兼容,使用.contiguous(),否则,直接深拷贝torch.clone()即可。

    More about contiguous

    When you call contiguous(), it actually makes a copy of tensor so the order of elements would be same as if tensor of same shape created from scratch.

    Normally you don’t need to worry about this. If PyTorch expects contiguous tensor but if its not then you will get RuntimeError: input is not contiguousand then you just add a call to contiguous().

  2. reshape方法

    相当于view+contiguous

关于contiguous方法的一个应用场景

需求:将四张256*256的RGB图片截取成上下两部分,并保存为六张照片。

思考:实际就是[4,3,256,256]→[12,3,128,256]

img_PIL = torch.rand(4,3,256,256)

new_img_tensor = img_tensor.view(4,3,2,128,256)
new_img_tensor = new_img_tensor.permute(0,2,1,3,4)
new_img_tensor.contiguous()  # 注意,view方法只能处理语义和内存一致/兼容的张量,需要提前进行一致化处理
new_img_tensor = new_img_tensor.view(8,2,128,256)

张量极值和排序

使用案例

>>> x = torch.rand(3,4)
>>> x
tensor([[0.6890, 0.6742, 0.1902, 0.9747],
        [0.4639, 0.3918, 0.4839, 0.8264],
        [0.7149, 0.8934, 0.9888, 0.4748]])
>>> x.argmin(1) 
tensor([2, 1, 3])
>>> x.min(1) 
torch.return_types.min(
values=tensor([0.1902, 0.3918, 0.4748]),
indices=tensor([2, 1, 3]))
>>> torch.argmin(x,1) 
tensor([2, 1, 3])
>>> torch.min(x,1) 
torch.return_types.min(
values=tensor([0.1902, 0.3918, 0.4748]),
indices=tensor([2, 1, 3]))
>>> x.sort(1) 
torch.return_types.sort(
values=tensor([[0.1902, 0.6742, 0.6890, 0.9747],
        [0.3918, 0.4639, 0.4839, 0.8264],
        [0.4748, 0.7149, 0.8934, 0.9888]]),
indices=tensor([[2, 1, 0, 3],
        [1, 0, 2, 3],
        [3, 0, 1, 2]]))
>>> x.sort(1,descending=True)
torch.return_types.sort(
values=tensor([[0.9747, 0.6890, 0.6742, 0.1902],
        [0.8264, 0.4839, 0.4639, 0.3918],
        [0.9888, 0.8934, 0.7149, 0.4748]]),
indices=tensor([[3, 0, 1, 2],
        [3, 2, 0, 1],
        [2, 1, 0, 3]]))

argmin和min既可以使用torch模块下的方法,也可以使用tensor自带的方法,前者返回indices,后者返回最小值和其在原始张量上的位置,sort排序默认是升序,也可通过descending=True指定为降序。

张量乘法

前面的a.mul(b)是对应元素相乘,而a.mm(b)对应的是矩阵乘法,在Python3.5之后,根据PEP465提案,也可以用a@b进行矩阵乘法。

mini-batch 矩阵乘法:torch.bmm(a,b)针对有batch的情况,如果此时调用a@b,会自动调用bmm

对于更多维度的张量相乘的情况,需要决定各自张量元素乘积的结果沿着哪些维度求和,这个过程称作 缩并 contraction,需引入爱因斯坦求和约定 Einstein Summation Convention:

More about Einsum in PyTorch

但是,einsum的运行速度非常慢,下面在cpu上写一个测试时间的装饰器

# 验证torch.einsum的速度
import time
import torch

def time_test(func):
    def inner(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        end_time = time.time()
        return end_time - start_time
    return inner

@time_test
def common_test(a,b):
    for i in range(1000):
        a.mul(b)

@time_test
def einsum_test(a,b):
    for i in range(1000):
        torch.einsum('ik,kj->ij',[a,b])

def main():
    a = torch.arange(10000).reshape(100, 100)
    b = torch.arange(10000).reshape(100, 100)
    einsum_time = einsum_test(a,b)
    common_time = common_test(a,b)
    print("common_test takes %s\neinsum_test takes %.3f s"%(common_time,einsum_time))

if __name__ == "__main__":
    main()
# common_test takes 0.016 s
# einsum_test takes 2.647 s

张量的拼接与分割

拼接

  1. torch.stack 传入 张量列表,指定并创建一个维度堆叠
  2. torch.cat 传入张量列表,选中一个维度拼接

分割

  1. torch.split 传入 被分割张量,分割后维度的大小 整数/列表,分割的维度
  2. torch.chunk 传入 被分割张量,分割的段数 整数/列表,分割的维度

张量的扩增、压缩和广播

torch.squeezetorch.unsqueeze

广播机制,主要是用来解决 四则运算时 张量维度不一致的问题

  1. 如果两张量的形状不一致,不能进行计算,例如(3,4,5)只能和(3,4,5)形状的张量运算

  2. 除了一种特殊情况,张量的某个维度为1,则进行复制,使其满足条件1

    例如 (3,4,5)和(3,1,5),需要将(3,1,5)扩增为(3,4,5),然后进行运算

数/列表,分割的维度

张量的扩增、压缩和广播

torch.squeezetorch.unsqueeze

广播机制,主要是用来解决 四则运算时 张量维度不一致的问题

  1. 如果两张量的形状不一致,不能进行计算,例如(3,4,5)只能和(3,4,5)形状的张量运算

  2. 除了一种特殊情况,张量的某个维度为1,则进行复制,使其满足条件1

    例如 (3,4,5)和(3,1,5),需要将(3,1,5)扩增为(3,4,5),然后进行运算

猜你喜欢

转载自blog.csdn.net/weixin_43721070/article/details/120924336