一种改进DEIM(CVPR2025)的简单示例
本文为一个改进DEIM模型的简单示例。
1 源码准备
于github下载DEIM源码。
并自行寻找可用于替换DEIM原有模块的改进模块,或自行设计模块。
2 改进模块
以DEIM_DFine模型的hybridencoder-transformerlayer为例。
在engine/deim/hybrid_encoder.py中找到hybridencoder类。
我们要改进其中的transformerlayer
class TransformerEncoderLayer(nn.Module):def __init__(self,d_model, nhead, dim_feedforward=2048,dropout=0.1, activation="relu", normalize_before=False): super().__init__() self.normalize_before = normalize_beforeself.self_attn = nn.MultiheadAttention(d_model, nhead, dropout, batch_first=True) self.linear1 = nn.Linear(d_model, dim_feedforward) self.dropout = nn.Dropout(dropout) self.linear2 = nn.Linear(dim_feedforward, d_model)self.norm1 = nn.LayerNorm(d_model)self.norm2 = nn.LayerNorm(d_model) self.dropout1 = nn.Dropout(dropout) self.dropout2 = nn.Dropout(dropout) self.activation = get_activation(activation) @staticmethoddef with_pos_embed(tensor, pos_embed): return tensor if pos_embed is None else tensor + pos_embed def forward(self, src, src_mask=None, pos_embed=None) -> torch.Tensor: residual = src if self.normalize_before: src = self.norm1(src)q = k = self.with_pos_embed(src, pos_embed)src, _ = self.self_attn(q, k, value=src, attn_mask=src_mask)src = residual + self.dropout1(src) if not self.normalize_before: src = self.norm1(src)residual = src if self.normalize_before:src = self.norm2(src) src = self.linear2(self.dropout(self.activation(self.linear1(src)))) src = residual + self.dropout2(src) if not self.normalize_before:src = self.norm2(src) return src
新建一个类,命名按照自己的习惯,并继承原类。部分代码示例如下:
class TransformerEncoderLayer_imporvedNorm(TransformerEncoderLayer):def __init__(self,d_model,nhead,dim_feedforward=2048,dropout=0.1,activation="relu",normalize_before=False):super().__init__(d_model, nhead, dim_feedforward, dropout, activation, normalize_before)
新的类继承了原TransformerEncoderLayer类中的方法,因此仅重新定义需要改进的组件即可,不需要变动的组件代码无需重复写。
同样的,对于hybridencoder类。修改encoder_layer和encoder中的组件名。
@register()
class HybridEncoder_improved(HybridEncoder):__share__ = ['eval_spatial_size', ]def __init__(self,in_channels=[512, 1024, 2048], # 输入特征图的通道数列表,例如来自骨干网络的不同层feat_strides=[8, 16, 32], # 输入特征图的步幅列表,表示特征图相对于输入图像的缩放比例hidden_dim=256, # 隐藏层维度,所有特征图将被投影到这个维度nhead=8, # Transformer 编码器中多头自注意力的头数dim_feedforward=1024, # Transformer 编码器中前馈网络的维度dropout=0.0, # Transformer 编码器中的 dropout 概率enc_act='gelu', # Transformer 编码器中的激活函数类型use_encoder_idx=[2], # 指定哪些层使用 Transformer 编码器(索引列表)num_encoder_layers=1, # Transformer 编码器的层数pe_temperature=10000, # 位置编码的温度参数,用于控制频率expansion=1.0, # FPN 和 PAN 中特征扩展因子depth_mult=1.0, # 深度乘数,用于调整网络深度act='silu', # FPN 和 PAN 中使用的激活函数类型eval_spatial_size=None, # 评估时的空间尺寸 (H, W),用于预计算位置编码version='dfine', # 模型版本,决定使用哪些具体模块(如 'dfine' 或其他)):# 调用父类 nn.Module 的构造函数super().__init__(in_channels, feat_strides, hidden_dim, nhead, dim_feedforward, dropout, enc_act,use_encoder_idx,num_encoder_layers, pe_temperature, expansion, depth_mult, act, eval_spatial_size, version)self.in_channels = in_channels # 输入通道数列表self.feat_strides = feat_strides # 输入特征步幅列表self.hidden_dim = hidden_dim # 隐藏层维度self.use_encoder_idx = use_encoder_idx # 使用 Transformer 编码器的层索引self.num_encoder_layers = num_encoder_layers # Transformer 编码器层数self.pe_temperature = pe_temperature # 位置编码温度参数self.eval_spatial_size = eval_spatial_size # 评估时的空间尺寸self.out_channels = [hidden_dim for _ in range(len(in_channels))] # 输出通道数,统一为 hidden_dimself.out_strides = feat_strides # 输出步幅,与输入相同encoder_layer = TransformerEncoderLayer_imporvedNorm(hidden_dim, # 输入维度nhead=nhead, # 注意力头数dim_feedforward=dim_feedforward, # 前馈网络维度dropout=dropout, # dropout 概率activation=enc_act # 激活函数)self.encoder = nn.ModuleList([TransformerEncoder(copy.deepcopy(encoder_layer), num_encoder_layers) # 深拷贝确保独立性for _ in range(len(use_encoder_idx))])
改进后的hybridencoder类需要注册,通过
@register()
实现。
在deim文件夹中的init.py文件中导入改进的模块。
from .hybrid_encoder_improved import HybridEncoder_improved
3 配置文件
新建一个配置文件,内容直接复制原配置文件,然后修改两个地方。
DEIM:backbone: HGNetv2encoder: HybridEncoder_improved ###decoder: DFINETransformerHybridEncoder_improved: ###in_channels: [ 512, 1024 ]feat_strides: [ 16, 32 ]# intrahidden_dim: 128use_encoder_idx: [ 1 ]dim_feedforward: 512# crossexpansion: 0.34depth_mult: 0.5
修改完后,就可以尝试执行训练了,可能会报比如张量尺寸对不上的错误,因此还需要根据自己的情况多多调试。
这是一种简单改进的示例,更为复杂的改进可能需要不同的操作步骤。