PyTorch 是一个深度学习库。就像其他一些深度学习库一样,它对称为 **张量** 的数值数组进行操作。简单来说,张量就是多维数组。当我们处理张量时,有些操作被非常频繁地使用。在 PyTorch 中,有一些函数专门用于处理张量。
下面,我们将简要概述 PyTorch 在张量方面提供的功能以及我们如何使用它们。完成本教程后,您将了解:
- 如何创建和操作 PyTorch 张量
- PyTorch 的张量语法类似于 NumPy
- 您可以使用 PyTorch 来操作张量的常用函数
通过我的《用PyTorch进行深度学习》一书来启动你的项目。它提供了包含可用代码的自学教程。
让我们开始吧。

在 PyTorch 中操作张量。图片由 Big Dodzy 提供。部分权利保留。
概述
本教程分为四个部分;它们是:
- 创建张量
- 检查张量
- 操作张量
- 张量函数
创建张量
如果您熟悉 NumPy,您应该会记得有多种创建数组的方法。在 PyTorch 中创建张量也是如此。创建特定常数矩阵的最简单方法如下所示:
$$
\begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6
\end{bmatrix}
$$
是使用
1 2 3 |
import torch a = torch.tensor([[1,2,3], [4,5,6]], dtype=torch.int32) print(a) |
输出结果为:
1 2 |
tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.int32) |
dtype
参数指定张量中值的数据类型。它是可选的。您也可以提供 NumPy 数组的值并将其转换为 PyTorch 张量。
通常,您会出于特定目的创建张量。例如,如果您想在 -1 和 1 之间得到十个均匀分布的值,您可以使用 linspace()
函数。
1 2 |
a = torch.linspace(-1, 1, 10) print(a) |
输出结果为:
1 2 |
tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556, 0.7778, 1.0000]) |
但是,如果您想要一个随机值的张量(这在测试函数时非常有用),您可以如下创建:
1 2 |
a = torch.rand(3,4) print(a) |
它会打印,例如:
1 2 3 |
tensor([[0.4252, 0.1029, 0.9858, 0.7502], [0.1993, 0.6412, 0.2424, 0.6451], [0.7878, 0.7615, 0.9170, 0.8534]]) |
这个生成的张量是 $3\times 4$ 维的,每个值都在 0 和 1 之间均匀分布。如果您想让值呈正态分布,只需将函数更改为 randn()
。
1 |
a = torch.randn(3,4) |
如果您想让随机值为整数,例如在 3 到 10 之间,您可以使用 randint()
函数。
1 2 |
a = torch.randint(3, 10, size=(3,4)) print(a) |
这会生成,例如:
1 2 3 |
tensor([[4, 5, 7, 9], [3, 8, 8, 9], [4, 7, 7, 6]]) |
值在 $3 \le x < 10$ 的范围内。默认情况下,下限为零,因此如果您想让值在 $0 \le x < 10$,您可以使用:
1 |
a = torch.randint(10, size=(3,4)) |
其他常用张量是零张量和所有值都相同的张量。要创建一个零张量(例如,维度为 $2\times 3\times 4$),您可以使用:
1 2 |
a = torch.zeros(2, 3, 4) print(a) |
输出结果为:
1 2 3 4 5 6 7 |
tensor([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]]) |
而要创建一个所有值都为 5 的张量,您可以使用:
1 2 |
a = torch.full((2,3,4), 5) print(a) |
输出结果为:
1 2 3 4 5 6 7 |
tensor([[[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]], [[5, 5, 5, 5], [5, 5, 5, 5], [5, 5, 5, 5]]]) |
但是,如果您希望所有值都为一,有一个更简单的函数:
1 |
a = torch.ones(2,3,4) |
最后,如果您想要一个单位矩阵,您可以使用 diag()
或 eye()
来获取。
1 2 |
a = torch.eye(4) print(a) |
输出结果为:
1 2 3 4 |
tensor([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0., 0., 1.]]) |
想开始使用PyTorch进行深度学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
检查张量
一旦您有了张量并想了解更多关于它的信息,您可以通过 print()
简单地将其打印到屏幕上。但如果张量太大,通过检查其形状来显示其维度会更容易。
1 2 3 |
a = torch.zeros(2, 3, 4) print(a.shape) print(a.size()) |
输出结果为:
1 2 |
torch.Size([2, 3, 4]) torch.Size([2, 3, 4]) |
张量的形状可以通过 shape
属性或 size()
函数访问。如果您想查看有多少个维度(即,$2\times 3\times 4$ 是 3,而 $3\times 4$ 是 2),您可以读取 ndim
属性。
1 |
print(a.ndim) |
这将给出“3”。如果您使用 len()
来检查张量,它只会给出第一个维度的尺寸,例如:
1 2 |
a = torch.zeros(2, 3, 4) print(len(a)) |
输出结果为:
1 |
2 |
您想要了解的张量的另一个属性是其数据类型。通常,您在深度学习中使用浮点数,但有时张量应该是整数(例如,图像中的像素值)。要检查数据类型,您可以读取 dtype
属性。
1 |
print(a.dtype) |
输出结果为:
1 |
torch.float32 |
如果您想更改数据类型,您可以创建一个具有新类型的新张量。
1 2 3 |
b = a.type(torch.int32) print(a.dtype) print(b.dtype) |
上面的输出是:
1 2 |
torch.float32 torch.int32 |
操作张量
深度学习中张量的一个常见操作是更改张量形状。例如,您可能想将一个二维张量转换为一维,或者向张量添加一个虚拟维度。您可能还想从较大的张量中提取一个子张量。
例如,您可以创建如下张量:
1 2 |
a = torch.randn(3,4,5) print(a) |
如果您得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
tensor([[[-1.1271e-01, -7.1124e-01, 1.1335e+00, -8.5644e-01, -1.4191e+00], [-1.9065e+00, -6.8386e-02, 5.8727e-01, 6.5890e-03, -2.6947e-01], [ 6.3194e-01, -7.7426e-01, 1.6546e+00, 1.2647e-01, -1.0944e+00], [ 3.7819e-01, -8.8670e-01, 5.3772e-01, 1.4985e+00, 5.8396e-01]], [[ 1.8704e+00, 2.0914e+00, -9.1604e-01, 1.2317e+00, -1.5722e-01], [ 2.4689e-01, -2.3157e-01, -3.3033e-01, 1.4021e+00, -6.9540e-01], [ 3.0298e-01, -1.4936e-01, -6.8863e-01, 1.6977e-01, 2.4682e+00], [-8.1375e-01, 4.8489e-01, -1.2024e+00, -4.9771e-01, 1.1728e-01]], [[-1.6011e+00, 1.5686e-03, -1.0560e-01, -1.2938e+00, 5.3077e-01], [-9.7636e-01, -9.1854e-01, -1.0002e+00, 1.1852e+00, 1.0328e+00], [ 9.6664e-01, 5.3752e-01, -3.1340e-02, -6.7852e-02, -7.2317e-01], [-5.5263e-01, 9.4754e-01, -5.4503e-01, 6.3850e-02, 1.2879e+00]]]) |
它允许您使用与 NumPy 相同的语法进行切片。
1 |
print(a[1]) |
这将是:
1 2 3 4 |
tensor([[ 1.8704, 2.0914, -0.9160, 1.2317, -0.1572], [ 0.2469, -0.2316, -0.3303, 1.4021, -0.6954], [ 0.3030, -0.1494, -0.6886, 0.1698, 2.4682], [-0.8137, 0.4849, -1.2024, -0.4977, 0.1173]]) |
或者如果您使用:
1 |
print(a[1:, 2:4]) |
它将是:
1 2 3 4 5 |
tensor([[[ 0.3030, -0.1494, -0.6886, 0.1698, 2.4682], [-0.8137, 0.4849, -1.2024, -0.4977, 0.1173]], [[ 0.9666, 0.5375, -0.0313, -0.0679, -0.7232], [-0.5526, 0.9475, -0.5450, 0.0638, 1.2879]]]) |
您还可以利用相同的切片语法 **添加** 一个新维度。例如:
1 |
print(a[:, None, :, None].shape) |
你会看到:
1 |
torch.Size([3, 1, 4, 1, 5]) |
这里您使用 None
在特定位置插入一个新维度。如果您需要将图像转换为仅包含一张图像的批次,这会很有用。如果您熟悉 NumPy,您可能记得有一个名为 expand_dims()
的函数用于此目的,但 PyTorch 不提供它。一个类似的函数是 unsqueeze()
,如下所示:
1 2 3 |
b = torch.unsqueeze(a, dim=2) print(a.shape) print(b.shape) |
输出如下:
1 2 |
torch.Size([3, 4, 5]) torch.Size([3, 4, 1, 5]) |
NumPy 切片语法的强大之处在于布尔索引。这在 PyTorch 张量中也得到了支持。例如:
1 2 3 |
a = torch.randn(3,4) print(a) print(a[:, (a > -1).all(axis=0)]) |
您可能会看到:
1 2 3 4 5 6 |
tensor([[ 1.2548, 0.4078, 0.5548, -0.7016], [-0.3720, -0.5682, -0.3430, 0.0886], [ 0.2151, 0.3626, -2.0275, 1.8121]]) tensor([[ 1.2548, 0.4078, -0.7016], [-0.3720, -0.5682, 0.0886], [ 0.2151, 0.3626, 1.8121]]) |
上面选择了所有元素都大于 -1 的列。您也可以通过选择特定列来操作张量。
1 |
print(a[:, [1,0,0,1]]) |
这会产生:
1 2 3 |
tensor([[ 0.4078, 1.2548, 1.2548, 0.4078], [-0.5682, -0.3720, -0.3720, -0.5682], [ 0.3626, 0.2151, 0.2151, 0.3626]]) |
要将二维张量转换为一维,您可以使用:
1 2 3 |
a = torch.randn(3,4) print(a) print(a.ravel()) |
结果将是:
1 2 3 4 5 |
tensor([[-0.2718, -0.8309, 0.6263, -0.2499], [-0.1780, 1.1735, -1.3530, -1.2374], [-0.6050, -1.5524, -0.1008, -1.2782]]) tensor([-0.2718, -0.8309, 0.6263, -0.2499, -0.1780, 1.1735, -1.3530, -1.2374, -0.6050, -1.5524, -0.1008, -1.2782]) |
您也可以使用 reshape()
函数来实现相同的功能。
1 |
print(a.reshape(-1)) |
结果应该与 ravel()
相同。但通常,reshape()
函数用于更复杂的目标形状。
1 |
print(a.reshape(3,2,2)) |
这将打印:
1 2 3 4 5 6 7 8 |
tensor([[[-0.2718, -0.8309], [ 0.6263, -0.2499]], [[-0.1780, 1.1735], [-1.3530, -1.2374]], [[-0.6050, -1.5524], [-0.1008, -1.2782]]]) |
重塑张量的一个常见情况是进行矩阵转置。对于二维矩阵,它以与 NumPy 相同的方式轻松完成。
1 |
print(a.T) |
它会打印:
1 2 3 4 |
tensor([[-0.2718, -0.1780, -0.6050], [-0.8309, 1.1735, -1.5524], [ 0.6263, -1.3530, -0.1008], [-0.2499, -1.2374, -1.2782]]) |
但 PyTorch 中的 transpose()
函数要求您明确指定要交换的轴。
1 |
print(a.transpose(0, 1)) |
结果与上面相同。如果您有多个张量,可以通过堆叠它们来组合它们(vstack()
用于垂直堆叠,hstack()
用于水平堆叠)。例如:
1 2 3 4 5 |
a = torch.randn(3,3) b = torch.randn(3,3) print(a) print(b) print(torch.vstack([a,b])) |
这可能会打印:
1 2 3 4 5 6 7 8 9 10 11 12 |
tensor([[ 1.1739, 1.3546, -0.2886], [ 1.0444, 0.4437, -2.7933], [ 0.6805, 0.8401, -1.2527]]) tensor([[ 1.6273, 1.2622, -0.4362], [-1.6529, 0.6457, -0.1454], [-2.0960, -1.3024, -0.1033]]) tensor([[ 1.1739, 1.3546, -0.2886], [ 1.0444, 0.4437, -2.7933], [ 0.6805, 0.8401, -1.2527], [ 1.6273, 1.2622, -0.4362], [-1.6529, 0.6457, -0.1454], [-2.0960, -1.3024, -0.1033]]) |
concatenate 函数类似:
1 2 |
c = torch.concatenate([a, b]) print(c) |
您将得到相同的张量。
1 2 3 4 5 6 |
tensor([[ 1.1739, 1.3546, -0.2886], [ 1.0444, 0.4437, -2.7933], [ 0.6805, 0.8401, -1.2527], [ 1.6273, 1.2622, -0.4362], [-1.6529, 0.6457, -0.1454], [-2.0960, -1.3024, -0.1033]]) |
反向操作是分割,例如:
1 |
print(torch.vsplit(c, 2)) |
输出结果为:
1 2 3 4 5 |
(tensor([[ 1.1739, 1.3546, -0.2886], [ 1.0444, 0.4437, -2.7933], [ 0.6805, 0.8401, -1.2527]]), tensor([[ 1.6273, 1.2622, -0.4362], [-1.6529, 0.6457, -0.1454], [-2.0960, -1.3024, -0.1033]])) |
此函数指定要分割成的张量数量,而不是每个张量的大小。后者在深度学习中确实更有用(例如,将大型数据集的张量分割成许多小批量张量)。等效函数是:
1 |
print(torch.split(c, 3, dim=0)) |
这应该给出与之前相同的结果。因此,split(c, 3, dim=0)
意味着在维度 0 上进行分割,使得每个生成的张量的大小为 3。
张量函数
PyTorch 张量可以被视为数组。因此,您通常可以像使用 NumPy 数组一样使用它们。例如,您可以使用常见的数学函数:
1 2 3 4 5 6 7 8 9 10 11 12 |
a = torch.randn(2,3) print(a) print(torch.exp(a)) print(torch.log(a)) print(torch.sin(a)) print(torch.arctan(a)) print(torch.abs(a)) print(torch.square(a)) print(torch.sqrt(a)) print(torch.ceil(a)) print(torch.round(a)) print(torch.clip(a, 0.1, 0.9)) |
输出如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
tensor([[ 1.0567, -1.2609, -1.0856], [-0.9633, 1.3163, -0.4325]]) tensor([[2.8770, 0.2834, 0.3377], [0.3816, 3.7298, 0.6489]]) tensor([[0.0552, nan, nan], [ nan, 0.2749, nan]]) tensor([[ 0.8708, -0.9524, -0.8846], [-0.8211, 0.9678, -0.4191]]) tensor([[ 0.8130, -0.9003, -0.8264], [-0.7667, 0.9211, -0.4082]]) tensor([[1.0567, 1.2609, 1.0856], [0.9633, 1.3163, 0.4325]]) tensor([[1.1167, 1.5898, 1.1785], [0.9280, 1.7328, 0.1871]]) tensor([[1.0280, nan, nan], [ nan, 1.1473, nan]]) tensor([[ 2., -1., -1.], [-0., 2., -0.]]) tensor([[ 1., -1., -1.], [-1., 1., -0.]]) tensor([[0.9000, 0.1000, 0.1000], [0.1000, 0.9000, 0.1000]]) |
请注意,如果某个函数未定义(例如,负数的平方根),结果将是 nan
,但不会引发异常。在 PyTorch 中,您有一个函数来检查张量的值是否为 nan
。
1 2 3 |
b = torch.sqrt(a) print(b) print(torch.isnan(b)) |
您将获得
1 2 3 4 |
tensor([[1.0280, nan, nan], [ nan, 1.1473, nan]]) tensor([[False, True, True], [ True, False, True]]) |
确实,除了这些定义的函数之外,Python 的运算符也可以应用于张量。
1 2 3 4 5 6 7 |
a = torch.randn(2, 3) b = torch.randn(2, 3) print(a) print(b) print(a+b) print(a/b) print(a ** 2) |
您得到
1 2 3 4 5 6 7 8 9 10 |
tensor([[ 0.7378, -0.3469, 1.3089], [-1.9152, 0.3745, -0.7248]]) tensor([[-0.3650, -0.4768, 0.9331], [ 0.5095, 1.7169, -0.5463]]) tensor([[ 0.3729, -0.8237, 2.2421], [-1.4058, 2.0914, -1.2711]]) tensor([[-2.0216, 0.7275, 1.4027], [-3.7594, 0.2181, 1.3269]]) tensor([[0.5444, 0.1203, 1.7133], [3.6682, 0.1403, 0.5254]]) |
在各种运算符中,矩阵乘法在深度学习中非常重要。您可以通过以下方式执行此操作:
1 2 |
print(torch.matmul(a, b.T)) print(a @ b.T) |
输出如下:
1 2 3 4 |
tensor([[ 1.1176, -0.9347], [-0.1560, 0.0632]]) tensor([[ 1.1176, -0.9347], [-0.1560, 0.0632]]) |
这两者是相同的。事实上,Python 的 @
运算符也可用于向量点积,例如:
1 2 3 4 5 6 |
a = torch.randn(3) b = torch.randn(3) print(a) print(b) print(torch.dot(a, b)) print(a @ b) |
输出结果为:
1 2 3 4 |
tensor([-0.8986, -0.6994, 1.1443]) tensor([-1.0666, 0.1455, 0.1322]) tensor(1.0081) tensor(1.0081) |
如果将张量中的值视为样本,您可能还想查找有关它的某些统计信息。PyTorch 中也提供了一些。
1 2 3 4 5 6 |
a = torch.randn(3,4) print(a) print(torch.mean(a, dim=0)) print(torch.std(a, dim=0)) print(torch.cumsum(a, dim=0)) print(torch.cumprod(a, dim=0)) |
输出结果为:
1 2 3 4 5 6 7 8 9 10 11 |
tensor([[ 0.3331, -0.0190, 0.4814, -1.1484], [-0.5712, 0.8430, -1.6147, -1.1664], [ 1.7298, -1.7665, -0.5918, 0.3024]]) tensor([ 0.4972, -0.3142, -0.5750, -0.6708]) tensor([1.1593, 1.3295, 1.0482, 0.8429]) tensor([[ 0.3331, -0.0190, 0.4814, -1.1484], [-0.2381, 0.8240, -1.1333, -2.3148], [ 1.4917, -0.9425, -1.7251, -2.0124]]) tensor([[ 0.3331, -0.0190, 0.4814, -1.1484], [-0.1903, -0.0160, -0.7774, 1.3395], [-0.3291, 0.0283, 0.4601, 0.4051]]) |
但是,对于线性代数函数,您应该在 PyTorch 的 linalg 子模块中找到它们。例如:
1 |
print(torch.linalg.svd(a)) |
你会看到:
1 2 3 4 5 6 7 8 9 |
torch.return_types.linalg_svd( U=tensor([[-0.0353, 0.1313, 0.9907], [-0.5576, 0.8201, -0.1286], [ 0.8294, 0.5569, -0.0443]]), S=tensor([2.7956, 1.9465, 1.2715]), Vh=tensor([[ 0.6229, -0.6919, 0.1404, 0.3369], [ 0.2767, -0.1515, -0.8172, -0.4824], [ 0.2570, -0.0385, 0.5590, -0.7874], [ 0.6851, 0.7048, -0.0073, 0.1840]])) |
对于卷积神经网络,填充张量是通过以下方式完成的:
1 2 |
b = torch.nn.functional.pad(a, (1,1,0,2), value=0) print(b) |
输出如下:
1 2 3 4 5 |
tensor([[ 0.0000, 0.3331, -0.0190, 0.4814, -1.1484, 0.0000], [ 0.0000, -0.5712, 0.8430, -1.6147, -1.1664, 0.0000], [ 0.0000, 1.7298, -1.7665, -0.5918, 0.3024, 0.0000], [ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000], [ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]) |
此 `pad()` 函数的示例是为维度 0 创建 (1,1) 填充,为维度 1 创建 (0,2) 填充。换句话说,对于每个维度 0(行),我们在开头和结尾添加一个虚拟值 (0)。对于每个维度 1(列),我们在开头添加零个虚拟值,在结尾添加两个虚拟值。
最后,由于 PyTorch 张量可以被视为数组,您可以直接将它们与其他工具(如 matplotlib)一起使用。下面是使用 PyTorch 张量绘制曲面的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import matplotlib.pyplot as plt import torch # 创建张量 x = torch.linspace(-1, 1, 100) y = torch.linspace(-2, 2, 100) # 创建曲面 xx, yy = torch.meshgrid(x, y, indexing="xy") # xy-indexing is matching numpy z = torch.sqrt(1 - xx**2 - (yy/2)**2) print(xx) fig = plt.figure(figsize=(8,8)) ax = plt.axes(projection="3d") ax.set_xlim([-2, 2]) ax.set_ylim([-2, 2]) ax.set_zlim([0, 2]) ax.plot_surface(xx, yy, z, cmap="cividis") ax.view_init(45, 35) plt.show() |
meshgrid 生成的 `xx` 张量为:
1 2 3 4 5 6 7 |
tensor([[-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000], [-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000], [-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000], ..., [-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000], [-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000], [-1.0000, -0.9798, -0.9596, ..., 0.9596, 0.9798, 1.0000]]) |
创建的图为:
总结
在本教程中,您将了解如何操作 PyTorch 张量。具体来说,您将学习:
- 什么是张量
- 如何在 PyTorch 中创建各种张量
- 如何在 PyTorch 中重塑、切片和操作张量
- 可以应用于 PyTorch 张量的常用函数
该行
“z = torch.sqrt(1 – xx**2 – (yy/2)**2)”
返回一个包含 NaN 的张量。
这个公式有误吗?
嗨 John…我们会对此进行研究,但如果您是手动输入代码还是复制粘贴的代码,那将会有所帮助。
嗨 James,
我手动输入的。我对此进行了一些尝试,因为对于 Z 的值,获取负数的平方根似乎是主要问题。
我在 `torch.sqrt()` 中插入了 `abs()`,它生成了一个与图中所示大致相似的图,只是在角落处有一些层状峰值。
嗨 John…谢谢您的更新!这很可能会对其他人也有帮助。
你好,
您能找到解决方案吗?