美文网首页
MMCV:MMDetction所依赖的调参基础库

MMCV:MMDetction所依赖的调参基础库

作者: CV算法恩仇录 | 来源:发表于2022-02-10 14:35 被阅读0次

原创:赵明明

做目标检测的同学应该已经接触过或者正要接触MMDetection,当你接触到MMDeteciton的时候,你会发现,它的代码有点奇怪:它不写train函数,不写eval函数,反而配置了一个runner,把训练过程配置到runner中,运行runner,就是运行train/eval函数.如下:


图1 配置runner完成训练过程

上图第84行,就是对runner的运行。在运行之前的第62行,第78行,分别将模型model,优化器optimizer,日志logger,学习率调整策略配置入runner内。配置好后,运行runner就可以i实现模型的训练了。

为什么要使用runner?

我认为最大的特点就是:方便了算法研究,同学们可以少花一点时间在写代码上,把更多的精力花在模型的设计上。runner里面集成了图像和视频处理,图像和标注结果的可视化,对训练过程记时统计,以及高质量的常见的GPU算子等,也集成了多种CNN网络机构,基本上常用的网络结构都已经集成在这个模型里了。

由于mmcv的便利性,基于mmcv的开发已经支持了CV各个领域的开源项目:

  • 有MMClassification,图像分类工具箱;
  • MMDetection图像检测工具箱;
  • MMDetection3D新一代通用3D目标检测平台,
  • 以及MMSegmentation图像分割工具箱。
  • 除了经典的视觉任务,还实现了视频理解工具箱MMAction2,
  • 目标感知平台MMTracking,
  • 姿态估计工具箱MMPose,
  • 文字检测工具箱MMOCR,
  • 图像视频编辑工具箱MMEditing等一些常见的应用场景
  • 最后,也有部署方面的模型压缩工具箱MMRazor,模型部署框架MMDeploy.

为何mmcv可以支持开发从模型设计,到模型应用,再到模型部署这样全方位的工具箱?

原因有很多,其中配置型的runner功不可没。

下面咱们就了解下runner的思路

runner通过配置信息构建了训练工作流,测试工作流。比如设置 workflow=[('train',200),('val',1)],表示循环的训练200个epoch,然后验证1个epoch.

构建逻辑如下:

while curr_epoch < max_epochs:
    # 遍历用户设置的工作流,例如 workflow = [('train', 200),('val', 1)]
    for i, flow in enumerate(workflow):
        # mode 是工作流函数,例如 train, epochs 是迭代次数
        # mode要么是 要么是train,要么是val
        mode, epochs = flow
        # 要么调用 self.train(),要么调用 self.val()
        epoch_runner = getattr(self, mode)
        # 按照设定的epoches=200运行对应工作流函数self.train()
        for _ in range(epochs):
            epoch_runner(data_loaders[i], **kwargs)

那么如上代码中,调用的self.train()函数即epoch_runner内部是怎样的?如下:

# epoch_runner目前可以是 train 或者 val
# 这里用train函数举例子
def train(self, data_loader, **kwargs):
    # 遍历 dataset,
    for i, data_batch in enumerate(data_loader):
        # 运行预先配置在训练前要执行的功能
        self.call_hook('before_train_iter')
        # run_iter运行迭代一次的代码,这个 run_iter函数内部其实就是我们原来常写的那种forward,loss,backword,optim.step等四句。
        # 当把train_mode=False时,叫表示验证val了
        self.run_iter(data_batch, train_mode=True, **kwargs)
        # 运行预先配置在训练后要执行的功能
        self.call_hook('after_train_iter')
   # 凡是call_hook都是预先配置好的     
   self.call_hook('after_train_epoch')

也就是说, runner把我们常用的训练四步:forward,loss,backword,optim.step变成默认项,然后又给了我们常用的功能留了配置的接口。比如训练过程中需要保存模型的功能,断点重训的功能,还可以自定义的一些特殊的操作。

大概了解了runner的设计思路,下面我们具体分析一个简单的例子,更细节的去认识一下runner:

以下代码具体地址:https://gitee.com/anjiang2020_admin/mmcv/blob/master/examples/train.py

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10

from mmcv.parallel import MMDataParallel
from mmcv.runner import EpochBasedRunner
from mmcv.utils import get_logger

# 模型类的编写
# 可以写的很复杂,也可以写的很简单
# 这里就简单也一个5层的网络LeNet

class Model(nn.Module):

    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
        self.loss_fn = nn.CrossEntropyLoss()

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
    # train_step函数是必须要写的
    # runner中会调用此函数,完成训练任务
    # 也就是说,当把model配置入runner时,
    # 就相当于把train_step配置给runner了
    # runner只是个调度器,真正的训练核心步骤,还是我们自己写的。
    def train_step(self, data, optimizer):
        images, labels = data
        predicts = self(images)  # -> self.__call__() -> self.forward()
        loss = self.loss_fn(predicts, labels)
        return {'loss': loss}


if __name__ == '__main__':
    # 声明一个我们定义的模型Model类型的变量model
    model = Model()
    # 如果可以,尽量使用gpu并行训练
    if torch.cuda.is_available():
        # only use gpu:0 to train
        # Solved issue https://github.com/open-mmlab/mmcv/issues/1470
        model = MMDataParallel(model.cuda(), device_ids=[0])

    # dataset and dataloader
    # 数据集准备 与数据加载器的准备

    # transform:数据预处理
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])
    
    # 获取cifar10数据集,读入trainset,如果磁盘上没有,就自动从网络下载
    trainset = CIFAR10(
        root='data', train=True, download=True, transform=transform)
    
    # trainset放入数据加载器trainloader
    # 这样数据加载器trainloader就准备好了
    trainloader = DataLoader(
        trainset, batch_size=128, shuffle=True, num_workers=2)
    
    #  准备优化器 optimizer
    optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
    
    # 准备日志管理器
    logger = get_logger('mmcv')
    
    # runner is a scheduler to manage the training
    # 将待训练模型model,优化器optimizer,日志管理器配置到runner中
    # runner此时已经包含了训练的大的框架:前向计算,反向传播,日志记录
    runner = EpochBasedRunner(
        model,
        optimizer=optimizer,
        work_dir='./work_dir',
        logger=logger,
        max_epochs=4)

    # learning rate scheduler config
    # 配置优化器中的训练学习率衰减策略
    lr_config = dict(policy='step', step=[2, 3])
    # configuration of optimizer
    # 配置优化器中是否使用梯度剪裁grad_clip
    optimizer_config = dict(grad_clip=None)
    # configuration of saving checkpoints periodically
    # 配置间隔interval个epoch保存一次训练参数
    checkpoint_config = dict(interval=1)
    # save log periodically and multiple hooks can be used simultaneously
    log_config = dict(interval=100, hooks=[dict(type='TextLoggerHook')])
    # register hooks to runner and those hooks will be invoked automatically
    # 使用register_training_hooks函数,将以上文件配置到runner中
    # register_training_hooks函数让我们可以自己定以训练过程中的细节
    runner.register_training_hooks(
        lr_config=lr_config,
        optimizer_config=optimizer_config,
        checkpoint_config=checkpoint_config,
        log_config=log_config)
    # 完成以上配置后,就可以运行了
    runner.run([trainloader], [('train', 1)])

可以看到,这里的主要步骤是:

  • 建立runner时,将模型本身,优化器本身配置入runner
  • 用runner的成员函数register_training_hooks对runner的训练细节进行配置,如学习路具体用哪种策略来调整,是否使用梯度剪裁等。

至此,我们看到,mmcv中的runner是一个调度器,我们只要把与训练模型的相关要素配置给它,它就可以帮我们把各种要素利用起来,完成我们的既定的训练或者测试的动作。

因为以上关于分类的工具箱,目标检测,图像分割,3D目标检测,视频理解,感知,ORC,姿态检测等工具箱都是用runner的思路为基础配置出来的。

所以说,MMCV是众多CV工具箱依赖的工具箱,推荐在需要人肉调参时使用,可以节省coding的时间,减少bug,总体提升效率。

mmcv源码下载链接以及安装使用方式:

源码链接:

https://gitee.com/anjiang2020_admin/mmcv.git

安装使用方式:

cpu安装:

pip install mmcv

gpu安装:

pip install mmcv-full

由于gpu加速库的版本与pytorch版本的适配性,gpu安装稍微繁琐些,详细细节在这里可以查询到:

https://gitee.com/-/ide/project/anjiang2020_admin/SEPC/edit/master/-/README.md

安装完成后,用以下命令使用:


git clone https://gitee.com/anjiang2020_admin/mmcv.git

cd mmcv/examples

python train.py

这个train.py里就是本文主要讲解的代码例子

运行起来后,它会自己下载数据,自己启动训练,如下图所示:


关于MMDetection的讲解到这里就结束了,大家快动手操练起来吧!

相关文章

  • MMCV:MMDetction所依赖的调参基础库

    原创:赵明明 做目标检测的同学应该已经接触过或者正要接触MMDetection,当你接触到MMDeteciton的...

  • [Angular]依赖注入

    JS中的依赖注入,举个荔枝: 1.回调函数的event就是依赖对象2.回调函数有形参就是依赖注入(设置方法或者函数...

  • 微信小程序报错Unhandled promise rejecti

    原因:基础库版本太高 解决方案:回调基础库

  • Task 4:建模调参

    Datawhale 零基础入门数据挖掘-Task4 建模调参 四、建模与调参 4.1 学习目标 了解常用的机器学习...

  • iOS 应用程序加载

    1. 理论基础速成 1.1 静态库与动态库 库是已写好的、供使用的 可复用代码,每个程序都要依赖很多基础的底层库。...

  • 2018-11-27 机器学习打卡

    课1 为什么学习公式: 选特征、调超参、换模型,称为调参工程师的三板斧,要想有章法地使用它们,理论基础还是必不可少...

  • AS发布依赖库到Jcenter

    AS发布依赖库到Jcenter 参考1 Android Studio 发布项目到 JCenter 以及常见错误 参...

  • ARouter-Android路由中间件

    目录 1)依赖和配置 2)初始化 3)路由操作3.1)跳转并传参3.2)跳转回调(startActivityFor...

  • 调参

    1首先是数据增强,因为正常情况下,数据量往往不够,因此需要通过裁剪 2数据预处理,pca,normalizatio...

  • 调参

    参数更新 神经网络的学习目的是找到使损失函数尽可能小的参数,即解决一个最优化问题.但是由于神经网络参数空间过于复杂...

网友评论

      本文标题:MMCV:MMDetction所依赖的调参基础库

      本文链接:https://www.haomeiwen.com/subject/jfinkrtx.html