029、层级交互的艺术:HAN层级注意力网络的创新点解析与训练技巧 029、层级交互的艺术HAN层级注意力网络的创新点解析与训练技巧去年夏天我接手了一个老照片修复项目。客户拿来的是一批上世纪80年代的全家福扫描分辨率只有72dpi人脸区域大概就30×30像素。我试了SRCNN、EDSR、RCAN效果都不理想——要么纹理糊成一团要么出现诡异的伪影。直到我偶然翻到一篇CVPR 2020的论文HANHierarchical Attention Network才真正找到突破口。当时我盯着HAN的结构图看了整整一个下午脑子里反复转着一个念头为什么之前的注意力机制都只关注“局部”或“全局”的单一层级而HAN偏偏要把它们串起来后来我在自己的数据集上跑通HAN后才恍然大悟——这玩意儿解决的是超分任务里一个极其隐蔽但致命的问题特征之间的“层级割裂”。从“注意力孤岛”到“层级对话”先说说我踩过的坑。早期用RCAN时我发现它的通道注意力Channel Attention确实能自适应地调整不同通道的重要性但有个毛病每个残差组Residual Group内部的注意力是独立的。打个比方就像一群人在开会每个人只盯着自己手里的笔记本完全不管隔壁桌在讨论什么。这导致浅层特征和深层特征之间缺乏交互尤其是当图像包含复杂的纹理和边缘时模型容易“顾此失彼”。HAN的聪明之处在于它引入了三种注意力机制并且让它们形成一种“层级对话”的关系通道注意力负责筛选每个残差组内部的重要特征空间注意力关注像素级别的空间相关性而最核心的层级注意力Layer-wise Attention则像是一个“调度员”把不同残差组的输出动态加权融合。别小看这个“调度员”。传统做法是把所有残差组的输出直接拼接或简单相加但HAN的做法是先对每个残差组的输出做全局平均池化得到一个描述向量然后通过一个轻量级的全连接网络学习出每个组的权重。这相当于让模型自己判断“哪一层的特征对当前重建更重要”。我在调试时发现对于纹理密集的区域比如头发丝模型往往会给中间层更高的权重而对于平滑区域比如天空浅层和深层权重更均衡。代码实现里的“暗坑”理论说完了直接上代码。这里我用PyTorch复现HAN的核心模块重点标注那些容易翻车的地方。importtorchimporttorch.nnasnnimporttorch.nn.functionalasFclassLayerAttention(nn.Module):def__init__(self,n_feats,n_resgroups,reduction16):super(LayerAttention,self).__init__()# 注意这里的n_resgroups是残差组的数量不是通道数# 我一开始写成了n_feats // reduction结果维度对不上debug了一整天self.bodynn.Sequential(nn.Conv2d(n_resgroups*n_feats,n_feats//reduction,1,padding0,biasTrue),nn.ReLU(inplaceTrue),nn.Conv2d(n_feats//reduction,n_resgroups,1,padding0,biasTrue),nn.Sigmoid())defforward(self,x):# x的shape: [B, n_resgroups, C, H, W]# 别这样写直接对x做全局平均池化会丢失组间信息# 正确做法先reshape成[B, n_resgroups*C, H, W]b,g,c,h,wx.shape x_reshapedx.view(b,g*c,h,w)# 全局平均池化得到描述向量gapF.adaptive_avg_pool2d(x_reshaped,1)# [B, g*c, 1, 1]# 通过全连接层得到每个组的权重weightsself.body(gap)# [B, g, 1, 1]# 权重广播到每个组的特征上weightsweights.view(b,g,1,1,1)return(x*weights).sum(dim1)# 加权求和输出[B, C, H, W]这里有个细节为什么要把所有组的特征先拼接再池化因为如果单独对每个组做池化会丢失组间的相对信息。HAN的论文里提到这种“全局描述”能让模型感知到不同层级特征的“能量分布”。训练技巧别让层级注意力“躺平”我在训练HAN时遇到一个典型问题层级注意力模块的权重很快收敛到均匀分布也就是每个组的权重都差不多等于没起作用。这通常是因为学习率设置不当或者初始化策略有问题。技巧一给层级注意力模块单独设置学习率。我习惯把主干网络的学习率设为1e-4而层级注意力模块设为5e-4让它“跑得快一点”。别担心过拟合这个模块参数极少就两个卷积层稍微激进一点没关系。技巧二在损失函数里加入权重熵正则项。如果层级注意力的权重分布太均匀说明模型偷懒了。可以在L1损失基础上加一个惩罚项loss 0.001 * (-weights * torch.log(weights 1e-8)).sum()。这能迫使权重分布更尖锐让模型真正学会“选择”。技巧三数据增强要“温柔”。超分任务里随机裁剪、翻转、旋转是常规操作但HAN对几何变换比较敏感。我试过用RandomResizedCrop结果层级注意力的权重分布变得极其不稳定。后来改成固定尺寸的随机裁剪比如96×96效果稳定很多。原因可能是HAN的层级注意力依赖于全局统计信息如果裁剪区域变化太大全局池化的结果会剧烈波动。实战效果老照片修复的“救星”回到开头那个项目。用HAN处理30×30的人脸区域效果让我吃了一惊——虽然不可能凭空恢复出皱纹或毛孔但至少把五官轮廓从“马赛克”变成了“模糊但可辨认”。对比RCANHAN在边缘处少了很多振铃效应尤其是在头发和背景的交界处。我后来分析了一下原因RCAN的通道注意力只关注“哪些通道重要”而HAN的层级注意力额外关注“哪些层级的特征重要”。对于人脸这种结构性强、纹理分布不均匀的图像浅层特征边缘、梯度和深层特征语义、轮廓需要动态平衡。HAN的“调度员”恰好做到了这一点。个人经验什么时候该用HAN如果你做的是通用超分比如DIV2K、Set5这些标准数据集HAN的提升可能只有0.1-0.2dB不值得折腾。但如果你面对的是纹理复杂、结构多变的场景比如人脸、遥感图像、医学影像HAN的层级交互机制会带来肉眼可见的改善。另外HAN的参数量比RCAN略大多了一个层级注意力模块但推理速度几乎没影响因为那个模块只做了一次全局池化和两个1×1卷积。所以别担心性能开销。最后说一句别迷信论文里的默认超参数。我在训练时把残差组的数量从10减少到6层级注意力的效果反而更好——因为组数太多时权重分布容易“稀释”。调参这件事还是得自己动手试。