4、卷积神经网络基础:卷积运算的数学定义、卷积核、步长、填充、输出尺寸计算
在深入 TFLite Micro 的 Conv2D 算子实现之前,必须先建立扎实的卷积神经网络(CNN)基础。卷积运算不仅是特征提取的核心数学工具,更是理解内存布局、计算量估算、算子优化策略的前提。本章将系统梳理卷积运算的数学本质、卷积核与特征图的关系、步长与填充的作用机制,以及输出尺寸的精确计算方法。这些概念将直接映射到后续章节中 TFLite Micro 的算子注册、维度推导和内核循环实现中。
4.1 卷积运算的数学定义与本质
卷积神经网络中的"卷积"并非严格意义上的数学卷积(数学卷积需要将核翻转 180°),而是互相关(cross-correlation)运算。在深度学习框架中,我们约定俗成地将其称为卷积,但实际执行的是逐元素乘累加操作。对于输入特征图 \(X\) 和卷积核 \(W\),在位置 \((i,j)\) 的输出值 \(Y[i,j]\) 定义为:
Y[i,j] = Σ_{m=0}^{K_h-1} Σ_{n=0}^{K_w-1} Σ_{c=0}^{C_in-1} X[i+m, j+n, c] × W[m, n, c] + b
其中 \(K_h\) 和 \(K_w\) 是卷积核的高度和宽度,\(C_in\) 是输入通道数,\(b\) 是偏置项。这个公式揭示了卷积运算的三个关键特征:局部连接(每个输出像素只与输入的一个局部窗口相关)、权值共享(同一个卷积核在整个输入上滑动)、通道聚合(所有输入通道的乘累加结果求和)。
从信号处理视角看,卷积核本质上是一个特征检测器。例如,一个 3×3 的 Sobel 边缘检测核可以提取水平边缘,而经过学习的卷积核能够自动发现更复杂的模式。在 MCU 上部署时,卷积核的数值通常来自训练后的量化模型,TFLite Micro 会将这些权重以 int8 或 uint8 格式存储在 Flash 中,运行时直接加载到内存参与计算。
4.2 卷积核:维度、数量与参数共享
卷积核的维度由 输出通道数 \(C_out\) 决定。具体来说,一组卷积核的 shape 为 \([C_out, K_h, K_w, C_in]\),其中每个输出通道对应一个独立的 \(K_h \times K_w \times C_in\) 三维核。例如,一个标准的 3×3 卷积层,输入 3 通道、输出 8 通道,则卷积核总参数量为 \(8 \times 3 \times 3 \times 3 = 216\) 个权重值加上 8 个偏置。
参数共享是 CNN 高效的关键:无论输入特征图多大,每个输出通道只使用一个卷积核在整个空间维度上滑动。这意味着模型的参数量与输入尺寸无关,只与核尺寸、通道数相关。在 TFLite Micro 中,这种共享特性使得权重存储非常紧凑——一个 8 位量化的 3×3 卷积层,216 个权重仅需 216 字节 Flash 空间。
| 卷积核配置 | 输入通道 | 输出通道 | 核尺寸 | 参数量 | 计算量(单像素) |
|---|---|---|---|---|---|
| 标准 3×3 | 3 | 8 | 3×3 | 216 | 216 次乘加 |
| 深度可分离 | 3 | 8 | 3×3 + 1×1 | 27 + 24 = 51 | 27 + 24 = 51 次乘加 |
| 1×1 卷积 | 16 | 32 | 1×1 | 512 | 512 次乘加 |
上表对比了不同卷积核配置的参数量与计算量。值得注意的是,深度可分离卷积将标准卷积分解为逐通道卷积和逐点卷积,参数量大幅减少,这正是 MobileNet 系列能在 MCU 上运行的关键设计。TFLite Micro 对深度可分离卷积有专门的内核实现,后续章节会详细拆解。
4.3 步长(Stride)与填充(Padding)的作用机制
步长控制卷积核在输入上滑动的间隔。步长为 1 时,卷积核逐像素移动,输出特征图尺寸与输入接近(取决于填充);步长为 2 时,卷积核每次跳过 1 个像素,输出尺寸约减半,起到下采样作用。在 MCU 场景中,步长 2 常用于替代池化层,减少后续层的计算量。
填充是在输入特征图边缘补零(zero-padding)或复制边缘像素的操作。填充的主要目的有两个:一是控制输出尺寸,避免卷积后特征图快速缩小;二是保留边缘信息,使边缘像素也能被卷积核充分覆盖。TFLite Micro 支持两种填充模式:
- 'valid'(有效填充):不进行任何填充,输出尺寸严格按公式计算,通常小于输入尺寸。
- 'same'(相同填充):自动计算填充量,使输出尺寸与输入尺寸相同(当步长为 1 时)。填充量 \(P\) 的计算公式为:\(P = \lfloor K/2 \rfloor\)(对于奇数核)。
在 TFLite Micro 的算子实现中,填充逻辑被编译为固定参数。对于 'same' 模式,框架会在输入张量周围分配额外的内存区域并置零,然后执行卷积。这意味着实际的内存访问范围比输入张量本身略大,开发者需要留意 SRAM 的边界检查。
4.4 输出尺寸计算:公式、推导与边界条件
输出特征图的尺寸由输入尺寸、卷积核尺寸、步长和填充共同决定。通用计算公式为:
H_out = floor((H_in + 2 × P_h - K_h) / S) + 1
W_out = floor((W_in + 2 × P_w - K_w) / S) + 1
其中 \(P_h\) 和 \(P_w\) 分别是高度和宽度方向的填充量,\(S\) 是步长(通常高度和宽度方向一致)。这个公式适用于所有标准卷积层,也是 TFLite Micro 在解析 TFLite 模型时计算输出张量 shape 的依据。
实际计算时需要注意几个边界条件:
- 核尺寸与填充的奇偶性:当卷积核为偶数(如 2×2)时,'same' 填充需要不对称分配(上方多补一行或下方多补一行),TFLite 规范中约定优先在左上侧填充。
- 步长大于 1 时的整除问题:如果 \((H_in + 2P - K)\) 不能被 \(S\) 整除,则向下取整,意味着部分边缘像素不会被卷积核覆盖。
- 空洞卷积(Dilated Convolution):虽然 TFLite Micro 基础版本不支持空洞卷积,但理解其扩展公式(将 \(K\) 替换为 \(K + (K-1) \times (d-1)\))有助于阅读部分量化模型的转换日志。
4.5 案例拆解:从 TFLite 模型到 MCU 维度推导
假设我们有一个用于关键词唤醒的 CNN 模型,输入为 16 kHz 音频提取的 40 维 MFCC 特征,时间帧数为 20,即输入张量 shape 为 [1, 20, 40, 1](batch=1, height=20, width=40, channels=1)。第一层卷积配置如下:
- 卷积核:8 个 3×3 核,输入通道 1
- 步长:1
- 填充:'same'
根据公式,\(P = \lfloor 3/2 \rfloor = 1\),则:
H_out = floor((20 + 2×1 - 3) / 1) + 1 = 20
W_out = floor((40 + 2×1 - 3) / 1) + 1 = 40
输出 shape 为 [1, 20, 40, 8],与输入空间尺寸一致,通道数从 1 扩展到 8。该层参数量为 \(8 \times 3 \times 3 \times 1 = 72\) 个权重,加上 8 个偏置,共 80 个参数。若采用 int8 量化,Flash 占用仅 80 字节。
在 TFLite Micro 中,这个维度推导过程发生在模型加载阶段。框架会调用 Prepare() 函数,根据 TFLite 模型文件中的算子参数(stride、padding、kernel 尺寸)计算输出张量的 shape,并在 TensorArena 中分配对应大小的内存。如果计算错误或内存不足,MCU 上会直接报错并停止推理。
4.6 常见误区与总结复盘
在嵌入式部署场景中,关于卷积基础概念的误区往往会导致严重的性能问题或功能错误。以下是几个高频误区:
- 误区一:混淆互相关与数学卷积。深度学习框架中的卷积不翻转核,因此反向传播时的梯度计算与数学卷积不同。在手动实现算子时,务必按照互相关方式编写循环。
- 误区二:忽略填充对内存的影响。'same' 填充虽然方便,但在 MCU 上意味着输入张量需要额外的边缘补零区域。如果 TensorArena 分配不当,可能导致内存越界访问。
- 误区三:步长与池化层混用。步长 2 的卷积可以替代最大池化,但两者的信息保留方式不同。步长卷积会丢失部分位置信息,而池化保留局部最大值。在量化模型中,步长卷积的量化误差通常比池化更大。
- 误区四:输出尺寸计算时忽略向下取整。当输入尺寸、核尺寸和步长不匹配时,向下取整会导致输出尺寸比预期小 1。这在连续多层卷积后可能累积误差,最终导致全连接层输入维度不匹配。