OpenMMLab 模型大联动,MMDet 也能用 MMCls 的网络!

简介: 使用过 OpenMMLab 旗下开源软件,如 mmdet、mmseg 的读者们,一定知道在这些软件中,我们通过配置文件来定义深度学习任务的方方面面,比如模型结构、训练所使用的优化器、数据集等。

使用过 OpenMMLab 旗下开源软件,如 mmdet、mmseg 的读者们,一定知道在这些软件中,我们通过配置文件来定义深度学习任务的方方面面,比如模型结构、训练所使用的优化器、数据集等。


以 Yolo V3 模型结构为例,典型的配置文件是这样的:

model = dict(
    type='YOLOV3',
    backbone=dict(
        type='MobileNetV2',
        out_indices=(2, 4, 6),
        ...),
    neck=dict(
        type='YOLOV3Neck',
        num_scales=3,
        in_channels=[320, 96, 32],
        out_channels=[96, 96, 96]),
    ...
)

可以看到,配置文件虽然使用了 Python 的语法(也支持 json 和 yaml 格式,这里暂且不表),但并不会像我们直接使用 PyTorch 一样,直接 import 相应的模块和类,然后进行类的实例化,而是使用字典来进行配置,使用 type 字段来标示该组件的类型。


这带来了一个问题,如果我想要引用另外一个代码库中注册的模型,该如何用配置文件的方式来实现呢?让我们从一个实际的例子入手。

640.png


1. 在 mmdet 中调用 mmcls 的 backbone



通常,在检测任务中,我们会使用一个主干网络来提取图片的特征。而由于图片特征提取对于各类图像任务是较为通用的需要,因而可以“借用”在分类任务中预训练的主干网络和相应的模型权重。因为分类任务比较简单,故而可以利用庞大的 ImageNet 数据集进行预训练,而在此基础上进一步训练检测网络,既能够提高模型收敛速度,又能够提高精度。


假设现在我们想要使用一个 MMDetection 中没有实现的主干网络进行特征提取,我们当然可以直接在 mmdet 中实现这个主干网络,但如果这个主干网络在分类代码库 MMClassification 中已经实现了,我们是可以直接通过修改配置文件来跨库调用的。比如我们想把上述 Yolo V3 中的主干网络从 MobileNet V2 换成 MobileNet V3,但 mmdet 还没有 Mobilenet V3 的实现,可以使用如下配置:

# 直接继承 yolo v3 的原始配置
_base_ = "./yolov3_mobilenetv2_320_300e_coco.py"
# 因为 mmdet 中没有 import mmcls
# 因而其中的主干网络并不会被注册到管理器中
# 这里我们需要手动用 custom_imports 来指定额外的导入
# 从而注册 mmcls 中的主干网络
custom_imports=dict(imports='mmcls.models', allow_failed_imports=False)
model = dict(
    backbone=dict(
        # 使用 "scope.type" 的语法,指定从 mmcls 中寻找需要的模块
        type='mmcls.MobileNetV3',
        # MobileNet V3 的其他设置
        arch='large',
        out_indices=(5, 11, 14),
        init_cfg=dict(
            type='Pretrained',
            checkpoint='mmcls://mobilenet_v3_large'),
        # 配置文件与继承的配置文件中相同字段的字典,默认会融合
        # 这里使用 `_delete_` 来删除继承的配置文件中的其他配置
        _delete_=True),
    # 主干网络发生变化,其他相应的配置也需要改变
    neck=dict(in_channels=[160, 112, 40])
)


2. 跨代码库调用机制



在 OpenMMLab 的 cfg 模式和 Registry 机制 一文中,我们简要介绍了关于 config 文件和 Registry 的架构和实现。而在这里,我们将从上文中跨仓库调用中涉及的两个关键点,来更进一步地了解 config 和 Registry 一些高级用法和实现。

640.jpg


custom_imports


在 OpenMMLab 的 cfg 模式和 Registry 机制 中,我们提到过类是在何时被注册到 Registry 中的:

通常, 在 import 相应模块时, 都会过一遍相应的定义被装饰对象的代码, 此时装饰器就已经运行了. 例如, 对于 mmdet 的 AnchorGenerator, SSDAnchorGenerator 这些类, 他们是在何时注册到 ANCHOR_GENERATORS 这个 Registry 类实例中的?


在 train.py 执行 from mmdet.core import DistEvalHook, EvalHook 时, 会调用并执行 mmdet/core/__init__.py 中的 from .anchor import *, 进而会调用并执行 mmdet/core/anchor/__init__.py 中的 from .anchor_generator import (AnchorGenerator, LegacyAnchorGenerator, YOLOAnchorGenerator), 从而完成 AnchorGenerator, SSDAnchorGenerator 这些类的注册。


也就是说只要 import 了相应的模块,模块中所有包含的类都会被注册到对应的 Registry 中。


那么问题来了,这些模块是在何时被 import 的呢?答案很简单,就是在我们代码执行的入口程序 tools/train.py 和 tools/test.py 中。以 MMClassification 中的 tools/train.py 为例:

...
from mmcv.runner import get_dist_info, init_dist
...
from mmcls.datasets import build_dataset
from mmcls.models import build_classifier

通过导入 mmcv.runner 包,完成了 mmcv/runner/__init__.py 中一系列执行器、钩子、优化器等类的注册。通过导入 mmcls.datasets 包,完成了mmcls/datasets/__init__.py 中一系列数据集的注册。通过导入mmcls.models 包,完成了mmcls/models/__init__.py中一系列主干网络、颈部头部函数的注册。


当然,注册不一定仅仅发生在入口程序的最外层,比如在入口程序中没有导入数据处理和增强相关的包,这些类是在执行 build_dataset 时,在 mmcls/datasets/base_dataset.py 中进行的注册。


因此,Registry 的注册其实没有什么魔法,就是单纯地通过在入口程序中导入相应的包,在导入过程中完成的注册。这也就为我们的跨代码库调用带来了第一个问题,入口程序中当然不会导入与自己无关的另外一个库中的包,那怎么注册我们我需要的类呢?修改入口程序当然是一种办法,但 MMCV 中提供了更直接的在配置文件中显式导入自定义包的方法 —— custom_imports。

custom_imports=dict(imports='mmcls.models', allow_failed_imports=False)

只要在配置文件中加入这么一行,MMCV 在解析配置文件时,会自动调用 mmcv.import_modules_from_strings 函数,借助 Python 内置的 importlib 库中的 import_module 函数,进而完成对应的一系列类的注册。


Regsitry 中的 scope


在上文的例子中,我们看到,在跨仓库调用 MMClassification 的主干网络时,使用了一种特殊的写法,也就是 type='mmcls.MobileNetV3',既然我们都已经通过 custom_imports实现了 MMClassification 中主干网络的注册,为什么还要在使用时注明 mmcls. 呢?这就涉及到了 Registry 的 scope 机制。


Scope 机制的引入,是为了解决一个基础的问题,即类型的冲突,MMDetection 注册了一个 MobileNetV2,MMClassification 也注册了一个 MobileNetV2,到底用哪一个呢?MMCV 中给出的要求十分简单,禁止同一个 Regsitry 中注册两个相同名字的类。进一步地,每一个代码库在注册自己的模型时,都会注册到代码库自己的 Regsitry 中,而不是注册到 MMCV 统一的 Registry 中,从而避免与其他代码库产生冲突。


与此同时,虽然每个代码库都是注册到自己的 Registry 中,这些 Registry 却又不是独立的,而是以 MMCV 中某个统一的 Registry 为父 Regsitry,从而形成如图所示的树状结构。

640.png

通过 scope 这一机制,我们避免了不同代码库之间重复注册产生的冲突,同时使用 scope 来指定 type 具体在哪一个 Registry 中,也使得跨代码库的调用更加显式和清晰


文章来源:公众号【OpenMMLab】

2021-11-23 19:52

目录
相关文章
|
2月前
|
C++
基于Reactor模型的高性能网络库之地址篇
这段代码定义了一个 InetAddress 类,是 C++ 网络编程中用于封装 IPv4 地址和端口的常见做法。该类的主要作用是方便地表示和操作一个网络地址(IP + 端口)
156 58
|
2月前
|
网络协议 算法 Java
基于Reactor模型的高性能网络库之Tcpserver组件-上层调度器
TcpServer 是一个用于管理 TCP 连接的类,包含成员变量如事件循环(EventLoop)、连接池(ConnectionMap)和回调函数等。其主要功能包括监听新连接、设置线程池、启动服务器及处理连接事件。通过 Acceptor 接收新连接,并使用轮询算法将连接分配给子事件循环(subloop)进行读写操作。调用链从 start() 开始,经由线程池启动和 Acceptor 监听,最终由 TcpConnection 管理具体连接的事件处理。
56 2
|
2月前
基于Reactor模型的高性能网络库之Tcpconnection组件
TcpConnection 由 subLoop 管理 connfd,负责处理具体连接。它封装了连接套接字,通过 Channel 监听可读、可写、关闭、错误等
69 1
|
2月前
|
JSON 监控 网络协议
干货分享“对接的 API 总是不稳定,网络分层模型” 看电商 API 故障的本质
本文从 OSI 七层网络模型出发,深入剖析电商 API 不稳定的根本原因,涵盖物理层到应用层的典型故障与解决方案,结合阿里、京东等大厂架构,详解如何构建高稳定性的电商 API 通信体系。
|
2月前
基于Reactor模型的高性能网络库之Poller(EpollPoller)组件
封装底层 I/O 多路复用机制(如 epoll)的抽象类 Poller,提供统一接口支持多种实现。Poller 是一个抽象基类,定义了 Channel 管理、事件收集等核心功能,并与 EventLoop 绑定。其子类 EPollPoller 实现了基于 epoll 的具体操作,包括事件等待、Channel 更新和删除等。通过工厂方法可创建默认的 Poller 实例,实现多态调用。
192 60
|
2月前
基于Reactor模型的高性能网络库之Channel组件篇
Channel 是事件通道,它绑定某个文件描述符 fd,注册感兴趣的事件(如读/写),并在事件发生时分发给对应的回调函数。
155 60
|
2月前
|
安全 调度
基于Reactor模型的高性能网络库之核心调度器:EventLoop组件
它负责:监听事件(如 I/O 可读写、定时器)、分发事件、执行回调、管理事件源 Channel 等。
168 57
|
2月前
基于Reactor模型的高性能网络库之时间篇
是一个用于表示时间戳(精确到微秒)**的简单封装类
125 57
|
17天前
|
算法 安全 网络安全
【多智能体系统】遭受DoS攻击的网络物理多智能体系统的弹性模型预测控制MPC研究(Simulink仿真实现)
【多智能体系统】遭受DoS攻击的网络物理多智能体系统的弹性模型预测控制MPC研究(Simulink仿真实现)
|
4月前
|
机器学习/深度学习 搜索推荐 PyTorch
基于昇腾用PyTorch实现CTR模型DIN(Deep interest Netwok)网络
本文详细讲解了如何在昇腾平台上使用PyTorch训练推荐系统中的经典模型DIN(Deep Interest Network)。主要内容包括:DIN网络的创新点与架构剖析、Activation Unit和Attention模块的实现、Amazon-book数据集的介绍与预处理、模型训练过程定义及性能评估。通过实战演示,利用Amazon-book数据集训练DIN模型,最终评估其点击率预测性能。文中还提供了代码示例,帮助读者更好地理解每个步骤的实现细节。

热门文章

最新文章