PyTorch日积月累_1-Tensor
时间:2022-10-30 18:00:01
文章目录
-
- 前言 _ PyTorch主要模块
- Tensor
-
- 张量数据类型
- 张量的创建
-
- 1. 对于预先有数据的情况,通过Torch.tensor()创建
- 2. 通过`torch`模块下的内置函数创建特殊形状的张量
- 张量存储设备
- 张量运算
- 张量维度
-
-
- 关于contiguous该方法的应用场景
-
- 张量极值和排序
- 张量乘法
- 拼接分割张量
- 扩张、压缩和广播
- 扩张、压缩和广播
前言 _ PyTorch主要模块
本专栏以张校捷为主PyTorch:从模型到源代码的阅读笔记,既有作者自己的思考和补充,又有更详细的内容,请支持原作者。
模块名 | 功能 |
---|---|
torch |
1. 常见的激活函数,例如torch.relu 和torch.sigmoid ;2. PyTorch中张量的一些操作,比如 torch.add(x,y) ,注:这些操作通常也可以通过张量本身来实现,两者都是等价的,例如x.add(y) ,对于一些常用的操作,PyTorch运算符的重载已经实现,例如x y 3. 产生一定形状的张量,如 torch.zeros 和torch.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.MSELoss 和nn.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.FloatTensor
和torch.cuda.FloatTensor
,但是目前PyTorch复数类型不支持。
可以通过数据类型to例如,转换方法>>> x.to(torch.FloatTensor)
张量的创建
1. 对于预先有数据的情况,通过Torch.tensor()创建
若已提前有数据x
(Python list 或 numpy array)可以通过这种方法创建torch.tensor(x)
,需要注意精度问题:
- 对于numpy数组,精度不变(numpy数组和tensor转换非常快,和tensor这里不表示底层实现)
- 对于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
实例。
张量运算
一般有两种等价的运算方法
torch.add(a,b)
或者torch.add(a,b,out=c)
,第二种需提前声明ca.add(b)
或者a.add_(b)
, 第二种方法后加下划线,表明是就地操作,即修改了a的值
张量维度
可以通过size(x)
或者x.shape
查看张量形状,两种方法等价,返回的都是PyTorch基于Python Tuple封装的torch.Size
类型,通过索引查看每个维度的大小。
改变张量形状:
-
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 contiguous
and then you just add a call tocontiguous()
. -
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
张量的拼接与分割
拼接
torch.stack
传入 张量列表,指定并创建一个维度堆叠torch.cat
传入张量列表,选中一个维度拼接
分割
torch.split
传入 被分割张量,分割后维度的大小 整数/列表,分割的维度torch.chunk
传入 被分割张量,分割的段数 整数/列表,分割的维度
张量的扩增、压缩和广播
torch.squeeze
和torch.unsqueeze
广播机制,主要是用来解决 四则运算时 张量维度不一致的问题
-
如果两张量的形状不一致,不能进行计算,例如(3,4,5)只能和(3,4,5)形状的张量运算
-
除了一种特殊情况,张量的某个维度为1,则进行复制,使其满足条件1
例如 (3,4,5)和(3,1,5),需要将(3,1,5)扩增为(3,4,5),然后进行运算
数/列表,分割的维度
张量的扩增、压缩和广播
torch.squeeze
和torch.unsqueeze
广播机制,主要是用来解决 四则运算时 张量维度不一致的问题
-
如果两张量的形状不一致,不能进行计算,例如(3,4,5)只能和(3,4,5)形状的张量运算
-
除了一种特殊情况,张量的某个维度为1,则进行复制,使其满足条件1
例如 (3,4,5)和(3,1,5),需要将(3,1,5)扩增为(3,4,5),然后进行运算