PyTorch 深度学习笔记(五)张量拼接与自动微分——构建神经网络基础

📅 2026/8/4 6:03:03 👁️ 阅读次数 📝 编程学习
PyTorch 深度学习笔记(五)张量拼接与自动微分——构建神经网络基础

系列导读:本系列共 6 篇,从 PyTorch 框架入门到实战案例,带你系统掌握深度学习开发。上一篇:张量运算与变形——数值计算与索引进阶

一、张量拼接操作

1.1 cat / concat:沿现有维度拼接

torch.cat沿现有维度连接一系列张量,不改变维度数

importtorch# 初始化数据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=[]foriinrange(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))# 64print(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)

  1. 跟踪哪些数据通过哪些操作组合产生输出
  2. 通过反向传播算法,根据损失函数的梯度调整参数(模型权重)
  3. torch.autograd引擎支持任意计算图的自动梯度计算

2.2 计算图可视化

importtorchfromtorchvizimportmake_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**2print("y -->",y)# 查看梯度函数类型print(y.grad_fn)# <PowBackward0 object># 计算梯度(y 是标量,直接 backward)y.backward()# x.grad 就是导数值print("x 的梯度值是:",x.grad)# 2*10 = 20.0

backward() 执行过程

  1. 从输出y出发,初始化上游梯度dy/dy = 1
  2. 进入PowBackward0算子,套用导数公式dy/dx = 2x
  3. 梯度流入AccumulateGrad节点,把算出的2x存入x.grad
  4. 执行完毕后,x.grad才有具体数值

3.4 向量张量求导

# 定义向量张量x=torch.tensor([10,20],requires_grad=True,dtype=torch.float32)print("x -->",x)# 定义曲线y=x**2print("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**2y.backward()print(x.grad)# 4.0# 再次 backward 会累加!y=x**2y.backward()print(x.grad)# 8.0(累加了)# 解决方法:每次 backward 前清零x.grad.zero_()

五、拼接与微分总结

操作方法说明关键参数
cattorch.cat(tensors, dim)沿现有维度拼接dim 指定拼接轴
stacktorch.stack(tensors, dim)在新维度拼接增加一个新维度
chunktorch.chunk(tensor, chunks, dim)均分张量chunks 份数
splittorch.split(tensor, sizes, dim)按大小拆分sizes 每份大小
requires_gradrequires_grad=True开启梯度追踪默认 False
backward.backward()计算梯度标量输出直接调用
detach.detach()脱离计算图共享数据,无梯度
grad.grad访问梯度值backward 后才有值
zero_grad.grad.zero_()梯度清零避免累加

六、下一篇预告

PyTorch 深度学习笔记(六):PyTorch 实战——线性回归预测房价完整案例

将使用 PyTorch 的各个组件,构建一个带噪声数据的线性回归模型,模拟"房屋面积-房价"预测任务。包括数据准备、模型构建、损失函数、优化器、训练循环和可视化全流程。


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!你的支持是我持续创作的动力。