PyTorch 张量操作详解:从基础运算到形状变换
张量(Tensor)是 PyTorch 的核心数据结构,而张量操作则是构建深度学习模型和进行数值计算的基础。无论是简单的加减乘除,还是复杂的形状变换,掌握这些操作对于高效使用 PyTorch 至关重要。本文将通过一段示例代码,详细解析 PyTorch 中常用的张量操作,帮助你理解它们的功能、用法和实际应用场景。
代码功能概述
让我们首先看看这段展示 PyTorch 基本张量操作的代码:
# tensor_operations.py
# Import necessary libraries
import torch
def main():
# Create a 3x3 tensor filled with ones
tensor = torch.ones(3, 3)
print(tensor)
# Resize the tensor to 9 elements (1D)
print(tensor.view(9).shape, end="")
print(tensor.view(9))
# Addition
print("Addition:", torch.add(tensor, tensor))
# Subtraction
print("Subtraction:", tensor.sub(tensor))
# Element wise multiplication
print("Element wise multiplication:", torch.mul(tensor, tensor))
# Element wise division
print("Element wise division:", torch.div(tensor, tensor))
# Mean
tensor = torch.Tensor([1, 2, 3, 4, 5])
print("Mean:", tensor.mean().item()) # .item() converts tensor to Python scalar
# Standart deviation (std)
print("std:", tensor.std().item()) # .item() converts tensor to Python scalar
if __name__ == "__main__":
main()
这段代码展示了 PyTorch 中几类常用的张量操作:
- 创建全 1 张量
- 张量形状变换(重塑)
- 基本算术运算(加、减、乘、除)
- 统计运算(均值、标准差)
- 张量与 Python 标量的转换
运行代码后,输出结果如下:
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
torch.Size([9])tensor([1., 1., 1., 1., 1., 1., 1., 1., 1.])
Addition: tensor([[2., 2., 2.],
[2., 2., 2.],
[2., 2., 2.]])
Subtraction: tensor([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
Element wise multiplication: tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
Element wise division: tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]])
Mean: 3.0
std: 1.4142135623730951
代码逐行解析
1. 导入 PyTorch 库
import torch
这行代码导入 PyTorch 库,使我们能够使用其提供的张量操作功能。torch
是 PyTorch 库的标准导入名称。
2. 创建 3x3 全 1 张量
tensor = torch.ones(3, 3)
print(tensor)
torch.ones(3, 3)
:创建一个 3 行 3 列(形状为(3, 3)
)的二维张量,所有元素的值均为 1.0。默认数据类型为float32
(单精度浮点数)。- 打印结果显示了一个 3x3 的矩阵,每个元素都是
1.
(带小数点表示浮点数)。
3. 张量形状变换(重塑)
print(tensor.view(9).shape, end="")
print(tensor.view(9))
这两行代码展示了如何使用view()
方法改变张量的形状:
view(9)
:将原来 3x3 的二维张量重塑为包含 9 个元素的一维张量。view()
方法需要保证新形状的元素总数与原张量相同(3×3=9),否则会报错。shape
属性:打印重塑后张量的形状,结果为torch.Size([9])
,表示这是一个包含 9 个元素的一维张量。- 重塑操作不会改变张量的元素值和总数,只改变其维度结构。这在神经网络中非常有用,例如将卷积层输出的多维特征图转换为全连接层需要的一维向量。
4. 加法操作
print("Addition:", torch.add(tensor, tensor))
torch.add(tensor, tensor)
:对两个张量进行元素级加法运算。这里两个操作数都是同一个 3x3 的全 1 张量,所以结果是一个 3x3 的全 2 张量。- 元素级运算(Element-wise Operation)指的是两个形状相同的张量对应位置的元素进行运算,得到的结果张量形状与输入相同。
- 也可以使用运算符简写形式:
tensor + tensor
,与torch.add(tensor, tensor)
效果完全相同。
5. 减法操作
print("Subtraction:", tensor.sub(tensor))
tensor.sub(tensor)
:对张量进行元素级减法运算(等价于torch.sub(tensor, tensor)
)。一个张量减去自身,所有元素的结果都是 0,所以输出是全 0 张量。- 运算符简写形式:
tensor - tensor
。
6. 乘法操作
print("Element wise multiplication:", torch.mul(tensor, tensor))
torch.mul(tensor, tensor)
:执行元素级乘法(Hadamard 积),即两个张量对应位置的元素相乘。全 1 张量与自身相乘的结果仍然是全 1 张量。- 注意:这与矩阵乘法(矩阵积)不同,矩阵乘法需要使用
torch.matmul()
或@
运算符。 - 运算符简写形式:
tensor * tensor
。
7. 除法操作
print("Element wise division:", torch.div(tensor, tensor))
torch.div(tensor, tensor)
:执行元素级除法,即两个张量对应位置的元素相除。全 1 张量除以自身的结果仍然是全 1 张量。- 运算符简写形式:
tensor / tensor
。
8. 均值计算
tensor = torch.Tensor([1, 2, 3, 4, 5])
print("Mean:", tensor.mean().item())
这两行代码首先创建了一个新的一维张量,然后计算其均值:
torch.Tensor([1, 2, 3, 4, 5])
:创建一个包含 5 个整数的一维张量,元素值分别为 1 到 5。tensor.mean()
:计算张量所有元素的平均值。对于张量[1,2,3,4,5]
,均值为(1+2+3+4+5)/5 = 3.0
。.item()
:将只包含一个元素的张量转换为 Python 标量(普通的 Python 数值类型)。这在需要获取计算结果的具体数值时非常有用。
9. 标准差计算
print("std:", tensor.std().item())
tensor.std()
:计算张量所有元素的标准差(Standard Deviation),衡量数据的离散程度。对于张量[1,2,3,4,5]
,标准差的计算过程如下:- 均值为 3.0
- 每个元素与均值的差的平方:
(1-3)²=4
,(2-3)²=1
,(3-3)²=0
,(4-3)²=1
,(5-3)²=4
- 方差为这些平方的平均值:
(4+1+0+1+4)/5 = 10/5 = 2
- 标准差为方差的平方根:
√2 ≈ 1.4142
- 同样使用
.item()
将结果转换为 Python 标量。
关键概念解析
1. 张量形状变换:view()
方法
view()
是 PyTorch 中重塑张量形状的核心方法,其工作原理和重要特性包括:
- 元素总数守恒:新形状的元素总数必须与原张量相同,例如 3x3 的张量可以重塑为 9x1、1x9 或 1x3x3 等,但不能重塑为 2x5(元素总数 10≠9)。
- 内存共享:
view()
返回的新张量与原张量共享底层数据,修改一个会影响另一个(不复制数据,高效)。 - -1 的特殊用法:可以用 - 1 表示 "自动计算该维度的大小",例如
tensor.view(-1, 3)
会将 3x3 的张量重塑为 3x3(-1 自动计算为 3),tensor.view(3, -1)
同样得到 3x3。
示例:
x = torch.ones(2, 4)
print(x.view(8)) # 形状: (8)
print(x.view(-1, 2)) # 形状: (4, 2)(-1自动计算为4)
print(x.view(2, 2, -1)) # 形状: (2, 2, 2)
2. 元素级运算(Element-wise Operations)
代码中展示的加减乘除都属于元素级运算,其核心特点是:
- 要求参与运算的张量具有相同的形状(或可广播为相同形状)
- 运算在对应位置的元素之间进行,不改变张量形状
- 支持运算符重载(
+
、-
、*
、/
)和函数调用两种方式 - 效率高,底层由 C++ 实现,无需 Python 循环
广播(Broadcasting)是元素级运算的重要扩展,允许形状不同但兼容的张量进行运算,例如:
a = torch.ones(3, 3)
b = torch.tensor([1, 2, 3])
print(a + b) # b会被广播为3x3的张量,每一行都是[1,2,3]
3. 张量与标量的转换:item()
方法
item()
方法用于将只包含一个元素的张量转换为 Python 标量,具有以下特点:
- 仅适用于包含单个元素的张量(0 维张量或形状为
(1,)
的 1 维张量) - 返回值是 Python 的基本数据类型(如
float
或int
) - 常用于提取损失值、准确率等标量指标的具体数值
- 与
numpy()
方法的区别:numpy()
返回 NumPy 数组,item()
返回 Python 标量
示例:
scalar_tensor = torch.tensor(3.14)
print(type(scalar_tensor)) # <class 'torch.Tensor'>
print(type(scalar_tensor.item())) # <class 'float'>
4. 统计运算
代码中展示的mean()
(均值)和std()
(标准差)是常用的统计运算,PyTorch 还提供了其他统计函数:
sum()
:计算元素总和max()
/min()
:计算最大值 / 最小值var()
:计算方差median()
:计算中位数argmax()
/argmin()
:返回最大值 / 最小值的索引
这些函数默认对张量所有元素进行计算,也可以通过dim
参数指定在某个维度上计算:
x = torch.tensor([[1, 2], [3, 4]])
print(x.mean(dim=0)) # 按列计算均值: tensor([2., 3.])
print(x.sum(dim=1)) # 按行计算总和: tensor([3., 7.])
实际应用场景
这些基础张量操作是构建复杂深度学习模型的基石,在实际应用中无处不在:
-
神经网络前向传播:
# 简单的线性层计算: output = input × weight + bias output = torch.matmul(input_tensor, weight_tensor) + bias_tensor
-
损失函数计算:
# 计算预测值与真实值的均方误差 loss = torch.mean((predictions - targets) **2)
3. 数据预处理 :
# 标准化数据: (x - mean) / std
mean = data.mean()
std = data.std()
normalized_data = (data - mean) / std
4.形状适配 :
# 将卷积特征图展平为全连接层输入
conv_output = torch.rand(16, 32, 7, 7) # 16个样本,32个通道,7x7特征图
fc_input = conv_output.view(16, -1) # 展平为16x(32×7×7)的张量
总结
通过这段代码,我们学习了 PyTorch 中几类最基础也最常用的张量操作,包括形状变换(view()
)、基本算术运算(加、减、乘、除)和统计运算(均值、标准差),以及张量与标量的转换(item()
)。
这些操作看似简单,却是构建复杂深度学习模型和进行数值计算的基础。掌握它们的工作原理和使用场景,能够帮助你更高效地编写 PyTorch 代码,理解神经网络的计算过程。
在实际应用中,这些操作通常会组合使用,形成更复杂的计算流程。随着学习的深入,你还会遇到更多高级张量操作,但它们的设计理念与这些基础操作是一致的 —— 高效、直观且面向深度学习任务优化。