引言

本篇介绍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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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) 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
2
3
4
5
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
2
3
4
5
6
7
8
9
10
11
12
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
2
3
4
5
6
7
8
9
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
2
3
4
5
6
7
8
9
10
11
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 由于交换了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() 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
2
3
4
5
6
7
8
9
10
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. python 中的GIL (全局解释器锁)详解

    1.GIL是什么? GIL全称Global Interpreter Lock,即全局解释器锁. 作用就是,限制多线程同时执行,保证同一时间内只有一个线程在执行. GIL并不是Python的特性,它是在 ...

  2. HTML嵌入多媒体对象

    [问题描述]如何在HTML中嵌入pdf.word,音频(如mp3),视频(如mp4),flash呢? [分析] 1 嵌入pdf (1) 利用object <object classid=&quo ...

  3. 灵活部署django缓存,并使用

    使用django内置的redis=============>pip3 install django-redisCACHES = { 'default':{ 'BACKEND':'django_r ...

  4. [转]Normal Map中的值, Tangent Space, 求算 Tangent 与 Binormal 与 TBN Matrix

    原文出处 https://www.cnblogs.com/lookof/p/3509970.html - Normal Map中的值 -   有没有想过,Normal Map(法线贴图)为什么看上去都 ...

  5. macOS关闭修改扩展名的提示

    关闭 defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false; killall Finder 开启 def ...

  6. could not load file or assembly "System.Web.Mvc...

    1.一般出现这个错误是因为Web.Config里面的版本号跟project用到的dll版本对应不上 更改webconfig <add assembly="System.Web.Mvc, ...

  7. git避免提交本地配置文件-来自同事的分享

    在项目协作中,对于已经更改的文件,不同的开发者常常需要根据自己的需要对文件进行更改已满足本地开发环境的需求(这种情况很常见,一般是对项目相关的配置项的更改,对业务逻辑代码的更改一般都是正常的协作编码过 ...

  8. 对AM信号FFT的matlab仿真

    普通调幅波AM的频谱,大信号包络检波频谱分析 u(t)=Ucm(1+macos t)cos ct ma称为调幅系数 它的频谱由载波,上下边频组成 , 包络检波中二极管截去负半周再用电容低通滤波,可 ...

  9. Flask-Response

    Flask中的HTTPResponse from flask import Flask,redirect app = Flask(__name__) @app.route("/index&q ...

  10. 部署Hadoop2.0高性能集群

    废话不多说直接实战,部署Hadoop高性能集群: 拓扑图: 一.实验前期环境准备: 1.三台主机配置hosts文件:(复制到另外两台主机上) [root@tiandong63 ~]# more /et ...