我正在使用maskrcnn_benchmark 仓库进行一个项目。我已经详细探索了这个基准测试仓库,试图找出它在 CPU 上性能较慢的原因,相较于此处链接描述来说。
为了对各个前向传递进行基准测试,我为每个部分设置了一个时间计数器,这样我就可以知道计算每个组件所需的时间。我很难准确找出整个架构中最慢的组件。我认为它可能是maskrcnn_benchmark/modeling/backbone/resnet.py
文件中的BottleneckWithFixedBatchNorm类。
我非常希望能得到关于定位这个架构中最大瓶颈的帮助。
回答:
我遇到了同样的问题,最好的解决方案是深入查看主代码,逐个模块地检查前向传递,并设置计时器来记录每个模块计算所花费的时间。我们的工作方法是创建一个架构,为每个类设置时间记录器,因此每个类的实例都会记录其执行时间。经过详细比较,至少在我们的案例中,我们发现延迟的原因是Resnet模块的深度,(考虑到Resnet的计算成本,这并不令人惊讶,唯一的解决方案是更多的并行化处理,因此要么确保使用更大的GPU来执行任务,要么减少Resnet网络的深度)。
我必须告知,maskrcnn_benchmark 已经废弃,现在有一个更新版本,即detectron2。考虑迁移您的代码,以在架构上获得显著的速度提升。
BottleneckWithFixedBatchNorm并不是架构中最昂贵的操作,当然也不是造成瓶颈的原因,尽管它的名字可能让人误解。这个类在计算上并不昂贵,并且即使在低端CPU机器上也能并行计算(至少在推理阶段)。
可以从路径maskrcnn_benchmark/modeling/backbone/resnet.py
中找到一个更好的跟踪每个模块性能的代码示例
class ResNet(nn.Module): def __init__(self, cfg): super(ResNet, self).__init__() # 如果我们想在forward()中使用cfg,那么我们应该制作一个副本并存储以备后用: # self.cfg = cfg.clone() # 将字符串名称转换为实现 stem_module = _STEM_MODULES[cfg.MODEL.RESNETS.STEM_FUNC] stage_specs = _STAGE_SPECS[cfg.MODEL.BACKBONE.CONV_BODY] transformation_module = _TRANSFORMATION_MODULES[cfg.MODEL.RESNETS.TRANS_FUNC] # 构建stem模块 self.stem = stem_module(cfg) # 构建指定的ResNet阶段 num_groups = cfg.MODEL.RESNETS.NUM_GROUPS width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS stage2_bottleneck_channels = num_groups * width_per_group stage2_out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS self.stages = [] self.return_features = {} for stage_spec in stage_specs: name = "layer" + str(stage_spec.index) stage2_relative_factor = 2 ** (stage_spec.index - 1) bottleneck_channels = stage2_bottleneck_channels * stage2_relative_factor out_channels = stage2_out_channels * stage2_relative_factor stage_with_dcn = cfg.MODEL.RESNETS.STAGE_WITH_DCN[stage_spec.index -1] module = _make_stage( transformation_module, in_channels, bottleneck_channels, out_channels, stage_spec.block_count, num_groups, cfg.MODEL.RESNETS.STRIDE_IN_1X1, first_stride=int(stage_spec.index > 1) + 1, dcn_config={ "stage_with_dcn": stage_with_dcn, "with_modulated_dcn": cfg.MODEL.RESNETS.WITH_MODULATED_DCN, "deformable_groups": cfg.MODEL.RESNETS.DEFORMABLE_GROUPS, } ) in_channels = out_channels self.add_module(name, module) self.stages.append(name) self.return_features[name] = stage_spec.return_features # 可选地冻结(requires_grad=False)骨干网络的部分 self._freeze_backbone(cfg.MODEL.BACKBONE.FREEZE_CONV_BODY_AT) def _freeze_backbone(self, freeze_at): if freeze_at < 0: return for stage_index in range(freeze_at): if stage_index == 0: m = self.stem # 阶段0是stem else: m = getattr(self, "layer" + str(stage_index)) for p in m.parameters(): p.requires_grad = False def forward(self, x): start_timer=time.time() outputs = [] x = self.stem(x) for stage_name in self.stages: x = getattr(self, stage_name)(x) if self.return_features[stage_name]: outputs.append(x) print("ResNet time :: ", time.time()-start_timer,file=open("timelogger.log","a")) return outputs
唯一需要做的更改是在前向传递中,所有由此类创建的实例将继承属性并记录时间(选择将记录写入文件而不是简单地输出到标准输出)。