【机器学习深度学习】张量基本操作
目录
一、张量基本操作
1.1 执行代码
1.2 运行结果
1.3 代码解析
✅ 1. 创建张量(tensor、randn、zeros)
✅ 2. 索引与切片(类似 NumPy)
✅ 3. 形状变换(reshape、转置、压缩)
✅ 4. 数学运算(逐元素 + 矩阵乘 + 求和)
✅ 5. 广播机制(不同维度也能加)
✅ 6. 内存共享验证(view 是原数据的视图)
二、什么是张量(Tensor)
三、示例代码中有哪些“张量”
四、小结一句话:
一、张量基本操作
1.1 执行代码
#张量的基本操作import torch
# ==============================
# 1. 张量的基本操作
# ==============================
print("="*50)
print("1. 张量基本操作示例")
print("="*50)# 创建张量
x = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)
y = torch.randn(2, 3) # 正态分布随机张量
z = torch.zeros(3, 2)
print(f"创建张量:\n x={x}\n y={y}\n z={z}")# 索引和切片
print("\n索引和切片:")
print("x[1, 2] =", x[1, 2].item()) # 获取标量值
print("x[:, 1:] =\n", x[:, 1:])# 形状变换
reshaped = x.view(3, 2) # 视图操作(不复制数据)
transposed = x.t() # 转置
squeezed = torch.randn(1, 3, 1).squeeze() # 压缩维度
print(f"\n形状变换:\n 重塑后: {reshaped.shape}\n 转置后: {transposed.shape}\n 压缩后: {squeezed.shape}")# 数学运算
add = x + y # 逐元素加法
matmul = x @ transposed # 矩阵乘法
sum_x = x.sum(dim=1) # 沿维度求和
print(f"\n数学运算:\n 加法:\n{add}\n 矩阵乘法:\n{matmul}\n 行和: {sum_x}")# 广播机制
a = torch.tensor([1, 2, 3])
b = torch.tensor([[10], [20]])
print(a.shape)
print(b.shape)
print(f"\n广播加法:\n{a + b}")# 内存共享验证
view_tensor = x.view(6)
view_tensor[0] = 100
print("\n内存共享验证(修改视图影响原始张量):")
print(f"视图: {view_tensor}\n原始: {x}")
1.2 运行结果
==================================================
1. 张量基本操作示例
==================================================
创建张量:
x=tensor([[1., 2., 3.],
[4., 5., 6.]])
y=tensor([[ 0.1558, -1.4728, 0.0306],
[ 0.8278, 1.3076, 0.5648]])
z=tensor([[0., 0.],
[0., 0.],
[0., 0.]])索引和切片:
x[1, 2] = 6.0
x[:, 1:] =
tensor([[2., 3.],
[5., 6.]])形状变换:
重塑后: torch.Size([3, 2])
转置后: torch.Size([3, 2])
压缩后: torch.Size([3])数学运算:
加法:
tensor([[1.1558, 0.5272, 3.0306],
[4.8278, 6.3076, 6.5648]])
矩阵乘法:
tensor([[14., 32.],
[32., 77.]])广播加法:
tensor([[11, 12, 13],
[21, 22, 23]])内存共享验证(修改视图影响原始张量):
视图: tensor([100., 2., 3., 4., 5., 6.])
原始: tensor([[100., 2., 3.],
[ 4., 5., 6.]])
1.3 代码解析
✅ 1. 创建张量(
tensor
、randn
、zeros
)x = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32) y = torch.randn(2, 3) # 标准正态分布随机数 z = torch.zeros(3, 2)
x
是一个 2行3列 的手动定义张量。
y
是一个 2x3 的 随机张量,服从 N(0,1)。
z
是一个 3x2 的 全零张量。
✅ 2. 索引与切片(类似 NumPy)
x[1, 2].item() # 取第1行第2列的数,结果是 6.0 x[:, 1:] # 所有行,保留第1列以后(含1)
x[1, 2] = 6
,就是第2行第3列的元素;
x[:, 1:]
等价于:[[2, 3],[5, 6]]
✅ 3. 形状变换(reshape、转置、压缩)
x.view(3, 2) # 重塑形状为 3行2列(原数据数量不变) x.t() # 转置(只支持二维):行变列、列变行 torch.randn(1, 3, 1).squeeze() # 删除维度为1的维
.view()
是 PyTorch 的 reshape,不复制数据;变形后变成:
[[1, 2],[3, 4],[5, 6]]
🔑 核心规则:总元素数量不变(2×3 = 3×2 = 6),只是重新安排形状。
.t()
是转置:原来 2x3 → 3x2;用
.t()
进行转置,行变列、列变行,结果是:[[1, 4],[2, 5],[3, 6]]
就像把矩阵绕主对角线翻了一下。
📌 注意:
.t()
只适用于二维张量;如果是三维或更高,需要用
.transpose(dim0, dim1)
。
.squeeze()
把 [1, 3, 1] 变成 [3],因为它删除所有 维度=1 的轴。原始张量:
a = torch.randn(1, 3, 1) print(a.shape) # torch.Size([1, 3, 1])
这代表一个形状是
[1, 3, 1]
的张量:它有 3 个元素,但加了两个维度为 1 的“壳”:
第 1 个维度是 1(只有 1 个“通道”)
第 3 个维度是 1(每个值外面包了一层)
【为啥会有维度为1的“壳”?】
很多场景(图像、批处理、模型输出)为了格式统一,会多包几层:
比如:
[Batch, Features, Channel]
即使某些维度大小为1,也会保留,以统一接口。
✅ 4. 数学运算(逐元素 + 矩阵乘 + 求和)
x + y # 对应位置相加(维度一样) x @ x.t() # 矩阵乘法(2x3 与 3x2) x.sum(dim=1) # 每一行求和,返回 [6, 15]
x + y
是元素加(相同位置相加);x = [[1, 2, 3],[4, 5, 6]]y = [[0.1, 0.2, 0.3],[0.4, 0.5, 0.6]]x + y = [[1+0.1, 2+0.2, 3+0.3],[4+0.4, 5+0.5, 6+0.6]] = [[1.1, 2.2, 3.3],[4.4, 5.5, 6.6]]#最终结果 tensor([[1.1, 2.2, 3.3],[4.4, 5.5, 6.6]])
x @ x.T
是线性代数中的矩阵乘法;这表示:
注意:
@
表示矩阵乘法,不是逐元素乘法!x = [[1, 2, 3],[4, 5, 6]]x.t() = [[1, 4],[2, 5],[3, 6]]
计算过程
第一行 dot 第一列: 1×1 + 2×2 + 3×3 = 1 + 4 + 9 = 14第一行 dot 第二列: 1×4 + 2×5 + 3×6 = 4 + 10 + 18 = 32第二行 dot 第一列: 4×1 + 5×2 + 6×3 = 4 + 10 + 18 = 32第二行 dot 第二列: 4×4 + 5×5 + 6×6 = 16 + 25 + 36 = 77✅ 结果: x @ x.t() = [[14, 32],[32, 77]]
x.sum(dim=1)
结果是:x.sum(dim=1):每一行求和x = [[1, 2, 3],[4, 5, 6]]沿着“行的方向”(dim=1)求和:第一行:1 + 2 + 3 = 6第二行:4 + 5 + 6 = 15 [1+2+3, 4+5+6] → [6, 15]✅ 输出: tensor([6, 15])
✅ 5. 广播机制(不同维度也能加)
a = torch.tensor([1, 2, 3]) # shape: [3] b = torch.tensor([[10], [20]]) # shape: [2, 1] a + b
a.shape = [3]
b.shape = [2, 1]
通过广播规则:
a
是一个长度为 3 的一维张量[1, 2, 3]
,形状是[3]
b
是一个二维张量:[[10],[20]]
形状是
[2, 1]
🔁广播规则回顾(Broadcasting Rules)
从后往前比较维度(右对齐),满足以下任一即可广播:
维度相等 ✅
其中一个维度为 1 ✅
缺失维度视为 1 ✅
记忆口诀:“广播先对齐,缺1来撑齐,横竖补成行列齐”
▲第一步:把
a
和b
的形状写完整!即把它们“补齐维度”,方便对齐:
张量 原始形状 看作是二维矩阵 a [3]
[1, 3]
→ 横向一排b [2, 1]
[2, 1]
→ 纵向两列你可以这样想象:
a = [1, 2, 3] → [[1, 2, 3]] ← 横排,1行3列 b = [[10], [20]] → [[10], ← 竖排,2行1列[20]]
▲第二步:广播机制让它们形状“变一样”
原则是:当两个张量维度不一致时,PyTorch 会尝试扩展它们
我们来手动画一下:
a 的形状
[1, 3]
:[[1, 2, 3]]
想和 b 的
[2, 1]
相加,就会自动复制 2 行,变成:[[1, 2, 3],[1, 2, 3]] ← 广播成 [2, 3]
b 的形状
[2, 1]
:[[10],[20]]
要和
[2, 3]
相加,就会复制每行的 1 个值成 3 列,变成:[[10, 10, 10],[20, 20, 20]] ← 广播成 [2, 3]
▲第三步:相加
两个
[2, 3]
的矩阵对应位置相加:[[1+10, 2+10, 3+10], = [11, 12, 13][1+20, 2+20, 3+20]] = [21, 22, 23]
最终结果是:
广播加法: tensor([[11, 12, 13],[21, 22, 23]])
✅ 6. 内存共享验证(view 是原数据的视图)
view_tensor = x.view(6) view_tensor[0] = 100 print(x)
x.view(6)
是将 x 展平成 1 维数组(6个元素);修改
view_tensor[0] = 100
,同时修改了原始 x 的第一个值;因为
.view()
是 共享内存的视图,不是拷贝副本。原始张量 x 是: x = torch.tensor([[1, 2, 3],[4, 5, 6]]) 形状是 (2, 3),也就是: 行 0: [1, 2, 3] 行 1: [4, 5, 6]
▲第一步:
view_tensor = x.view(6)
这一步是把
x
重塑成一个一维向量,有 6 个元素:view_tensor = [1, 2, 3, 4, 5, 6] # shape: [6]
⚠️ 注意:不是复制数据!只是换了形状!它和
x
共用同一块内存。▲第二步:
view_tensor[0] = 100
修改了第一个元素,把原来的 1 改成了 100:
view_tensor = [100, 2, 3, 4, 5, 6]
因为它和
x
共享内存,所以x
的第一个元素(也就是x[0, 0]
)也被改了!▲第三步:
print(x)
现在
x
的原始矩阵变成了:x = [[100, 2, 3],[ 4, 5, 6]]
最终结果:
x = tensor([[100., 2., 3.],[4., 5., 6.]])
🔚 总结
🔚 内存共享与不共享对比图
操作 功能 易错点 .view()
重塑形状 会共享内存,修改影响原张量 .t()
转置 仅支持2D .squeeze()
去掉维度=1 的轴 常用于数据降维 +
元素加 维度不一样时会广播 @
矩阵乘法 对应线性代数矩阵运算
二、什么是张量(Tensor)
张量(Tensor)就是“任意维度的数值容器”,一维、二维、三维、甚至 N 维都可以。
在 PyTorch 中,所有通过 torch.tensor()
、torch.randn()
、torch.zeros()
、torch.view()
等操作得到的对象,都是张量。
张量就是一种 可以包含数字、并支持数学操作的多维数组。它是 PyTorch 的核心数据结构,类似于 NumPy 的 ndarray
,但支持自动求导、GPU 运算等。
🎯 更清晰地说:
维度 | 名称 | 举例 | PyTorch 类型 |
---|---|---|---|
0 维 | 标量 | 3.14 | 不是张量,float |
1 维 | 向量 | [1, 2, 3] | 张量(shape=[3] ) |
2 维 | 矩阵 | [[1, 2], [3, 4]] | 张量(shape=[2, 2] ) |
3 维 | 张量 | RGB图像(3通道) | 张量(如 shape=[3, H, W] ) |
N 维 | 高维张量 | NLP中词向量batch等 | 张量 |
✅ 所以说:
所有的矩阵、向量,乃至于多维数组,在 PyTorch 中都是张量。
早期数学/物理中张量的严格定义:
张量通常是二维或更高的数学对象
标量是 0阶张量,向量是 1阶张量,矩阵是 2阶张量……
所以有时人们说“张量是多维结构”,把标量另算
但在 PyTorch / NumPy / 深度学习实际编程中:
所有数字类型(0维、1维、2维……)统一叫 张量
角度 标量是不是张量? 解释 数学传统 ❌ 不一定 有时专门分开谈 PyTorch 实际编程 ✅ 是 torch.Size([])
代表 0维张量NumPy 一致行为 ✅ 是 np.array(3.14).ndim == 0
✅ 但注意:不是“多维才是张量”
0维(只有一个数的情况)也是张量!
例子:
x = torch.tensor(7)
print(x.shape) # torch.Size([])
这是一个0维张量,只是你看不到形状,它只有一个数字。
张量 = 标量(0维) + 向量(1维) + 矩阵(2维) + 多维数组(3维以上)
可以理解为:
“张量 = 包括所有维度的数组”
“维度越高,数据结构越复杂,但都是张量”
三、示例代码中有哪些“张量”
变量名 | 是不是张量 | 来源 | 注释说明 |
---|---|---|---|
x | ✅ 是 | torch.tensor(...) | 2x3 手动创建的浮点张量 |
y | ✅ 是 | torch.randn(...) | 2x3 正态分布随机张量 |
z | ✅ 是 | torch.zeros(...) | 3x2 全 0 张量 |
x[1, 2] | ❌ 不是 | 张量的元素 | 是张量中的标量值(float),用 .item() 取出 |
x[:, 1:] | ✅ 是 | x 的切片 | 子张量,仍然是张量 |
reshaped | ✅ 是 | x.view(...) | 同一块内存的不同形状张量 |
transposed | ✅ 是 | x.t() | 转置后张量(2x3 → 3x2) |
squeezed | ✅ 是 | squeeze() | 压缩维度后的张量 |
add | ✅ 是 | x + y | 张量加法结果(形状相同) |
matmul | ✅ 是 | x @ x.t() | 张量矩阵乘法结果 |
sum_x | ✅ 是 | x.sum(dim=1) | 每行求和后的张量(形状为 [2] ) |
a | ✅ 是 | torch.tensor(...) | 一维张量,shape: [3] |
b | ✅ 是 | torch.tensor(...) | 二维张量,shape: [2, 1] |
a + b | ✅ 是 | 广播相加后的结果 | shape: [2, 3] |
view_tensor | ✅ 是 | x.view(6) | 原张量的“扁平化视图” |
view_tensor[0] | ❌ 不是 | 单个元素 | 张量里的一个标量,不再是张量本体 |
四、小结一句话:
只要是
torch.xxx(...)
创建出来的、或张量切片、变换、计算出来的结果,都是张量;
只有.item()
或[i][j]
取出的是具体数字(标量),不是张量。