系列导读:本系列共 6 篇,从 PyTorch 框架入门到实战案例,带你系统掌握深度学习开发。上一篇:张量运算与变形——数值计算与索引进阶
一、张量拼接操作
1.1 cat / concat:沿现有维度拼接
torch.cat 沿现有维度连接一系列张量,不改变维度数。
import torch
# 初始化数据
data1 = torch.randint(0, 6, (2, 3))
data2 = torch.randint(0, 6, (2, 3))
print(data1.shape) # torch.Size([2, 3])
print(data2.shape) # torch.Size([2, 3])
# 按 dim=0 拼接(行方向,垂直堆叠)
data = torch.cat([data1, data2], dim=0)
print(data.shape) # torch.Size([4, 3])
# 按 dim=1 拼接(列方向,水平拼接)
data = torch.cat([data1, data2], dim=1)
print(data.shape) # torch.Size([2, 6])
# 按 dim=2 拼接(需要三维张量)
data1 = torch.randint(0, 6, (2, 3, 4))
data2 = torch.randint(0, 6, (2, 3, 4))
data = torch.cat([data1, data2], dim=2)
print(data.shape) # torch.Size([2, 3, 8])
关键规则:拼接维度可以不一致,但其他维度必须相同。
1.2 stack:在新维度上拼接
torch.stack 在新维度上连接张量,增加一个新维度,所有输入张量形状必须完全相同。
data1 = torch.randint(0, 10, [2, 3])
data2 = torch.randint(0, 10, [2, 3])
# dim=0:新维度在最前面 → (2, 2, 3)
new_data = torch.stack([data1, data2], dim=0)
print(new_data.shape) # torch.Size([2, 2, 3])
# dim=1:新维度在中间 → (2, 2, 3)
new_data = torch.stack([data1, data2], dim=1)
print(new_data.shape) # torch.Size([2, 2, 3])
# dim=2:新维度在最后 → (2, 3, 2)
new_data = torch.stack([data1, data2], dim=2)
print(new_data.shape) # torch.Size([2, 3, 2])
经典应用:
- 单通道灰度图
[H, W],三张 stack 模拟 RGB →[3, H, W] - 多张图片张量
[C, H, W],stack 得到批次[B, C, H, W]
1.3 chunk / split:拆分张量
化整为零,各个击破:大拆小,小运行。
data = torch.tensor([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[11, 12, 13]
])
# chunk:均分成 chunks 份
chunks = torch.chunk(data, chunks=2, dim=0)
print('第1块:', chunks[0].shape) # torch.Size([2, 3])
print('第2块:', chunks[1].shape) # torch.Size([2, 3])
# split:按指定大小拆分
splits = torch.split(data, [1, 1, 2], dim=0)
print(splits[0].shape) # torch.Size([1, 3])
print(splits[1].shape) # torch.Size([1, 3])
print(splits[2].shape) # torch.Size([2, 3])
1.4 stack 与 chunk 互逆操作
# 生成 64 张 3 通道 32x32 的图片张量
imgs = []
for i in range(64):
img = torch.randn(3, 32, 32) # 3 通道,32 高,32 宽
imgs.append(img)
# stack 拼接:64 张 → (64, 3, 32, 32)
imgs2 = torch.stack(imgs, dim=0)
print(imgs2.shape) # torch.Size([64, 3, 32, 32])
# chunk 拆分:(64, 3, 32, 32) → 64 张 (1, 3, 32, 32)
imgs_restore = torch.chunk(imgs2, chunks=64, dim=0)
print(len(imgs_restore)) # 64
print(imgs_restore[0].shape) # torch.Size([1, 3, 32, 32])
# squeeze 降维:(1, 3, 32, 32) → (3, 32, 32)
imgs_new = imgs_restore[0].squeeze(0)
print(imgs_new.shape) # torch.Size([3, 32, 32])
二、自动微分模块 autograd ⭐⭐⭐
2.1 什么是自动微分
自动微分 = 自动计算梯度 = 自动计算导数
训练神经网络时,PyTorch 会根据模型构建计算图(Computational Graph):
- 跟踪哪些数据通过哪些操作组合产生输出
- 通过反向传播算法,根据损失函数的梯度调整参数(模型权重)
torch.autograd引擎支持任意计算图的自动梯度计算
2.2 计算图可视化

import torch
from torchviz import make_dot
# 简单模型
x = torch.randn(1, requires_grad=True)
y = x ** 2
# 生成计算图图片
make_dot(y, params={"x": x}).render("y=x²的计算图", format="png")
2.3 梯度基本概念
| 概念 | 解释 |
|---|---|
| 梯度 | 对函数求导的值 |
| 梯度下降法 | 求最优梯度,使损失函数最小 |
| 链式法则 | 复合函数求导,反向传播的核心 |
| 反向传播 | 从后向前求导,传播梯度 |
梯度经典语录:
- 在某一点上,对函数求导得到的值就是该点的梯度
- 在平面上,梯度就是某一点上的斜率
- 反向传播传播的是梯度
- 链式法则中梯度相乘,就是传说中的梯度传播
三、梯度基本计算
3.1 关键前提
PyTorch 不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导。
x是张量 →y必须是标量(一个值)才能求导
3.2 requires_grad:梯度开关
# 默认不开启自动求导
x = torch.arange(9).view(3, 3)
print(x.requires_grad) # False
# 开启自动求导
x = torch.rand(3, 3, requires_grad=True)
print(x.requires_grad) # True
3.3 标量张量求导
# 定义变量(开启梯度追踪)
x = torch.tensor(10, requires_grad=True, dtype=torch.float32)
print("x -->", x)
# 定义曲线:y = x²
y = x ** 2
print("y -->", y)
# 查看梯度函数类型
print(y.grad_fn) # <PowBackward0 object>
# 计算梯度(y 是标量,直接 backward)
y.backward()
# x.grad 就是导数值
print("x 的梯度值是:", x.grad) # 2*10 = 20.0
backward() 执行过程:
- 从输出
y出发,初始化上游梯度dy/dy = 1 - 进入
PowBackward0算子,套用导数公式dy/dx = 2x - 梯度流入
AccumulateGrad节点,把算出的2x存入x.grad - 执行完毕后,
x.grad才有具体数值
3.4 向量张量求导
# 定义向量张量
x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float32)
print("x -->", x)
# 定义曲线
y = x ** 2
print("y -->", y)
# y 是向量,先转成标量再求导
y.sum().backward()
# x.grad 保存每个元素的梯度
print("x.grad -->", x.grad) # tensor([20., 40.])
四、梯度计算注意事项
4.1 不能将求导张量转 NumPy
x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)
# ❌ 报错!不能直接转 NumPy
# print(x.numpy())
# RuntimeError: Can't call numpy() on Tensor that requires grad.
# ✅ 先用 detach() 脱离计算图
x2 = x.detach()
print(x2.numpy())
detach() 的作用:
- 产生一个新的张量,与原始张量共享数据
- 新张量不会自动微分(requires_grad=False)
x.data_ptr()和x2.data_ptr()相同(共享内存)id(x)和id(x2)不同(不同 Python 对象)
4.2 梯度累加问题
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
y.backward()
print(x.grad) # 4.0
# 再次 backward 会累加!
y = x ** 2
y.backward()
print(x.grad) # 8.0(累加了)
# 解决方法:每次 backward 前清零
x.grad.zero_()
五、拼接与微分总结
| 操作 | 方法 | 说明 | 关键参数 |
|---|---|---|---|
| cat | torch.cat(tensors, dim) | 沿现有维度拼接 | dim 指定拼接轴 |
| stack | torch.stack(tensors, dim) | 在新维度拼接 | 增加一个新维度 |
| chunk | torch.chunk(tensor, chunks, dim) | 均分张量 | chunks 份数 |
| split | torch.split(tensor, sizes, dim) | 按大小拆分 | sizes 每份大小 |
| requires_grad | requires_grad=True | 开启梯度追踪 | 默认 False |
| backward | .backward() | 计算梯度 | 标量输出直接调用 |
| detach | .detach() | 脱离计算图 | 共享数据,无梯度 |
| grad | .grad | 访问梯度值 | backward 后才有值 |
| zero_grad | .grad.zero_() | 梯度清零 | 避免累加 |
六、下一篇预告
PyTorch 深度学习笔记(六):PyTorch 实战——线性回归预测房价完整案例
将使用 PyTorch 的各个组件,构建一个带噪声数据的线性回归模型,模拟"房屋面积-房价"预测任务。包括数据准备、模型构建、损失函数、优化器、训练循环和可视化全流程。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!你的支持是我持续创作的动力。
张量拼接与自动微分——构建神经网络基础&spm=1001.2101.3001.5002&articleId=163449196&d=1&t=3&u=2f081e483672412d93d6e3dcadb3edf1)
2509

被折叠的 条评论
为什么被折叠?



