残差结构(Residual Connection)是 ResNet 提出来的经典设计,核心思想非常朴素:
我把输入x加到输出f(x)上,比让网络完全靠f(x)更容易学习。
但问题来了👇 两个东西能加法运算,必须是同样形状! 就像不能把一条裤子和一件秋衣叠在一起一样😆。
当残差连接遇到维度不一致(维度 mismatch),怎么办? 常见有三种主要办法。
🧠 什么时候会出现维度不一致?
比如:
| 情况 | 描述 |
|---|---|
| 通道数不一致 | 卷积改变了通道,例如 64→128 |
| 尺寸不一致 | 特征图 H、W 或 T 发生变化 |
| 多模态特征融合 | skeleton、RGB、flow融合 |
| 深层网络重复下采样 | stride=2 |
举个"奶奶能懂"的例子: 输入是一盘饺子 ,输出是一盘包子------数量都变了,这俩盘子不能直接相加,必须转化!
🛠️ 方法一:Padding/截断(Pad or Crop)
📌 原理
- 维度不一致时,把小的补齐(padding),或者把大的截断,使尺寸一致。
✔️ 优点
-
轻量、简单
-
运算开销小
❌ 缺点
- 不是严格学习式对齐,存在信息损失(crop)或无意义补零(pad)
📌 适用场景
-
临时拼接
-
时间序列中滑动窗口
🧩 核心代码示例(PyTorch)
python
def pad_or_crop(x, y):
# 假设最后一个维度不一致
if x.shape[-1] < y.shape[-1]:
pad_size = y.shape[-1] - x.shape[-1]
x = torch.nn.functional.pad(x, (0, pad_size))
else:
x = x[..., :y.shape[-1]]
return x + y
🛠️ 方法二:卷积做投影(Projection via 1×1 Conv)
📌 原理
给残差项 x 加个 1x1卷积 ,做线性映射,让它和 f(x) 一致。
数学上:
y = f(x) + W * x
✔️ 优点
-
参数可学习
-
严格保留信息
-
卷积还能改变通道数/尺寸
❌ 缺点
- 计算量比 padding 大
📌 适用场景
-
ResNet经典用法
-
GCN/ST-GCN 中通道数变化
-
下采样模块
🧩 核心代码示例(PyTorch)
python
import torch.nn as nn
class ResidualConv(nn.Module):
def __init__(self, in_ch, out_ch, stride=1):
super().__init__()
self.conv = nn.Conv2d(in_ch, out_ch, kernel_size=3, padding=1, stride=stride)
self.proj = nn.Conv2d(in_ch, out_ch, kernel_size=1, stride=stride)
def forward(self, x):
return self.conv(x) + self.proj(x)
非常像"奶奶做饺子馅"------ 啥样的肉馅搅进啥样的蔬菜,全靠那把大菜刀来"投影融合"🤭
🛠️ 方法三:线性映射(Fully Connected Projection)
📌 原理
当模型是序列 (文本、骨骼序列 Temporal),卷积就不方便了,这时用 Linear 层 做投影。
y = f(x) + Linear(x)
✔️ 优点
-
适用于序列
-
对时间维度友好
❌ 缺点
- 参数量可能较多
📌 适用场景
-
NLP Transformer
-
骨骼动作序列(ST-GCN)
-
TAL 时间特征融合
🧩 核心代码示例
python
import torch.nn as nn
class ResidualLinear(nn.Module):
def __init__(self, dim_in, dim_out):
super().__init__()
self.linear = nn.Linear(dim_in, dim_out)
def forward(self, x, fx):
return fx + self.linear(x)
🛠️ 方法四:上采样/下采样(Upsample/Downsample)
如果维度差异来自尺寸(空间或时间),可以先调尺寸再相加。
比如 ST-GCN 时 Temporal stride=2:
🧩 代码示例
python
import torch.nn.functional as F
def match_size(x, y):
if x.shape[-1] != y.shape[-1]:
x = F.interpolate(x, size=y.shape[-1], mode='nearest')
return x + y
📊 四种方案优缺点总结
| 方法 | 是否增加参数 | 计算复杂度 | 信息保留 | 常用程度 |
|---|---|---|---|---|
| 1×1卷积 | 有 | 中等 | ✔️很好 | ⭐⭐⭐⭐⭐ |
| 线性投影 | 有 | 中低 | ✔️较好 | ⭐⭐⭐ |
| 零填充 | 无 | 低 | ❌一般 | ⭐⭐ |
| Pooling降维 | 无 | 低 | ❌较差 | ⭐⭐ |
📌 总结
不同方案各有千秋,用得恰如其分才是高手。 正所谓:
"工欲善其事,必先利其器;器用得当,事半功倍。"
残差维度不一致是一块"绊脚石", 搞明白之后,它又变成你模型里的一块"垫脚石"🚀