注意
单击此处下载完整的示例代码
简介 ||张量 ||Autograd ||建筑模型 ||TensorBoard 支持 ||训练模型 ||模型理解
PyTorch 张量简介¶
创建时间: 2021年11月30日 |上次更新时间: 2024-8-01 |上次验证: Nov 05, 2024
请跟随下面的视频或在 youtube 上观看。
张量是 PyTorch 中的核心数据抽象。这个互动的
Notebook 提供了对该类的深入介绍。torch.Tensor
首先,让我们导入 PyTorch 模块。我们还将添加 Python 的 math 模块来简化一些示例。
import torch
import math
创建 Tensor¶
创建张量的最简单方法是使用调用:torch.empty()
x = torch.empty(3, 4)
print(type(x))
print(x)
<class 'torch.Tensor'>
tensor([[-1.9601e+10, 3.0637e-41, 0.0000e+00, 2.3510e-38],
[ 1.5736e-27, 3.0630e-41, 1.0842e-19, 0.0000e+00],
[ 1.6411e-27, 3.0630e-41, 1.5773e-27, 3.0630e-41]])
让我们解开我们刚刚所做的:
我们使用众多工厂方法之一创建了一个张量 附加到模块。
torch
张量本身是二维的,有 3 行 4 列。
返回的对象类型是 ,它是一个 的别名 ;默认情况下,PyTorch 张量为 填充 32 位浮点数。(有关数据类型的更多信息 下面。
torch.Tensor
torch.FloatTensor
在打印 张肌。该调用为张量 但不使用任何值对其进行初始化 - 所以你看到的是 分配时内存中的任何内容。
torch.empty()
有关张量及其维数的简要说明,以及 术语:
您有时会看到一个称为向量的一维张量。
同样,二维张量通常称为矩阵。
任何具有两个以上维度的东西通常都只是 称为张量。
通常情况下,您需要使用一些
价值。常见情况是全 0、全 1 或随机值,该模块为所有这些提供了工厂方法:torch
zeros = torch.zeros(2, 3)
print(zeros)
ones = torch.ones(2, 3)
print(ones)
torch.manual_seed(1729)
random = torch.rand(2, 3)
print(random)
tensor([[0., 0., 0.],
[0., 0., 0.]])
tensor([[1., 1., 1.],
[1., 1., 1.]])
tensor([[0.3126, 0.3791, 0.3087],
[0.0736, 0.4216, 0.0691]])
工厂方法都按照你的期望做 - 我们有一个张量 全是 0,另一个全是 1,还有一个是随机值 介于 0 和 1 之间。
随机张量和种子¶
说到随机张量,您是否注意到对紧接在它的调用?初始化张量,
例如模型的学习权重,具有随机值很常见,但
有时 - 尤其是在研究环境中 - 你会想要
对结果的可重复性有一定的保证。手动设置
你的随机数生成器的种子是做到这一点的方法。让我们看看
更紧密地:torch.manual_seed()
torch.manual_seed(1729)
random1 = torch.rand(2, 3)
print(random1)
random2 = torch.rand(2, 3)
print(random2)
torch.manual_seed(1729)
random3 = torch.rand(2, 3)
print(random3)
random4 = torch.rand(2, 3)
print(random4)
tensor([[0.3126, 0.3791, 0.3087],
[0.0736, 0.4216, 0.0691]])
tensor([[0.2332, 0.4047, 0.2162],
[0.9927, 0.4128, 0.5938]])
tensor([[0.3126, 0.3791, 0.3087],
[0.0736, 0.4216, 0.0691]])
tensor([[0.2332, 0.4047, 0.2162],
[0.9927, 0.4128, 0.5938]])
您应该在上面看到的是 carry
相同的值,如 do 和 .手动设置
RNG 的 seed 会重置它,因此相同的计算取决于
在大多数情况下,随机数应该提供相同的结果。random1
random3
random2
random4
有关更多信息,请参阅有关 可重复性。
张量形状¶
通常,当您对两个或多个张量执行操作时,它们
将需要具有相同的形状 - 即,具有相同数量的
维度和每个维度中相同数量的单元格。为此,我们
有以下方法:torch.*_like()
x = torch.empty(2, 2, 3)
print(x.shape)
print(x)
empty_like_x = torch.empty_like(x)
print(empty_like_x.shape)
print(empty_like_x)
zeros_like_x = torch.zeros_like(x)
print(zeros_like_x.shape)
print(zeros_like_x)
ones_like_x = torch.ones_like(x)
print(ones_like_x.shape)
print(ones_like_x)
rand_like_x = torch.rand_like(x)
print(rand_like_x.shape)
print(rand_like_x)
torch.Size([2, 2, 3])
tensor([[[-7.0046e-35, 3.0630e-41, -7.0202e-35],
[ 3.0630e-41, 8.9683e-44, 0.0000e+00]],
[[ 1.1210e-43, 0.0000e+00, -9.1574e+14],
[ 3.0637e-41, -7.0381e-35, 3.0630e-41]]])
torch.Size([2, 2, 3])
tensor([[[-9.4520e+10, 3.0637e-41, 1.4013e-45],
[ 0.0000e+00, 1.4013e-45, 0.0000e+00]],
[[ 1.4013e-45, 0.0000e+00, 1.4013e-45],
[ 0.0000e+00, 1.4013e-45, 0.0000e+00]]])
torch.Size([2, 2, 3])
tensor([[[0., 0., 0.],
[0., 0., 0.]],
[[0., 0., 0.],
[0., 0., 0.]]])
torch.Size([2, 2, 3])
tensor([[[1., 1., 1.],
[1., 1., 1.]],
[[1., 1., 1.],
[1., 1., 1.]]])
torch.Size([2, 2, 3])
tensor([[[0.6128, 0.1519, 0.0453],
[0.5035, 0.9978, 0.3884]],
[[0.6929, 0.1703, 0.1384],
[0.4759, 0.7481, 0.0361]]])
上面代码单元中的第一个新内容是在张量上使用 property 。此属性包含
张量的每个维度 - 在我们的例子中,是一个三维
形状为 2 x 2 x 3 的张量。.shape
x
在此之下,我们调用 、 、 和 方法。使用该属性,我们可以验证这些方法中的每一个都返回一个
相同的维度和范围。.empty_like()
.zeros_like()
.ones_like()
.rand_like()
.shape
创建将覆盖的张量的最后一种方法是指定其数据 直接从 PyTorch 集合中获取:
some_constants = torch.tensor([[3.1415926, 2.71828], [1.61803, 0.0072897]])
print(some_constants)
some_integers = torch.tensor((2, 3, 5, 7, 11, 13, 17, 19))
print(some_integers)
more_integers = torch.tensor(((2, 4, 6), [3, 6, 9]))
print(more_integers)
tensor([[3.1416, 2.7183],
[1.6180, 0.0073]])
tensor([ 2, 3, 5, 7, 11, 13, 17, 19])
tensor([[2, 4, 6],
[3, 6, 9]])
使用 是创建
tensor (如果 Python 元组或列表中已有数据)。如图所示
上面,嵌套集合将产生一个多维
张肌。torch.tensor()
注意
torch.tensor()
创建数据的副本。
张量数据类型¶
可以通过以下几种方式设置张量的数据类型:
a = torch.ones((2, 3), dtype=torch.int16)
print(a)
b = torch.rand((2, 3), dtype=torch.float64) * 20.
print(b)
c = b.to(torch.int32)
print(c)
tensor([[1, 1, 1],
[1, 1, 1]], dtype=torch.int16)
tensor([[ 0.9956, 1.4148, 5.8364],
[11.2406, 11.2083, 11.6692]], dtype=torch.float64)
tensor([[ 0, 1, 5],
[11, 11, 11]], dtype=torch.int32)
设置张量底层数据类型的最简单方法是使用
optional 参数。在上面单元格的第一行中,
我们为张量 设置。当我们打印时,
我们可以看到它充满了 Python 的微妙
提示这是一个整数类型而不是浮点类型。dtype=torch.int16
a
a
1
1.
关于打印需要注意的另一件事是,与我们
left 作为默认值(32 位浮点),打印
tensor 还指定其 .a
dtype
dtype
您可能还发现,我们从指定张量的 shape 作为一系列整数参数,将这些参数分组到 元。这并不是绝对必要的 - PyTorch 将采用一系列 initial、未标记的整数参数作为张量形状 - 但在添加 optional 参数,它可以使您的 intent 更具可读性。
设置 data type 的另一种方法是使用 method.在
cell 中,我们以通常的
道路。之后,我们通过转换为 32 位来创建
integer 替换为 method.请注意,包含所有相同的
值指定为 ,但被截断为整数。.to()
b
c
b
.to()
c
b
有关更多信息,请参阅数据类型文档。
使用 PyTorch 张量的数学和逻辑¶
现在,您已经了解了创建 Tensor 的一些方法...你能做什么 和他们在一起?
让我们先看看基本算术,以及张量如何与 简单标量:
ones = torch.zeros(2, 2) + 1
twos = torch.ones(2, 2) * 2
threes = (torch.ones(2, 2) * 7 - 1) / 2
fours = twos ** 2
sqrt2s = twos ** 0.5
print(ones)
print(twos)
print(threes)
print(fours)
print(sqrt2s)
tensor([[1., 1.],
[1., 1.]])
tensor([[2., 2.],
[2., 2.]])
tensor([[3., 3.],
[3., 3.]])
tensor([[4., 4.],
[4., 4.]])
tensor([[1.4142, 1.4142],
[1.4142, 1.4142]])
正如你在上面看到的,张量和标量之间的算术运算,
例如加法、减法、乘法、除法和
幂分布在张量的每个元素上。因为
这种操作的输出将是一个张量,你可以将它们链接起来
与通常的运算符优先级规则一起,如
我们创建 .threes
两个 Tensor 之间的类似操作也与您的行为类似 直观地期望:
tensor([[ 2., 4.],
[ 8., 16.]])
tensor([[5., 5.],
[5., 5.]])
tensor([[12., 12.],
[12., 12.]])
这里需要注意的是,前面代码中的所有张量 细胞的形状相同。当我们尝试执行 如果形状不同,则对张量进行二进制运算?
注意
以下单元格引发运行时错误。这是有意为之的。
a = torch.rand(2, 3)
b = torch.rand(3, 2)
print(a * b)
在一般情况下,您无法对不同形状的张量进行操作 这样,即使在像上面的单元格这样的情况下,张量有一个 相同数量的元素。
简述:Tensor 广播¶
注意
如果您熟悉 NumPy 中的广播语义 ndarrays,您会发现相同的规则在这里适用。
相同形状规则的例外是张量广播。这是 一个例子:
rand = torch.rand(2, 4)
doubled = rand * (torch.ones(1, 4) * 2)
print(rand)
print(doubled)
tensor([[0.6146, 0.5999, 0.5013, 0.9397],
[0.8656, 0.5207, 0.6865, 0.3614]])
tensor([[1.2291, 1.1998, 1.0026, 1.8793],
[1.7312, 1.0413, 1.3730, 0.7228]])
这里的诀窍是什么?我们是如何将 2x4 张量乘以 1x4 张量?
广播是一种在具有 它们的形状相似。在上面的示例中,单行 四列张量乘以两行的两行 四列张量。
这是深度学习中的一项重要操作。常见的例子是 将学习权重的张量乘以一批输入张量, 将操作分别应用于 Batch 中的每个实例,以及 返回一个相同形状的张量 - 就像我们的 (2, 4) * (1, 4) 一样 上面的示例返回了形状为 (2, 4) 的张量。
广播规则是:
每个张量必须至少有一个维度 - 没有空张量。
比较两个张量的维度大小,从最后一个到 第一:
每个维度必须相等,或者
其中一个维度的大小必须为 1,或者
维度不存在于其中一个张量中
当然,相同形状的张量很容易“可广播”,如 你之前看到了。
以下是一些遵守上述规则和 允许广播:
a = torch.ones(4, 3, 2)
b = a * torch.rand( 3, 2) # 3rd & 2nd dims identical to a, dim 1 absent
print(b)
c = a * torch.rand( 3, 1) # 3rd dim = 1, 2nd dim identical to a
print(c)
d = a * torch.rand( 1, 2) # 3rd dim identical to a, 2nd dim = 1
print(d)
tensor([[[0.6493, 0.2633],
[0.4762, 0.0548],
[0.2024, 0.5731]],
[[0.6493, 0.2633],
[0.4762, 0.0548],
[0.2024, 0.5731]],
[[0.6493, 0.2633],
[0.4762, 0.0548],
[0.2024, 0.5731]],
[[0.6493, 0.2633],
[0.4762, 0.0548],
[0.2024, 0.5731]]])
tensor([[[0.7191, 0.7191],
[0.4067, 0.4067],
[0.7301, 0.7301]],
[[0.7191, 0.7191],
[0.4067, 0.4067],
[0.7301, 0.7301]],
[[0.7191, 0.7191],
[0.4067, 0.4067],
[0.7301, 0.7301]],
[[0.7191, 0.7191],
[0.4067, 0.4067],
[0.7301, 0.7301]]])
tensor([[[0.6276, 0.7357],
[0.6276, 0.7357],
[0.6276, 0.7357]],
[[0.6276, 0.7357],
[0.6276, 0.7357],
[0.6276, 0.7357]],
[[0.6276, 0.7357],
[0.6276, 0.7357],
[0.6276, 0.7357]],
[[0.6276, 0.7357],
[0.6276, 0.7357],
[0.6276, 0.7357]]])
仔细查看上面每个张量的值:
创建的乘法运算是 在 的每个“层”上广播。
b
a
对于 ,该操作在 的每个层和行上广播 - 每个 3 元素列都相同。
c
a
对于 ,我们将其切换过来 - 现在每一行都是相同的, 跨层和列。
d
有关广播的更多信息,请参阅 PyTorch 有关该主题的文档。
以下是一些将失败的广播尝试示例:
注意
以下单元格引发运行时错误。这是有意为之的。
a = torch.ones(4, 3, 2)
b = a * torch.rand(4, 3) # dimensions must match last-to-first
c = a * torch.rand( 2, 3) # both 3rd & 2nd dims different
d = a * torch.rand((0, )) # can't broadcast with an empty tensor
使用 Tensor 进行更多数学运算¶
PyTorch 张量有 300 多项操作可供执行 在他们身上。
以下是一些主要操作类别的一小部分示例:
# common functions
a = torch.rand(2, 4) * 2 - 1
print('Common functions:')
print(torch.abs(a))
print(torch.ceil(a))
print(torch.floor(a))
print(torch.clamp(a, -0.5, 0.5))
# trigonometric functions and their inverses
angles = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
sines = torch.sin(angles)
inverses = torch.asin(sines)
print('\nSine and arcsine:')
print(angles)
print(sines)
print(inverses)
# bitwise operations
print('\nBitwise XOR:')
b = torch.tensor([1, 5, 11])
c = torch.tensor([2, 7, 10])
print(torch.bitwise_xor(b, c))
# comparisons:
print('\nBroadcasted, element-wise equality comparison:')
d = torch.tensor([[1., 2.], [3., 4.]])
e = torch.ones(1, 2) # many comparison ops support broadcasting!
print(torch.eq(d, e)) # returns a tensor of type bool
# reductions:
print('\nReduction ops:')
print(torch.max(d)) # returns a single-element tensor
print(torch.max(d).item()) # extracts the value from the returned tensor
print(torch.mean(d)) # average
print(torch.std(d)) # standard deviation
print(torch.prod(d)) # product of all numbers
print(torch.unique(torch.tensor([1, 2, 1, 2, 1, 2]))) # filter unique elements
# vector and linear algebra operations
v1 = torch.tensor([1., 0., 0.]) # x unit vector
v2 = torch.tensor([0., 1., 0.]) # y unit vector
m1 = torch.rand(2, 2) # random matrix
m2 = torch.tensor([[3., 0.], [0., 3.]]) # three times identity matrix
print('\nVectors & Matrices:')
print(torch.linalg.cross(v2, v1)) # negative of z unit vector (v1 x v2 == -v2 x v1)
print(m1)
m3 = torch.linalg.matmul(m1, m2)
print(m3) # 3 times m1
print(torch.linalg.svd(m3)) # singular value decomposition
Common functions:
tensor([[0.9238, 0.5724, 0.0791, 0.2629],
[0.1986, 0.4439, 0.6434, 0.4776]])
tensor([[-0., -0., 1., -0.],
[-0., 1., 1., -0.]])
tensor([[-1., -1., 0., -1.],
[-1., 0., 0., -1.]])
tensor([[-0.5000, -0.5000, 0.0791, -0.2629],
[-0.1986, 0.4439, 0.5000, -0.4776]])
Sine and arcsine:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7854, 1.5708, 0.7854])
Bitwise XOR:
tensor([3, 2, 1])
Broadcasted, element-wise equality comparison:
tensor([[ True, False],
[False, False]])
Reduction ops:
tensor(4.)
4.0
tensor(2.5000)
tensor(1.2910)
tensor(24.)
tensor([1, 2])
Vectors & Matrices:
tensor([ 0., 0., -1.])
tensor([[0.7375, 0.8328],
[0.8444, 0.2941]])
tensor([[2.2125, 2.4985],
[2.5332, 0.8822]])
torch.return_types.linalg_svd(
U=tensor([[-0.7889, -0.6145],
[-0.6145, 0.7889]]),
S=tensor([4.1498, 1.0548]),
Vh=tensor([[-0.7957, -0.6056],
[ 0.6056, -0.7957]]))
这是一个较小的操作示例。有关的更多详细信息和完整清单 math 函数,请查看文档。 有关线性代数运算的更多详细信息和完整清单,请查阅 请看这个文档。
就地更改张量¶
对张量的大多数二进制操作将返回第三个新张量。什么时候
我们说 (where and are Tensors),新的 Tensor 将占据一个与其他 Tensor 不同的内存区域。c = a * b
a
b
c
不过,有时您可能希望就地更改张量 -
例如,如果您正在进行元素计算,则可以
丢弃中间值。为此,大多数数学函数都有一个
version 中附加了下划线 (),该 () 将更改
地方。_
例如:
a = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
print('a:')
print(a)
print(torch.sin(a)) # this operation creates a new tensor in memory
print(a) # a has not changed
b = torch.tensor([0, math.pi / 4, math.pi / 2, 3 * math.pi / 4])
print('\nb:')
print(b)
print(torch.sin_(b)) # note the underscore
print(b) # b has changed
a:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7854, 1.5708, 2.3562])
b:
tensor([0.0000, 0.7854, 1.5708, 2.3562])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
tensor([0.0000, 0.7071, 1.0000, 0.7071])
对于算术运算,有些函数的行为类似:
Before:
tensor([[1., 1.],
[1., 1.]])
tensor([[0.3788, 0.4567],
[0.0649, 0.6677]])
After adding:
tensor([[1.3788, 1.4567],
[1.0649, 1.6677]])
tensor([[1.3788, 1.4567],
[1.0649, 1.6677]])
tensor([[0.3788, 0.4567],
[0.0649, 0.6677]])
After multiplying
tensor([[0.1435, 0.2086],
[0.0042, 0.4459]])
tensor([[0.1435, 0.2086],
[0.0042, 0.4459]])
请注意,这些就地算术函数是对象上的方法,而不是像许多函数那样附加到模块
其他函数(例如 )。从 中可以看到,调用张量是 中更改的张量
地方。torch.Tensor
torch
torch.sin()
a.add_(b)
还有另一个选项可以将计算结果放在
现有的、分配的 Tensor 的 Tensor 中。我们见过的许多方法和函数
到目前为止 - 包括创建方法!- 有一个参数
允许您指定一个 Tensor 来接收输出。如果张量
是正确的形状,并且 ,这可能会在没有新内存的情况下发生
分配:out
out
dtype
a = torch.rand(2, 2)
b = torch.rand(2, 2)
c = torch.zeros(2, 2)
old_id = id(c)
print(c)
d = torch.matmul(a, b, out=c)
print(c) # contents of c have changed
assert c is d # test c & d are same object, not just containing equal values
assert id(c) == old_id # make sure that our new c is the same object as the old one
torch.rand(2, 2, out=c) # works for creation too!
print(c) # c has changed again
assert id(c) == old_id # still the same object!
tensor([[0., 0.],
[0., 0.]])
tensor([[0.3653, 0.8699],
[0.2364, 0.3604]])
tensor([[0.0776, 0.4004],
[0.9877, 0.0352]])
复制张量¶
与 Python 中的任何对象一样,将张量分配给变量会使 variable 张量的标签,并且不会复制它。例如:
tensor([[ 1., 561.],
[ 1., 1.]])
但是,如果您希望处理数据的单独副本怎么办?方法就在那里:clone()
tensor([[True, True],
[True, True]])
tensor([[1., 1.],
[1., 1.]])
使用 ''clone()'' 时需要注意一件重要的事情。如果您的源张量启用了 autograd,则克隆也将启用。这将在 autograd 上的视频中更深入地介绍,但是如果 你想要轻量级的细节,继续。
在许多情况下,这将是您想要的。例如,如果您的模型
在其方法中有多个计算路径,并且原始张量及其克隆All对模型的输出有贡献,那么
要启用模型学习,您需要为两个张量开启 Autograd。
如果你的源张量启用了 autograd(如果
它是一组学习权重,或者从涉及
权重),那么您将获得所需的结果。forward()
另一方面,如果你在执行计算时, 原始张量及其克隆项都不需要跟踪梯度,那么只要 源张量已关闭 autograd,一切顺利。
不过,还有第三种情况:假设您正在执行计算
在模型的函数中,其中 gradients 处于打开状态
默认情况下,所有内容,但您希望提取一些值
mid-stream 生成一些指标。在这种情况下,您不希望
用于跟踪梯度的源张量的克隆副本 - 性能为
在关闭 Autograd 的历史记录跟踪后进行了改进。为此,您可以
对源张量使用该方法:forward()
.detach()
tensor([[0.0905, 0.4485],
[0.8740, 0.2526]], requires_grad=True)
tensor([[0.0905, 0.4485],
[0.8740, 0.2526]], grad_fn=<CloneBackward0>)
tensor([[0.0905, 0.4485],
[0.8740, 0.2526]])
tensor([[0.0905, 0.4485],
[0.8740, 0.2526]], requires_grad=True)
这里发生了什么?
我们在 on-on 的情况下进行创建。我们还没有 已经覆盖了这个可选参数,但是会在 autograd 的
a
requires_grad=True
当我们打印 时,它会告诉我们属性 - 这意味着 autograd 和 computation 历史记录跟踪已打开。
a
requires_grad=True
我们克隆并标记它。当我们打印时,我们可以看到 它正在跟踪其计算历史记录 - 它继承了 autograd 的设置,并添加到计算历史记录中。
a
b
b
a
我们克隆到 ,但我们先调用 。
a
c
detach()
打印 ,我们看不到计算历史记录,也没有 。
c
requires_grad=True
该方法将张量与其计算分离
历史。它说,“做接下来发生的任何事情,就像 autograd 关闭一样。它
执行此操作而不更改 - 你可以看到,当我们在末尾再次打印时,它会保留其属性。detach()
a
a
requires_grad=True
迁移到 GPU¶
PyTorch 的主要优势之一是它在 兼容 CUDA 的 Nvidia GPU。(“CUDA”代表计算统一设备 架构,这是 Nvidia 的并行计算平台。所以 到目前为止,我们所做的一切都在 CPU 上。我们如何实现更快的 硬件?
首先,我们应该使用 method.is_available()
注意
如果您没有兼容 CUDA 的 GPU 和 CUDA 驱动程序 installed 时,此部分中的可执行单元格将不会执行任何 GPU 相关代码。
if torch.cuda.is_available():
print('We have a GPU!')
else:
print('Sorry, CPU only.')
We have a GPU!
一旦我们确定一个或多个 GPU 可用,我们就需要将 我们的数据在 GPU 可以看到的地方。您的 CPU 执行计算 在计算机 RAM 中的数据上。您的 GPU 已连接专用内存 到它。每当要在设备上执行计算时,都必须 将该计算所需的所有数据移动到 可访问的内存中 那个设备。(通俗地说,“将数据移动到 GPU“ 缩写为 ”moving the data to the GPU”。
有多种方法可以将数据传输到目标设备上。你 可以在创建时执行此操作:
if torch.cuda.is_available():
gpu_rand = torch.rand(2, 2, device='cuda')
print(gpu_rand)
else:
print('Sorry, CPU only.')
tensor([[0.3344, 0.2640],
[0.2119, 0.0582]], device='cuda:0')
默认情况下,新的 Tensor 是在 CPU 上创建的,因此我们必须指定
当我们想使用 optional 参数在 GPU 上创建张量时。当我们打印新张量 PyTorch 时,您可以看到
通知我们在哪个设备上(如果它不在 CPU 上)。device
您可以使用 查询 GPU 的数量。如果
您有多个 GPU,您可以通过索引指定它们: , , etc.torch.cuda.device_count()
device='cuda:0'
device='cuda:1'
作为编码实践,使用 string 指定我们的设备 常量非常脆弱。在理想情况下,您的代码将执行 无论您使用的是 CPU 还是 GPU 硬件,都能稳定地运行。您可以通过以下方式执行此操作 创建可传递给张量的设备句柄,而不是 字符串:
if torch.cuda.is_available():
my_device = torch.device('cuda')
else:
my_device = torch.device('cpu')
print('Device: {}'.format(my_device))
x = torch.rand(2, 2, device=my_device)
print(x)
Device: cuda
tensor([[0.0024, 0.6778],
[0.2441, 0.6812]], device='cuda:0')
如果你有一个现有的张量存在于一台设备上,你可以将其移动到
另一个与 method.以下代码行创建一个
tensor 的 CPU 上,并将其移动到您在 CPU 上获取的任何设备句柄
前一个单元格。to()
y = torch.rand(2, 2)
y = y.to(my_device)
重要的是要知道,为了进行涉及两个或 more tensors,则所有 tensor 必须位于同一设备上。这 以下代码将引发运行时错误,无论您是否 有可用的 GPU 设备:
x = torch.rand(2, 2)
y = torch.rand(2, 2, device='gpu')
z = x + y # exception will be thrown
操作张量形状¶
有时,您需要更改张量的形状。下面,我们将 看看一些常见的情况,以及如何处理它们。
更改维度数¶
可能需要更改维度数的一种情况是 将单个 input 实例传递给模型。PyTorch 模型 通常需要批量输入。
例如,假设有一个模型,该模型适用于 3 x 226 x 226 图像 -
具有 3 个颜色通道的 226 像素正方形。加载和转换时
it,您将获得一个 shape 为 的张量。不过,您的模型
需要 shape 的输入,其中 是
批次中的图像数。那么你如何制作一批 1 呢?(3, 226, 226)
(N, 3, 226, 226)
N
torch.Size([3, 226, 226])
torch.Size([1, 3, 226, 226])
该方法添加范围为 1 的维度。 将其添加为新的第 0 个维度 - 现在您有一个
一批一瓶!unsqueeze()
unsqueeze(0)
那么,如果这是不挤压的呢?我们所说的挤压是什么意思?我们正在采取 区 1 的任何维度都不会更改这一事实的优势 张量中的元素数。
c = torch.rand(1, 1, 1, 1, 1)
print(c)
tensor([[[[[0.2347]]]]])
继续上面的示例,假设模型的输出是
每个输入的 20 个元素向量。然后,您希望输出为
have shape ,其中 是
input batch 的 intent 值。这意味着对于我们的单输入批处理,我们将获得一个
shape 的输出 。(N, 20)
N
(1, 20)
如果您想对该输出进行一些非批处理计算,该怎么办 - 它只是期望一个 20 个元素的向量?
torch.Size([1, 20])
tensor([[0.1899, 0.4067, 0.1519, 0.1506, 0.9585, 0.7756, 0.8973, 0.4929, 0.2367,
0.8194, 0.4509, 0.2690, 0.8381, 0.8207, 0.6818, 0.5057, 0.9335, 0.9769,
0.2792, 0.3277]])
torch.Size([20])
tensor([0.1899, 0.4067, 0.1519, 0.1506, 0.9585, 0.7756, 0.8973, 0.4929, 0.2367,
0.8194, 0.4509, 0.2690, 0.8381, 0.8207, 0.6818, 0.5057, 0.9335, 0.9769,
0.2792, 0.3277])
torch.Size([2, 2])
torch.Size([2, 2])
从形状中可以看出,我们的二维张量现在是
一维,如果你仔细观察上面单元格的输出
您将看到打印显示一组“额外”的方括号,因为具有额外的尺寸。a
[]
您只能对范围 1 的尺寸进行标注。请参阅上面我们
尝试在 中挤压大小为 2 的维度,并得到相同的
形状。调用 和 can
仅作用于范围 1 的维度,因为否则将更改
张量中的元素数。squeeze()
c
squeeze()
unsqueeze()
您可能使用的另一个地方是简化广播。
回想一下上面的示例,我们有以下代码:unsqueeze()
a = torch.ones(4, 3, 2)
c = a * torch.rand( 3, 1) # 3rd dim = 1, 2nd dim identical to a
print(c)
这样做的最终效果是在维度 0 上广播操作
和 2,导致随机的 3 x 1 张量按元素乘以
中的每个 3 元素列。a
如果随机向量只是 3 元素向量呢?我们会失去
进行广播的能力,因为最终维度不会
根据广播规则进行匹配。 来到
救援:unsqueeze()
a = torch.ones(4, 3, 2)
b = torch.rand( 3) # trying to multiply a * b will give a runtime error
c = b.unsqueeze(1) # change to a 2-dimensional tensor, adding new dim at the end
print(c.shape)
print(a * c) # broadcasting works again!
torch.Size([3, 1])
tensor([[[0.1891, 0.1891],
[0.3952, 0.3952],
[0.9176, 0.9176]],
[[0.1891, 0.1891],
[0.3952, 0.3952],
[0.9176, 0.9176]],
[[0.1891, 0.1891],
[0.3952, 0.3952],
[0.9176, 0.9176]],
[[0.1891, 0.1891],
[0.3952, 0.3952],
[0.9176, 0.9176]]])
和 方法也具有
版本和 :squeeze()
unsqueeze()
squeeze_()
unsqueeze_()
batch_me = torch.rand(3, 226, 226)
print(batch_me.shape)
batch_me.unsqueeze_(0)
print(batch_me.shape)
torch.Size([3, 226, 226])
torch.Size([1, 3, 226, 226])
有时你会想要更彻底地改变张量的形状,
同时仍保留元素的数量及其内容。一
发生这种情况的情况是在卷积
层和模型的线性层 - 这在
图像分类模型。卷积核将产生一个输出
shape 特征 x 宽度 x 高度的张量,但以下线性
layer 需要一个 1 维输入。 将为您执行此操作,
前提是您请求的维度产生相同数量的
元素作为输入张量具有:reshape()
output3d = torch.rand(6, 20, 20)
print(output3d.shape)
input1d = output3d.reshape(6 * 20 * 20)
print(input1d.shape)
# can also call it as a method on the torch module:
print(torch.reshape(output3d, (6 * 20 * 20,)).shape)
torch.Size([6, 20, 20])
torch.Size([2400])
torch.Size([2400])
注意
单元格最后一行中的参数
这是因为 PyTorch 在指定
Tensor 形状 - 但是当形状是方法的第一个参数时,它会
让我们作弊,只使用一系列整数。在这里,我们必须添加
括号和逗号,以使方法相信这实际上是一个
单元素元组。(6 * 20 * 20,)
如果可以,将返回一个关于张量的视图
changed - 即,一个单独的 Tensor 对象查看相同的
内存的底层区域。这一点很重要:这意味着任何变化
made 的值将反映在该张量的视图中,
除非你它。reshape()
clone()
在一些情况下,超出了本介绍的范围,其中必须返回一个携带数据副本的张量。为
有关更多信息,请参阅文档。reshape()
NumPy 桥¶
在上面关于广播的部分中,提到了 PyTorch 的 广播语义与 NumPy 的 - 但亲属关系 PyTorch 和 NumPy 之间的差距甚至比这更深入。
如果您现有的 ML 或科学代码数据存储在 NumPy 中 ndarrays,您可能希望表示与 PyTorch 张量相同的数据, 是利用 PyTorch 的 GPU 加速,还是利用其 用于构建 ML 模型的高效抽象。切换简单 在 ndarrays 和 PyTorch 张量之间:
import numpy as np
numpy_array = np.ones((2, 3))
print(numpy_array)
pytorch_tensor = torch.from_numpy(numpy_array)
print(pytorch_tensor)
[[1. 1. 1.]
[1. 1. 1.]]
tensor([[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
PyTorch 创建相同形状并包含相同数据的张量 作为 NumPy 数组,甚至保留了 NumPy 的默认 64 位浮点数 数据类型。
转换可以很容易地走向相反的方向:
pytorch_rand = torch.rand(2, 3)
print(pytorch_rand)
numpy_rand = pytorch_rand.numpy()
print(numpy_rand)
tensor([[0.8716, 0.2459, 0.3499],
[0.2853, 0.9091, 0.5695]])
[[0.87163675 0.2458961 0.34993553]
[0.2853077 0.90905803 0.5695162 ]]
重要的是要知道这些转换的对象使用相同的 底层内存作为其源对象,这意味着 将更改为一个 反映在另一个
numpy_array[1, 1] = 23
print(pytorch_tensor)
pytorch_rand[1, 1] = 17
print(numpy_rand)
tensor([[ 1., 1., 1.],
[ 1., 23., 1.]], dtype=torch.float64)
[[ 0.87163675 0.2458961 0.34993553]
[ 0.2853077 17. 0.5695162 ]]
脚本总运行时间:(0 分 0.161 秒)