Pytorch03_张量变化

Tensor变化

 

导入库

 

import numpy as np # 导入Numpy
import torch  # 导入Torch

 

1. 创建张量

 1)通过numpy的array创建

# 创建(3,3)的全1矩阵
arr = np.ones((3, 3))  
# 创建张量,默认存放在CPU
t1 = torch.tensor(arr)
# 创建张量,存放在GPU
t2 = torch.tensor(arr, device='cuda') 

 2)torch.from_numpy

# 创建矩阵
arr = np.array([[1, 2, 3], [4, 5, 6]])  
# 转化为torch的张量形式
t = torch.from_numpy(arr) 

# 修改arr[0,0] 张量会同时变化,即共用内存
arr[0, 0] = 0
print(t) 
>>> tensor([[0, 2, 3],[4, 5, 6]], dtype=torch.int32)

 3)torch.zeros 、torch.ones 、torch.full

# torch.zeros
t = torch.zeros(3,3)  
# input:矩阵尺寸3,3
print(t)
>>> tensor([[0., 0., 0.],[0., 0., 0.],[0., 0., 0.]])

# torch.ones
t = torch.ones(3,3)  
# input:矩阵尺寸3,3
print(t)
>>> tensor([[1., 1., 1.],[1., 1., 1.],[1., 1., 1.]])

# torch.full
t = torch.full((3,3),2)   
# input:(矩阵尺寸3,3), 填充数值2
print(t)
>>> tensor([[2., 2., 2.],[2., 2., 2.],[2., 2., 2.]])

 4) 数列类 torch.arange 、torch.linspace

# torch.arange
t = torch.arange(2, 10, 2)   
# input:数值区间[2,10),间隔2
# 注意:是闭区间到开区间[2,10)
print(t)
>>> tensor([2, 4, 6, 8])

# torch.linspace
t = torch.linspace(2, 10, 6) 
#input:数值区间[2,10],创建元素个数6
# 注意是闭区间[2,10]
print(t)
>>> tensor([ 2.0000,  3.6000,  5.2000,  6.8000,  8.4000, 10.0000])

 5)分布类(正态分布)torch.normal

t_normal = torch.normal(0, 1, size=(4,))  
# input:均值,标准差,张量尺寸
print(t_normal)
>>> tensor([-0.7996,  0.5916,  0.8975,  0.6617])

# 均值:tensor([1., 2., 3., 4.])
mean = torch.arange(1, 5, dtype=torch.float)  
# 标准差:tensor([1., 2., 3., 4.])
std = torch.arange(1, 5, dtype=torch.float)   
t_normal = torch.normal(mean, std)
print(t_normal)

>>> tensor([0.7053, 2.7317, 1.1439, 2.1598])  
# 分别满足(1,1),(2,2),(3,3),(4,4)正态分布

2. 张量信息

 1)tensor.size() 与 tensor.shape 的区别

# 创建一定维度的张量
a1 = torch.zeros(1)
a2 = torch.zeros(2, 3)
a3 = torch.zeros(2, 3, 5)

# 输出维度信息 结果相同
print('a1.size() = ', a1.size())
print('a2.size() = ', a2.size())
print('a3.size() = ', a3.size())

print('a1.shape() = ', a1.shape)
print('a2.shape() = ', a2.shape)
print('a3.shape() = ', a3.shape)

>>> a1.size() =  torch.Size([1])
>>> a2.size() =  torch.Size([2, 3])
>>> a3.size() =  torch.Size([2, 3, 5])

>>> a1.shape() =  torch.Size([1])
>>> a2.shape() =  torch.Size([2, 3])
>>> a3.shape() =  torch.Size([2, 3, 5])

区别,size(index) 可以通过指定参数,获取某维度的具体信息,shape不可以,但是 shape 可以使用 shape[index],达到同样的效果.

print('a1.size(0) = ', a1.size(0))  # a1.size(0) =  1
print('a2.size(1) = ', a2.size(1))  # a2.size(1) =  3
print('a3.size(2) = ', a3.size(2))  # a3.size(2) =  5

print('a1.shape(0) = ', a1.shape[0])  # a1.shape(0) =  1
print('a2.shape(1) = ', a2.shape[1])  # a2.shape(1) =  3
print('a3.shape(2) = ', a3.shape[2])  # a3.shape(2) =  5

 2) 其他信息

 数据类型、晚晚继续

 

 

3. 维度变化

 1)改变 shape

torch.reshape()、torch.view() 可以调整 Tensor 的 shape,返回一个新 shape 的 Tensor,torch.view() 是老版本的实现,torch.reshape() 是最新的实现,两者在功能上是一样的。

import torch
# 随机生成维度为4,1,28,28的矩阵
a = torch.rand(4, 1, 28, 28)
print(a.shape)  # torch.Size([4, 1, 28, 28])
print(a.view(4 * 1, 28, 28).shape)  # torch.Size([4, 28, 28])
print(a.reshape(4 * 1, 28, 28).shape) # torch.Size([4, 28, 28])
print(a.reshape(4, 1 * 28 * 28).shape) # torch.Size([4, 784])

注意:维度变换的时候要注意实际意义。

 2)删减维度

 torch.unsqueeze(index) 可以为 Tensor 增加一个维度,增加的这一个维度的位置由我们自己定义,新增加的这一个维度不会改变数据本身,只是为数据新增加了一个组别,这个组别是什么由我们自己定义。

 index的范围不能超过数据本身已有的维度范围,可以为负数,同负索引。

a = torch.randn(4, 1, 28, 28)
print(a.shape)  # torch.Size([4, 1, 28, 28])
print(a.unsqueeze(0).shape)  # torch.Size([1, 4, 1, 28, 28])
print(a.unsqueeze(-1).shape)  # torch.Size([4, 1, 28, 28, 1])
print(a.unsqueeze(3).shape)  # torch.Size([4, 1, 28, 1, 28])
print(a.unsqueeze(4).shape)  # torch.Size([4, 1, 28, 28, 1])
print(a.unsqueeze(-4).shape)  # torch.Size([4, 1, 1, 28, 28])
print(a.unsqueeze(-5).shape)  # torch.Size([1, 4, 1, 28, 28])
print(a.unsqueeze(5).shape)  # 报错,超出范围了,原始数据只有四个维度(最大索引为3),因此最多只能在第五个维度上增加(索引为4)

Traceback (most recent call last):
  File "/home/lhy/workspace/mmdetection/my_code/pytorch_ws/tensotr_0.py", line 218, in <module>
    print(a.unsqueeze(5).shape)
IndexError: Dimension out of range (expected to be in range of [-5, 4], but got 5)

删减维度实际上是一个维度挤压的过程,直观地看是把那些多余的[]给去掉,也就是只是去删除那些size=1的维度。

import torch
 
a = torch.Tensor(1, 4, 1, 9)
print(a.shape)  # 
print(a.squeeze().shape) # 删除所有的size=1的维度
print(a.squeeze(0).shape) # 删除0号维度,ok
print(a.squeeze(2).shape) # 删除2号维度,ok
print(a.squeeze(3).shape) # 删除3号维度,但是3号维度是9不是1,删除失败

>>> torch.Size([1, 4, 1, 9])
>>> torch.Size([4, 9])
>>> torch.Size([4, 1, 9])
>>> torch.Size([1, 4, 9])
>>> torch.Size([1, 4, 1, 9])

 3)维度扩展

expand 就是在某个 size=1 的维度上改变 size,改成更大的一个大小,实际就是在每个 size=1 的维度上的标量的广播操作。
import torch
# 想要把bias加到data上面去
bias = torch.rand(32)
data = torch.rand(4, 32, 14, 14)

# 先进行维度增加
bias = bias.unsqueeze(1).unsqueeze(2).unsqueeze(0)
print(bias.shape)  # torch.Size([1, 32, 1, 1])
 
# 再进行维度扩展
bias = bias.expand(4, -1, 14, 14)  # -1表示这个维度保持不变,这里写32也可以
print(bias.shape)  # torch.Size([4, 32, 14, 14])

# 可以看出,效果就是把维度为1的地方复制了很多份,以完成扩展的目的
data + bias

repeat 就是将每个位置的维度都重复至指定的次数,以形成新的 Tensor,功能与维度扩展一样,但是 repeat 会重新申请内存空间,repeat() 参数表示各个维度指定的重复次数。

import torch

b = torch.Tensor(1, 32, 1, 1)
print(b.shape)  # torch.Size([1, 32, 1, 1])
# 维度重复,32这里不想进行重复,所以就相当于"重复至1次"
b = b.repeat(4, 1, 14, 14)
print(b.shape)  # torch.Size([4, 32, 14, 14])

 4)转置操作

 Pytorch的转置操作只适用于dim=2的Tensor,也就是矩阵。

c = torch.Tensor(2, 4)  # shape=(2,4)
print(c.t().shape)  # torch.Size([4, 2])

 5)维度交换

transpose

# transpose 交换维度
d = torch.Tensor(6, 3, 1, 2)
# 1号维度和3号维度交换
# 注意这种交换使得存储不再连续,再执行一些reshape的操作会报错,所以要调用一下contiguous()使其变成连续的维度。
print(d.transpose(1, 3).contiguous().shape)  

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

 举个例子验证一下

下面这个例子比较一下每个位置上的元素都是一致的,来验证一下这个交换->压缩shape->展开shape->交换回去是没有问题的。

e = torch.rand(4, 3, 6, 7)
e2 = e.transpose(1, 3).contiguous().reshape(4, 7 * 6 * 3).reshape(4, 7, 6, 3).transpose(1, 3)
print(e2.shape)  # torch.Size([4, 3, 6, 7])
# 比较下两个Tensor所有位置上的元素是否都相等,返回1表示相等。
print(torch.all(torch.eq(e, e2)))  # tensor(1, dtype=torch.uint8)

permute

如果四个维度表示上节的 [batch,channel,h,w] ,如果想把 channel 放到最后去,形成 [batch,h,w,channel],那么如果使用前面的维度交换,至少要交换两次(先13交换再12交换)。而使用 permute 可以直接指定维度新的所处位置,更加方便。

a = torch.rand(4, 3, 6, 7)
print(a.permute(0, 2, 3, 1).shape)  # torch.Size([4, 6, 7, 3])

逻辑是:原有维度顺序为,0、1、2、3...  参数表示的含义为原有维度的索引。如上面例子表示,现在的第一个位置是原来的 0 维度(索引),现在的第二个位置是原来的2维度(索引)...



















posted @ 2021-06-09 14:12  Haozi_D17  阅读(96)  评论(0编辑  收藏  举报