Pytorch中的张量数据类型(Tensor)

文章目录

      • 1.常用的张量数据类型
      • 2.张量的属性获取
      • 3.张量与其他数据类型的相互转换
      • 4.生成满足条件的张量
      • 5.对张量进行索引和切片
      • 6.对张量进行维度变换
      • 7.Broadcasting机制
      • 8.对张量的拼接和拆分
      • 9.张量的数学运算
      • 10.张量的布尔值运算
      • 11.张量的统计值计算
      • 12.where函数和gather函数
      • 13.节省内存地进行张量运算
      • 14.张量的保存和加载

对张量的概述

数学中有标量、向量和矩阵的概念,它们的维度分别是0、1、2。也就是说,标量中元素的位置固定,向量中元素的位置需要通过其索引确定,矩阵中的元素位置需要通过其行号和列号确定。张量可以视为矩阵的扩展,可以用于表示无穷维度的数据。

张量(Tensor)是Pytorch库中的基本数据类型,Pytorch中各种基本数字类型都有其对应的Tensor类型,但是在Pytorch中没有内嵌的字符串类型。

1.常用的张量数据类型

常用的Pytorch数值型数据类型

  • torch.FloatTensor(单精度浮点型);
  • torch.DoubleTensor(双精度浮点型);
  • torch.IntTensor(基本整型);
  • torch.LongTensor(长整型);
  • torch.ByteTensor(字节整型)。

由于Pytorch库中没有布尔类型,因此常常使用字节整型张量来表示布尔值。

上面的类型都是针对CPU而言的,对于在GPU上的数据类型,还需要添加一个cuda,也就是下面这些形式:

  • torch.cuda.FloatTensor(GPU单精度浮点型);
  • torch.cuda.DoubleTensor(GPU双精度浮点型);
  • torch.cuda.IntTensor(GPU基本整型);
  • torch.cuda.LongTensor(GPU长整型);
  • torch.cuda.ByteTensor(GPU字节整型)。

将一个普通张量类型转化为GPU张量类型的方法普通张量变量名.cuda()。该函数返回一个GPU张量的引用。

查看张量数据类型的方法:直接使用Python自带的type函数查看Tensor的具体类型是不行的,这样返回的类型值始终是“torch.Tensor”。因此需要使用到下面的两种方法。

  • type方法:使用张量名.type()的方式可以查看张量的具体类型。
  • isinstance函数:isinstance函数是Python的自带函数,其使用语法为isinstance(张量名,类型名),返回值是一个布尔值。譬如下面的这个例子:
isinstance(torch.randn(2,3),torch.FloatTensor)

2.张量的属性获取

张量的形状确定

  • 属性确定:可以使用张量的shape属性确定张量的形状;
  • 方法确定:可以使用张量的size()方法确定张量的形状。

张量中元素个数确定张量名.numel()

张量的维度确定张量名.dim()

测试样例

import torch
# 创建一个3行3列的张量
a=torch.ones((3,3))
print(a.numel()) # 输出9
print(a.dim())   # 输出2

3.张量与其他数据类型的相互转换

将不同类型的数据转化为张量:可以通过已有的单个数值、Python列表、Numpy数组等方式来生成张量。也可以先不指定所生成的张量的元素值,而只生成指定元素个数的张量。

  • 标量(零维张量)的生成方法torch.tensor(标量值)。标量也就是一个数字。
  • 将Python列表转换为张量torch.tensor(列表)

备注:在使用tensor函数创建tensor张量对象时还可以使用dtype参数指定数据类型,使用device参数指定运算设备是CPU还是GPU,此处不再赘述。

  • 将Numpy数组转换为张量torch.from_numpy(numpy数组)。同时,也可以直接使用torch.tensor(numpy数组)的形式来生成张量。

备注1:可以通过使用Python的list()函数将张量转化为Python列表。

  • 生成元素数据类型指定的张量:可以使用FloatTensor等方法直接生成元素类型指定的张量,这一类生成函数的使用方法与tensor函数几乎相同,相当于带有dtype参数的tensor函数。具体有下面几种类型:
# 浮点型张量
tensor.FloatTensor(标量/列表/numpy数组)  # 生成元素均为单精度浮点型的张量
tensor.DoubleTensor(标量/列表/numpy数组) # 生成元素均为双精度浮点型的张量
tensor.HalfTensor(标量/列表/numpy数组)   # 生成元素均为半精度浮点型的张量
# 整型张量
tensor.IntTensor(标量/列表/numpy数组)    # 生成元素均为基本整型的张量
tensor.ShortTensor(标量/列表/numpy数组)  # 生成元素均为短整型的张量
tensor.LongTensor(标量/列表/numpy数组)   # 生成元素均为长整型的张量
# 布尔型张量
tensor.BoolTensor(标量/列表/numpy数组)   # 生成元素均为布尔类型的张量

设置所有生成的张量的默认元素类型torch.set_default_tensor_type(类型名)。Pytorch的默认张量类型是torch.FloatTensor。增强学习中使用DoubleTensor的使用会更多。

将张量转换为其他数据类型

  • 将张量转换为numpy数组张量名.numpy()
  • 将只有一个元素的张量转换为标量张量名.item()

4.生成满足条件的张量

生成指定形状的随机张量

  • 生成不经过元素初始化的指定形状的张量torch.empty(维度1长度,维度2长度...)

备注:为什么有能够进行初始化生成张量的函数还要考虑empty函数呢?这是因为empty函数由于没有进行内存初始化的过程,因此执行效率会比需要初始化的ones和zeros等函数更高。当在后续操作过程中会对张量中的元素进行手动初始化时,更加推荐使用empty函数。

  • 生成元素服从0到1均匀分布的指定形状的张量torch.rand(维度1长度,维度2长度...)

  • 生成与指定张量形状相同的元素服从0到1均匀分布的张量torch.rand_like(张量名)

  • 生成元素为指定区间均匀分布整数的指定形状的张量torch.randint(最小值,最大值,形状列表)。注意该区间不包括右端点(最大值)。

  • 生成元素服从标准正态分布的指定形状的张量torch.randn(维度1长度,维度2长度...)

  • 生成元素服从指定均值和标准差的正态分布的一维张量torch.normal(表示均值的张量,表示标准差的张量)torch.normal函数返回张量的形状与与均值和标准差张量的形状相同。

  • 生成全部元素为1的指定形状的张量torch.ones(形状元组)

  • 生成全部元素为0的指定形状的张量torch.zeros(形状元组)

  • 生成指定填充值的指定形状的张量torch.full(形状列表,填充值)

  • 生成元素满足对角矩阵的指定形状的张量torch.eye(行数,列数)

生成等差张量和等比张量(一维)

  • 生成指定区间和步长的等差张量torch.arange(起始值,终止值,steps=步长)。生成数字的区间不包括终止值。
  • 生成指定区间和元素个数的等差张量torch.linspace(起始值,终止值,steps=生成元素个数)。注意,生成的元素包括终止值。
  • 生成指定区间和指定元素个数的指定底数的对数张量torch.logspace(起始值,终止值,steps=元素个数,base=底数)。生成的每一个元素都是对当前值求log10对数的结果。

生成随机打散的整数张量

tensor.randperm(n):返回一个含有n个元素的一维张量,张量中的元素位置随机,但是都是从0到n-1之间的不重复的整数。

根据一个已知张量复制一个新张量张量名.clone()

5.对张量进行索引和切片

  • 对张量进行索引张量名[维度1,维度2...]
  • 对张量进行切片张量名[维度1起始位置:维度1终止位置,维度2起始位置:维度2终止位置...]

备注
①如果需要对整个维度进行切片,则直接使用冒号“:”表示整个维度即可。
②切片区间是左闭右开的,也就是不包含终止位置的元素。
③和Python中的列表相同,张量的索引和切片也可以用负数形式的反向索引和切片。
④进行切片时还可以指定步长,其语法形式为起始位置:终止位置:步长

  • 选择某一维度上指定位置的元素(对于一维张量)张量名.index_select(维度,位置)。注意位置需要用一个一维张量来进行表示。
# 取0维度上的1、5、6号对象
TestTensor.index_select(0,torch.tensor([1,5,6]))
  • 从多维张量中选择所有指定位置的元素

masked_select 是 Pytorch 库中的一个函数,可以用来从输入张量中按照给定的掩码(mask)选取元素。掩码是一个布尔类型的张量,其形状和输入张量相同,掩码中每个元素的值决定了是否选取输入张量中对应位置的元素。

masked_select(input, mask) 函数接受两个参数:

input:要从中选取元素的输入张量,可以是任意形状的张量。
mask:用来控制选取哪些元素的掩码,必须是一个形状和 input 相同的布尔类型张量。

函数返回一个一维张量,其中包含了所有符合条件的元素。

以下是一个示例:

import torch
# 创建一个 2x3 的张量
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
# 创建一个与 x 相同形状的掩码
mask = torch.tensor([[True, False, True], [False, True, False]])
# 使用掩码从 x 中选取符合条件的元素
result = torch.masked_select(x, mask)
print(result)  # 输出 tensor([1, 3, 5])

6.对张量进行维度变换

注意事项:对张量进行维度变换并不改变张量本身的存储方式,只是对原本存储的内容换了一种方法进行解释。

  • 对张量进行数据量不变的维度变换张量名.reshape(新维度1,新维度2...)

备注:使用reshape操作一定要确保变换前后的张量具有相同数量的元素。同时,使用reshape操作会使得原始张量的维度信息丢失,因此使用时一定要谨慎。

  • 在张量中插入新的维度张量名.unsqueeze(插入索引)。插入索引表示在当前哪一个维度的索引前插入新的维度。索引可以用正向索引和可以用负向索引,但是不建议负向索引,因为容易混淆。
  • 对张量的维度进行删减张量名.squeeze(删除位置索引)。删除位置表示删除哪一个维度的索引。只有长度为1的维度才能进行挤压,否则原始张量不会发生变化。
  • 对张量形状进行扩展张量名.expand(维度1长度,维度2长度...)
    • 该方法只能进行维度值为1的维度进行扩展,无需扩展的维度可以直接写原始维度的大小或用-1表示。因此,输入参数时可以只输入扩展后的张量的最高几个维度即可。
    • 经过扩展后的张量并不会分配新的内存,只是在原先张量的基础上建立新的视图并返回。因此具有较快的执行速度。
    • 返回的张量内存是不连续的,如果希望张量连续存储,则需要使用contiguous()函数。
    • 通过expand()函数作用在张量最高维度的前一个维度上,可以实现原始张量的升维。
  • 对张量形状进行扩展的另一种方法张量名.repeat(维度1拷贝次数,维度2拷贝次数...)
    • repeat()函数实现的是将整个张量视为一个元素,构造一个指定大小的张量。
    • repeat()函数会真正实现对数据的复制,这一点与expand()函数不同。
  • 对二维张量(矩阵)进行转置二维张量名.t()。也可以通过二维张量名.T的方式进行矩阵转置。
  • 交换张量中的两个维度张量名.transpose(维度1,维度2)
  • 对张量进行维度重排:transpose方法一次只能交换两个维度,如果需要对整个张量进行维度重排,就需要使用到permute方法。permute方法的语法如下:
output_tensor = input_tensor.permute(*dims)

其中,input_tensor是要进行维度重排的张量,dims是一个整数序列,表示新的维度顺序。output_tensor是维度重排后得到的新张量。

例如,假设有一个大小为(3, 4, 5)的张量x,要将维度顺序变为(4, 5, 3),可以使用如下代码:

import torch
x = torch.randn(3, 4, 5)
y = x.permute(1, 2, 0)

将张量进行连续存储张量名.contiguous()

  • 在Pytorch中,张量对象可能不是连续存储的,也就是内存中的元素不是按照元素在张量中出现的顺序进行存储的。
  • 当需要对一个张量进行一些操作时,如果这个张量不是连续存储的,那么Pytorch就需要将它复制到一个新的连续存储的张量中,这样会增加额外的计算开销和内存开销,这个过程被称为内存整理。
  • contiguous()函数的作用就是确保一个张量是连续存储的。如果这个张量已经是连续存储的,那么就不进行任何操作;否则会创建一个新的连续存储的张量并将原始张量中的数据复制到新的张量中。这个操作通常在需要进行连续存储的操作之前使用,用来避免额外的时间空间开销。

7.Broadcasting机制

Broadcasting机制的作用:在 Pytorch 中,Broadcasting (广播)是一种自动扩展张量形状的机制,使得不同形状的张量可以进行算术运算。其实现方式是在需要进行运算的张量上自动添加维度,使其形状能够匹配另一个张量的形状,而不需要手动对参与运算的张量进行扩展或者重复。

Broadcasting机制是如何进行的

  • 如果两个张量的维度不同,将维度较小的张量在前面添加维度1来使得参与运算的张量维度相同。
  • 对于任意维度大小为1的张量,沿着该维度进行扩展,使得其大小与参与运算的另外一个张量的大小相同。
  • 如果两个张量在某个维度的大小都不为1,且大小不相等,那么就不能进行扩展,此时的运算会失败并抛出异常。

例如,如果有一个形状为 (3, 1) 的张量 a 和一个形状为 (1, 4) 的张量 b,那么在对它们进行加法运算时,PyTorch 会自动地将它们扩展为形状分别为 (3, 4) 的张量,然后进行加法运算。

备注:这种自动扩展张量形状的机制可以大大简化代码的书写,提高程序的可读性和可维护性。但是在使用时,需要注意张量形状的匹配关系,以免出现意外的错误。

8.对张量的拼接和拆分

  • 对多个张量进行拼接torch.cat([张量1,张量2...],dim=拼接维度下标)。使用该函数要求所拼接的张量除了需要进行拼接的维度的值不同外,维度相同且其他维度的数值都相同。
  • 将两个张量在一个更高的维度进行拼接torch.stack([张量1,张量2...],dim=新创建维度)。要求进行拼接的两个张量的形状必须完全相同。
  • 将一个张量在指定维度上拆分为多个长度指定的张量torch.split([张量1长度,张量2长度...],dim=被拆分的维度)。被拆分得到的多个张量以一个元组的形式作为返回值返回。
  • 将一个张量在指定维度上拆分为指定个数的张量torch.chunk(拆分个数,dim=被拆分的维度)

9.张量的数学运算

  • 可以直接使用加减乘除运算符对两个张量对应位置的元素进行运算,但是两个操作数需要满足张量运算的规则

四则运算函数:除了直接使用运算符外,也可以使用对应的函数进行运算。加减乘除的对应函数分别为torch.add()、torch.sub()、torch.mul()、torch.div(),传入的参数是进行运算的两个张量。

  • 对两个二维张量进行矩阵相乘torch.matmul(张量1,张量2)。这个函数也可以对更高维度的张量进行运算,具体结果形式此处不再解释。

备注:在pytorch中,运算符@也是表示两个矩阵相乘。

  • 对张量的元素进行幂运算张量名.pow(次数)
  • 对张量的元素进行开方运算张量名.sqrt()
  • 对张量的元素进行自然指数运算torch.exp(张量名)。具体来说,就是以张量的元素为指数,求出自然对数e的幂。
  • 对张量的元素求自然对数torch.log(张量名)。如果需要以2为底数或以10为底数,可以分别使用torch.log2()torch.log10()两个函数。
  • 对张量的元素进行向下/向上取整张量名.ceil()/张量名.floor()
  • 对张量的元素进行四舍五入张量名.round()
  • 裁剪出张量的整数部分和小数部分张量名.trunc()/张量名.frac()
  • 设置张量元素的取值区间张量名.clamp(最小值,最大值)。使用该函数后,张量中本来比最小值还要小的元素将被修改为最小值,比最大值还要大的元素将被修改为最大值。

10.张量的布尔值运算

  • 判断是否张量中的所有元素都是Truetorch.all(张量名)。该函数返回一个零维张量,如果元素为1表示所有元素都是True,否则返回0。
  • 判断两个形状相同的张量的对应元素是否相等torch.eq(张量1,张量2)。该函数返回一个布尔类型的张量。
  • 判断第一个张量中的元素是否大于等于第二个张量中的对应元素torch.ge(张量1,张量2)
  • 判断第一个张量中的元素是否大于第二个张量中的对应元素torch.gt(张量1,张量2)
  • 判断第一个张量中的元素是否小于等于第二个张量中的对应元素torch.le(张量1,张量2)
  • 判断第一个张量中的元素是否小于第二个张量中的对应元素torch.lt(张量1,张量2)

11.张量的统计值计算

  • 求张量的指定阶数的范数torch.norm(张量名,p=范数类型,dim=求范数维度)
    • 张量名:需要求范数的张量。
    • 范数类型:常见的范数类型包括fro(Frobenius范数),float类型数值(表示求几阶范数),nuc(核范数)。
    • 范数维度:可以是一个整数或者一个列表。如果不指定则是对整个张量进行计算。
  • 求张量的最大值/最小值张量名.max(dim=需要求最大值的维度)张量名.min(dim=需要求最小值的维度)
  • 求张量的平均值张量名.mean()
  • 求张量的累加结果或累乘结果张量名.sum()张量名.prod()
  • 求张量的最大值或最小值的索引张量名.argmax(dim=求解维度)张量名.argmin(dim=求解维度)
  • 求张量中最大/小的前K个元素张量名.topk(元素个数,dim=求最值的维度,largest=最大/最小)
    • largestTrue表示求最大的几个元素;largestFalse表示求最小的几个元素。
    • 该方法会返回两个张量,第一个张量表示最大或最小的元素,第二个张量表示这些元素各自的索引。
  • 求张量中第K小的元素张量名.kthvalue(K,dim=求最值的维度)。该方法也会像topk方法一样返回两个张量。

12.where函数和gather函数

where函数的使用方法

在 Pytorch 中,torch.where() 函数的作用是根据条件选择输入张量的元素,并返回一个新的张量。

函数的使用方法如下:

torch.where(condition, x, y)

其中,condition 是一个布尔类型的张量,x 和 y 分别是两个张量,它们的形状可以不相同,但是必须有相同的维度。函数返回一个新的张量,其形状与 condition 张量相同,其元素根据 condition 张量中的值选择自 x 或 y 中的相应元素。

具体地说,当 condition[i] 为 True 时,新张量的第 i 个元素将等于 x[i],否则为 y[i]。

下面是一个简单的示例,说明如何使用 torch.where() 函数:

import torch

x = torch.randn(3, 3)
y = torch.ones(3, 3)

result = torch.where(x > 0, x, y)
print(result)

在上面的例子中,我们首先创建了两个 3×3 的张量 x 和 y,其中 x 中的元素是随机的,y 中的元素都是 1。然后,我们使用 torch.where() 函数根据 x > 0 的条件选择 x 或 y 中的元素,将结果保存到 result 张量中,并打印输出。

gather函数的使用方法

在 Pytorch 中,gather() 函数用于按索引从输入张量中收集元素,并将它们组成一个新的张量返回。

函数的使用方法如下:

torch.gather(input, dim, index, out=None)

其中,input 是输入张量,dim 是指定的维度,index 是索引张量,out 是输出张量(可选参数)。函数返回一个新的张量,其形状与 index 张量相同。

具体地说,对于 2D 张量 input,dim 可以是 0 或 1,分别表示在行或列上进行索引。而对于多维张量 input,dim 则可以是任意维度。

下面是一个简单的示例,说明如何使用 gather() 函数:

import torch

input = torch.tensor([[10, 20], [30, 40], [50, 60]])
index = torch.tensor([[0, 1], [1, 0], [0, 1]])

output = torch.gather(input, 1, index)

print(output)

在上面的例子中,我们首先创建了一个 3×2 的张量 input,其中包含了一些元素。然后,我们创建了一个与 input 相同大小的索引张量 index,用于按行收集元素。最后,我们使用 gather() 函数按行从 input 张量中收集元素,并将结果保存到 output 张量中,并打印输出。

输出结果为:

tensor([[10, 20],
        [40, 30],
        [50, 60]])

可以看到,输出张量中的每个元素都是从 input 张量中按照相应的索引收集而来的。例如,第一个元素 10 是来自 input[0, 0],第二个元素 20 是来自 input[0, 1],以此类推。

13.节省内存地进行张量运算

在运行一些张量操作时可以需要为新的结果重新分配内存,例如,如果我们使用Y=Y+X,那么我们将取消Y指向的张量,而是指向新分配的内存处的张量。

Python中可以通过id函数查看某个变量的具体地址:

id_number=id(变量名)

通过id()函数我们即可看出一个变量的存储位置是否发生了改变。

下面给出一个浪费内存空间的例子:

before = id(Y)
Y = Y + X
id(Y) == before

在机器学习和深度学习任务中要避免重新分配内存,原因如下:

  • 任务中的参数量一般都很大,更新参数需要额外极大的时间开销和空间开销。
  • 如果不进行原地更新,那么其他引用仍然指向旧的内存位置,这样我们的代码就可能无意间引用旧的参数,导致程序运行出错。

可以通过切片操作将用新的张量覆盖已有的不用的张量来实现节约内存。

before=id(Y)
Y[:]=Y+X
id(Y)==before

14.张量的保存和加载

  • 张量的保存方法torch.save(张量名,保存文件名)
  • 张量的加载方法torch.load(保存文件名)

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年11月30日
下一篇 2023年11月30日

相关推荐