资讯动态

卷积层FLOPs计算全解析:从标准卷积到各类变体

发布时间:2026/10/3 1:01:44 来源:尧图企业网站定制
先把一个最容易被忽略的问题摆在前面你真的会手算一个卷积层的计算量吗我见过不少同学模型跑起来了、精度也调上去了但问他一层的FLOPs是多少他第一反应是打开thop库敲一行代码。工具当然没问题可一旦工具输出和理论对不上、或者你需要在两种结构之间做快速取舍时不会手推公式就非常被动。这个题目看着像教科书内容实际上它是模型设计、论文消融实验、端侧部署前评估成本的基本功。这篇文章我会从标准卷积出发把复杂度公式完整推一遍再把分组卷积、深度可分离卷积、膨胀卷积、转置卷积这些常见变体的复杂度一个个算清楚最后讲几个我在实际项目中踩过的、和复杂度计算有关的坑。内容不绕弯子直接上推导和数字你跟着算一遍以后看任何模型结构都能在脑子里快速估出成本。1. 复杂度公式从哪里来标准卷积的完整推导1.1 先把参数定义清楚输入、核、输出的记号很多推导看起来复杂其实不是公式难而是符号混乱。我先统一记号后面所有推导都用这套。假设输入特征图是 H_in × W_in × C_in卷积核大小是 K × K输出通道数是 C_outpadding 为 Pstride 为 S。那么输出特征图的高度和宽度分别是H_out (H_in 2P - K) / S 1 W_out (W_in 2P - K) / S 1这是空间维度的尺寸变化公式不管你后面用不用它算FLOPs都得先把这个写对。因为卷积计算量的核心是“输出特征图每个位置都要做一次卷积”所以输出尺寸直接决定了乘加次数。这里有一个细节实际工程中输入输出尺寸通常都能整除但在推导公式时最好带着向下取整的写法避免边界情况翻车。严谨一点上面公式应该写成H_out floor((H_in 2P - K) / S) 11.2 一次乘加到底发生几次从滑窗视角推FLOPs现在从最朴素的角度开始。一个 K×K 的卷积核在输入特征图的某个位置上要覆盖 K×K×C_in 个输入值这K×K×C_in个值分别和卷积核对应的权重相乘然后再全部加起来得到输出特征图一个通道上的一个像素值。所以输出特征图上的一个点需要 K × K × C_in 次乘法以及 K × K × C_in - 1 次加法。但在工程统计里乘加操作MACmultiply-accumulate通常被当成一次操作几乎所有深度学习框架的计算量统计也默认一个MAC代表一次乘加组合。那么一个输出通道、整张输出特征图需要多少次MACH_out × W_out × K × K × C_in如果输出有 C_out 个通道标准卷积的总MAC数就是总MACs C_out × H_out × W_out × K × K × C_in这就是标准卷积的时间复杂度。如果换算成FLOPs一个MAC对应两次浮点运算一次乘法、一次加法所以FLOPs 2 × C_out × H_out × W_out × K × K × C_in我一般习惯用MACs作为思考单位因为大部分profile工具默认输出的也是MACs比如thop输出的是MACsptflops虽然名字带FLOPs但它统计的实际上是乘加次数。这一点特别容易搞混后面我再细说。1.3 光记公式不够得知道每个符号为什么这么乘公式本身不难背但如果你只是背下来换个变体就可能不会用了。我拆开说说每个因子的含义。C_in × K × K 这个因子表示一个卷积核在单个空间位置上覆盖了多少输入数据点。你可以把这个积想象成卷积核的“体积”深度方向有C_in个通道每个通道平面是K×K。H_out × W_out 表示输出特征图上有多少个空间位置每个位置都会完整执行一次上述的乘加操作。这个因子的存在意味着分辨率越大计算量增长越明显。事实上输出分辨率通常和输入分辨率同量级所以卷积的计算量是随输入尺寸平方级增长的。C_out 表示我们有C_out个不同的卷积核每个核都会在全部空间位置上扫一遍各自产生一张输出通道图。因此它也是直接乘进去的。把这三个层次搞清楚你就掌握了“空间位置 × 核体积 × 核数量”这个通用框架。后面所有变体的复杂度计算本质上都是在调整这三个因子中的一个或几个。2. 把公式用到工程里实例计算与成本盘算2.1 一个具体例子手算一遍光有公式太抽象拿一个真实例子过一遍。假设输入是 224×224×3 的RGB图像第一层卷积用 3×3 卷积核stride2padding1输出通道数设为32。这个配置在常见分类网络的第一层里很典型。首先算输出尺寸H_out (224 2×1 - 3) / 2 1 112 W_out (224 2×1 - 3) / 2 1 112然后套公式MACs 32 × 112 × 112 × 3 × 3 × 3 32 × 12544 × 27 10838016约等于 1084万 次MAC也就是 10.8 MMACs。换算成FLOPs再乘以2大约是 21.7 MFLOPs。这只是一个卷积层。一个ResNet50输入224×224常用的统计口径是约 4.1 GMACs也就是41亿次乘加。你拿这个数字和单层对比就能感受到深层网络的计算量大多堆在哪里早期层分辨率高、通道数少后期层分辨率低但通道数多中间层往往是计算量最大的区域。2.2 网络是多层的复杂度要逐层累加很多刚入门的人会犯一个错误只算了卷积层忘了把其他层算进去。一个完整模型的总计算量是所有层的累加包括卷积、全连接、池化、归一化等。我做一个MobileNetV1的快速估算。它的核心是深度可分离卷积这个后面专门讲这里先看总账。在224×224输入下MobileNetV1大约有 569M MACs。而一个标准卷积组成的VGG16大约是 15.5G MACs。两个模型精度差不多计算量却差了将近30倍这就是结构选型的价值。所以你在做模型对比时不要只比较精度指标要把计算量、参数量放在同一张表里看。计算量决定推理延迟上限参数量决定存储占用这两个维度很多时候是独立的。一个模型可能参数很少但计算量很大比如全连接层少的卷积网络也可能参数很多但计算量不大比如全连接层被替换成全局平均池化后的结构。2.3 理论FLOPs和实际耗时的差距Roofline视角复杂度公式算出来的是理论计算量但你在实际设备上测到的延迟往往和理论FLOPs不成正比。这个现象我用Roofline模型解释过很多次核心在于硬件实际能达到的算力取决于计算密度也就是每次访存可以喂给计算单元多少有效数据。举个例子1×1卷积的计算密度很高因为它的数据复用特别好每个输入像素可以被C_out个输出通道复用权重和特征图都适合缓存放得下所以GPU上跑起来能接近理论峰值。但深度可分离卷积里的depthwise卷积每个卷积核只作用于一个输入通道数据复用极少访存开销占比很高哪怕FLOPs算下来很低实际跑起来也不一定快。这就是为什么你在移动端做模型加速时只看FLOPs选模型是会被坑的。我吃过这个亏选了一个FLOPs只有对手一半的模型结果在某个特定芯片上跑得比对手还慢因为访存模式太差缓存命中率低。3. 卷积家族改型后复杂度怎么变3.1 分组卷积分组数G如何影响FLOPs分组卷积最早在AlexNet里出现原因是当时两块GPU显存放不下整个模型把通道拆成两组分别计算。后来它成了高效网络设计的重要工具。分组卷积的思想是把输入通道分成G组每组包含 C_in / G 个通道输出通道也对应分成G组每组包含 C_out / G 个通道。组与组之间不互通只在组内做标准卷积。对应的MACs公式MACs_group G × (C_out / G) × H_out × W_out × (C_in / G) × K × K C_out × H_out × W_out × (C_in / G) × K × K看到没分组数G直接除到了C_in上。和标准卷积相比计算量变成了原来的 1/G。这意味着分组数越大计算量越小当G C_in时就退化成了depthwise卷积。但分组卷积不是免费的午餐。组数增加了每组能看到的输入通道就少了特征交互能力下降。这直接影响了模型表达能力ShuffleNet之所以要在分组卷积后加channel shuffle就是在弥补组间信息不流通的问题。3.2 深度可分离卷积为什么MobileNet能省这么多深度可分离卷积是MobileNet系列的基石它把标准卷积拆成两步depthwise卷积加pointwise卷积。第一步depthwise卷积每个输入通道单独用一个K×K卷积核处理输出通道数等于输入通道数计算量为MACs_dw C_in × H_out × W_out × K × K注意这里没有C_out因子因为没有跨通道融合。第二步pointwise卷积用1×1卷积把通道数从C_in变换到C_out计算量为MACs_pw C_out × H_out × W_out × C_in总和MACs_dsc C_in × H_out × W_out × (K × K C_out)对比标准卷积MACs_std C_out × H_out × W_out × C_in × K × K两者比值MACs_dsc / MACs_std 1 / C_out 1 / (K × K)拿常见的3×3卷积核、输出64通道来算比值大约是 1/64 1/9 ≈ 0.126。也就是说深度可分离卷积大约能把计算量降到标准卷积的八分之一左右。这就是MobileNetV1能在保持相近精度的前提下把计算量做下来的核心原因。不过这里要特别提醒这个比值是在理想情况下成立的理论结论。实际部署时depthwise卷积在GPU上的效率往往不如理论值因为它的并行度和数据复用都远低于标准卷积。这也是为什么后来的MobileNetV2在结构上做了很多调整不只是图省计算量还要考虑硬件执行效率。3.3 膨胀卷积感受野扩大但计算量怎么变膨胀卷积dilated convolution通过在不增加参数量的前提下让卷积核覆盖更大的区域来扩大感受野。它的核心参数是膨胀率d卷积核在输入上采样时相邻权重之间间隔d-1个像素。很多人误以为膨胀卷积的计算量会变大因为感受野变大了。实际上参与计算的权重数量没有变仍然是 K × K 个只不过它们对应的输入位置被拉远了。MACs_dilated C_out × H_out × W_out × K × K × C_in和标准卷积一模一样。差异只在于输出特征图的尺寸可能会因为padding策略不同而改变但单看每个点的计算量公式形式不变。这里有一个工程上的坑膨胀卷积在GPU上不一定比标准卷积慢但取决于实现方式。如果你直接按膨胀的方式读取输入会产生非连续的内存访问影响缓存效率。有些框架会用等效的稀疏卷积实现实际效率差异很大。我在做语义分割模型时把标准卷积换成膨胀卷积后FLOPs没变但实际推理延迟涨了不少就是这个原因。3.4 转置卷积上采样时FLOPs怎么算转置卷积transposed convolution常被叫成反卷积用于上采样比如语义分割的decoder部分。它的计算量计算比标准卷积稍微绕一点。理解转置卷积的一个有效方式它的前向传播等价于标准卷积的反向传播。如果你要在数学上严格推导它做的事情是把输入通过补零、插值的方式扩展到更大尺寸然后做一次标准卷积。多数情况下我们可以用下面的近似公式来估算MACs_transposed ≈ C_in × C_out × H_out × W_out × K × K其中H_out和W_out是转置卷积的输出尺寸。和标准卷积的公式对比差别只在H_out和W_out是变大而不是变小。但注意转置卷积的实际计算过程远比这个公式复杂。因为输入像素会被映射到输出图上的多个位置重叠区域要做累加。实际工程中cudnn会用col2im或类似的底层算法实现内存开销比标准卷积大不少。如果你在显存受限的场景做上采样转置卷积并不是最经济的选择可以考虑用插值上采样加标准卷积替代。3.5 1×1卷积被低估的大计算量来源1×1卷积看起来简单就是KK1的标准卷积。它的公式退化为MACs_1x1 C_out × H_out × W_out × C_in这个公式极其常见因为在很多网络里1×1卷积是通道变换的主力比如ResNet的bottleneck结构先1×1降维再3×3最后1×1升维。这些1×1卷积加起来往往占总计算量的相当大比例。举个例子ResNet50的bottleneck第一层把256通道降到64通道输入分辨率是56×56那么这一层的MACs 64 × 56 × 56 × 256 51485056大约51M。一套流程下来降维和升维两个1×1层加起来超过100M而中间的3×3层因为通道数降到了64反而只有约28M。所以1×1卷积是隐形的计算量大户做模型压缩时千万别只盯着3×3卷积。4. 其他常见层的复杂度与避坑清单4.1 池化、全连接、激活函数的成本池化层的计算量通常被忽略不计但从严格角度平均池化还需要做除法操作最大池化则完全不需要乘法只做比较运算。在FLOPs统计上很多库直接跳过池化层因为它的成本相对卷积和全连接来说太小了。全连接层的计算量公式很直接MACs_fc C_in × C_out其中C_in是输入特征维数C_out是输出维数。如果输入是展开的特征图C_in等于所有空间位置和通道数的乘积。全连接层的问题是参数量和计算量都集中在矩阵乘法里一旦特征维数很大成本会非常夸张。这也是为什么现代网络普遍用全局平均池化代替最后的全连接层。激活函数ReLU、Sigmoid等在传统的FLOPs统计里通常不单独计入因为它们的计算量相对卷积来说非常小。但在一些特殊硬件上激活函数可能触发额外的内存读写实际成本并没有想象那么低。我做端侧部署时遇到过一个模型因为GELU激活函数在某个低端芯片上没有硬件加速导致推理延迟比理论预期高了一倍。4.2 推导时最容易出错的三个细节第一个坑是FLOPs和MACs的混淆。这两个术语在论文里经常被混用有的论文写FLOPs实际指的是MACs有的库输出FLOPs但按MACs统计。换算关系是FLOPs 2 × MACs。我在对比论文数据时一定会先确认对方用的哪个口径否则数字直接比是没意义的。第二个坑是忘记batch size。上面所有公式都是针对单张输入的。如果输入是batch为B的数据总计算量要乘以B。thop这类工具默认batch1所以它输出的数字只是一个样本的成本。但在评估训练耗时或者GPU利用率时必须乘上batch size。第三个坑是偏置项。标准卷积通常会加偏置每个输出位置多一次加法。在FLOPs统计里这个加法有时被计入有时不计入。对于大卷积核来说偏置的影响微乎其微但1×1卷积输出位置很多累加起来也有一定比例。目前绝大多数工具都忽略偏置你在手算时保持一致即可关键是在同一个体系内不要一会儿算一会儿不算。4.3 复杂度公式在模型选型和压缩中的实际用途公式不只是考试题它是你做技术决策的工具。我举三个真实场景。第一个场景是模型选型。你要在移动端跑一个图像分类任务云端已经有一个精度96%的大模型但它有200M FLOPs手机跑不动。你有两个候选一个80M FLOPs精度95%另一个60M FLOPs精度94%。这时候你只需要把总FLOPs除以芯片的算力上限就能估算出大致推理时间再对比精度损失做出取舍。这比把两个模型都部署到真机上测要快得多。第二个场景是网络结构设计。你在搭一个分割网络解码器的转置卷积导致FLOPs暴涨。用公式算一下发现是上采样分辨率太大导致的于是你把转置卷积替换成插值上采样加3×3卷积FLOPs立刻降了一个量级精度基本不受影响。第三个场景是模型压缩。你被要求把模型的FLOPs降低30%用公式逐层扫描计算结果发现某几个中间层的通道数可以减半。改完之后用公式重新算一遍确认达标再开始训练验证精度。这个流程在论文的实验章节里特别常见你学会手推公式就能独立复现这类分析。最后再分享一个我在实际项目中经常用的验证方法无论你用thop还是ptflops还是手算结果出来之后挑一层结构简单的自己用公式手算一遍两边对照。只要有一次对不上就说明你对这个工具或者这个结构的理解有偏差排查清楚再继续往下走。这个方法帮我抓出过不少问题包括我自己代码里padding设置错了导致输出尺寸和预期不一致这种低级错误。工具好用但验证工具的人始终得是你自己。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑