4、卷积神经网络基础:卷积运算的数学定义、卷积核、步长、填充、输出尺寸计算

在深入 TFLite Micro 的 Conv2D 算子实现之前,必须先建立扎实的卷积神经网络(CNN)基础。卷积运算不仅是特征提取的核心数学工具,更是理解内存布局、计算量估算、算子优化策略的前提。本章将系统梳理卷积运算的数学本质、卷积核与特征图的关系、步长与填充的作用机制,以及输出尺寸的精确计算方法。这些概念将直接映射到后续章节中 TFLite Micro 的算子注册、维度推导和内核循环实现中。

Conv2D 运算核心要素关系图 输入特征图 H_in × W_in × C_in (如 32×32×3) 卷积核 (Kernel) K_h × K_w × C_in (如 3×3×3) 输出特征图 H_out × W_out × C_out (如 30×30×8) 步长 (Stride) 滑动窗口每次移动的像素数 stride=1 → 密集采样 stride=2 → 下采样 填充 (Padding) 在输入边缘补零或镜像 'valid' → 无填充 'same' → 保持尺寸 输出尺寸公式 H_out = ⌊(H_in + 2P - K_h)/S⌋ + 1 W_out = ⌊(W_in + 2P - K_w)/S⌋ + 1 TFLite Micro 维度推导核心: padding 属性 + stride 参数 + kernel 尺寸 → 确定输出张量形状 内存分配与算子调度均依赖此计算结果

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 中,运行时直接加载到内存参与计算。

核心理解:卷积运算的数学本质是"滑动窗口内的加权求和"。在嵌入式环境中,这个操作被拆解为三层循环:输出通道循环、输出像素循环、乘累加内循环。TFLite Micro 的优化重点就在于如何高效组织这三层循环,充分利用 MCU 的 DSP 指令和有限的 SRAM。

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 的边界检查。

工程经验:在 MCU 上,'same' 填充虽然方便,但会引入额外的内存拷贝和边界判断开销。如果模型允许,优先使用 'valid' 填充配合步长 2 进行下采样,可以减少约 25% 的内存访问量。TFLite Micro 的 Arena 内存分配器会为填充预留空间,但过多的填充会降低缓存命中率。

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 的依据。

实际计算时需要注意几个边界条件:

  1. 核尺寸与填充的奇偶性:当卷积核为偶数(如 2×2)时,'same' 填充需要不对称分配(上方多补一行或下方多补一行),TFLite 规范中约定优先在左上侧填充。
  2. 步长大于 1 时的整除问题:如果 \((H_in + 2P - K)\) 不能被 \(S\) 整除,则向下取整,意味着部分边缘像素不会被卷积核覆盖。
  3. 空洞卷积(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 上会直接报错并停止推理。

常见陷阱:在 MCU 上调试卷积层时,最容易犯的错误是手动计算输出尺寸与框架计算结果不一致。这通常源于对填充模式的理解偏差——TFLite 的 'same' 填充在步长大于 1 时,输出尺寸并非简单地等于输入尺寸除以步长,而是需要严格套用公式。建议在开发板上打印实际输出 shape 与预期值对比,而不是凭直觉判断。

4.6 常见误区与总结复盘

在嵌入式部署场景中,关于卷积基础概念的误区往往会导致严重的性能问题或功能错误。以下是几个高频误区:

  • 误区一:混淆互相关与数学卷积。深度学习框架中的卷积不翻转核,因此反向传播时的梯度计算与数学卷积不同。在手动实现算子时,务必按照互相关方式编写循环。
  • 误区二:忽略填充对内存的影响。'same' 填充虽然方便,但在 MCU 上意味着输入张量需要额外的边缘补零区域。如果 TensorArena 分配不当,可能导致内存越界访问。
  • 误区三:步长与池化层混用。步长 2 的卷积可以替代最大池化,但两者的信息保留方式不同。步长卷积会丢失部分位置信息,而池化保留局部最大值。在量化模型中,步长卷积的量化误差通常比池化更大。
  • 误区四:输出尺寸计算时忽略向下取整。当输入尺寸、核尺寸和步长不匹配时,向下取整会导致输出尺寸比预期小 1。这在连续多层卷积后可能累积误差,最终导致全连接层输入维度不匹配。