张量的概念在深度学习领域里,是可以使用GPU进行运算的多维数组。
张量一共有三种类型,分别是:整数型、浮点型和布尔型。其中整数型和浮点型张量的精度分别有8位、
16位、32位和64位。
获得一个张量的数据类型可以通过指令 Tensor.dtype
实现;
如果给这个表达式赋值,则将这个张量的数据类型改为目标类型。
PyTorch可以通过不同方式形态达到同样的目的。
约定:如果有写Tensor.xxx()
,那么这个Tensor
指的是一个具体的张量。
在Pytorch中,张量的很多运算既可以通过它自身的方法,也可以作为Pytorch中的一个低级函数来实现。
比如两个张量a
和b
相加,既可以写成torch.add(a,b)
,也可以写成a.add(b)
。
很多张量的属性既可以在创建时声明,也可以在之后任何时间声明。
比如把一个值为1
的 32 位整数张量赋给变量a
,可以在生成时一步到位,
a = torch.tensor(1, dtype=torch.int32)
也可以先生成a
的张量,然后再改变它的数据类型。
a = torch.tenor(1)
a.dtype = torch.int32
张量存储在连续的内存中,被torch.Storage
控制。
一个Storage是一个一维的包含数据类型的内存块。
一个 PyTorch 的Tensor
本质上是一个能够索引一个Storage的视角。
例如:你可以访问一个Tensor
的Storage:
>>> points = torch.tensor([[1.0, 4.0], [2.0, 1.0], [3.0, 5.0]])
>>> points.storage()
1.0
4.0
2.0
1.0
3.0
5.0
[torch.FloatStorage of size 6]
你不能对一个Storage进行二维索引。
因为Storage是一维的张量的存储,修改它同样会改变张量本身。
我们先定义一个张量:
>>> points = torch.tensor([[1.0, 4.0], [2.0, 1.0], [3.0, 5.0]])
>>> points
tensor([[1., 4.],
[2., 1.],
[3., 5.]])
获得一个张量的形状有四种方法:
Tensor.size()
>>> points.size()
torch.Size([3, 2])
Tensor.shape
>>> points.shape
torch.Size([3, 2])
可以看出,两者的区别在于 Tensor.shape
没有 ()
。
Tensor.numel()
查看 tensor 内的元素个数。
>>> points.numel()
6
Tensor.dim()
或 Tensor.ndim
查看张量的维数,即有几维。
查看张量内的相应元素与内存中第一个元素的相对位移。
>>> second_point = points[1]
>>> second_point.storage_offset()
2
因为 points
的 storage
是 1.0, 4.0, 2.0, 1.0, 3.0, 5.0
,second_point
距离这个张量在内存中的第一个元素的距离是 2。
指的是当索引增加 1 时,每个维度内需要跳过的元素个数,是一个元组。
>>> points.stride()
(2, 1)
Tensor.view()
,Tensor.reshape()
或 Tensor.resize()
括号里面的数值用小括号、中括号或者不用括号括起来都可以,维数自定,只要所有数字的乘积与原尺寸的乘积相同即可。Tensor.view()
和 Tensor.reshape()
的维度中可以有一个 -1,表示该维的长度由其他维度决定。Tensor.resize()
的维度中不能有 -1。
>>> points.reshape((1, 2, 1, -1))
tensor([[[[1., 4., 2.]],
[[1., 3., 5.]]]])
Tensor.t()
Tensor.T
或 Tensor.transpose(dim1, dim2)
Tensor.t()
只能转置维度小于等于 2 的张量,转置第 0、1 维。
>>> a = torch.arange(4).reshape(2, 2)
>>> a.t()
tensor([[0, 2],
[1, 3]])
Tensor.T
把整个张量的维度进行颠倒。
>>> new_points = points.reshape(1, 2, -1, 1, 3)
>>> new_points.shape
torch.Size([1, 2, 1, 1, 3])
>>> new = new_points.T
>>> new.shape
torch.Size([3, 1, 1, 2, 1])
而 Tensor.transpose(dim1, dim2)
可以转置任意两个维度。
>>> new2 = new_points.transpose(1, 4)
>>> new2.shape
torch.Size([1, 3, 1, 1, 2])
Tensor.squeeze()
所谓降维,就是消去元素个数为 1 的维度。可以指定想消去的维度,若该维度不能消去,则该命令无效,但是不报错。若没有指定维度,则消去所有长度为 1 的维度。
>>> new_points2 = new.squeeze(1)
>>> new_points2.shape # 降维成功
torch.Size([3, 1, 2, 1])
>>> new_points3 = new.squeeze(0)
>>> new_points3.shape # 降维失败
torch.Size([3, 1, 1, 2, 1])
>>> new_points4 = new_points.squeeze()
>>> new_points4.shape # 降维成功
torch.Size([2, 3])
Tensor.unsqueeze()
升维必须指定增加的维度,必须在张量的已有维度 (-dim-1, dim+1)
之间。相当于在两个维度之间“加塞”,后面的维度顺移一位。
>>> new_points4.unsqueeze(2).shape
torch.Size([2, 3, 1])
因为张量本质上是连续内存地址的索引,我们把一段内存赋值给一个变量,再赋值给另一个变量后,修改一个变量中的索引往往会改变另一个变量的相同索引:
>>> a = torch.tensor([1, 2, 3, 4])
>>> b = a
>>> b[1] = 10
>>> a, b
(tensor([ 1, 10, 3, 4]), tensor([ 1, 10, 3, 4]))
我们希望能够控制这种现象。
使用 Tensor.clone()
复制一段内存上的数据到另一段内存上,这两个张量相互独立。
>>> a = torch.tensor([1, 2, 3, 4])
>>> b = a.clone()
>>> b[1] = 10
>>> a, b
(tensor([ 1, 10, 3, 4]), tensor([ 1, 10, 3, 4]))
如果我们能够避免引入新张量,直接在原始张量上修改,不就可以避免混淆了吗?很多张量操作都支持原地(in-place)操作,只要在原始函数后面加上 _
就表明是原地修改。比如:
>>> a = torch.ones(2, 2) # 创建一个 2 x 2 的全 1 张量
>>> a
tensor([[1., 1.],
[1., 1.]])
>>> a.add_(1) # 原地每个元素加 1
>>> a
tensor([[2., 2.],
[2., 2.]])
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。