为深度学习创建PyTorch张量 - 最佳选项

devtools/2025/1/16 20:22:08/

为深度学习创建PyTorch张量 - 最佳选项

正如我们所看到的,PyTorch张量是torch.Tensor​ PyTorch类的实例。张量的抽象概念与PyTorch张量之间的区别在于,PyTorch张量为我们提供了一个可以在代码中操作的具体实现。

在上一篇文章中,我们看到了如何使用数据(如Python列表、序列和NumPy ndarrays)在PyTorch中创建张量。给定一个numpy.ndarray​,我们发现有四种方法可以创建一个torch.Tensor​对象。

这里是一个快速回顾:

> data = np.array([1,2,3])
> type(data)
numpy.ndarray> o1 = torch.Tensor(data)
> o2 = torch.tensor(data)
> o3 = torch.as_tensor(data)
> o4 = torch.from_numpy(data)
> print(o1)
tensor([1., 2., 3.])
> print(o2)
tensor([1, 2, 3], dtype=torch.int32)
> print(o3)
tensor([1, 2, 3], dtype=torch.int32)
> print(o4)
tensor([1, 2, 3], dtype=torch.int32)

我们在这篇文章中的任务是探索这些选项之间的区别,并为我们创建张量的需求提出最佳选项。

不同系统上的Numpy dtype​行为

根据你的机器和操作系统,你的dtype​可能与这里和视频中显示的不同。

Numpy根据它是在32位还是64位系统上运行来设置其默认dtype​,并且在Windows系统上的行为也有所不同。

这个链接提供了关于在Windows系统上看到的差异的更多信息。受影响的方法是:tensor​、as_tensor​和from_numpy​。

感谢hivemind的David找出了这一点!

张量创建操作:有什么区别?

让我们开始并找出这些差异都是关于什么的。

大写/小写:torch.Tensor()​与torch.tensor()

注意第一个选项torch.Tensor()​有一个大写的T​,而第二个选项torch.tensor()​有一个小写的t​。这个区别是怎么回事?

第一个选项带有大写的T​是torch.Tensor​类的构造函数,第二个选项是我们所说的_工厂函数_,它构建torch.Tensor​对象并返回给调用者。

在这里插入图片描述

你可以将torch.tensor()​函数视为一个工厂,它根据一些参数输入构建张量。工厂函数是一种创建对象的软件设计模式。如果你想了解更多,可以查看这里。

好的,这就是大写T​和小写t​之间的区别,但在这两种方式中,哪一种更好?答案是使用任何一个都可以。然而,工厂函数torch.tensor()​有更好的文档和更多的配置选项,所以它目前是胜出的选择。

默认dtype​与推断的dtype

好吧,在我们从使用列表中删除torch.Tensor()​构造函数之前,让我们回顾一下我们在打印的张量输出中观察到的区别。

区别在于每个张量的dtype​。让我们看看:

> print(o1.dtype)
torch.float32> print(o2.dtype)
torch.int32> print(o3.dtype)
torch.int32> print(o4.dtype)
torch.int32

这里的区别在于,torch.Tensor()​构造函数在构建张量时使用默认的dtype​。我们可以使用torch.get_default_dtype()​方法验证默认的dtype​:

> torch.get_default_dtype()
torch.float32

通过代码验证,我们可以这样做:

> o1.dtype == torch.get_default_dtype()
True

其他调用根据传入的数据选择dtype​。这被称为类型推断。dtype​是根据传入的数据推断的。请注意,也可以通过将dtype​作为参数指定,为这些调用显式设置dtype​:

> torch.tensor(data, dtype=torch.float32)
> torch.as_tensor(data, dtype=torch.float32)

使用torch.Tensor()​,我们无法向构造函数传递dtype​。这是torch.Tensor()​构造函数缺乏配置选项的一个例子。这是选择torch.tensor()​工厂函数来创建张量的另一个原因。

让我们看看这些替代创建方法之间的最后一个隐藏区别。

为了性能共享内存:复制与共享

第三个区别隐藏在幕后。为了揭示这个区别,我们需要在用ndarray​创建我们的张量后,改变原始输入数据在numpy.ndarray​中。

让我们这样做,看看我们得到什么:

> print('old:', data)
old: [1 2 3]> data[0] = 0> print('new:', data)
new: [0 2 3]> print(o1)
tensor([1., 2., 3.])> print(o2)
tensor([1, 2, 3], dtype=torch.int32)> print(o3)
tensor([0, 2, 3], dtype=torch.int32)> print(o4)
tensor([0, 2, 3], dtype=torch.int32)

注意,最初我们有data[0]=1​,还要注意我们只改变了原始numpy.ndarray​中的数据。注意我们没有明确地对我们的张量(o1​,o2​,o3​,o4​)进行任何更改。

然而,在设置data[0]=0​之后,我们可以看到我们的一些张量发生了变化。前两个o1​和o2​仍然在索引0​处有原始值1​,而后两个o3​和o4​在索引0​处有新值0​。

这是因为torch.Tensor()​和torch.tensor()​在输入数据时_复制_它们,而torch.as_tensor()​和torch.from_numpy()​在内存中与原始输入对象_共享_它们的输入数据。

共享数据复制数据
torch.as_tensor()torch.tensor()
torch.from_numpy()torch.Tensor()

这种共享只是意味着内存中的实际数据存在于一个地方。因此,对底层数据发生的任何更改都将反映在两个对象中,即torch.Tensor​和numpy.ndarray​。

共享数据比复制数据更有效,使用的内存更少,因为数据不会写入内存中的两个位置。

如果我们有一个torch.Tensor​,我们想将其转换为numpy.ndarray​,我们可以这样做:

> print(o3.numpy())
[0 2 3]> print(o4.numpy())
[0 2 3]

这给出了:

> print(type(o3.numpy()))
<class 'numpy.ndarray'>> print(type(o4.numpy()))
<class 'numpy.ndarray'>

这确立了torch.as_tensor()​和torch.from_numpy()​都与它们的输入数据共享内存。然而,我们应该使用哪一个,它们之间有什么区别?

torch.from_numpy()​函数只接受numpy.ndarray​,而torch.as_tensor()​函数接受各种数组式对象,包括其他PyTorch张量。因此,torch.as_tensor()​是在内存共享游戏中的胜出选择。

那为什么要这么多种函数呢?

在PyTorch中创建张量的最佳选项

鉴于所有这些细节,这两个是最佳选项:

  • torch.tensor()
  • torch.as_tensor()

torch.tensor()​调用是主要的选择,而torch.as_tensor()​应该在调整我们的代码以提高性能时使用。

在这里插入图片描述

关于内存共享(在可能的地方工作)的一些注意事项:

  1. 由于numpy.ndarray​对象分配在CPU上,当使用GPU时,as_tensor()​函数必须将数据从CPU复制到GPU。
  2. as_tensor()​的内存共享不适用于内置的Python数据结构,如列表。
  3. as_tensor()​调用需要开发人员了解共享功能。这是必要的,这样我们就不会在不知不觉中对底层数据进行了不想要的更改,而没有意识到更改会影响到多个对象。
  4. 如果numpy.ndarray​对象和张量对象之间有很多来回操作,as_tensor()​的性能提升将更大。然而,如果只是一个单一的加载操作,从性能角度来看不应该有太大影响。
总结

此时,我们应该对PyTorch的tensor​创建选项有了更好的理解。我们学习了工厂函数,并且看到了内存_共享与复制_如何影响性能和程序行为。下次见!


http://www.ppmy.cn/devtools/151046.html

相关文章

【Elasticsearch】filterQuery过滤查询

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

C语言:数据的存储

本文重点&#xff1a; 1. 数据类型详细介绍 2. 整形在内存中的存储&#xff1a;原码、反码、补码 3. 大小端字节序介绍及判断 4. 浮点型在内存中的存储解析 数据类型结构的介绍&#xff1a; 类型的基本归类&#xff1a; 整型家族 浮点家族 构造类型&#xff1a; 指针类型&…

在 CentOS/Red Hat Linux 中安装 Docker

在 Red Hat Linux 中安装 Docker 在 Red Hat Linux (RHEL) 中安装 Docker 需要一些准备工作&#xff0c;尤其是针对不同版本的系统&#xff08;如 RHEL 7、8、9&#xff09;。以下是具体的安装步骤&#xff1a; 步骤 1&#xff1a;检查系统版本 在安装前&#xff0c;确认系统…

【可持久化线段树】 [SDOI2009] HH的项链 主席树(两种解法)

文章目录 1.题目描述2.思路3.解法一解法一代码 4.解法二解法二代码&#xff08;版本一&#xff09;解法二代码&#xff08;版本二&#xff09; 1.题目描述 原题&#xff1a;https://www.luogu.com.cn/problem/P1972 [SDOI2009] HH的项链 题目描述 HH 有一串由各种漂亮的贝壳…

性能测试工具Jmeter影响负载的X因素有哪些?

在场景运行时&#xff0c;我们提到了Jmeter GUI方式比较占资源&#xff0c;其实不管是GUI方式还是非GUI方式&#xff0c;运行时都会占用一定资源&#xff0c;那我们有没有办法提高负载机性能呢&#xff1f;既然是纯Java 开发&#xff0c;我们就可以调整其性能参数&#xff0c;让…

网管平台(进阶篇):路由器的管理实践

在当今数字化时代&#xff0c;路由器作为网络连接的核心设备&#xff0c;其管理对于确保网络的稳定、高效和安全至关重要。本文旨在深入探讨路由器管理的重要性、基本设置步骤、高级功能配置以及日常维护&#xff0c;帮助读者构建一个高效且安全的网络环境。 一、路由器管理的…

UDP、TCP特性

1.简介 在传输层中&#xff0c;最重要的两个协议就是UDP协议和TCP协议&#xff0c;其中TCP协议更为重要。 对于TCP协议&#xff0c;其性质为有连接、可靠传输、面向字节流、全双工&#xff1b; 对于UDP协议&#xff0c;其性质为无连接、不可靠传输、面向数据报、全双工。 下…

大数据学习(34)-mapreduce详解

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…