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维度(索引)...