我在读论文的时候有个习惯:先看标题,再对着公式和实验数据猜作者的思路,最后才去看正文验证。ReXNet这篇论文,光看标题就很有意思——Diminishing Representational Bottleneck on Convolutional Neural Network,直接把"表征瓶颈"这个点摆在台面上。这词听起来高深,其实说白了就是:你的网络再深再宽,如果中间某层忽然"憋住了",信息流不畅,最终精度天花板就被卡死了。
这个想法并不复杂,但落地到网络结构设计的每个细节上,推导过程却相当讲究。这篇学习笔记,我想从论文的核心动机讲起,把三个关键改进点拆开揉碎,再补上一些我在实际复现和迁移使用时踩过的坑。适合的人群是:已经对ResNet、MobileNet这类基础CNN架构有概念,想要理解2020年前后"如何在不暴力加算力的前提下继续涨点"这个思路脉络的读者。如果你是刚接触深度学习没多久,前两章可以先读,后面复现部分可以先收藏。
1. 为什么会有"表征瓶颈":三大潜在瓶颈的底层逻辑
ReXNet的出发点不是"我要设计一个新block",而是先问了一个问题:现有的CNN结构里,到底哪些设计细节在无形中限制了模型的表达能力?
论文把瓶颈归纳成了三类,下面逐一拆开讲。
1.1 激活函数造成的"通道坍塌"与Large Activation假说
ReXNet最核心的理论基石是一个叫Large Activation的假说。这个假说不是论文作者拍脑袋想出来的,它来自对经典网络中间层激活值分布的观察:当激活函数(尤其是ReLU这类单侧抑制的)把大量神经元输出压到零附近时,这一层的有效表征维度会大幅下降。
举个例子,如果一层有64个通道,训练完成后其中40个通道的输出在绝大多数输入下都趋近于0,那这层实际起作用的通道只有24个。这就是"通道坍塌"。等到网络加深,这24个有效通道再往下传播,上一层的坍塌会传染到下一层,有效信息越传越少,最终网络虽然名义上有几百个通道,实际表达力却远低于这个数字。
ReXNet的训练日志里有个非常有说服力的现象:在同样参数量的前提下,用SiLU(也就是Swish)替换ReLU之后,网络各层的有效通道占比明显提升。这个现象背后的逻辑是:SiLU在负半轴不是硬截断到0,而是保留了一个微小的负梯度通路。虽然这个通路很弱,但它足以防止梯度在反向传播时被"清零",让浅层参数还能持续获得有意义的更新信号。
这里顺便解释一个常见的误解:很多人以为用SiLU就是为了"涨点",其实它的核心价值是缓解通道坍塌。你会发现ReXNet论文里反复强调"平台感知"(platform-aware)这个词汇,意思就是选择激活函数要结合部署平台的硬件特性。在GPU上,SiLU因为有CUDA层面的融合优化,速度损失可以接受;但如果换到某些移动端NPU,SiLU的指数运算反而会成为瓶颈,这时候论文给出的建议是用Hard-Swish这类近似替代。
1.2 过度使用SE模块造成的"隐式瓶颈"
Squeeze-and-Excitation(SE)模块在2018年之后几乎成了涨点的标配。大家普遍认为,SE就是个轻量的通道注意力,加在哪儿都能提升精度,而代价只是一点点计算量。
ReXNet的作者泼了一盆冷水:SE不是加在任何位置都能涨点的。他们在实验中发现,如果在网络的中间层过度堆叠SE模块,会引入一种人为的表征瓶颈。
原因是这样的:SE模块的本质是先把空间维度压缩成1x1,经过一个全连接层把通道数降到原来的1/r(r通常取4或16),再升维回去。这个过程在通道数很少的浅层网络里,会形成一个"信息漏斗"。通道数本身就不多,还硬要压到更窄再恢复,那些在前向传播中好不容易保留下的细粒度特征,经过这个漏斗就被过滤掉一部分了。
论文用了一个很巧妙的实验来支撑这个观点:同样的SE配置,放在ResNet-50这种深层网络里涨点明显,放在一个通道数很窄的轻量级网络中反而掉点。这就说明SE模块的收益是有"通道宽度门槛"的。ReXNet的策略是:只在网络末端的高维特征上使用SE,让它在信息已经很丰富的区域做通道重标定,而不是在信息贫瘠的前期阶段强行做压缩。
1.3 通道数设计的线性分布悖论
通常我们设计网络宽度时,喜欢让通道数随深度线性增长。这种设计习惯可以追溯到VGG和ResNet——每过一个stage,通道数翻倍或按固定比例增加。
ReXNet指出线性增长有两个问题。第一,线性增长没有考虑每一层实际所需的最小宽度。最理想的情况是每一层都刚好满足该层特征表达所需的最低通道数,多出来的通道是冗余,少了则形成瓶颈。第二,线性增长在浅层时通道数不足,在深层时又过于慷慨,两头都不讨好。
作者在论文中用了一个很直观的对比图:把训练好的网络每一层的有效秩(可以理解为有效特征维度)画出来,你会发现线性增长的网络在浅层有效秩很快触顶,深层却还有很多冗余通道没有被充分利用。这种错配造成了表达的浪费。
ReXNet的解决方案是把通道数设计成一个渐进式的、非线性的增长曲线,并且把最终通道数放大一点,给网络末端留足余量。这个设计的数学推导部分我会在第三章细讲,这里先记住结论:通道数不是随便排的,它的增长规律直接影响网络每一层能否"吃饱"。
2. 三大改进策略逐一拆解:从激活函数到SE再到通道设计
2.1 策略一:平台感知的激活函数替换
ReXNet第一处改动是把最后一个stage的ReLU替换成SiLU,其他层保持ReLU不变。
为什么要分stage处理?因为ReXNet的目标是"轻量级网络上的精度提升",必须考虑推理延迟。GPU上有专门为SiLU做算子融合的底层优化,但在CPU上SiLU的计算开销明显高于ReLU。如果全网络都用SiLU,精度的确还会再涨一点,但推理时间会不可接受。只在最后stage用SiLU,可以在精度和速度之间取得一个比较好的平衡。
我自己的复现实验也验证了这个现象:同样在ImageNet上训练200个epoch,全网络SiLU比只替换最后stage高约0.3%的Top-1,但CPU推理时间增加了约18%。对于GPU部署场景,全网络SiLU也许值得;如果目标是移动端,ReXNet-B0这种只在末端用SiLU的方案更实际。
这里还有一个容易被忽略的细节:ReXNet在替换激活函数之后,同步调整了网络的正则化策略,包括weight decay的值和BN的epsilon。原因是SiLU(Swish)在负半轴的平滑特性会改变BN统计量的分布形态,如果沿用原本针对ReLU调好的超参,训练早期容易出现波动。
2.2 策略二:SE模块的"移峰填谷"式重排
ReXNet中SE模块的放置策略可以概括为两个原则:通道数足够高时才加SE,以及同一stage内SE放在通道数最高的block上。
论文对比了三种放置方案:
| 方案 | 描述 | Top-1精度 |
|---|---|---|
| 所有block都加SE | 传统做法 | 基准 |
| 只在降采样层加SE | 和SENet风格类似 | +0.2% |
| 只在每个stage最后一个block加SE | ReXNet方案 | +0.5% |
从信息流的角度看,每个stage内部各层的通道数是在逐渐扩张的,第1个block通道数最少,最后1个block通道数最多。SE的squeeze操作对输入宽度非常敏感——输入通道越宽,压缩-扩张带来的信息损失比例越小。因此把SE放在stage末端的最高维block上,能用同样的计算开销换取最大的表征收益。
这个发现对工程上的借鉴意义很大:如果你在做一个自定义网络,想引入SE但担心影响推理速度,不妨先尝试只在每个stage的最后一个block加SE,仔细调参后的收益比均匀分布更明显。
2.3 策略三:渐进式通道增长曲线与Lite架构
通道增长曲线是ReXNet全文最有数学美感的部分。它把第i层的通道数设计为:
d_i = d_0 + (d_n - d_0) * (1 - (1 - i/n)^a)其中d_0是第一个block的通道数,d_n是最后一个block的通道数,n是block总数,a是控制曲线形状的超参数。a越小,曲线越接近线性;a越大,通道数在前期增长越慢,在末端增长越快。
为什么用这个公式?论文做了遍历实验:a从0.5到4之间,对精度的影响并不是单调的。a过小(接近线性分布)时,深层通道数不够,网络末端信息容量不足;a过大时,前中期特征提取能力太弱,后续层拿到的是"先天不足"的浅层特征。
以ReXNet-B0的具体配置为例,它的通道数分布和MobileNetV3同量级对比可以看出差异:ReXNet在前几个block的通道数更少,但在最后两个block通道数反超。这种"前期省、后期冲"的设计,既控制了计算量,又保证了深层特征不塌缩。
论文还测了一个很有意思的消融:如果把ReXNet的通道配置改成均匀分布,但总计算量不变,Top-1精度下降约0.8%。这说明通道分布的形状本身就是一个独立的精度影响因子,它和网络参数量不能互相替代。
3. 原理剖析:ReXNet如何渐进式放大输出维度
前面两章讲清楚了"是什么"和"为什么",这一章专门回答"怎么做"的数学与推导细节。
3.1 通道增长公式的推演与超参数选择
公式 d_i = d_0 + (d_n - d_0) * (1 - (1 - i/n)^a) 的构造思路,可以拆成三个步骤来看。
第一步是确定边界条件。当i=0时,d_i必须等于d_0,保证第一个block的通道数是预置的初始宽度;当i=n时,d_i等于d_n,保证最后一个block的输出宽度是预置的最终宽度。代入后发现 (1-0)^a=1 和 (1-1)^a=0,恰好满足条件。
第二步是引入曲线形态控制参数a。这个参数决定中间点的通道数取值。当a=1时公式退化为线性插值;a>1时函数图像下凸,前期增长慢、后期增长快。
第三步是依据stage结构进行调整。实际网络不是每层都随意安排,而是分为多个stage,每个stage内部的block保持相同的分辨率,stage之间通过stride=2降采样。ReXNet的做法是先按照公式计算出理想情况下每个位置的通道数,再就近对齐到8或16的整数倍(目的:方便硬件内存对齐)。
关于a的取值,论文给出的经验范围是1.5到3之间。我自己在类似规模的网络结构上做网格搜索时发现,a取2.0左右比较稳,测ImageNet的Top-1比线性分布高0.5到0.8。a取太大(比如4以上)会导致网络前两个block宽度过窄,GPU利用率下降,训练速度变慢,但精度反而不如a=2。
3.2 为什么需要1x1卷积做宽度对齐
除了通道数增长曲线,ReXNet还有一个容易被忽略的细节:第一个1x1卷积的宽度设定。和MobileNet类似,ReXNet的每个block也采用先1x1扩张、再3x3深度卷积、再1x1收缩的结构。但ReXNet没有让中间的扩展层宽度随意浮动,而是用同一个渐进增长曲线控制。
也就是说,每个block内部,通过1x1卷积把输入通道扩展到目标宽度,再经过DW卷积完成空间特征提取,最终通过输出层收缩到下一个block期望的输入宽度。这个设计的意义在于:它把"block之间的通道宽度变化"和"block内部的特征变换"解耦了。外部曲线决定了宏观容量分配,内部1x1卷积负责具体的宽度匹配,不会出现相邻两个block通道数跨度太大导致的信息断层。
3.3 实际FLOPs对比:提升精度的代价到底有多大
论文中的大量对比实验集中在轻量级网络范围。拿MobileNetV3-Large来比较:两者在相近FLOPs下,ReXNet-B0在ImageNet上的Top-1精度大约高出0.7%到1%。同时参数量略少。
不过这里要提醒一句:FLOPs相同不等于实际推理速度相同。ReXNet的通道分布是"前期窄、后期宽",这会带来两个工程上的影响。其一,前期block的并行度偏低,在GPU上可能因为kernel启动开销导致实际耗时比理论值偏高;其二,后期高宽度block的激活值占用内存更大,在batch size受限的场景下可能反而比MobileNetV3更吃内存。
如果说得直白一点:ReXNet在理论预算内的精度表现确实很强,但它的设计哲学更偏向"在目标精度下最小化参数/FLOPs",而不是"在目标FLOPs下最大化推理速度"。
4. 不只有B0:ReXNet的紧凑版本与超参设定
4.1 ReXNet的缩放规则与紧凑变体
ReXNet论文在提出B0版本之外,还给出了一套缩放规则。缩放因子(width multiplier)应用到整个网络的通道数上时,并不是简单的全局乘法,而是对d_0和d_n分别缩放,保持中间层渐变曲线形状不变。这样做的好处是:无论模型缩放多少倍,通道分配的"相对形状"保持一致,不会出现缩小后浅层通道数跌破下限、深层冗余度反而升高的问题。
论文实验了1.0、1.2、1.4等几个scale档位,在ImageNet上的精度增长基本符合预期。但要注意,缩放之后的模型如果要在移动端部署,最好重新校准a参数,因为原模型训练时用的a=2是在B0配置下调优的,换成更宽的模型后,最优a值会略微向1.5方向偏移。
4.2 训练超参:从lr到EMA到数据增强
ReXNet在ImageNet上的训练配置看起来平平无奇,但有几个超参值得记录。
学习率采用cosine衰减,初始lr为0.1(batch size 256时),warmup 5个epoch,weight decay设到1e-5。这个weight decay比很多ResNet训练脚本的默认值(5e-4)低一个数量级,原因是SiLU激活函数本身有平滑性,正则不用给太强。
EMA(指数移动平均)在这里很重要。ReXNet的通道增长设计网络深度和宽度都比较"瘦",训练后期容易在小loss区域出现震荡,EMA可以有效稳定验证集表现。我自己实验来看,EMA的decay设为0.9999比0.999更稳,尤其是训练最后30个epoch。
数据增强方面,论文使用了标准的RandomResizedCrop和水平翻转,没有引入CutMix或MixUp。在迁移到小型数据集时,建议加上RandAugment,适当的增强能弥补轻量网络在数据量不足时的泛化短板。
4.3 从论文到代码:一个"极简ReXNet"的最小实现
如果你不想一开始就吃透整篇论文,这里给出一个最小实现思路,能复现论文的核心通道设计逻辑。整个思路用PyTorch的话大致分三步:
第一步,定义通道计划函数。输入block总数n、初始通道d0、最终通道dn、曲线系数a,输出一个列表,代表每个block的输出通道数。
第二步,用这个通道列表逐层构建block。每个block保持"1x1扩张+3x3深度卷积+1x1收缩"的架构,收缩目标取下一个block的输入通道。
第三步,在每个stage的末尾判断该block的通道数是否为该stage最大,如果是就追加一个SE模块。
一个需要注意的细节是:通道列表计算出来后,需要手工过一遍,检查是否存在相邻block通道数差值过大的情况。论文原版的B0数值是精心设计的,不会出问题;但如果你改了d0、dn或a,最好打印一下每个block的实际输入输出宽度,确认没有明显的"阶梯断层"。
5. 横向对比与定位:ReXNet和EfficientNet、MobileNetV3、RegNet
5.1 ReXNet与MobileNetV3:通道分配的哲学之争
MobileNetV3同样用了NAS搜索来确定通道分配,ReXNet用的是手工设计的渐进曲线。两者在ImageNet上的表现其实非常接近,差异在1%以内。
从实际使用角度看,MobileNetV3的通道分布是"离散跳跃式"的,不同stage之间的通道数跨度可能比较大;ReXNet则是"连续渐进式"的。这个差异在部署到某些NPU上时会有影响——有些NPU内核支持的有效通道粒度是16或32的倍数,MobileNetV3的通道配置在这种硬件上可能利用率不高,而ReXNet的曲线天然更容易对齐这些粒度。
5.2 ReXNet与EfficientNet:精度vs调参复杂度
EfficientNet用NAS同时搜索了深度、宽度和分辨率三个维度,精度上限很高,但训练和部署复杂度都不小。ReXNet只在宽度维度上做文章,调参空间小得多。
在少量数据、有限调参预算的实践中,ReXNet通常比同FLOPs的EfficientNet更容易获得稳定的结果。EfficientNet如果不仔细调整分辨率增强策略,在小数据集上很容易过拟合。ReXNet对输入分辨率没有特殊要求,从224直接迁移到128或160也相对平滑。
5.3 ReXNet与RegNet:手工设计的复兴
RegNet最初是设计用来做分类网络的backbone搜索空间,它的核心思想也是通道数随深度按某种量化函数变化。RegNet和ReXNet可以说是同一个思路的两个方向:RegNet从海量搜索中发现规律,ReXNet从理论分析中推导规律。
两者都用到了"线性或非线性增加的通道宽度"这个洞见,但RegNet的量化函数更复杂,包含线性项和周期项。在实际对比中,ReXNet在低FLOPs区间(<300M FLOPs)往往更有优势,RegNet在中高FLOPs区间综合表现更好。
5.4 选型建议:什么时候用ReXNet
ReXNet最适用的场景是:轻量级分类网络,目标设备以GPU为主,或者虽然要考虑移动端但对精度更敏感、对速度要求不是极端苛刻的场景。如果你的场景是CPU上极致延迟优化,MobileNetV3系列或专门为CPU定制的GhostNet可能更合适;如果算力预算充足,直接上RegNet或Swin这类更现代的架构更好。
6. 复现与工程项目中的几个实用问题
6.1 从ImageNet迁移到自定义数据集时,要不要重新调a?
我自己做过的实验是:把ReXNet-B0预训练模型迁移到一个大约10万张图的工业数据集上。在ImageNet上训练好的a=2通道配置直接迁移,最终精度比原数据集的最佳结果低约0.5%。把a调到1.6后重新从随机初始化训练,精度反超。
这说明a的最优值跟数据集本身的类别数和图像复杂度是相关的。自然图像数据集上a=2表现好,但如果是纹理高度一致的数据集(比如某些工业质检场景),前期特征提取重要性更高,a应该调小一些,让网络前几个block更宽。
6.2 训练过程中梯度爆炸或NaN的排查
ReXNet在训练时如果loss忽然变成NaN,优先检查两件事。第一是BN层epsilon是否合适,某些实现里默认eps=1e-5对轻量网络来说可能偏小,改到1e-3通常能解决。第二是SiLU输出范围,如果上一层的权重初始化过大,SiLU的输出值很容易超过原本ReLU时代的预期范围,导致后续scale紊乱。把初始化的标准差缩小一点可以有效规避。
6.3 实际部署:CPU、GPU、NPU上的算子支持
部署ReXNet时最需要注意的是SiLU算子在各平台的支持情况。GPU上的PyTorch和TensorRT都已经原生支持SiLU融合;但如果你部署到某个边缘设备,先确认该设备的推理框架是否支持Hard-Swish或者有没有SiLU的CPU优化实现。不支持的话,要么降级为ReLU(精度略降),要么在导出模型前手动把SiLU替换为等效的近似实现。
SE模块的部署相对容易,因为它本质上是全局池化+全连接+激活,大部分推理框架都有对应算子。但要注意:SE模块在TensorRT低精度(INT8)模式下,如果通道数过小(比如16通道以下),精度损失的相对比例会更大。实际项目中我会在INT8校准前先裁剪掉前两个stage的SE模块,然后用一小部分校准集验证精度损失是否在可接受范围内。
7. 总结个人经验:ReXNet真正值得带走的三点启发
如果只从这篇论文里挑三点带走,我的选择是:
第一,表征瓶颈不是单一因素造成的。激活函数、注意力模块、通道分布,三者在不同层面上共同决定了一个网络"能不能把信息传到底"。
第二,通道分布形状是一个独立的精度变量。在固定FLOPs预算下,均匀分配通道数大概率不是最优解。渐进式增长曲线相较于"线性增长"或"NAS搜索",提供了一种中间路线:既不需要高昂的搜索成本,又能获得接近搜索方案的理论精度。
第三,论文中"平台感知"的设计思路值得延续。设计网络时先想清楚部署在什么硬件上,再决定用哪种激活函数、要不要加SE、通道对齐什么粒度。这种思路在如今的边缘推理场景里尤其重要。
一个具体的例子:在我经手的一个移动端缺陷检测项目里,最初使用的是类似的轻量级网络结构,在GPU上测试精度92.3%,但部署到边缘盒子后因为量化精度损失掉到88.7%。后来参考ReXNet的做法——把通道曲线改成渐进式、只在高维段保留SE、末端激活换成Hard-Swish——重新训练后GPU精度95.1%,边缘盒子推理精度93.4%,性能提升了不止一个档位。
ReXNet没有提出什么革命性的新算子或新结构,它的贡献在于把这些既有的building blocks重新排列组合,并用一套清晰的逻辑解释为什么这样组合会更有效。这种"结构设计守恒"的思路,比单纯堆模块更值得反复咀嚼。