引言

本篇介绍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. 4.caffe资源汇总(更新中)

    学习需要更新,网上有一些非常不错博客. 感谢这些博主,他们都很认真. 00.tornadomeet 0.denny的学习专栏 1.xizero00 2.lingerlanlan 3.iamzhangz ...

  2. python+selenium之——错误:selenium.common.exceptions.WebDriverException: Message: ‘geckodriver’ executable needs to be in PATH.

    此时,需要自己配置geckodriver 下载geckodriver,地址:https://github.com/mozilla/geckodriver/releases 下载后解压得到geckodr ...

  3. git -C <other-git-repo-path> [git-command] 指定其它路径的仓库 执行命令

    git -C <other-git-repo-path> [git-command] 指定其它路径的仓库 执行命令 注意,-C 要在命令之前 例如: git -C d:/testRepo ...

  4. FFmpeg常用命令学习笔记(五)裁剪与合并命令

    裁剪与合并命令 1.音视频裁剪 ffmpeg -i input.mp4 -ss 00:01:00 -t 10 out.mp4 -ss:起始时间(HH:MM:SS).-t:裁剪时长(秒) 2.视频合并 ...

  5. windows 10安装mongodb数据库

    简介 MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当 ...

  6. CSS效果篇--纯CSS+HTML实现checkbox的思路与实例

    checkbox应该是一个比较常用的html功能了,不过浏览器自带的checkbox往往样式不怎么好看,而且不同浏览器效果也不一样.出于美化和统一视觉效果的需求,checkbox的自定义就被提出来了. ...

  7. 源码安装ROS Melodic Python3 指南 (转) + 安装记录

    这篇文章转自   https://blog.csdn.net/id9502/article/details/80410989  csdn真是作大死,我保存这篇博客的时候还不需要花钱就能看,现在居然要v ...

  8. mybatis 多级级联(多级嵌套)

    注:笔者这里的嵌套可以用词有点欠缺,忘见谅 需求:用一个查询接口查出其结果集,这里就用伪代码标识要返回前端的类与类之间的关系. class 顶层{ String otherValue; LinkedL ...

  9. vue 源码解析computed

    计算属性 VS 侦听属性 Vue 的组件对象支持了计算属性 computed 和侦听属性 watch 2 个选项,很多同学不了解什么时候该用 computed 什么时候该用 watch.先不回答这个问 ...

  10. oracle面试题2

    1.题目要求 为管理岗位业务培训信息,建立3个表:S (S#,SN,SD,SA) S#,SN,SD,SA 分别代表学号.学员姓名.所属单位.学员年龄C (C#,CN ) C#,CN 分别代表课程编号. ...