Pytorch的数据类型(张量)及其属性和运算方式

基本数据

Pytorch的基本数据类型为Tensor(张量),本质上是一种包含单一数据类型元素的多为矩阵。从使用角度看,其与numpy的ndarrays类似,两者之间也可以相互转换,区别是tensor支持在GPU上运算。

Tensor数据类型

与其他数据一样,tensor也包括多种数据类型,其中包括7种cpu tensor类型和8种gpu tensor类型。在使用时我们要根据模型所需的精度和硬件设备的条件选取合适的数据类型。

Tensor数据类型介绍
数据类型CPU TensorGPU Tensor
32位浮点torch.FloatTensortorch.cuda.FloatTensor
64位浮点torch.doubleTensortorch.cuda.doubleTensor
16位半浮点/torch.cuda.halfTensor
8位无符号整型torch.byteTensortorch.cuda.byteTensor
8位有符号整型torch.charTensortorch.cuda.charTensor
16位有符号整型torch.shortTensortorch.cuda.shortTensor
32位有符号整型torch.intTensortorch.cuda.intTensor
64位有符号整型torch.longTensortorch.cuda.longTensor

pytorch可以通过set_default_tensor_type函数设置默认全局使用的Tensor类型,但是在局部使用完后所需类型后,还需要重新设置为全局使用的Tensor类型。

Tensor的创建方式

Tensor有多种创建方法,最基础的有torch.Tensor(M, N),默认创建一个数据类型为float的M*N的矩阵;如果想要指定数据类型创建,可以使用torch.DoubleTensor(M, N)。除此之外,类似于numpy的ndarrays类似,torch.zeros(M, N)、torch.ones(M, N)、torch.eye(M, N)、torch.randn(M, N)、torch.Tensor([[1, 2], [4, 5]])以及顺序排列张量torch.arange(0, 5, 1)、随机排列张量torch.randperm(M)…

>>> a=torch.Tensor(2,2)
>>> a 
tensor(1.00000e-18 * [[-8.2390, 0.0000], [ 0.0000, 0.0000]]) 
>>> b = torch.DoubleTensor(2,2) 
>>> b 
tensor(1.00000e-310 * [[ 0.0000, 0.0000], [ 6.9452, 0.0000]], dtype=torch.float64) 
# 使用Python的list序列进行创建 
>>> c = torch.Tensor([[1, 2], [3, 4]]) 
>>> c 
tensor([[ 1., 2.], [ 3., 4.]]) 
# 使用zeros()函数,所有元素均为0 
>>> d = torch.zeros(2, 2) 
>>> d 
tensor([[ 0., 0.], [ 0., 0.]]) 
# 使用ones()函数,所有元素均为1 
>>> e = torch.ones(2, 2)
>>> e 
tensor([[ 1., 1.], [ 1., 1.]]) 
# 使用eye()函数,对角线元素为1,不要求行列数相同,生成二维矩阵 
>>> f = torch.eye(2, 2) 
>>> f 
tensor([[ 1., 0.], [ 0., 1.]]) 
# 使用randn()函数,生成随机数矩阵 
>>> g = torch.randn(2, 2) 
>>> g 
tensor([[-0.3979, 0.2728], [ 1.4558, -0.4451]]) 
# 使用arange(start, end, step)函数,表示从start到end,间距为step,一维向量 
>>> h = torch.arange(1, 6, 2) 
>>> h 
tensor([ 1., 3., 5.]) 
# 使用linspace(start, end, steps)函数,表示从start到end,一共steps份,一维向量 
>>> i = torch.linspace(1, 6, 2) 
>>> i 
tensor([ 1., 6.]) 
# 使用randperm(num)函数,生成长度为num的随机排列向量 
>>> j = torch.randperm(4) 
>>> j 
tensor([ 1, 2, 0, 3]) 
# PyTorch 0.4中增加了torch.tensor()方法
# 参数可以为Python的list、NumPy的ndarray等
>>> k = torch.tensor([1, 2, 3])
>>> k
>>> tensor([1, 2, 3)

在对网络模型进行设计和调试的时候,我们需要查看每层tensor的维度或者tensor中的元素总数,查看维度可以使用Tensor.shape或者size()函数实现,查看元素总个数通过Tensor.numel()或者Tensor.nelement()函数完成。

>>> a=torch.randn(2,2) 
>>> a.shape # 使用shape查看Tensor维度
torch.Size([2, 2]) 
>>> a.size() # 使用size()函数查看Tensor维度 
torch.Size([2, 2])
>>> a.numel() # 使用Tensor.numel()查看Tensor元素数量
>>> 4
>>> a.nelement() # 使用Tensor.nelement()查看Tensor元素数量
>>> 4
Tensor的组合与分块

在一些较为复杂的网络模型中,我们需要将各层网络的输入输出进行组合或者分块进行运算。
组合主要有torch.cat()和torch.stack()两个函数。torch.cat()是沿着已有数据的某一维进行拼接,操作后数据的总维度不变。在进行拼接时,除了拼接的维度外,其余维度必须相同才可以。而torch.stack()函数是指增加新的维度,并按照指定的维度进行叠加。

# 创建两个2×2的Tensor 
>>> a=torch.Tensor([[1,2],[3,4]]) 
>>> a 
tensor([[ 1., 2.], [ 3., 4.]]) 
>>> b = torch.Tensor([[5,6], [7,8]]) 
>>> b 
tensor([[ 5., 6.], [ 7., 8.]]) 
# 以第一维进行拼接,则变成4×2的矩阵 
>>> torch.cat([a,b], 0) 
tensor([[ 1., 2.], [ 3., 4.], [ 5., 6.], [ 7., 8.]]) 
# 以第二维进行拼接,则变成2×4的矩阵
>>> torch.cat([a,b], 1) 
tensor([[ 1., 2., 5., 6.], [ 3., 4., 7., 8.]])
# 以第0维进行stack,叠加的基本单位为序列本身,即a与b,因此输出[a, b],输出维度为2×2×2 
>>> torch.stack([a,b], 0) 
tensor([[[ 1., 2.], [ 3., 4.]], 
		[[ 5., 6.], [ 7., 8.]]]) 
# 以第1维进行stack,叠加的基本单位为每一行,输出维度为2×2×2 
>>> torch.stack([a,b], 1) 
tensor([[[ 1., 2.], [ 5., 6.]], 
		[[ 3., 4.], [ 7., 8.]]]) 
# 以第2维进行stack,叠加的基本单位为每一行的每一个元素,输出维度为2×2×2 
>>> torch.stack([a,b], 2) 
tensor([[[ 1., 5.], [ 2., 6.]], 
		[[ 3., 7.], [ 4., 8.]]])

Tensor的分块也是有两个函数实现,分别是torch.chunk(Tensor, 子张量的数量, dim)和torch.split(Tensor, 子张量的大小, dim)。

>>> a=torch.Tensor([[1,2,3],[4,5,6]]) 
>>> a 
tensor([[ 1., 2., 3.], [ 4., 5., 6.]]) 
# 使用chunk,沿着第0维进行分块,一共分两块,因此分割成两个1×3的Tensor 
>>> torch.chunk(a, 2, 0) 
(tensor([[ 1., 2., 3.]]), tensor([[ 4., 5., 6.]])) 
# 沿着第1维进行分块,因此分割成两个Tensor,当不能整除时,最后一个的维数会小于前面的 
# 因此第一个Tensor为2×2,第二个为2×1 
>>> torch.chunk(a, 2, 1) 
(tensor([[ 1., 2.], [ 4., 5.]]), tensor([[ 3.], [ 6.]])) 
# 使用split,沿着第0维分块,每一块维度为2,由于第一维维度总共为2,因此相当于没有分割 
>>> torch.split(a, 2, 0) 
(tensor([[ 1., 2., 3.],
		 [ 4., 5., 6.]]),) 
# 沿着第1维分块,每一块维度为2,因此第一个Tensor为2×2,第二个为2×1
>>> torch.split(a, 2, 1) 
(tensor([[ 1., 2.], 
		 [ 4., 5.]]), 
 tensor([[ 3.], 
		 [ 6.]])) 
# split也可以根据输入的list进行自动分块,list中的元素代表了每一个块占的维度 
>>> torch.split(a, [1,2], 1)
(tensor([[ 1.], 
		 [ 4.]]), 
 tensor([[ 2., 3.], 
 	     [ 5., 6.]]))
Tensor的索引与变形

索引主要有下标索引、表达式索引、使用torch.where()和torch.clamp()进行选择性索引。这里主要介绍表达式索引和选择性索引。

>>> a = torch.Tensor([[0,1], [2, 3]]) 
>>> a 
tensor([[ 0., 1.], [ 2., 3.]]) 
# 选择a中大于0的元素,返回和a相同大小的Tensor,符合条件的置1,否则置0 
>>> a>0 
tensor([[ 0, 1], [ 1, 1]], dtype=torch.uint8) 
# 选择符合条件的元素并返回,等价于torch.masked_select(a, a>0) 
>>> a[a>0] 
tensor([ 1., 2., 3.]) 
# 选择非0元素的坐标,并返回 
>>> torch.nonzero(a) 
tensor([[ 0, 1], [ 1, 0], [ 1, 1]]) 
# torch.where(condition, x, y),满足condition的位置输出x,否则输出y 
>>> torch.where(a>1, torch.full_like(a, 1), a) 
tensor([[ 0., 1.], [ 1, 1.]]) 
# 对Tensor元素进行限制可以使用clamp()函数,示例如下,限制最小值为1,最大值为2 
>>> a.clamp(1,2) 
tensor([[ 1., 1.], [ 2., 2.]])

其中这些函数的参数都必须是张量,否则无法参与运算。
变形操作主要是改变Tensor的维度,以适应不同网络层结构之间的运算。Pytorch中主要有四种方法实现变形操作。

变形操作功能
view()、resize()、reshape()改变Tensor的形状,不改变元素个数
permute()、transpose()维度之间交换位置
squeeze()、unsqueeze()处理size为1的维度
expand()、expand_as()复制元素来拓展维度

view()、resize()、reshape()可以在不改变元素数量的前提下任意改变tensor的形状,而且变换之后共享内存

>>> a=torch.arange(1,5) 
>>> a 
tensor([ 1., 2., 3., 4.]) 
# 分别使用view()、resize()及reshape()函数进行维度变换 
>>> b=a.view(2,2) 
>>> b 
tensor([[ 1., 2.], [ 3., 4.]]) 
>>> c=a.resize(4,1) 
>>> c tensor([[ 1.], [ 2.], [ 3.], [ 4.]]) 
>>> d=a.reshape(4,1) 
>>> d 
tensor([[ 1.], [ 2.], [ 3.], [ 4.]]) 
# 改变了b、c、d的一个元素,a也跟着改变了,说明两者共享内存 
>>> b[0,0]=0 
>>> c[1,0]=0 
>>> d[2,0]=0 
>>> a 
tensor([ 0., 0., 0., 4.])

如果想要改变元素的个数,可以使用resize_()函数,超出原tensor的元素个数,则多出来的添加0;如果少于原tensor的元素个数,则会保留隐藏剩余的元素。值得一提的是,resize_()之后得到的tensor会重新分配内存。

>>> a.resize_(5, 1)
tensor([[1],
        [2],
        [3],
        [4],
        [0]])
>>> a.resize_(2, 1)
tensor([[1],
        [2]])

transpose()函数可以将指定的两个维度的元素进行转置,而 permute()函数则可以按照给定的维度进行维度变换。

>>> a=torch.randn(2,2,2) 
>>> a 
tensor([[[-0.9268, 0.6006], 
		 [ 1.0213, 0.5328]], 
		[[-0.7024, 0.7978], 
		 [ 1.0553, -0.6524]]]) 
# 将第0维和第1维的元素进行转置 
>>> a.transpose(0,1) 
tensor([[[-0.9268, 0.6006], 
		 [-0.7024, 0.7978]], 
		[[ 1.0213, 0.5328], 
		 [ 1.0553, -0.6524]]]) 
# 按照第2、1、0的维度顺序重新进行元素排列 
>>> a.permute(2,1,0) 
tensor([[[-0.9268, -0.7024], 
		 [ 1.0213, 1.0553]], 
		[[ 0.6006, 0.7978], 
		 [ 0.5328, -0.6524]]])

在实际的应用中,经常需要增加或减少Tensor的维度,尤其是维度为1的情况,这时候可以使用squeeze()与unsqueeze()函数,前者用于去除 size为1的维度,而后者则是将指定的维度的size变为1。

>>> a=torch.arange(1,4) 
>>> a.shape 
torch.Size([3]) 
# 将第0维变为1,因此总的维度为1、3 
>>> a.unsqueeze(0).shape 
torch.Size([1, 3]) 
# 第0维如果是1,则去掉该维度,如果不是1则不起任何作用 
>>> a.unsqueeze(0).squeeze(0).shape 
torch.Size([3])

有时需要采用复制元素的形式来扩展Tensor的维度,这时expand就 派上用场了。expand()函数将size为1的维度复制扩展为指定大小,也可以使用expand_as()函数指定为示例Tensor的维度。

>>> a=torch.randn(2,2,1) 
>>> a 
tensor([[[ 0.5379], 
		 [-0.6294]], 
		[[ 0.7006], 
		 [ 1.2900]]]) 
# 将第2维的维度由1变为3,则复制该维的元素,并扩展为3 
>>> a.expand(2,2,3) 
tensor([[[ 0.5379, 0.5379, 0.5379], 
		 [-0.6294, -0.6294, -0.6294]], 
		[[ 0.7006, 0.7006, 0.7006], 
		 [ 1.2900, 1.2900, 1.2900]]])

值得注意的是,在进行Tensor操作时,有些操作如transpose()、permute()等可能会把Tensor在内存中变得不连续,而有些操作如view()等是需要Tensor内存连续的,这种情况下需要使用contiguous()操作先将内存变为连续的。在PyTorch v0.4版本中增加了reshape()操作,可以看做是 Tensor.contiguous().view()。

Tensor的排序与取极值

tensor的排序功能主要靠sort()函数实现,可选择的沿着某一维度进行排序,并返回排序后的tensor以及对应的索引位置。max()和min()函数则是沿着指定维度选择最大与最小元素,返回该元素及其对应的索引位置。

>>> a = torch.randn(3, 3)
>>> a
tensor([[-0.2316, -0.0734,  2.1122],
        [-0.2480, -0.7257, -0.2866],
        [-0.2092,  0.1340,  1.5125]])
# 0是指沿着每行进行比较选择排序,True是指升序,False是指降序
>>> a.sort(0, True)
torch.return_types.sort(
values=tensor([[-0.2092,  0.1340,  2.1122],
        [-0.2316, -0.0734,  1.5125],
        [-0.2480, -0.7257, -0.2866]]),
indices=tensor([[2, 2, 0],
        [0, 0, 2],
        [1, 1, 1]]))
>>> a.max(0)
torch.return_types.max(
values=tensor([-0.2092,  0.1340,  2.1122]),
indices=tensor([2, 2, 0]))
>>> a.min(0)
torch.return_types.min(
values=tensor([-0.2480, -0.7257, -0.2866]),
indices=tensor([1, 1, 1]))
>>>

此外,还有一些元素级别的运算,例如:abs(), sqrt(), log(), pow()和三角函数等。

Tensor的自动广播机制和向量化

自动广播机制是指不同形状的tensor进行运算时,可自动扩展到较大张量的形状,在进行运算。广播机制的前提是任何一个张量至少有一个维度,且从尾部遍历tensor维度时,两者维度要么相等,要么其中一个是1或者不存在
向量化的作用是进行并行运算,避免元素级的运算,从而减少for循环带来的低效率。

Tensor的内存共享

Pytorch为了实现高校运算,提供了一些原地操作符,不经过复制,直接在原来的内存上进行计算。主要有以下三种内存共享的情况。
在这里插入图片描述
这里重点说一下常用的Tensor与numpy的ndarray之间的相互转换。

>>> a=torch.randn(1,2) 
>>> a 
tensor([[-0.3228, 1.2726]]) 
# Tensor转为NumPy 
>>> b=a.numpy() 
>>> b 
array([[-0.32281783, 1.2725701 ]], dtype=float32) 
# NumPy转为Tensor 
>>> c=torch.from_numpy(b) 
>>> c 
tensor([[-0.3228, 1.2726]]) 
#Tensor转为list 
>>> d=a.tolist() 
>>> d
[[-0.3228178322315216, 1.2725701332092285]]
Tensor数据类型转换
torch.set_default_tensor_type("torch.DoubleTensor")

对于Tensor之间的类型转换,可以通过type(new_type)、type_as()、int()等多种方式进行操作。其中使用较多的有type_as()函数方法,此方法不需要指定数据类型。

# 创建新Tensor,默认类型为torch.FloatTensor 
>>> a = torch.Tensor(2, 2) 
>>> a 
tensor(1.00000e-36 * [[-4.0315, 0.0000], [ 0.0700, 0.0000]]) 
# 使用int()、float()、double()等直接进行数据类型转换 
>>> b = a.double() 
>>> b 
tensor(1.00000e-36 * [[-4.0315, 0.0000], [ 0.0700, 0.0000]], dtype=torch.float64) 
# 使用type()函数 
>>> c = a.type(torch.DoubleTensor) 
>>> c 
tensor(1.00000e-36 * [[-4.0315, 0.0000], [ 0.0700, 0.0000]], dtype=torch.float64) 
# 使用type_as()函数 
>>> d = a.type_as(b) 
>>> d 
tensor(1.00000e-36 * [[-4.0315, 0.0000], [ 0.0700, 0.0000]], dtype=torch.float64)

总结

这篇文章主要回顾了一下Pytorch的基本数据----张量,张量的数据类型,张量的创建方式,不同数据类型的张量之间可以通过int(),type(), type_as()等互相转换,张量的维度查看以及维度、形状变换,张量的组合与分块,张量的索引方式,张量的排序与取极值以及元素级别的运算等,张量的自动广播机制和向量化,还有张量的高效工作方式----内存共享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值