目录

简介 ||张量 ||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.Tensortorch.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()

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 会重置它,因此相同的计算取决于 在大多数情况下,随机数应该提供相同的结果。random1random3random2random4

有关更多信息,请参阅有关 可重复性

张量形状

通常,当您对两个或多个张量执行操作时,它们 将需要具有相同的形状 - 即,具有相同数量的 维度和每个维度中相同数量的单元格。为此,我们 有以下方法:torch.*_like()

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 的张量。.shapex

在此之下,我们调用 、 、 和 方法。使用该属性,我们可以验证这些方法中的每一个都返回一个 相同的维度和范围。.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.int16aa11.

关于打印需要注意的另一件事是,与我们 left 作为默认值(32 位浮点),打印 tensor 还指定其 .adtypedtype

您可能还发现,我们从指定张量的 shape 作为一系列整数参数,将这些参数分组到 元。这并不是绝对必要的 - PyTorch 将采用一系列 initial、未标记的整数参数作为张量形状 - 但在添加 optional 参数,它可以使您的 intent 更具可读性。

设置 data type 的另一种方法是使用 method.在 cell 中,我们以通常的 道路。之后,我们通过转换为 32 位来创建 integer 替换为 method.请注意,包含所有相同的 值指定为 ,但被截断为整数。.to()bcb.to()cb

有关更多信息,请参阅数据类型文档

使用 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 之间的类似操作也与您的行为类似 直观地期望:

powers2 = twos ** torch.tensor([[1, 2], [3, 4]])
print(powers2)

fives = ones + fours
print(fives)

dozens = threes * fours
print(dozens)
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]]])

仔细查看上面每个张量的值:

  • 创建的乘法运算是 在 的每个“层”上广播。ba

  • 对于 ,该操作在 的每个层和行上广播 - 每个 3 元素列都相同。ca

  • 对于 ,我们将其切换过来 - 现在每一都是相同的, 跨层和列。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 * babc

不过,有时您可能希望就地更改张量 - 例如,如果您正在进行元素计算,则可以 丢弃中间值。为此,大多数数学函数都有一个 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])

对于算术运算,有些函数的行为类似:

a = torch.ones(2, 2)
b = torch.rand(2, 2)

print('Before:')
print(a)
print(b)
print('\nAfter adding:')
print(a.add_(b))
print(a)
print(b)
print('\nAfter multiplying')
print(b.mul_(b))
print(b)
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.Tensortorchtorch.sin()a.add_(b)

还有另一个选项可以将计算结果放在 现有的、分配的 Tensor 的 Tensor 中。我们见过的许多方法和函数 到目前为止 - 包括创建方法!- 有一个参数 允许您指定一个 Tensor 来接收输出。如果张量 是正确的形状,并且 ,这可能会在没有新内存的情况下发生 分配:outoutdtype

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 张量的标签,并且不会复制它。例如:

a = torch.ones(2, 2)
b = a

a[0][1] = 561  # we change a...
print(b)       # ...and b is also altered
tensor([[  1., 561.],
        [  1.,   1.]])

但是,如果您希望处理数据的单独副本怎么办?方法就在那里:clone()

a = torch.ones(2, 2)
b = a.clone()

assert b is not a      # different objects in memory...
print(torch.eq(a, b))  # ...but still with the same contents!

a[0][1] = 561          # a changes...
print(b)               # ...but b is still all ones
tensor([[True, True],
        [True, True]])
tensor([[1., 1.],
        [1., 1.]])

使用 ''clone()'' 时需要注意一件重要的事情。如果您的源张量启用了 autograd,则克隆也将启用。这将在 autograd 上的视频中更深入地介绍,但是如果 你想要轻量级的细节,继续。

在许多情况下,这将是您想要的。例如,如果您的模型 在其方法中有多个计算路径,并且原始张量及其克隆All对模型的输出有贡献,那么 要启用模型学习,您需要为两个张量开启 Autograd。 如果你的源张量启用了 autograd(如果 它是一组学习权重,或者从涉及 权重),那么您将获得所需的结果。forward()

另一方面,如果你在执行计算时 原始张量及其克隆项都不需要跟踪梯度,那么只要 源张量已关闭 autograd,一切顺利。

不过,还有第三种情况:假设您正在执行计算 在模型的函数中,其中 gradients 处于打开状态 默认情况下,所有内容,但您希望提取一些值 mid-stream 生成一些指标。在这种情况下,您不希望 用于跟踪梯度的源张量的克隆副本 - 性能为 在关闭 Autograd 的历史记录跟踪后进行了改进。为此,您可以 对源张量使用该方法:forward().detach()

a = torch.rand(2, 2, requires_grad=True) # turn on autograd
print(a)

b = a.clone()
print(b)

c = a.detach().clone()
print(c)

print(a)
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 的arequires_grad=True

  • 当我们打印 时,它会告诉我们属性 - 这意味着 autograd 和 computation 历史记录跟踪已打开。arequires_grad=True

  • 我们克隆并标记它。当我们打印时,我们可以看到 它正在跟踪其计算历史记录 - 它继承了 autograd 的设置,并添加到计算历史记录中。abba

  • 我们克隆到 ,但我们先调用 。acdetach()

  • 打印 ,我们看不到计算历史记录,也没有 。crequires_grad=True

该方法将张量与其计算分离 历史。它说,“做接下来发生的任何事情,就像 autograd 关闭一样。它 执行此操作而不更改 - 你可以看到,当我们在末尾再次打印时,它会保留其属性。detach()aarequires_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

a = torch.rand(3, 226, 226)
b = a.unsqueeze(0)

print(a.shape)
print(b.shape)
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 个元素的向量?

a = torch.rand(1, 20)
print(a.shape)
print(a)

b = a.squeeze(0)
print(b.shape)
print(b)

c = torch.rand(2, 2)
print(c.shape)

d = c.squeeze(0)
print(d.shape)
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()csqueeze()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 秒)

由 Sphinx-Gallery 生成的图库

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源