2. 卷积神经网络基础:卷积层、池化层、全连接层的计算模式与数据复用特征

各位同学,咱们今天聊聊CNN的三大核心构件。说实话,我见过不少做AI加速器的同行,一上来就扎进数据流优化里,结果连卷积层到底怎么复用数据都没搞透。嗯,这就像盖楼不看图纸——迟早要返工。

我个人习惯,在讲Eyeriss之前,先把这三层掰开揉碎。你想想看,没有这个基础,后面那些花哨的数据流映射方案,你根本听不懂它在解决什么问题。

2.1 卷积层:计算模式与数据复用

卷积层是CNN的主力。我当年在做一个边缘端加速器项目时,发现90%的计算时间都耗在卷积上。所以,搞懂卷积,你就抓住了加速器设计的命门。

2.4.1 计算模式

说白了,卷积就是一个滑动窗口的乘累加操作。输入特征图和一个卷积核做点积,然后滑到下一个位置继续做。公式长这样:

输出特征图(o, i, h, w) = Σ(Σ(Σ(输入特征图(i, c, h+s, w+t) × 卷积核(o, c, s, t))))

这里o是输出通道,i是输入通道,h和w是空间位置,s和t是卷积核内的偏移。看着复杂?其实就是一个四重循环。

我在项目中遇到过一个问题:很多人以为卷积的计算量就是乘累加次数,其实不对。你得考虑数据搬运的成本。计算一次乘累加可能只要1个时钟周期,但把数据从DDR搬到片上SRAM,可能要几百个周期。这就是为什么数据复用如此重要。

2.1.2 数据复用特征

卷积层的数据复用,说白了就是同一个数据被多次使用。我总结了三种复用类型:

复用类型 描述 复用次数
输入特征图复用 同一个输入像素被多个卷积核使用 等于输出通道数
卷积核复用 同一个卷积核在空间位置上滑动 等于输出特征图的空间尺寸
部分和复用 同一个累加结果被多个输出通道共享 取决于数据流映射

关键洞察:Eyeriss的核心创新,就是通过Row-Stationary数据流,把这三类复用都利用到了极致。它不像传统加速器只盯着一种复用,而是让三种复用同时发生。

举个例子。假设输入特征图是32x32,卷积核是3x3,输出通道是64。那么每个输入像素会被64个卷积核各用一次,这就是输入复用。每个卷积核会在32x32个位置上滑动,这就是权重复用。你想想看,如果不利用这些复用,每次计算都从外面搬数据,那功耗得高到什么程度?

2.2 池化层:计算模式与数据复用

池化层相对简单,但别小看它。我见过不少加速器设计,在池化层上栽了跟头。

2.2.1 计算模式

池化层就是下采样。常见的有最大池化和平均池化。最大池化取窗口内的最大值,平均池化取平均值。公式很简单:

输出特征图(h, w) = max(输入特征图(h*stride + s, w*stride + t))  // 最大池化
输出特征图(h, w) = average(输入特征图(h*stride + s, w*stride + t))  // 平均池化

池化层没有可学习的参数,计算量也小。但它有一个特点:数据复用率极低。为什么?因为池化窗口不重叠(或者重叠很少),每个输入像素只被用一次。

避坑指南:我曾经在一个项目中,试图给池化层做数据复用优化,折腾了两周发现收益几乎为零。后来才明白,池化层的瓶颈不在计算,而在数据搬运。你只需要保证数据流顺畅就行,别在复用上浪费精力。

2.2.2 数据复用特征

池化层的数据复用,说白了就是几乎没有。每个输入像素只参与一次计算。所以加速器设计时,池化层通常走旁路,或者用专门的简单硬件处理。

但有一个细节要注意:池化层的输入来自卷积层的输出。如果你能把卷积层和池化层融合在一起,让池化操作直接在卷积的输出缓冲上完成,就能省掉一次数据搬运。这个技巧我在多个项目中都用过,效果不错。

2.3 全连接层:计算模式与数据复用

全连接层,说白了就是一个大矩阵乘法。输入向量和权重矩阵相乘,加上偏置,再经过激活函数。

2.3.1 计算模式

公式长这样:

输出(j) = Σ(输入(i) × 权重(i, j)) + 偏置(j)

全连接层的计算量取决于输入维度和输出维度。假设输入是4096维,输出是1000维,那么一次前向传播就要做4096×1000=409.6万次乘累加。这个量不小。

我记得有一次做语音识别芯片,全连接层占了模型参数量的80%以上。但计算量其实还好,因为全连接层的计算密度高,容易把硬件利用率做上去。

2.3.2 数据复用特征

全连接层的数据复用,和卷积层完全不同。它的特点是:

  • 权重复用:同一个权重只被用一次(因为每个输入只和每个权重乘一次)
  • 输入复用:同一个输入被所有输出神经元使用,复用次数等于输出维度
  • 部分和复用:没有部分和复用,因为每个输出独立计算

你发现没有?全连接层的复用模式很单一,主要就是输入复用。所以加速器设计时,通常把权重放在片上,输入流式进来,输出累加后写回。

个人经验:全连接层的加速,关键在于权重压缩。我做过一个实验,用剪枝把全连接层的权重稀疏度做到90%以上,计算量直接降了一个数量级。但代价是硬件要支持非规则访存,这个取舍要看你具体的应用场景。

2.4 三层对比:一张图看懂

说了这么多,咱们用一张图总结一下。这张图是我做课程时画的,把三层的计算模式和复用特征放在一起对比,一目了然。

CNN三层核心对比 卷积层 计算模式:滑动窗口乘累加 数据复用:三重复用 • 输入复用:输出通道数 • 权重复用:输出空间尺寸 • 部分和复用:取决于映射 计算量:★★★★★ 池化层 计算模式:下采样取极值/均值 数据复用:几乎无复用 • 每个输入只用一次 • 无权重参数 • 可融合到卷积层 计算量:★★☆☆☆ 全连接层 计算模式:矩阵乘法 数据复用:单一输入复用 • 输入复用:输出维度 • 权重只用一次 • 无部分和复用 计算量:★★★★☆ 核心结论 卷积层是加速器设计的重点(三重复用+高计算量),池化层可融合处理,全连接层需关注权重压缩。 数据复用特征对比 复用类型 卷积层 全连接层 输入复用 高(输出通道数) 高(输出维度) 权重复用 高(空间滑动) 无 部分和复用 中等(取决于映射) 无

2.5 对Eyeriss的启示

好了,基础讲完了。咱们回到Eyeriss。你想想看,Eyeriss为什么选择Row-Stationary数据流?

原因很简单:卷积层有三重复用,但传统加速器只利用了一两种。Eyeriss通过把计算任务按行拆分,让输入、权重和部分和三种复用同时发生。池化层虽然复用少,但Eyeriss通过融合处理避免了额外开销。全连接层虽然复用单一,但Eyeriss的架构也能高效支持。