PyTorch深度学习实战(5)—— Tensor的命名张量和基本结构

news2024/9/22 17:25:51

1. 命名张量

命名张量(Named Tensors)允许用户将显式名称与Tensor的维度关联起来,便于对Tensor进行其他操作。笔者推荐使用维度的名称进行维度操作,这样可以避免重复计算Tensor每个维度的位置。支持命名张量的工厂函数(factory functions)有tensoremptyoneszerosrandn等。

下面举例说明命名张量的使用,其中N代表batch_size,C代表通道数,H代表高度,W代表宽度。

In: # 命名张量API在后续还有可能还有变化,系统会提示warning,在此忽略
    import warnings
    warnings.filterwarnings("ignore")
    # 直接使用names参数创建命名张量
    imgs = t.randn(1, 2, 2, 3, names=('N', 'C', 'H', 'W'))
    imgs.names
 
 Out:('N', 'C', 'H', 'W')
 
 In: # 查看旋转操作造成的维度变换
    imgs_rotate = imgs.transpose(2, 3)
    imgs_rotate.names
 Out:('N', 'C', 'W', 'H')
 
 In: # 通过refine_names对未命名的张量命名,不需要名字的维度可以用None表示
    another_imgs = t.rand(1, 3, 2, 2)
    another_imgs = another_imgs.refine_names('N', None, 'H', 'W')
    another_imgs.names
 Out:('N', None, 'H', 'W')
 
 In: # 修改部分维度的名称
    renamed_imgs = imgs.rename(H='height', W='width')
    renamed_imgs.names
 Out:('N', 'C', 'height', 'width')
 
 In: # 通过维度的名称做维度转换
    convert_imgs = renamed_imgs.align_to('N', 'height', 'width','C')
    convert_imgs.names
 Out:('N', 'height', 'width', 'C')

在进行张量的运算时,命名张量可以提供更高的安全性。例如,在进行Tensor的加法时,如果两个Tensor的维度名称没有对齐,那么即使它们的维度相同也无法进行计算。

In: a = t.randn(1, 2, 2, 3, names=('N', 'C', 'H', 'W'))
    b = t.randn(1, 2, 2, 3, names=('N', 'H', 'C', 'W'))
    # a + b 
    # 报错,RuntimeError: Error when attempting to broadcast dims ['N', 'C', 'H', 'W'] and dims ['N', 'H', 'C', 'W']: dim 'H' and dim 'C' are at the same position from the right but do not match.

2. Tensor与NumPy

Tensor在底层设计上参考了NumPy数组,它们彼此之间的相互转换非常简单高效。因为NumPy中已经封装了常用操作,同时与Tensor之间在某些情况下共享内存,所以当遇到CPU Tensor不支持的操作时,可以先将其转成NumPy数组,完成相应处理后再转回Tensor。由于这样的操作开销很小,所以在实际应用中经常进行二者的相互转换,下面举例说明:

In: import numpy as np
    a = np.ones([2, 3], dtype=np.float32)
    aOut:array([[1., 1., 1.],
           [1., 1., 1.]], dtype=float32)In: # 从NumPy数组转化为Tensor,由于dtype为float32,所以a和b共享内存
    b = t.from_numpy(a)
    # 该种情况下,使用t.Tensor创建的Tensor与NumPy数组仍然共享内存
    # b = t.Tensor(a)
    bOut:tensor([[1., 1., 1.],
            [1., 1., 1.]])In: # 此时,NumPy数组和Tensor是共享内存的
    a[0, 1] = -1
    b # 修改a的值,b的值也会被修改Out:tensor([[ 1., -1.,  1.],
            [ 1.,  1.,  1.]])

注意:使用torch.Tensor()创建的张量默认dtype为float32,如果NumPy的数据类型与默认类型不一致,那么数据仅会被复制,不会共享内存。

In: a = np.ones([2, 3])
    # 注意和上面的a的区别(dtype不是float32)
    a.dtypeOut:dtype('float64')In: b = t.Tensor(a) # 此处进行拷贝,不共享内存
    b.dtypeOut:torch.float32In: c = t.from_numpy(a) # 注意c的类型(DoubleTensor)
    cOut:tensor([[1., 1., 1.],
            [1., 1., 1.]], dtype=torch.float64)In: a[0, 1] = -1
    print(b) # b与a不共享内存,所以即使a改变了,b也不变
    print(c) # c与a共享内存Out:tensor([[1., 1., 1.],
            [1., 1., 1.]])
    tensor([[ 1., -1.,  1.],
            [ 1.,  1.,  1.]], dtype=torch.float64)

注意:无论输入类型是什么,torch.tensor()都只进行进行数据拷贝,不会共享内存。读者需要注意torch.Tensor()torch.from_numpy()torch.tensor()在内存共享方面的区别。

In: a_tensor = t.tensor(a)
    a_tensor[0, 1] = 1
    a # a和a_tensor不共享内存Out:array([[ 1., -1.,  1.],
           [ 1.,  1.,  1.]])

除了使用上述操作完成NumPy和Tensor之间的数据转换,PyTorch还构建了torch.utils.dlpack模块。该模块可以实现PyTorch张量和DLPack内存张量结构之间的相互转换,因此,用户可以轻松实现不同深度学习框架的张量数据的交换。注意:转换后的DLPack张量与原PyTorch张量仍然是共享内存的。

3. Tensor的基本结构

Tensor的数据结构如图3-1所示。Tensor分为头信息区(Tensor)和存储区(Storage),头信息区主要保存Tensor的形状(size)、步长(stride)、数据类型(type)等信息,真正的数据在存储区保存成连续数组。头信息区元素占用内存较少,主要内存占用取决于Tensor中元素的数目,即存储区的大小。

一般来说,一个Tensor有与之对应的Storage,Storage是在data之上封装的接口。Tensor的内存地址指向Tensor的头(head),不同Tensor的头信息一般不同,但可能使用相同的Storage。关于Tensor的很多操作虽然创建了一个新的head,但是它们仍共享同一个Storage,下面举例说明。

In: a = t.arange(0, 6).float()
    b = a.view(2, 3)
    # Storage的内存地址一样,即它们是同一个Storage
    a.storage().data_ptr() == b.storage().data_ptr()
 Out:True
 
 In: # a改变,b也随之改变,因为它们共享Storage
    a[1] = 100
    b
 Out:tensor([[  0., 100.,   2.],
            [  3.,   4.,   5.]])
 
 In: # 对a进行索引操作,只改变了head信息,Storage相同
    c = a[2:] 
    a.storage().data_ptr() == c.storage().data_ptr()
 Out:True
 
 In: c.data_ptr(), a.data_ptr() # data_ptr返回Tensor首元素的内存地址
    # 可以看出两个内存地址相差8,这是因为2×4=8:相差两个元素,每个元素占4个字节(float)
    # 如果差值不是8,如16,那么可以用a.type()查看一下数据类型是不是torch.FloatTensor
 Out:(94880397551496, 94880397551488)
 
 In: c[0] = -100 # c[0]的内存地址对应a[2]的内存地址
    a
 Out:tensor([   0.,  100., -100.,    3.,    4.,    5.])
 
 In: d = t.Tensor(c.storage()) # d和c仍然共享内存
    d[0] = 6666
    b
 Out:tensor([[ 6.6660e+03,  1.0000e+02, -1.0000e+02],
            [ 3.0000e+00,  4.0000e+00,  5.0000e+00]])
 
 In: # 下面四个Tensor共享Storage
    a.storage().data_ptr() == b.storage().data_ptr() == c.storage().data_ptr() == d.storage().data_ptr()
 Out:True
 
 In: # c取得a的部分索引,改变了偏移量
    a.storage_offset(), c.storage_offset(), d.storage_offset()
 Out:(0, 2, 0)
 
 In: e = b[::2, ::2] # 隔2行/列取一个元素
    print(a.storage().data_ptr() == e.storage().data_ptr()) # 共享内存
    print(e.is_contiguous()) # e的存储空间是不连续的
 Out:True
    False

由此可见,绝大多数操作不是修改Tensor的Storage,而是修改了Tensor的头信息。这种做法更节省内存,同时提升了处理速度。此外,有些操作会导致Tensor不连续,这时需要调用tensor.contiguous()方法将它们变成连续的数据。该方法会复制数据到新的内存,不再与原来的数据共享Storage。

读者可以思考一个问题,高级索引一般不共享Storage,而基本索引共享Storage,这是为什么呢?(提示:基本索引可以通过修改Tensor的offset、stride和size实现,不用修改Storage的数据,高级索引则不行。)

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

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

相关文章

怎么删除iPhone重复照片:解放你的存储空间

在数字化时代,iPhone已成为我们记录生活点滴的重要工具。从家庭聚会的快乐时光到户外冒险的壮观景象,我们依靠iPhone捕捉无数珍贵瞬间。然而,这种便利性带来的一个副作用是,相册很快就会充满重复的照片,不仅占用了宝贵…

【IC设计】时序分析面试题总结(亚稳态、建立/保持裕量计算、最高时钟频率计算、时序违例解决办法)

文章目录 基本概念亚稳态建立时间和保持时间 常见问题1.为什么触发器要满足建立时间和保持时间?2.建立时间裕量和保持时间裕量的计算?3.最高时钟频率的计算?流水线思想?4.时序违例的解决办法? 基本概念 亚稳态 亚稳态…

简单的 CompletableFuture学习笔记

简单的 CompletableFuture学习笔记 这里记录一下自己学习的内容,简单记录一下方便后续学习,内容部分参考 CompletableFuture学习博客 1. CompletableFuture简介 在api接口调用时间过长,调用过多外围接口时,为了提升性能&#x…

Self-study Python Fish-C Note14 P50to51

函数 (part 4) 本节主要讲函数 递归 递归 (recursion) 递归就是函数调用自身的过程 示例1: def fun1(i):if i > 0: print(something)i-1fun1(i) fun1(5) # 这样就会打印五遍 somethingsomething something something something something要让递归正常工作&am…

IDEA2024.2重磅发布,更新完有4G!

JetBrains 今天宣布了其 IDE 家族版本之 2024.2 更新,其亮点是新 UI 现在已是默认设置,并且对 AI Assistant (AI助手)进行了几项改进。 安装密道 新 UI 的设计更加简约,可以根据需要以视觉方式扩展复杂功能。值得开发…

Arduino学习笔记2——初步认识Arduino程序

Arduino使用的编程语言是C。 一、注释文字 我们可以在程序中插入注释文字来提示开发者代码的作用。在Arduino中,单行注释用的是两个斜杠,多行注释用的是对称的斜杠加星号: 二、函数 和C语言相同,可以看到在打开IDE自动打开的默…

高并发下的分布式缓存 | Cache-Aside缓存模式

Cache-aside 模式的缓存操作 Cache-aside 模式,也叫旁路缓存模式,是一种常见的缓存使用方式。在这个模式下,应用程序可能同时需要同缓存和数据库进行数据交互,而缓存和数据库之间是没有直接联系的。这意味着,应用程序…

Java数据结构 | 二叉树基础及基本操作

二叉树 一、树型结构1.1 树的概念1.2 关于树的一些常用概念(很重要!!!)1.3 树的表示形式1.4 树的应用 二、二叉树2.1 二叉树的概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基本操作2.5.1 代…

【前端可视化】 大屏可视化项目二 scale适配方案 g6流程图 更复杂的图表

项目介绍 第二个大屏可视化,整个项目利用scale进行按比例适配。 图表更加复杂,涉及到图表的叠加,mark,地图,g6流程图的能等 始终保持比例适配(本项目方案),始终满屏适配(项目一). echarts绘制较为复杂图表&#xff0…

C++:string类(auto+范围for,typeid)

目录 前言 auto typeid 范围for 使用方法 string类的模拟实现 默认构造函数 拷贝构造函数 swap 赋值重载 析构函数 迭代器iterator begin和end c_str clear size capacity []运算符重载 push_back reserve append 运算符重载 insert erase find npos…

postgresql 宝塔 连接不上,prisma

不太熟悉pgsql; 配置搞了半天; 一直连不上远程数据库; 后台经过探索发现需要以下配置 1. 端口放行; 5422 (pgsql的端口) 2.编辑 pg_hba.conf 文件最后新增一条,这样可以外部使用postgres超级管理员账号 host all all 0.0.0.0/0 md5 3. pris…

数据结构复杂度

文章目录 一. 数据结构前言1.1 数据结构1.2 算法 二. 算法效率2.1 时间复杂度2.1.1 T(N)函数式2.1.2 大O的渐进表示法 一. 数据结构前言 1.1 数据结构 什么是数据结构呢?打开一个人的主页,有很多视频,这是数据(杂乱无章&#xf…

了解k8s架构,搭建k8s集群

kubernetes 概述 Kubernetes 集群图例 安装控制节点 安装网络插件 安装 calico 安装计算节点 2、node 安装 查看集群状态 kubectl get nodes # 验证容器工作状态 [rootmaster ~]# kubectl -n kube-system get pods

【学习笔记】:Maven初级

一、Maven简介 1、为什么需要maven Maven是一个依赖管理工具,解决如下问题: 项目依赖jar包多jar包来源、版本问题jar包导入问题jar包之间的依赖Maven是一个构建工具: 脱离IDE环境的项目构建操作,需要专门的工具2、Maven介绍 https://maven.apache.org/what-is-maven.htm…

代码随想录算法训练营第44天|LeetCode 1143.最长公共子序列、1035.不相交的线、53. 最大子序和、392.判断子序列

1. LeetCode 1143.最长公共子序列 题目链接:https://leetcode.cn/problems/longest-common-subsequence/description/ 文章链接:https://programmercarl.com/1143.最长公共子序列.html 视频链接:https://www.bilibili.com/video/BV1ye4y1L7CQ…

苹果离线打包机配置和打包

1、虚拟机安装 macOS虚拟机安装全过程(VMware)-腾讯云开发者社区-腾讯云 给 windows 虚拟机装个 mac 雪之梦 1、安装苹果镜像 去网上下载,打包机的镜像要和自己mac电脑上的保持一致。 同时打包机的用户名也需要和自己的mac保持一致。 2、…

云原生专题-k8s基础系列-k8s-namespaces详解

获取所有的pod实例: k8s中,命名空间(Namespace)提供一种机制,将同一集群中的资源划分为相互隔离的组。同一命名空间内的资源名称要唯一,命名空间是用来隔离资源的,不隔离网络。 https://kubern…

Kafka 实战使用、单机搭建、集群搭建、Kraft集群搭建

文章目录 实验环境单机服务启动停止服务简单收发消息其他消费模式理解Kakfa的消息传递机制 集群服务为什么要使用集群部署Zookeeper集群部署Kafka集群理解服务端的Topic、Partition和Broker总结 Kraft集群相关概念 实验环境 准备三台虚拟机 三台机器均预装CentOS7 操作系统。…

探索Transformer中的多头注意力机制:如何利用GPU并发

什么是多头注意力机制? 首先,什么是多头注意力机制?简单来说,它是Transformer模型的核心组件之一。它通过并行计算多个注意力头(attention heads),使模型能够从不同的表示子空间中捕捉不同的特…

Oracle服务器windows操作系统升级出现计算机名称改变导致数据库无法正常连接

1.数据库莫名奇妙无法正常连接,经排查是主机名称改变,导致oracle无法正常运行 如何查看ORACLE主机名称及路径:需要修改 listener 和 tnsnames的配置的主机名 2.修改tnsnames配置的主机名称,HOST主机名称 3.修改listener中的主机…