日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

mmdetection源码笔记(二):创建网络模型之registry.py和builder.py解读(上)

發布時間:2024/8/1 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mmdetection源码笔记(二):创建网络模型之registry.py和builder.py解读(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言:

在上篇文章中,講了train.py訓練文件,主要是讀取命令行函數和主函數main。main主要先做了一些config,work_dir以及log等操作(這些操作都是從命令行獲得的,或者從命令行帶有的文件里得到的參數等。)。最主要的三個步驟就是調用build_detector()來創建模型,然后同樣調用build_dataset()對數據集創建模型,然后在訓練檢測器train_detector()。
注:build_dataset()和build_detector()不在同一個builder.py中實現,所以以下的builder.py實現的是build_detector(),是在mmdet/model/下的py文件。
具體詳情看

  • mmdetection源碼筆記(一):train.py解讀

本篇文章主要就是講一下,搭建模型的思路,以及registry.py和builder.py中各個函數塊的作用。

注:builder.py是在mmdet/model文件夾下,是用來創建BACKBONES、NECKS、ROI_EXTRACTORS、SHARED_HEADS、HEADS、LOSSES、DETECTORS的模型的。而關于build_dataset()(在mmdet/datasets/builder.py中),在后面講到數據集的時候再來講它。

在mmdet/utils文件夾下的registry.py為主要的實現過程,后面詳細講解。
先來看在mmdet/models文件夾下的registry.py,較簡單,代碼如下:

# -*- coding: utf-8 -*- from mmdet.utils import RegistryBACKBONES = Registry('backbone') NECKS = Registry('neck') ROI_EXTRACTORS = Registry('roi_extractor') SHARED_HEADS = Registry('shared_head') HEADS = Registry('head') LOSSES = Registry('loss') DETECTORS = Registry('detector') #類的實例化,Registry是一個類,傳入的是一個字符串。該字符串為Registry類的name屬性值

舉個例子:DETECTORS為注冊表Registry的實例化對象,DETECTORS .name = 'detector',Registry類的定義在mmdet/utils/文件中。

所以,根據上面代碼,我們就應該知道了,不止一個名為DETECTORS的注冊表Registry,后面還會有名為NECKS、ROI_EXTRACTORS 、SHARED_HEADS 、HEADS 、LOSSES 的注冊表,這些注冊表下的_module_dict屬性,則是用來存對應的相同類對象的,舉個例子:比如DETECTORS的_module_dict下就有可能有:Faster R-CNN、Cascade R-CNN、FPN、HTC等常見的檢測器。到這或許你就明白了注冊表的作用咯

而在mmdet/utils/Registry.py中,有一個類Registry的定義和一個方法:build_from_cfg()的實現。
build_from_cfg()方法的作用是從 congfig/py配置文件中獲取字典數據,創建module(其實也就是一個class類),然后將這個module添加到之前創建的注冊表Registry的屬性_module_dict中(這是一個字典,key為類名,value為具體的類),返回值是一個實例化后的類對象。

所以,可以這樣理解,從config/py配置文件中,將字典提取出來,然后為其映射成一個類,放進Registry對象的_module_dict屬性中。(具體看下面的代碼)

Registry.py文件

以下代碼分三部分

Part one:

inspect模塊是針對模塊,類,方法,功能等對象提供些有用的方法。例如可以幫助我們檢查類的內容,檢查方法的代碼,提取和格式化方法的參數等。

# -*- coding: utf-8 -*- import inspect import mmcv
Part two:

通過前面第一段的代碼段,我們知道DETECTORS = Registry('detector')
detector是干什么的 ???
其實,DETECTORS = Registry('detector') 只是注冊了一個對象名為DETECTORS ,屬性name為detector的對象。然后用屬性_module_dict 來保存config配置文件中的對應的字典數據所對應的class類(看第三部分代碼)。請看如下類Registry的定義代碼:

class Registry(object):def __init__(self, name): #此處的self,是個對象(Object),是當前類的實例,name即為傳進來的'detector'值self._name = nameself._module_dict = dict() #定義的屬性,是一個字典def __repr__(self):#返回一個可以用來表示對象的可打印字符串,可以理解為java中的toString()。format_str = self.__class__.__name__ + '(name={}, items={})'.format(self._name, list(self._module_dict.keys()))return format_str@property #把方法變成屬性,通過self.name 就能獲得name的值。def name(self):return self._name #因為沒有定義它的setter方法,所以是個只讀屬性,不能通過 self.name = newname進行修改。@propertydef module_dict(self): #同上,通過self.module_dict可以獲取屬性_module_dict,也是只讀的return self._module_dictdef get(self, key):#普通方法,獲取字典中指定key的value,_module_dict是一個字典,然后就可以通過self.get(key),獲取value值return self._module_dict.get(key, None)def _register_module(self, module_class): #關鍵的一個方法,作用就是Register a module. #在model文件夾下的py文件中,里面的class定義上面都會出現 @DETECTORS.register_module,意思就是將類當做形參, #將類送入了方法register_module()中執行。@的具體用法看后面解釋。"""Register a module.Args:module (:obj:`nn.Module`): Module to be registered."""if not inspect.isclass(module_class): #判斷是否為類,是類的話,就為True,跳過判斷raise TypeError('module must be a class, but got {}'.format(type(module_class)))module_name = module_class.__name__ #獲取類名if module_name in self._module_dict: #看該類是否已經登記在屬性_module_dict中raise KeyError('{} is already registered in {}'.format(module_name, self.name))self._module_dict[module_name] = module_class #在module中dict新增key和value。key為類名,value為類對象def register_module(self, cls): #對上面的方法,修改了名字,添加了返回值,即返回類本身self._register_module(cls)return cls
note:

@的含義:
Python當解釋器讀到@的這樣的修飾符之后,會先解析@后的內容,直接就把@下一行的函數或者類作為@后邊的函數的參數,然后將返回值賦值給下一行修飾的函數對象。
在網上看到一個這樣的例子:

def a(x):if x==2:return 4return 6 def b(x):if x==1:return 2return 3 @a @b def c():return 1

python會按照自下而上的順序把各自的函數結果作為下一個函數(上面的函數)的形參輸入,也就是a(b(c()))。


Part three:

以下我們通過配置文件cascade_rcnn_r50_fpn_1x.py進行講解 build 模型的過程。
在train中,最先執行Registry的是DETECTORS,傳入的參數是配置文件中的model字典。

#在 train.py中model = build_detector(cfg.model, train_cfg=cfg.train_cfg, test_cfg=cfg.test_cfg) #在builder.py中 def build_detector(cfg, train_cfg=None, test_cfg=None):return build(cfg, DETECTORS, dict(train_cfg=train_cfg, test_cfg=test_cfg))

所以,后面出現的參數cfg,指的就是配置文件中的model字典。下面是model字典的部分截圖。

我們繼續往下看
先看build_from_cfg()方法的參數:
Args:

  • cfg (dict): Config dict. It should at least contain the key “type”.
    這個cfg就是py配置文件中的字典。在py配置文件中,基本上dict都會有一個key為"type",當然也有不是的,不是的,這一步就不會執行,也就不會為他創建module。也就是這邊創建成module的dict,都必須有key為"type"才可以創建(這里,我們主要講的是注冊表DETECTORS,所以此時cfg對應的是配置文件中的model字典,看上面截圖)。
    舉個例子:比如type='CascadeRCNN',后面我們會知道,這個value為"CascadeRCNN"的,其實就是models文件夾中某py文件中的類名,他們通過@DETECTORS.register_module,將類名當做形參,傳入register_module。并保存下來。

  • registry (:obj:Registry): The registry to search the type from.

  • default_args (dict, optional): Default initialization arguments.

def build_from_cfg(cfg, registry, default_args=None):"""Build a module from config dict.Args:cfg (dict): Config dict. It should at least contain the key "type".registry (:obj:`Registry`): The registry to search the type from.default_args (dict, optional): Default initialization arguments.Returns:obj: The constructed object."""assert isinstance(cfg, dict) and 'type' in cfgassert isinstance(default_args, dict) or default_args is None #兩個是斷言,相當于判斷,否的話拋出異常。args = cfg.copy() #args相當于temp中間變量,是個字典。obj_type = args.pop('type') #字典的pop作用:移除序列中key為‘type’的元素,并且返回該元素的值if mmcv.is_str(obj_type): obj_type = registry.get(obj_type) #獲取obj_type的value。#如果obj_type已經注冊到注冊表registry中,即在屬性_module_dict中,則obj_type 不為Noneif obj_type is None:raise KeyError('{} is not in the {} registry'.format(obj_type, registry.name))elif not inspect.isclass(obj_type): raise TypeError('type must be a str or valid type, but got {}'.format(type(obj_type)))if default_args is not None:for name, value in default_args.items():#items()返回字典的鍵值對用于遍歷args.setdefault(name, value)#將default_args的鍵值對加入到args中,將模型和訓練配置進行整合,然后送入類中返回return obj_type(**args)

obj_type(**args),* *args是將字典unpack得到各個元素,分別與形參匹配送入函數中;看上面model的截圖,所以這邊,其實就是將除了’type’的所有字段,當做形參,送入了名為CascadeRCNN()的類中(type = ’CascadeRCNN‘)。所以字典里的key就是類中的屬性?繼續看下面。

根據Cascade R-CNN的例子,我們在models/detectors找cascade_rcnn的py文件
參考里面的參數時,直接打開對應的cascade_rcnn配置文件,在init中,里面的參數則
對應了配置文件中的字典名。下面兩個截圖分別是配置文件cascade_rcnn.py和model/detectors/cascade_rcnn.py中的類定義。


注意的是,在py配置文件中,好多py文件中都有type = ‘CascadeRCNN’,所以有些參數和屬性對不上很正常(畢竟已經設置為None了),因為這個參數可能是其他的cascade R-CNN里面的字典。
所以,我們在訓練時,測試時,就要給出配置文件,配置文件可以不同,但相同type的
detector等文件是相同的,畢竟已經將數據和實現完全的分離了。

注意:無論訓練/檢測,都會build DETECTORS;

builder.py文件

builder文件較為簡單,因為train.py中,只出現了build_detector(),所以我們先記住里面的兩個方法:build_detector和build()。

  • build_detector:是創建一個detector,方法里調用了build()方法(所有的build_xx都是直接調用build方法,所以看懂這一個也就看懂所有了)。
  • build():則是調用的Registry.py文件中的build_from_cfg()方法,這個方法我們已經在上面講過了。

import:

# -*- coding: utf-8 -*- from torch import nn from mmdet.utils import build_from_cfg #此處不會在執行registry而是直接進行sys.modules查詢得到 from .registry import (BACKBONES, NECKS, ROI_EXTRACTORS, SHARED_HEADS, HEADS,LOSSES, DETECTORS) #上面的registry是在models文件夾下,registry類的具體實現是在mmdet/utils文件夾下

只需要看一下build()的兩個參數:cfg, registry
build_detector()在train.py中的調用,我們就可以知道,cfg是py配置文件中的字典, 以registry是DETECTORS為例,cfg就是model字典 (后面注冊表為BACKBONES、NECKS等時,就是配置文件中的其他的字典了,不是model) 。

build()方法中,主干是一個判斷結構,其實就是判斷傳進來的cfg是字典列表還是單獨的字典,來分情況處理。(以注冊表DETECTORS為例,是一個單獨的字典)

  • 字典列表的話:挨個調用build_from_cfg(),將其加到注冊表******的_module_dict中,然后再返回return nn.Sequential(*modules),這個地方的作用,有待博主繼續研究一下下???
  • 字典的話:直接調用build_from_cfg(),將其添加到注冊表DETECTORS中(以DETECTORS為例)。
def build(cfg, registry, default_args=None):if isinstance(cfg, list):modules = [build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg#build_from_cfg()返回值是一個帶形參的類,返回時也就完成了實例化的過程。]#所以modules就是一個class類的列表return nn.Sequential(*modules)#nn.Sequential 一個有序的容器,神經網絡模塊將按照在傳入構造器的順序依次被添加到計算圖中執行,同時以神經網絡模塊為元素的有序字典也可以作為傳入參數else:return build_from_cfg(cfg, registry, default_args) #Config dictdef build_detector(cfg, train_cfg=None, test_cfg=None):return build(cfg, DETECTORS, dict(train_cfg=train_cfg, test_cfg=test_cfg))#DETECTORS = Registry('detector'),創建一個名為DETECTORS的注冊表Registry。def build_backbone(cfg):return build(cfg, BACKBONES) def build_neck(cfg):return build(cfg, NECKS) def build_roi_extractor(cfg):return build(cfg, ROI_EXTRACTORS) def build_shared_head(cfg):return build(cfg, SHARED_HEADS) def build_head(cfg):return build(cfg, HEADS) def build_loss(cfg):return build(cfg, LOSSES)

后面的幾個build_XXXXX()的方法也就跟build_detector()相同咯。

還是以注冊表DETECTORS為例,配置文件為cascade_rcnn_r50_fpn_1x.py來講解:在model文件夾下的cascade_rcnn.py文件中,有類Cascade_RCNN()的定義,在配置文件中,對應的key被傳入類中當做屬性,這些屬性被初始化的時候,調用對應的build_XXXXX(),由此創建它們對應的注冊表。
再以NECK為例,調用build_neck(cfg);然后執行build(cfg, NECKS),這一步,形參用到NECKS,所以在Registry中,又多了一個名為NECKS的注冊表了。然后將配置文件中,字典名為neck的,然后生成一個類(類名是neck字典中的type的值,該類在model/necks文件夾下),同時將該類添加到了注冊表NECKS的_module_dict中。

#在model/detectors/cascade_rcnn.py中 if neck is not None:self.neck = builder.build_neck(neck) #再builder.py中 def build_neck(cfg):return build(cfg, NECKS) #在configs/cascade_rcnn_r50_fpn_1x.py中 neck=dict(type='FPN',in_channels=[256, 512, 1024, 2048],out_channels=256,num_outs=5),

到這,NECK的注冊和數據讀入,相信大家已經很清楚了,其他的注冊表也是類似的。

總結:

搭建模型思路:

  • 首先,創建一個名為DETECTORS的注冊表Registry。這個注冊表有屬性name='detector',和屬性_module_dict。_module_dict 是一個字典,專門用來存各個對象名和對應的對象。
  • 其次,讀取py配置文件,py配置文件是個字典,(字典里還有字典,這里面的字典,也是后面來創建模型的,道理是一樣的)。根據key為’type’的字典,創建module,對于的value為其module名,然后再model文件夾下中,已經存在了這些module的類。將字典中的其他數據,作為形參,實例化這些類。并保存這些module到屬性_module_dict中。
  • 到這,配置文件的數據,里面的字典(含有type的字典)對應著一個類,type為類名,其他字段則為其屬性(其他字段也可能是個字典,后面也有可能要再為它們搭建模型哦)。由此完成模型的搭建。

這是搭建模型的一個思路,雖然講得篇幅很大,有點亂亂的感覺,但是看懂后,就會發現很簡單。

mmdetection搭建模型用途:
mmdetection將配置文件中,字典名為:backbone、neck、roi_extractor、shared_head、head、loss、detector的字典,全部實例化成注冊表(Registry),然后這些字典里的type,都被實例化成對應的類(module),并添加到注冊表的屬性_module_dict中,其他的字段,則為這個類的屬性,由此完成模型的建立,實際上,就是將配置文件的字典數據保存到類(module)中,以便后面讀取數據,加載數據。


接下來,請看博主的下面的文章:

  • mmdetection源碼筆記(一):train.py解讀
  • mmdetection源碼筆記(二):創建網絡模型之cascade_rcnn.py的解讀(中)
  • mmdetection源碼筆記(二):cascade_rcnn.py搭建模型過程中各個module的forward()的代碼解讀(下)(待完成)
  • mmdetection源碼筆記(三):創建數據集模型之datasets/coco.py的解讀(上)
  • mmdetection源碼筆記(三):創建數據集模型之datasets/custom.py的解讀(下)
  • mmdetection源碼筆記(四):訓練模型之train_detector()的解讀
  • mmdetection源碼筆記(五):測試之test()部分的解讀

注:因為有好幾個py文件,博主也是按照自己的理解,盡量講得通俗易懂,如果有不理解的地方,底下評論。如果有錯誤的地方,還請指出學習,感激不盡。


更新2021.10.13

有大半年沒有更新博客了,這段時間一直準備春招,實習,秋招,到今天基本上可以結束坐等開獎了;
目前手里有近10個CV算法工程師意向書:字節,商湯,百度,順豐,華為,小紅書,拼多多,小馬智行,海康威視,騰訊等
接下來會花很多時間在小紅書上整理這一過程所有的經驗以及技巧,包括:豐富簡歷內容,如何打比賽(大佬止步),如何刷題,刷什么題,刷到什么程度,面試技巧,面經整理,hr面技巧,如何反問面試官,如何argue薪資等等等,大家有需要可以關注一波哈,內容保證豐富!助力大家秋招收割offer到手軟!
小紅書號:371845174(Activewaste,博客同名)

后面也會在小紅書分享好用工具,插件,裝逼神器等,手把手教學,大家關注起來哈!
另外我比較少上csdn,大家給我私信要聯系方式或者評論問問題有時沒看到(一個月沒上,200+的通知),大家直接去小紅書私信提問,或者催我撒!

總結

以上是生活随笔為你收集整理的mmdetection源码笔记(二):创建网络模型之registry.py和builder.py解读(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。