Python数组存储方式与向量化、按行按列展平/展开

news2024/9/21 4:23:32

文章目录

  • 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

链接文章:NumPy中 ravel() 和 flatten() 展平数组 对比——Dust_Evc


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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141884.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis缓存雪崩缓存击穿缓存穿透

Redis缓存雪崩&缓存击穿&缓存穿透一 缓存更新策略二 缓存雪崩三 缓存击穿四 缓存穿透一 缓存更新策略 目前redis缓存更新存在3种主流策略,分别是:内存淘汰、超时剔除、主动更新。 1、内存淘汰 LRU/LFU/FIFO算法剔除:例如maxmemory-…

【云原生进阶之容器】第二章Controller Manager原理2.4节--Informer机制剖析

1 Informer机制剖析 如上图所示,client-go 包中一个非常核心的工具就是 informer,informer 可以让与 kube-apiserver 的交互更加优雅。 Informer的主要作用包括如下两个方面: 同步数据到本地缓存。Informer 会不断读取 Delta FIFO 队列中的 Object,在触发事件回调之…

MySQL8.0锁情况排查

GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。作者: 杨延昭文章来源:GreatSQL社区投稿 在数据库中,除传统的计算资源(CPU、RAM、IO)的争用…

【C语言】-关于指针的知识你真的都知道了??快进来看看这里面的指针会让你眼前一亮

🎇作者:小树苗渴望变成参天大树 🎊作者宣言:认真写好每一篇博客 🎉 作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 指针进阶💦前言💖一、…

MYSQL 数据行列交换(交换数据) 存储过程实现

行列转换的存储过程 初始图: 效果图: 实现过程: 1:建表、建数据 CREATE TABLE changeprice ( id bigint(20) NOT NULL auto_increment, sid bigint(20) NOT NULL, datecreated timestamp NOT NULL defaultCURRENT_TIMESTAMP…

nvue2中使用axios和插槽

一、组件的生命周期1、组件的生命周期经历的阶段:(1)创建阶段:beforeCreate、created、beforeMount、mounted(2)运行阶段:beforeUpdate、update(3)销毁阶段:2…

C语言二维数组和二重指针详解

二维数组 一个二维数组,在本质上,是一个一维数组的列表。声明一个 x 行 y 列的二维整型数组,形式如下: type arrayName [x][y]; 这个表示,有x个一维数组,每个一维数组的元素个数是y个。 声明示例&#xff1…

手写VITE-MockJS插件

mockJS是什么 mockJS是模拟接口数据,拦截客户端的请求的一个工具。 vite插件编写流程 pnpm init 初始化 pnpm install vite -D 安装Vite 配置package.json文件中的脚本 "scripts": {"dev": "vite","build": "vite…

K8s集群离线安装-kubeadm-详细篇

1、部署k8s的两种方式:kubeadm 和二进制源码安装 #本次实验采用的部署Kubernetes方式: kubeadm Kubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。2、环境准备 #服务器要求: 建…

JavaSE学习day1_02, JDK安装

1.4 环境变量 1.4.1 为什么配置环境变量 在初次学习编程的时候,环境变量我们经常提到,但是环境变量到底是什么?是干嘛的?弄清楚这些问题很重要. 如果我们想要在CMD的任意路径下都可以打开任意的软件,那么就需要把软件的路径配置到环境变量当中。 为了便于大家…

Zotero | 快速入门

文章目录0. 前言1. Zotero快速入门1.1 下载Zotero和Connector2.2 联动sci-hub实现英文文献批量下载2.3 英文文献翻译2.4 中文文献元数据识别2.5 Zotero其他配置2.5.1 语言切换2.5.2 数据存储位置更改参考0. 前言 Zotero是一款自由及开放源代码的文献管理软件,管理书…

告别2022,喜迎2023

2022只剩下最后几天,新的一年即将拉开序幕。你的2022,是苦尽甘来、柳暗花明,还是安适如常、平安喜乐?你会用什么词来形容你的2022,你期待的2023又是什么样的呢? 时光荏苒,岁月悠悠,转眼间,202…

猿代码超算实习生计划之编程语言分析

编程语言特别多,很多同学都特别纠结以后到底选择哪一种编程语言,才好找高薪实习和工作。 其实编程语言没有好坏之分。很多同学选择未来就业和实习方向时很多也都是参考的薪资和岗位数量。就像前几年很多人选的前端、现在卷到不行的Java、还有今年连实习…

【Cortex-A7核PWM实验】

Cortex-A7核PWM实验 ---蜂鸣器、风扇、震动马达PWN概念PWM硬件电路图如何产生PWM方波捕获/比较寄存器工作原理代码实现PWN概念 PWM是指脉冲宽度调制(Pulse Width Modulation),是一种常用的模拟信号转换为数字信号的方法。 1.脉冲&#xff1a…

C#WinForm实现多语言切换

因项目需要,所以在网上找了一些方法实现了该功能,本文也是做一个总结和记录。使用resx文件实现Winform多语言切换,以实现简体中文、英文、泰语的切换为例。如果后续需要增加其它语言的切换,只需要按照步骤重复操作即可。 效果图如…

【Kotlin】函数 ⑨ ( Kotlin 语言中的闭包概念 | Java 语言中函数作为参数的替代方案 )

文章目录一、闭包概念二、Java 中函数作为参数的替代方案 ( 匿名内部类 )一、闭包概念 匿名函数 就是 Lambda 表达式 , 同时也是 闭包 , 三者的是相同的概念 ; 闭包意义 : 在 Java 中 , 通过 Package 包 , Class 类 , 将作用域区分开 , 将变量 定义在 不同的 包 或 类中 , 可…

阿里云创世纪之盘古传奇

文章目录飞天(Apsara)云计算平台简介面向私有云的Apsara Stack盘古横空出世盘古的架构盘古基本介绍盘古API基于C语言的SDK基于命令行的文件操作接口pu盘古中的目录和文件盘古目录盘古中的文件盘古中的文件类型盘古应用场景盘古的功能特性盘古主要性能盘古的数据安全盘古的边界盘…

Docker - Docker网络

一、Docker网络介绍 Docker是基于Linux Kernel(内核)的namespace,CGroups,UnionFileSystem等技术封装成的一种自定义容器格式,从而提供了—套虚拟运行环境。 1、namespace:用来做隔离的,比如pid[进程].、…

Java多线程案例之阻塞队列

文章目录一. 认识阻塞队列1. 什么是阻塞队列2. 生产者消费者模型3. 标准库中阻塞队列类二. 基于循环队列实现的简单阻塞队列1. 循环队列的简单实现2. 阻塞队列的简单实现一. 认识阻塞队列 1. 什么是阻塞队列 阻塞队列本质上还是一种队列, 和普通队列一样, 遵循先进先出, 后进…

291. 蒙德里安的梦想(状态压缩dp详解)

求把 NM 的棋盘分割成若干个 12 的长方形,有多少种方案。 例如当 N2,M4 时,共有 5 种方案。当 N2,M3 时,共有 3 种方案。 如下图所示: 输入格式 输入包含多组测试用例。 每组测试用例占一行&#xff0c…