前向,反向成本计算
我们来详细推导并计算卷积层的前向传播和反向传播在计算成本(乘法与加法次数)和内存占用方面的表达式。
📌 基本设定
- 输入尺寸:$ c_i \times h \times w $
- 卷积核尺寸:$ c_o \times c_i \times k_h \times k_w $
- 填充:$ (p_h, p_w) $
- 步幅:$ (s_h, s_w) $
输出空间尺寸为:
\[h_{out} = \left\lfloor \frac{h + 2p_h - k_h}{s_h} \right\rfloor + 1,\quad
w_{out} = \left\lfloor \frac{w + 2p_w - k_w}{s_w} \right\rfloor + 1
\]
✅ 第一部分:前向传播(Forward Pass)
1. 计算成本(FLOPs)
每个输出点的计算量:
- 卷积核大小:$ k_h \times k_w \times c_i $ 个权重
- 每个权重与输入对应位置相乘 → $ k_h \cdot k_w \cdot c_i $ 次乘法
- 所有结果累加 → $ k_h \cdot k_w \cdot c_i - 1 $ 次加法
所以每个输出点需要:
- 乘法数:$ k_h \cdot k_w \cdot c_i $
- 加法数:$ k_h \cdot k_w \cdot c_i - 1 $
总共有 $ c_o \cdot h_{out} \cdot w_{out} $ 个输出点。
总 FLOPs(乘法 + 加法):
\[\text{FLOPs}_{\text{forward}} = c_o \cdot h_{out} \cdot w_{out} \cdot k_h \cdot k_w \cdot c_i \cdot 2 - c_o \cdot h_{out} \cdot w_{out}
\]
忽略常数项,通常简化为:
\[\boxed{
\text{FLOPs}_{\text{forward}} \approx 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
}
\]
2. 内存占用(Forward Memory)
考虑以下几部分内存开销:
| 组成 | 大小 |
|---|---|
| 输入特征图 | $ c_i \cdot h \cdot w $ |
| 卷积核参数 | $ c_o \cdot c_i \cdot k_h \cdot k_w $ |
| 输出特征图 | $ c_o \cdot h_{out} \cdot w_{out} $ |
注意:前向传播过程中不需要保存中间激活值用于梯度计算(除非显式开启
retain_grad()),因此不额外增加内存。
总内存占用:
\[\boxed{
\text{Memory}_{\text{forward}} = c_i \cdot h \cdot w + c_o \cdot c_i \cdot k_h \cdot k_w + c_o \cdot h_{out} \cdot w_{out}
}
\]
✅ 第二部分:反向传播(Backward Pass)
3. 反向传播的内存占用
反向传播中需要存储的内容包括:
| 组成 | 大小 | 说明 |
|---|---|---|
| 输入特征图缓存 | $ c_i \cdot h \cdot w $ | 前向时保存,用于计算梯度 |
| 权重参数 | $ c_o \cdot c_i \cdot k_h \cdot k_w $ | 用于梯度更新 |
| 输出梯度 | $ c_o \cdot h_{out} \cdot w_{out} $ | 已知 |
| 权重梯度 | $ c_o \cdot c_i \cdot k_h \cdot k_w $ | 计算得到 |
| 输入梯度 | $ c_i \cdot h \cdot w $ | 计算得到 |
总内存占用:
\[\boxed{
\text{Memory}_{\text{backward}} = 2 \cdot c_i \cdot h \cdot w + 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w + c_o \cdot h_{out} \cdot w_{out}
}
\]
4. 反向传播的计算成本(FLOPs)
非常好的问题!我们来详细推导反向传播的计算成本(FLOPs),并解释为什么它是前向传播的 两倍,即:
\[\text{FLOPs}_{\text{backward}} \approx 4 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
\]
🔁 回顾前向传播计算量(FLOPs)
- 输入:$ c_i \times h \times w $
- 卷积核:$ c_o \times c_i \times k_h \times k_w $
- 输出:$ c_o \times h_{out} \times w_{out} $
每个输出点需要:
- $ k_h \cdot k_w \cdot c_i $ 次乘法
- $ k_h \cdot k_w \cdot c_i - 1 $ 次加法
所以每点约 $ 2 \cdot k_h \cdot k_w \cdot c_i $ 次运算。
总 FLOPs:
\[\text{FLOPs}_{\text{forward}} = c_o \cdot h_{out} \cdot w_{out} \cdot 2 \cdot k_h \cdot k_w \cdot c_i
= 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
\]
🔁 反向传播的两个主要部分
在反向传播中,我们需要计算两个梯度:
1️⃣ 对输入的梯度(Backward with respect to input, grad_input)
这一步类似于一个“转置卷积”操作,目的是将输出梯度传递回输入空间。
计算方式:
- 输入梯度尺寸:$ c_i \times h \times w $
- 每个输入通道对每个输出通道进行滑动窗口操作。
- 每次滑动窗口操作涉及 $ k_h \cdot k_w $ 个元素(因为是 kernel 大小)
- 总共有 $ c_i \cdot c_o \cdot h \cdot w $ 个这样的位置(每个输入通道 × 每个输出通道 × 空间位置)
但注意,实际上不是所有位置都参与计算。更准确地说:
- 每个输出通道有 $ h_{out} \cdot w_{out} $ 个激活值
- 每个激活值影响输入图上的 $ k_h \cdot k_w $ 区域
- 所以总的计算次数为:$ c_o \cdot h_{out} \cdot w_{out} \cdot k_h \cdot k_w \cdot c_i $
因此:
\[\text{FLOPs}_{\text{grad\_input}} = 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
\]
和前向传播相同。
2️⃣ 对权重的梯度(Backward with respect to weights, grad_weight)
这一步用于更新卷积核参数。
计算方式:
- 权重梯度尺寸:$ c_o \times c_i \times k_h \times k_w $
- 每个权重需要与对应的局部输入区域做乘加操作
- 每个输出通道 × 输入通道 × kernel 位置 → 需要遍历输出特征图的所有位置
具体来说:
- 每个输出通道有 $ h_{out} \cdot w_{out} $ 个输出点
- 每个点对应一个 $ k_h \cdot k_w $ 的局部区域
- 所以每个 weight 参数对应的乘加次数为:$ h_{out} \cdot w_{out} $
总计算量:
\[\text{FLOPs}_{\text{grad\_weight}} = 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
\]
同样和前向传播相同。
📊 总结反向传播的总计算量
\[\text{FLOPs}_{\text{backward}} = \text{FLOPs}_{\text{grad\_input}} + \text{FLOPs}_{\text{grad\_weight}}
= 2 \cdot (\text{FLOPs}_{\text{forward}})
= 4 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
\]
✅ 最终公式
\[\boxed{
\text{FLOPs}_{\text{backward}} = 4 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out}
}
\]
📝 补充说明
| 项目 | 计算量 |
|---|---|
| 前向传播 | $ 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
| 反向传播(输入梯度) | $ 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
| 反向传播(权重梯度) | $ 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
| 总计反向传播 | $ 4 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
✅ 最终总结表格
| 项目 | 表达式 |
|---|---|
| 前向计算量(FLOPs) | $ 2 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
| 前向内存占用 | $ c_i h w + c_o c_i k_h k_w + c_o h_{out} w_{out} $ |
| 反向计算量(FLOPs) | $ 4 \cdot c_o \cdot c_i \cdot k_h \cdot k_w \cdot h_{out} \cdot w_{out} $ |
| 反向内存占用 | $ 2 c_i h w + 2 c_o c_i k_h k_w + c_o h_{out} w_{out} $ |
如果你有具体的数值(如输入尺寸、kernel大小等),我也可以帮你代入公式快速计算出实际数值。欢迎继续提问!

浙公网安备 33010602011771号