引言

本篇介绍tensor的维度变化。

维度变化改变的是数据的理解方式!

  • view/reshape:大小不变的条件下,转变shape
  • squeeze/unsqueeze:减少/增加维度
  • transpose/t/permute:转置,单次/多次交换
  • expand/repeat:维度扩展

view reshape

  • 在pytorch0.3的时候,默认是view .为了与numpy一致0.4以后增加了reshape。
  • 损失维度信息,如果不额外存储/记忆的话,恢复时会出现问题。
  • 执行view/reshape是有一定的物理意义的,不然不会这样做。
  • 保证tensor的size不变即可/numel()一致/元素个数不变。
  • 数据的存储/维度顺序非常非常非常重要
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
  1. In[4]: a = torch.rand(4,1,28,28)
    In[5]: a.shape
    Out[5]: torch.Size([4, 1, 28, 28])
    In[6]: a.view(4,28*28) # 4, 1*28*28 将后面的进行合并/合并通道,长宽,忽略了通道信息,上下左右的空间信息。适合全连接层。
    Out[6]:
    tensor([[0.1483, 0.6321, 0.8864, ..., 0.0646, 0.4791, 0.0892],
    [0.5868, 0.5278, 0.8514, ..., 0.0682, 0.7815, 0.2724],
    [0.4945, 0.4957, 0.0047, ..., 0.4253, 0.4135, 0.1234],
    [0.0618, 0.4257, 0.1960, ..., 0.1377, 0.5776, 0.4071]])
    In[7]: a.view(4,28*28).shape
    Out[7]: torch.Size([4, 784])
    In[8]: a.view(4*28, 28).shape # 合并batch,channel,行合并 放在一起为N [N,28] 每个N,刚好有28个像素点,只关心一行数据
    Out[8]: torch.Size([112, 28])
    In[9]: a.view(4*1,28,28).shape # 4张叠起来了
    Out[9]: torch.Size([4, 28, 28])
    In[10]: b = a.view(4,784) # a原来的维度信息是[b,c,h,w],但a这样赋值后,它是恢复不到原来的
    In[11]: b.view(4,28,28,1) # logic Bug # 语法上没有问题,但逻辑上 [b h w c] 与以前是不对应的。
    a.view(4,783)
    RuntimeError: shape '[4, 783]' is invalid for input of size 3136

squeeze 与 unsqueeze

unsqueeze

  • unsqueeze(index) 拉伸(增加一个维度) (增加一个组别)
  • 参数的范围是 [-a.dim()-1, a.dim()+1) 如下面例子中范围是[-5,5)
  • -5 –> 0 … -1 –> 4 这样的话,0表示在前面插入,-1表示在后面插入,正负会有些混乱,所以推荐用正数。
  • 0与正数,就是在xxx前面插入。
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  1. In[17]: a.shape
    Out[17]: torch.Size([4, 1, 28, 28])
    In[18]: a.unsqueeze(0).shape # 在0的前面插入一个维度
    Out[18]: torch.Size([1, 4, 1, 28, 28]) # 理解上就是在batch的基础上增加了组。
    In[19]: a.unsqueeze(-1).shape # 在-1之后插入一个维度
    Out[19]: torch.Size([4, 1, 28, 28, 1]) # 理解上可能增加一个方差之类的
    In[20]: a.unsqueeze(4).shape
    Out[20]: torch.Size([4, 1, 28, 28, 1])
    In[21]: a.unsqueeze(-4).shape
    Out[21]: torch.Size([4, 1, 1, 28, 28])
    In[22]: a.unsqueeze(-5).shape
    Out[22]: torch.Size([1, 4, 1, 28, 28])
    In[23]: a.unsqueeze(-6).shape
    IndexError: Dimension out of range (expected to be in range of [-5, 4], but got -6)
  2.  
  3. In[24]: a = torch.tensor([1.2,2.3])
    In[27]: a.shape
    Out[27]: torch.Size([2])
    In[25]: a.unsqueeze(-1) # 维度变成 [2,1] 2行1列
    Out[25]:
    tensor([[1.2000],
    [2.3000]])
    In[26]: a.unsqueeze(0)
    Out[26]: tensor([[1.2000, 2.3000]]) # 维度变成 [1,2] 1行2列

实际案例

给一个bias(偏置),bias相当于给每个channel上的所有像素增加一个偏置

为了做到 f+b 我们需要改变b的维度

  1. 1
    2
    3
    4
    5
  1. In[28]: b = torch.rand(32)
    In[29]: f = torch.rand(4,32,14,14)
    In[30]: b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
    In[31]: b.shape
    Out[31]: torch.Size([1, 32, 1, 1])

后面进一步扩张到 [4,32,14,14]

queeze

  • squeeze(index) 当index对应的dim为1,就产生作用。
  • 不写参数,会挤压所有维度为1的。
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  1. In[38]: b.shape
    Out[38]: torch.Size([1, 32, 1, 1])
    In[39]: b.squeeze().shape # 默认将所有维度为1的进行挤压 这32个channel,每个channel有一个值
    Out[39]: torch.Size([32])
    In[40]: b.squeeze(0).shape
    Out[40]: torch.Size([32, 1, 1])
    In[41]: b.squeeze(-1).shape
    Out[41]: torch.Size([1, 32, 1])
    In[42]: b.squeeze(1).shape
    Out[42]: torch.Size([1, 32, 1, 1])
    In[43]: b.squeeze(-4).shape
    Out[43]: torch.Size([32, 1, 1])

expand / repeat

  • Expand:broadcasting (推荐)

    • 只是改变了理解方式,并没有增加数据
    • 在需要的时候复制数据
  • Reapeat:memory copied
    • 会实实在在的增加数据

上面提到的b [1, 32, 1, 1] f[ 4, 32, 14, 14 ]

目标是将b的维度变成与f相同的维度。

expand

  • 扩展(expand)张量不会分配新的内存,只是在存在的张量上创建一个新的视图(view)
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
  1. In[44]: a = torch.rand(4,32,14,14)
    In[45]: b.shape
    Out[45]: torch.Size([1, 32, 1, 1]) # 只有1-->N才是可行的, 3 -> N 是需要规则的
    In[46]: b.expand(4,32,14,14).shape
    Out[46]: torch.Size([4, 32, 14, 14])
    In[47]: b.expand(-1,32,-1,-1).shape # -1表示这个维度不变
    Out[47]: torch.Size([1, 32, 1, 1])
    In[48]: b.expand(-1,32,-1,-4).shape # -4这里是一个bug,没有意义,最新版已经修复了
    Out[48]: torch.Size([1, 32, 1, -4])

repeat

  • 主动复制原来的。
  • 参数表示的是要拷贝的次数/是原来维度的倍数
  • 沿着特定的维度重复这个张量,和expand()不同的是,这个函数拷贝张量的数据。
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  1. In[49]: b.shape
    Out[49]: torch.Size([1, 32, 1, 1])
    In[50]: b.repeat(4,32,1,1).shape
    Out[50]: torch.Size([4, 1024, 1, 1])
    In[51]: b.repeat(4,1,1,1).shape
    Out[51]: torch.Size([4, 32, 1, 1])
    In[52]: b.repeat(4,1,32,32)
    In[53]: b.repeat(4,1,32,32).shape
    Out[53]: torch.Size([4, 32, 32, 32])
    In[55]: b.repeat(4,1,14,14).shape # 这样就达到目标了
    Out[55]: torch.Size([4, 32, 14, 14])

转置

.t

转置操作

  • .t 只针对 2维矩阵
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
  1. a = torch.randn(3,4)
    a.t().shape
    Out[58]: torch.Size([4, 3])
    In[60]: a
    Out[60]:
    tensor([[ 0.5629, -0.5085, -0.3371, 1.2387],
    [ 0.2142, -1.7846, 0.2297, 1.7797],
    [-0.3197, 0.6116, 0.3791, 0.9218]])
    In[61]: a.t()
    Out[61]:
    tensor([[ 0.5629, 0.2142, -0.3197],
    [-0.5085, -1.7846, 0.6116],
    [-0.3371, 0.2297, 0.3791],
    [ 1.2387, 1.7797, 0.9218]])
    b.t()
    RuntimeError: t() expects a tensor with <= 2 dimensions, but self is 4D

transpose

  • 在结合view使用的时候,view会导致维度顺序关系变模糊,所以需要人为跟踪。
  • 错误的顺序,会导致数据污染
  • 一次只能两两交换
  • contiguous
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  1. # 由于交换了1,3维度,就会变得不连续,所以需要用contiguous,来吧数据变得连续。
    In[17]: a1 = a.transpose(1,3).view(4,3*32*32).view(4,3,32,32)
    RuntimeError: invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Call .contiguous() before .view()
  2.  
  3. In[8]: a = torch.randn(4,3,32,32)
    In[9]: a.shape
    Out[9]: torch.Size([4, 3, 32, 32])
    In[10]: a1 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,3,32,32)
    #[b c h w] 交换1,3维度的数据 [b w h c],再把后面的三个连在一起,展开后变为 [b c w h] 导致和原来的顺序不同,造成数据污染!!!
    In[11]: a1.shape
    Out[11]: torch.Size([4, 3, 32, 32])
    In[12]: a2 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,32,32,3).transpose(1,3)
    # [b c h w] -> [b w h c] -> [b w h c] -> [b c h w] 和原来顺序相同。
    In[13]: a2.shape
    Out[13]: torch.Size([4, 3, 32, 32])
    # 验证向量一致性
    In[14]: torch.all(torch.eq(a,a1))
    Out[14]: tensor(0, dtype=torch.uint8)
    In[15]: torch.all(torch.eq(a,a2))
    Out[15]: tensor(1, dtype=torch.uint8)

permute

  • 会打乱内存顺序,待补充!!!
  • 由于transpose一次只能两两交换,所以变换后在变回去至少需要两次操作,而permute一次就好。例如对于[b,h,w,c]
  • [b,h,w,c]是numpy存储图片的格式,需要这一步才能导出numpy
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  1. In[18]: a = torch.rand(4,3,28,28)
    In[19]: a.transpose(1,3).shape # [b c h w] -> [b w h c] h与w的顺序发生了变换,导致图像发生了变化
    Out[19]: torch.Size([4, 28, 28, 3])
    In[20]: b = torch.rand(4,3,28,32)
    In[21]: b.transpose(1,3).shape
    Out[21]: torch.Size([4, 32, 28, 3])
    In[22]: b.transpose(1,3).transpose(1,2).shape
    Out[22]: torch.Size([4, 28, 32, 3]) # [b,h,w,c]是numpy存储图片的格式,需要这一步才能导出numpy
    In[23]: b.permute(0,2,3,1).shape
    Out[23]: torch.Size([4, 28, 32, 3])

Broadcast

自动扩展

  • 维度扩展,自动调用expand
  • without copying data ,不需要拷贝数据。

核心思想

  • 在前面插入1维
  • 将size 1 扩展成相同 size 的维度

例子:

  • 对于 feature maps : [4, 32, 14, 14],想给它添加一个偏置Bias
  • Bias:[32] –> [32, 1 , 1] (这里是手动的) => [1, 32, 1, 1] => [4, 32, 14, 14]
  • 目标:当Bias和feature maps的size一样时,才能执行叠加操作!!!

Why broadcasting?

就像下图表示的一样:我们希望进行如下的几种计算,但需要满足数学上的约束(size相同),为了节省人们为满足数学上的约束而手动复制的过程,而产生的Broadcast,它节省了大量的内容消耗。

  • 第二行数据中 [3] => [1, 3] => [4, 3] (行复制了4次)
  • 第三行数据中
    • [4,1] => [4, 3] (列复制了3次)
    • [1,3] => [4, 3] (行复制了4次)
  • broadcast = unsqueze(插入新维度) + expand(将1dim变成相同维度)

例子:

  • 有这样的数据 [class, students, scores],具体是4个班,每个班32人,每人8门课程[4, 32, 8] 。
  • 考试不理想,对于这组数据我们需要为每一位同学的成绩加5分
  • 要求: [4, 32, 8] + [4, 32, 8]
  • 实际上:[4, 32, 8] + [5.0]
  • 操作上:[1] =>(unsqueeze) [1, 1, 1] =>(expand_as) [4, 32, 8],这样需要写3个接口。
  • 所以才会有 broadcast!!

内存分析:

  • [4, 32, 8] => 1024
  • [5.0] => 1 如果是手动复制的话,内存消耗将变为原来的1024倍

使用条件?

A [ 大维度 —> 小维度 ]

从最后一位(最小维度)开始匹配,如果维度上的size是0,1或相同,则满足条件,看下一个维度,直到都满足条件为止。

  • 如果当前维度是1,扩张到相同维度
  • 如果没有维度,插入一个维度并扩张到相同维度
  • 当最小维度不匹配的时候是没法使用broadcastiong,如共有8门课程,但只给了4门课程的变化,这样就会产生歧义。

note:小维度指定,大维度随意

小维度指定:假如英语考难了,只加英语成绩 [0 0 5 0 0 0 0 0]

案例

情况一

A[4, 32, 14, 14]

B[1, 32, 1, 1] => [4,,32, 14, 14]

情况二

A[4, 32, 14, 14]

B[14, 14] => [1, 1, 14, 14] => [4, 32, 14, 14]

情况三

不符合条件

A[4, 32, 14, 14]

B[2, 32, 14, 14]

理解这种行为

  • 小维度指定,大维度随意。小维度设定规则(加5分),大维度默认按照这个规则(通用)。
  • 维度为1才满足条件,是为了保证公平(统一的规则)

常见使用情景

  • A [4, 3, 32, 32] b,c,h,w
  • +[32, 32] 叠加一个相同的feature map,做一些平移变换。相当于一个base(基底),
  • +[3, 1, 1] 针对 RGB 进行不同的补充,如R 0.5 、G 0 、B 0.3
  • +[1, 1, 1, 1] 对于所有的都加一个数值,抬高一下,如加0.5.

Pytorch-tensor的维度变化的更多相关文章

  1. Pytorch Tensor 维度的扩充和压缩

    维度扩展 x.unsqueeze(n) 在 n 号位置添加一个维度 例子: import torch x = torch.rand(3,2) x1 = x.unsqueeze(0) # 在第一维的位置 ...

  2. Pytorch 中 tensor的维度拼接

    torch.stack() 和 torch.cat() 都可以按照指定的维度进行拼接,但是两者也有区别,torch.satck() 是增加新的维度进行堆叠,即其维度拼接后会增加一个维度:而torch. ...

  3. 深度学习实践-物体检测-faster-RCNN(原理和部分代码说明) 1.tf.image.resize_and_crop(根据比例取出特征层,进行维度变化) 2.tf.slice(数据切片) 3.x.argsort()(对数据进行排列,返回索引值) 4.np.empty(生成空矩阵) 5.np.meshgrid(生成二维数据) 6.np.where(符合条件的索引) 7.tf.gather取值

    1. tf.image.resize_and_crop(net, bbox, 256, [14, 14], name)  # 根据bbox的y1,x1,y2,x2获得net中的位置,将其转换为14*1 ...

  4. 【转载】 opencv, PIL.Image的彩色图片维度 && caffe和pytorch的矩阵维度

    原文地址: https://blog.csdn.net/u011668104/article/details/82718375 ------------------------------------ ...

  5. pytorch tensor 维度理解.md

    torch.randn torch.randn(*sizes, out=None) → Tensor(张量) 返回一个张量,包含了从标准正态分布(均值为0,方差为 1)中抽取一组随机数,形状由可变参数 ...

  6. pytorch 调整tensor的维度位置

    target.permute([0, 3, 1, 2]) 一定要使用permute以及中括号 一些在我这里没起到作用的网上的例子: 1. https://blog.csdn.net/zouxiaolv ...

  7. 关于类型为numpy,TensorFlow.tensor,torch.tensor的shape变化以及相互转化

    https://blog.csdn.net/zz2230633069/article/details/82669546 2018年09月12日 22:56:50 一只tobey 阅读数:727   1 ...

  8. Pytorch Tensor 常用操作

    https://pytorch.org/docs/stable/tensors.html dtype: tessor的数据类型,总共有8种数据类型,其中默认的类型是torch.FloatTensor, ...

  9. SQLServer如何处理数据集的维度变化

    Student表有三列,分别是姓名.课程.成绩 Name Curricula Mark 张三 语文 70 李四 数学 80 王朝 英语 59 城南 马哲 70 王朝 语文 90 我想得到的效果是,列出 ...

随机推荐

  1. Make It Connected CodeForces - 1095F (建图+最小生成树)

    Make It Connected CodeForces - 1095F You are given an undirected graph consisting of nn vertices. A ...

  2. ext系统的超级块

    什么是超级块 如果说inode块是Linux操作系统中文件的核心,那么超级块就是文件系统的心脏.启动Lnux操作系统后,发现某个文件系统无法使用,很有 可能就是超级块出现了问题.为什么这个超级块有这么 ...

  3. Docker清除容器镜像命令:

    # ~/.bash_aliases # Kill all running containers. alias dockerkillall='docker kill $(docker ps -q)' # ...

  4. C语言Ⅰ作业-05

    这个作业属于哪个课程 C语言程序设计Ⅰ 这个作业要求在哪里 https://www.cnblogs.com/tongyingjun/p/11722665.html 我在这个课程的目标是 熟练掌握如何用 ...

  5. np中的温故知新

    1.一维数组中寻找与某个数最近的数 # 一维数组中寻找与某个数最近的数 Z=np.random.uniform(0,1,20) print("随机数组:\n",Z) z=0.5 m ...

  6. Centos创建用户并授权

    创建新用户 [root@VM ~]# adduser it为这个用户初始化密码,linux会判断密码复杂度,不过可以强行忽略:[root@VM_~]# passwd itChanging passwo ...

  7. javascript数据结构之单链表

    下面是用javascript实现的单链表,但是在输出的时候insert方法中存在问题,chrome的console报错说不能读取空的属性,调试了很久都没有通过,先在这里存着,以后再来修改一下. //数 ...

  8. 【java设计模式】-01设计模式简介

    简介 设计模式(Design pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的. 设计模式是一套被反复使 ...

  9. JavaScript设计模式—适配器模式

    适配器模式介绍 旧接口格式和使用者不兼容,中间加一个适配器转换接口 在生活中,我们有许多的适配器,例如iPhone7以后的耳机接口从3.5mm圆孔接口更改成为了苹果专属的 lightning接口.许多 ...

  10. 16.合并两个排序的链表 Java

    题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 解题思路 两种解法:递归和非递归 参考代码 /* public class ListNode { ...