张量/数组存储方式与向量化展开/展平、view、reshape、permute对比

本文主要是介绍张量/数组存储方式与向量化展开/展平、view、reshape、permute对比,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 1 逻辑存储与内存存储
    • 1.1 高维张量
    • 1.2 按行展开和按列展开
  • 2 矩阵向量化、按行按列展开
    • 2.1 numpy中:
      • numpy.ravel()
      • ndarray.flatten()
    • 2.2 Pytorch中:
  • 3 pytoch中的view,reshape,permute对比
    • 3.1 view
    • 3.2 reshape
    • 3.3 permute

链接文章:
python pytorch中 .view()函数讲解
pytorch中张量的分块.chunk()方法和拆分.split()方法
NumPy中 ravel() 和 flatten() 展平数组 对比——Dust_Evc
NumPy:确定 ndarray 是查看还是复制以及它是否共享内存
tensor数据存储与内存共享
深入理解Tensor对象_下:从内存看Tensor


1 逻辑存储与内存存储

对于一个Tensor来说,我们可以认为它有两种存储方式

  • 逻辑存储

在这里插入图片描述

  • 内存存储

在这里插入图片描述

1.1 高维张量

  • 在torch/numpy中, 即使是高维张量在内存中也是存储在一块连续的内存区域中
  • 会记录一些元信息来描述数组的"形态", 例如起始地址, 步长 (stride), 大小 (size)等.
  • 对高维张量进行索引时我们采用起始地址 + 地址偏移量的计算方式, 而地址偏移量就需要用到stride和size的信息

在这里插入图片描述

当我们在使用view去修改tensor的时候, 其实我们并没有修改tensor在内存中的存储, 而只是通过修改stride和size来描述张量形状的变化

1.2 按行展开和按列展开

在这里插入图片描述

  • 如果张量x的行优先展开形式和其内存存储一致, 则我们称之为 C-contiguous
    • Numpy, Pytorch中的contiguous指的就是C-contiguous
  • 如果张量x的列优先展开形式和其内存存储一致, 则我们称之为 Fortran-contiguous
    • Matlab, Fortran中的contiguous指的是Fortran-contiguous.
  • 可用转置实现两种存储格式的转换,以pytorch为例:
vp=torch.randn(1,204)
vp1=vp.view(68,-1)  ## torch.size ([68,3])
vp2=vp1.t()  ## torch.size ([3,68])# vp2 就是 vp 的 Fortran-contiguous 版本

2 矩阵向量化、按行按列展开

2.1 numpy中:

numpy.ravel()

numpy.ravel(a, order=‘C’)

返回一个连续的展平数组。

返回包含输入元素的一维数组。仅在需要时制作副本。

从 NumPy 1.10 开始,返回的数组将与输入数组具有相同的类型。(例如,将为masked 数组输入返回masked 数组)

参数:

输入:

aarray_like

​ 输入数组。_a_中的元素按order 指定的 顺序 读取,并打包为数组。

order {‘C’,‘F’, ‘A’, ‘K’}, 可选

​ 使用此索引顺序读取_a_的元素。‘C’ 表示以行优先、C 风格的顺序对元素进行索引,最后一个轴索引变化最快,回到第一个轴索引变化最慢。‘F’ 表示以列优先、Fortran 风格的顺序对元素进行索引,第一个索引变化最快,最后一个索引变化最慢。请注意,“C”和“F”选项不考虑底层数组的内存布局,仅参考轴索引的顺序。_‘A’ 表示如果a_是 Fortran_连续_的,则以类似 Fortran 的索引顺序读取元素在内存中,否则是类似 C 的顺序。‘K’ 表示按照元素在内存中出现的顺序读取元素,除了在步幅为负时反转数据。默认情况下,使用“C”索引顺序。

输出:

yarray_like

y 是与a_具有相同子类型的数组,形状为(a.size,)。请注意,矩阵是为了向后兼容而特制的,如果_a 是矩阵,则 y 是一维 ndarray。

例1:

import numpy as np
nd15=np.arange(6).reshape(2,-1)print(nd15)
#按照列优先,展平。
print("按列优先,展平")
print(nd15.ravel('F'))#按照行优先,展平。
print("按行优先,展平")
print(nd15.ravel())

结果:

[[0 1 2] [3 4 5]]
按列优先,展平
[0 3 1 4 2 5]
按行优先,展平
[0 1 2 3 4 5]

例2:

它相当于.reshape(-1, order=order)

>> > x = np.array([[ 1 , 2 , 3 ], [ 4 , 5 , 6 ]])
>> > np.ravel(x)
array([ 1 , 2 , 3 , 4 , 5 , 6 ])
>>> x .reshape ( -1 )
array( [1, 2, 3, 4, 5, 6] )
>> > np.ravel(x, order= 'F' )
array([ 1 , 4 , 2 , 5 , 3 , 6 ])

order是 ‘A’ 时,它将保留数组的 ‘C’ 或 ‘F’ 排序:

>> > np.ravel(xT)
array([ 1 , 4 , 2 , 5 , 3 , 6 ])
>> > np.ravel(xT, order= 'A' )
array([ 1 , 2 , 3 , 4 , 5 , 6 ])

ndarray.flatten()

返回折叠成一维数组的副本。

  • 参数:

    order {‘C’, ‘F’, ‘A’, ‘K’}, 可选==‘C’ 表示按行优先(C 样式)顺序展平。“F”表示按列优先(Fortran 风格)顺序展平。‘A’ 表示如果a在内存中是 Fortran连续的,则按列优先顺序展平,否则按行优先顺序展平==。‘K’ 表示 按照元素在内存中出现的顺序展平*a 。*默认值为“C”。

  • 输出:

    yndarray _输入数组的副本,展平为一维。

例1:

>>> a = np.array([[1,2], [3,4]])
>>> a.flatten()  # 按行展平
array([1, 2, 3, 4]) 
>>> a.flatten('F')  # 按列展平
array([1, 3, 2, 4])

例2:

a = torch.Tensor([[1,2], [3,4]])a.detach().numpy().flatten('C') #returns array([1., 2., 3., 4.], dtype=float32)  # 按行展平
a.detach().numpy().flatten('F') #returns array([1., 3., 2., 4.], dtype=float32)  # 按列展平
a.flatten() #returns tensor([1., 2., 3., 4.])  # 按行展平

2.2 Pytorch中:

pytorch中,flatten()ravel()都是只能按行展平的,因此,如需按列展平,先转置、再展平。

​ 并且官方文档里指出:与始终复制输入数据的 NumPy 的flatten不同,此函数可能会返回原始对象、视图或副本。如果没有尺寸被展平,则返回原始对象input。否则,如果可以将输入视为扁平形状,则返回该视图。最后,只有当输入不能被视为扁平形状时,才会复制输入的数据。

例子参考:https://discuss.pytorch.org/t/how-to-flatten-a-tensor-in-column-major-order/78636

例1:

# 使用转置方法reshape张量,然后再展平:
x = torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
x.flatten()
tensor([1, 2, 3, 4, 5, 6, 7, 8, 9])
x.transpose(1, 0).flatten()
tensor([1, 4, 7, 2, 5, 8, 3, 6, 9])

例2:

t = torch.randn(3, 5)
t = t.t().contiguous().t()  # .t()表转置
print(t.shape)  # torch.Size([3, 5])
print(t.stride())  # (1, 3)

例3 (高维数组情况):

t = torch.rand(3, 3, 3)# convert to column-major order
t.set_(t.storage(), t.storage_offset(), t.size(), tuple(reversed(t.stride())))  
t.flatten()  # 1D array in column-major order

注意,如果你只是想要一个张量的一维以列为主序表示,上面的操作将改变张量t中元素的顺序。

下面这个函数不改变原张量t中元素的顺序(通过创建副本):

def flatten_fortran_w_clone(t):    nt = t.clone()nt = nt.set_(nt.storage(), nt.storage_offset(), nt.size(), tuple(reversed(nt.stride())))    return nt.flatten()

自己以下面这个进行测试,发现最长一行执行后数据错误异常

t = torch.arange(24).view(2,3,4)# convert to column-major order
t.set_(t.storage(), t.storage_offset(), t.size(), tuple(reversed(t.stride())))  
t_1 = t.flatten()  # 1D array in column-major order

最长一行执行后,数据变为,并导致最后结果不对:

tensor([[[                  0,                  12, 4198805246726073967,2333189101087841629],[                  4,                  16, 4990343733199463764,2819301914315665708],[                  8,                  20, 4404627256239728488,3181614316598867291]],[[                  1,                  13, 4913279190858748719,3344032066469896253],[                  5,                  17, 6712455194575194719,7165912501647646817],[                  9,                  21, 8371743533659288608,8390891584407283488]]])

例4 (转为ndarray再展平,与上面ndarray.flatten()中的一个例子相同):

a = torch.Tensor([[1,2], [3,4]])a.detach().numpy().flatten('C') #returns array([1., 2., 3., 4.], dtype=float32) (row)
a.detach().numpy().flatten('F') #returns array([1., 3., 2., 4.], dtype=float32) (column)
a.flatten() #returns tensor([1., 2., 3., 4.]) (row)

3 pytoch中的view,reshape,permute对比

3.1 view

  • view要求输入和输出的 tensor 都是 contiguous 的, 否则会 throw exception
  • 换言之, 你不管对一个 tensor 使用了多少次view, 你都只是在改变 stride 和 size, 并没有修改这个 tensor 的内存存储

3.2 reshape

  • 对于 contiguous 的输入, reshape等于view
  • 对于 incontiguous 的输入, reshape等于tensor.contigous().view
    • 其中contiguous()会开辟一块新的内存空间, 将 incontiguous 的张量按照行优先展开的方式存储进去.
    • 所以reshape是有可能修改内存存储的结构的

3.3 permute

  • 虽然permuteview一样, 都是修改 stride 和 size, 但是permute并不保证返回的 tensor 是 contiguous 的.
  • 换言之permute().contiguous()就有可能修改内存存储方式了.
    在这里插入图片描述

参考:https://coderlemon17.github.io/posts/2022/08-19-view/

这篇关于张量/数组存储方式与向量化展开/展平、view、reshape、permute对比的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/205688

相关文章

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

【即时通讯】轮询方式实现

技术栈 LayUI、jQuery实现前端效果。django4.2、django-ninja实现后端接口。 代码仓 - 后端 代码仓 - 前端 实现功能 首次访问页面并发送消息时需要设置昵称发送内容为空时要提示用户不能发送空消息前端定时获取消息,然后展示在页面上。 效果展示 首次发送需要设置昵称 发送消息与消息展示 提示用户不能发送空消息 后端接口 发送消息 DB = []@ro

免费也能高质量!2024年免费录屏软件深度对比评测

我公司因为客户覆盖面广的原因经常会开远程会议,有时候说的内容比较广需要引用多份的数据,我记录起来有一定难度,所以一般都用录屏工具来记录会议内容。这次我们来一起探索有什么免费录屏工具可以提高我们的工作效率吧。 1.福晰录屏大师 链接直达:https://www.foxitsoftware.cn/REC/  录屏软件录屏功能就是本职,这款录屏工具在录屏模式上提供了多种选项,可以选择屏幕录制、窗口

脏页的标记方式详解

脏页的标记方式 一、引言 在数据库系统中,脏页是指那些被修改过但还未写入磁盘的数据页。为了有效地管理这些脏页并确保数据的一致性,数据库需要对脏页进行标记。了解脏页的标记方式对于理解数据库的内部工作机制和优化性能至关重要。 二、脏页产生的过程 当数据库中的数据被修改时,这些修改首先会在内存中的缓冲池(Buffer Pool)中进行。例如,执行一条 UPDATE 语句修改了某一行数据,对应的缓