当前位置:   article > 正文

【2】深度学习之Pytorch——数据类型、索引张量、Numpy的互通性的概念、序列化张量_pytorch浮点数精度 numpy

pytorch浮点数精度 numpy

在这里插入图片描述
本期文章我们继续介绍张量的数据类型和一些基本的操作

数据类型

张量构造函数(即tensor、ones、zeros之类的函数)的dtype参数指定了张量中的数据类型。

数据类型指定张量可以容纳的可能值(整数还是浮点数)以及每个值的字节数。

dtype参数被故意设计成类似于同名的标准NumPy参数。以下是dtype参数的可能取值的列表:

  • torch.float32或torch.float —— 32位浮点数
  • torch.float64或torch.double —— 64位双精度浮点数
  • torch.float16或torch.half —— 16位半精度浮点数
  • torch.int8 —— 带符号8位整数
  • torch.uint8 —— 无符号8位整数
  • torch.int16或torch.short —— 带符号16位整数
  • torch.int32或torch.int —— 带符号32位整数
  • torch.int64或torch.long —— 带符号64位整数

每个torch.float、torch.double等等都有一个与之对应的具体类:

torch.FloatTensor、torch.DoubleTensor等等。

torch.int8对应的类是torch.CharTensor,而torch.uint8对应的类是torch.ByteTensor。torch.Tensor是torch.FloatTensor的别名,即默认数据类型为32位浮点型。

创建时指定数据类型
double_points = torch.ones(10, 2, dtype=torch.double)
short_points = torch.tensor([[1, 2], [3, 4]], dtype=torch.short)
  • 1
  • 2

我们在创建张量的时候,可以指定数据类型即可,这样方便我们管理数据,在不同的算法模型中对数据的处理和需求不一样。

并且通过访问dtype属性来获得张量的数据类型

转换数据类型

可以使用相应的转换方法将张量创建函数的输出转换为正确的类型

double_points = torch.zeros(10, 2).double()
short_points = torch.ones(10, 2).short()
  • 1
  • 2

或者

double_points = torch.zeros(10, 2).to(torch.double)
short_points = torch.ones(10, 2).to(dtype=torch.short)
  • 1
  • 2

在实现内部,type和to执行相同的操作,即“检查类型如果需要就转换(check-and-convert-if-needed)”,但是to方法可以使用其他参数。

你始终可以使用type方法将一种类型的张量转换为另一种类型的张量:

总而言之就是在已有的张量上的时候可以使用to.(dtype=),如果是常见的时候就可以直接使用dtype=即可。

在这里插入图片描述

索引张量

对于索引,在Python的索引知识中,对其有比较详细的解释,如果你对其还有很多的疑惑的话,可以去看看这一篇文章:索引知识点

some_list = list(range(6))
some_list[:]     # 所有元素
some_list[1:4]   # 第1(含)到第4(不含)个元素
some_list[1:]    # 第1(含)个之后所有元素
some_list[:4]    # 第4(不含)个之前所有元素
some_list[:-1]   # 最末尾(不含)元素之前所有元素
some_list[1:4:2] # 范围1(含)到4(不含),步长为2的元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

把握一个原则,索引是从0开始的,末尾的取不了,如果想要快速的确定元素的个数的话,直接用其索引长度确定就可以了。

PyTorch张量使用相同的表示,并且具有与NumPy和其他Python科学库一样的额外好处,即我们可以对张量的每个维使用区间索引:

其实就是我们学习pandas和numpy的一些思想,如果你对其还没有具体的掌握的话,可以移步去这里看看关于numpy的全套的语法,这样对学习这些基础的东西有很大的帮助。

一文带你斩杀Python之Numpy☀️Pandas全部操作【全网最详细】❗❗❗

points[1:]    # 第1行及之后所有行,(默认)所有列
points[1:, :] # 第1行及之后所有行,所有列
points[1:, 0] # 第1行及之后所有行,仅第0列
  • 1
  • 2
  • 3

先行后列是重点

numpy与pytorch的互通性

PyTorch张量可以方便地转换为NumPy数组,反之亦然。这样,你就可以使用围绕NumPy数组类型构建的更广泛的Python生态系统中的大量功能。

与NumPy数组的这种零拷贝互通性是由于(PyTorch的)存储是遵守Python缓冲协议的。

Python缓冲协议是一个用于管理缓存的协议,它可以帮助我们更有效地使用缓存,从而提高程序的效率。它通过分配缓存空间,控制缓存的读写,以及清除不再需要的缓存数据来实现这一目的。

通过pytorch中的张量来创建numpy数组,例如:要从points张量创建NumPy数组,请调用

points = torch.ones(3, 4)
points_np = points.numpy()
points_np
  • 1
  • 2
  • 3

它返回尺寸、形状和数值类型正确的NumPy多维数组。有趣的是,返回的数组与张量存储共享一个基础缓冲区。因此,只要数据位于CPU RAM中,numpy方法就可以几乎零花费地高效执行,并且修改得到的NumPy数组会导致原始张量发生变化。

如果在GPU上分配了张量,(调用numpy方法时)PyTorch会将张量的内容复制到在CPU上分配的NumPy数组中。

相反,你可以通过以下方式从NumPy数组创建PyTorch张量:

points = torch.from_numpy(points_np)
  • 1

from_numpy使用相同的缓冲共享策略。

序列化张量

动态创建张量是很不错的,但是如果其中的数据对你来说具有价值,那么你可能希望将其保存到文件中并在某个时候加载回去。

毕竟你可不想每次开始运行程序时都从头开始重新训练模型!PyTorch内部使用pickle来序列化张量对象和实现用于存储的专用序列化代码。下面展示怎样将points张量保存到ourpoints.t文件中:

保存
torch.save(points, './ourpoints.t')
  • 1
with open('./ourpoints.t','wb') as f:
    torch.save(points, f)
  • 1
  • 2
加载
points = torch.load('./ourpoints.t')
  • 1
with open('./ourpoints.t','rb') as f:
    points = torch.load(f)
  • 1
  • 2

如果只想通过PyTorch加载张量,则上述例子可让你快速保存张量,但这个文件格式本身是不互通(interoperable)的,你无法使用除PyTorch外其他软件读取它。

根据实际使用情况,上述情况可能问题不大,但应该学习一下如何在有的时候(即想用其他软件读取的时候)互通地保存张量。尽管实际情况都是独一无二的,但当你想将PyTorch引入已经依赖于不同库的现有系统中时,上述情况会很常见;而全新的项目可能不需要经常互通地保存张量。

对于需要(互通)的情况,你可以使用HDF5格式和库。HDF5是一种可移植的、广泛支持的格式,用于表示以嵌套键值字典形式组织的序列化多维数组。Python通过h5py库支持HDF5,该库以NumPy数组的形式接收和返回数据。

例如:通过将points张量转换为NumPy数组(如前所述,此操作几乎零花费)并将其传递给create_dataset函数来保存points张量:

import h5py

f = h5py.File('./ourpoints.hdf5', 'w')
dset = f.create_dataset('coords', data=points.numpy())
f.close()
  • 1
  • 2
  • 3
  • 4
  • 5

这里,coords是传入HDF5文件的键值。你还可以有其他键值,甚至是嵌套键值。HDF5中的一件有趣的事情是,你可以索引在磁盘的数据并且仅访问你感兴趣的元素。

例如你只想加载数据集中的最后两个点数据:

f = h5py.File('./ourpoints.hdf5', 'r')
dset = f['coords']
last_points = dset[1:]
  • 1
  • 2
  • 3

上例中,当你打开文件或需要数据集时并未加载数据。相反,数据一直保留在磁盘上,直到你请求数据集中的第二行和最后一行。此时,h5py才访问这两行并返回了一个包含你想要数据的类似NumPy数组的对象,该对象的行为类似于NumPy数组,并且具有相同的API。

基于这个事实,你可以将返回的对象传递给torch.from_numpy函数以直接获取张量。需要注意的是,在这种情况下,数据将复制到张量存储中:

last_points = torch.from_numpy(dset[1:])
f.close()
# last_points = torch.from_numpy(dset[1:]) # 会报错, 因为f已经关了
  • 1
  • 2
  • 3

将张量转移到GPU上运行

PyTorch张量的最后一点是关于在GPU上计算。每一个Torch张量都可以转移到GPU上去执行快速、大规模并且可以并行的计算。在张量上执行的所有操作均由PyTorch自带的GPU特定例程执行。

除了dtype之外,PyTorch张量还具有设备(device)的概念,这是在设置计算机上放张量(tensor)数据的位置。 通过为构造函数指定相应的参数,可以在GPU上创建张量:

points_gpu = torch.tensor([[1.0, 4.0], [2.0, 1.0], [3.0, 4.0]], device='cuda')
  • 1

你可以使用to方法将在CPU上创建的张量(tensor)复制到GPU:

points_gpu = points.to(device='cuda')
  • 1

现在数据已经存放在本地的GPU中,当在张量上运行数字运算时,你可以看见很好的加速效果。

并且,这个新GPU支持的张量的类也更改为torch.cuda.FloatTensor(一开始输入的类型为torch.FloatTensor;torch.cuda.DoubleTensor等等也存在对应关系)。

在大部分样例中,基于CPU和GPU的张量都公开面向用户相同的API,这使得与繁琐数字运算过程中无关的代码的编写更加容易。

如果你的机器拥有多个GPU,你可以通过传递从零开始的整数来确定张量分配给哪个GPU,该整数标志着机器上的GPU下标:

points_gpu = points.to(device='cuda:0')
  • 1

此时,在GPU上执行对张量的任何操作,例如将所有元素乘以一个常数。

points = 2 * points # 在CPU上做乘法
points_gpu = 2 * points.to(device='cuda') # 在GPU上做乘法
  • 1
  • 2

请注意,当计算结果产生后,points_gpu的张量并不会返回到CPU。这里发生的是以下三个过程:

  • 将points张量复制到GPU
  • 在GPU上分配了一个新的张量,并用于存储乘法的结果
  • 返回该GPU张量的句柄

因此,如果你还想向结果加上一个常量:

points_gpu = points_gpu + 4
  • 1

加法仍然在GPU上执行,并且没有信息流到CPU(除非您打印或访问得到的张量)。 如果要将张量移回CPU,你需要为to方法提供一个cpu参数:

points_cpu = points_gpu.to(device='cpu')
  • 1

有时候我们在看其他人的代码时候,发现在使用GPU的时候代码都比较哦的简单,类似于下面:

points_gpu = points.cuda() # 默认为GPU0
points_gpu = points.cuda(0)
points_cpu = points_gpu.cpu()
  • 1
  • 2
  • 3

特别是第一种

但是,我们使用to方法时,可以通过提供device和dtype参数来同时更改位置和数据类型。

这就是它的一个优势吧,如果我们不需要指定其数据类型,可以直接使用cuda()即可

张量常见的API

有少量的操作仅作为张量对象的方法存在。你可以通过名称中的下划线来识别它们,例如zero_,下划线标识表明该方法是就地(inplace)运行的,即直接修改输入而不是创建新的输出并返回。例如,zero_方法会将输入的所有元素清零。任何不带下划线的方法都将保持源张量不变并返回新的张量:

a = torch.ones(3, 2)
a.zero_()
a
  • 1
  • 2
  • 3
tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])
  • 1
  • 2
  • 3
  • 创建操作 —— 构造张量的函数,例如ones和from_numpy;

  • 索引、切片、联接和变异操作 —— 更改形状、步长或张量内容,例如transpose;

  • 数学操作 —— 通过计算来操纵张量内容的函数:

  1. 按点(pointwise)操作 —— 将函数分别应用于每个元素(例如abs和cos)的函数

  2. 简化(reduction)操作 —— 通过张量迭代计算合计值的函数,例如mean、std和norm;
    比较操作 —— 用于比较张量的函数,例如equal和max;
    频谱操作 —— 在频域中转换和运行的函数,例如stft和hamming_window;
    其他操作 —— 一些特殊函数,例如对于向量的cross,对于矩阵的trace;
    BLAS和LAPACK操作 —— 遵循BLAS(Basic Linear Algebra Subprograms)规范的函数,用于标量、向量与向量、矩阵与向量和矩阵与矩阵的运算。

  3. 随机采样操作 —— 从概率分布中随机采样值的函数,例如randn和normal;

  4. 序列化操作 —— 用于保存和加载张量的函数,例如save和load;

  5. 并行操作 —— 控制并行CPU执行的线程数的函数,例如set_num_threads;

学完这些,当我们需要一些不常见的,我们可以使用搜索引擎去了解这些语法,当然也可以去了解官方文档。

https://pytorch.org/docs/stable/index.html

总结

  • 神经网络将浮点表示形式转换为其他浮点表示形式,起始和结束的表示形式通常是人类可以理解的,但中间表示则不是这样。
  • 这些浮点表示存储在张量中
  • 张量是多维数组,它是PyTorch中的基本数据结构。
  • PyTorch有一个全面的标准库,用于张量创建、操作和数学运算。
  • 张量可以序列化存储到磁盘上也可以加载回来。
  • PyTorch中的所有张量操作都可以在CPU和GPU上执行,无需更改代码。
  • PyTorch使用结尾下划线标识来表示就地操作(例如Tensor.sqrt_)。

每文一语

努力提升自己才能拥有高质量的爱

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/747342
推荐阅读
相关标签
  

闽ICP备14008679号