Detectron2導覽: FAIR推出能快速建置Object Detection/Segmentation的訓練平台 (上)

S.H.H
TryTech
Published in
13 min readJul 11, 2020
Object detection demo
擷取自: https://github.com/facebookresearch/detectron2

從推出Detectron,到之後的maskrcnn-benchmark,以及現在的Detectron2,都展現了Facebook於AI領域的野心,學習架構也從當初的Caffe2進展到Pytorch上了,Pytorch因其動態運算圖的設計,建置上語法較為直覺簡單,對初學者來說方便上手,市佔率也逐步上升,雖然仍與Tensorflow差了一段距離,但最近剛推出的Tensorflow 2.0也增加了支援動態建置的功能,看來Google還是感受到Facebook緊追在後的威脅性…不過上述都不是本文重點,本文主要是分享對Detectron2學習上的觀念拆解,可分為(1)模型結構(2)資料集(3)訓練和測試以及(4)日誌紀錄這四個面向來了解模型是如何在上面運作,應有助於之後在建置自己的模型時,能更快速地客製化方法且符合平台設計邏輯。由於之前並沒有實際使用過前兩代的版本,所以可能有些是承襲之前的特點,這邊筆者沒有特別去調查比較,如有贅述還請多多包涵🙏

(Detectron2至今Github Repo已11.5k stars✨,至少已經超過maskrcnn-benchmark的7.7k啦)

環境安裝

Detectron2的安裝非常簡單,跟下載一般python套件差異不大,只是要注意若不是載pre-built版本(Linux only),在執行安裝時要確保GPU Driver或cuda都運行正常,不然很可能會在實際呼叫的時候吐出跟GPU相關的錯誤訊息,在git clone大絕開下去安裝好後,就可以開始來感受Detectron2的實戰價值。 (註:官方沒有推出相容於windows的版本,可能要自己爬文去找windows下需要的一些設定或安裝套件,這在maskrcnn-benchmark時應就有相關解法)
Detectron2支持的Model除自家的Fast R-CNN, Faster R-CNN, Mask R-CNN等,也另外開源基於Detectron2的research projects,包含TridenNet, DenseNet, TensorMask等,歡迎使用者自取自用,也可以當作很好的example看看FAIR的研究人員是如何設計程式架構並實作模型訓練,接下來就來看看如何在Detectron2上建置自己的模型吧!

CfgNode

這個class對整個Detectron2運作扮演了相當重要的角色,裡面包含了非常多模型建構或訓練所需要的參數,而這個類別存在的目的是便於使用者在呼叫不同方法時,不必再一一對照參數名稱,而是直接將整個cfg視為參數,裡頭的方法才再根據需求直接從cfg取用,雖然相較起來比較佔用資源,同時也得花些時間去認識裡頭的定義的名稱,但也因此便讓很多方法變得簡潔很多,若有自己需要的參數也提供自行擴充的彈性。以待會要介紹的訓練方法為例,在Detectron2上只需要下列程式碼,就可以開始模型訓練,就連模型設定也都塞在cfg裡頭了!

trainer = DefaultTrainer(cfg)
trainer.resume_or_load() # load last checkpoint or MODEL.WEIGHTS
trainer.train()

另外,也可以發現FAIR的projects都會另外開一個config.py來作這些必要的設定。

(1) 模型結構 Model Structure

使用Detectron2上的pre-trained model

Detectron2需利用build_model方法來建立和DetectionCheckpointer類別載入權重,前者只輸入cfg作為參數,所以通常需要先設定好相對應的值,後者則是輸入回傳的model和欲存放路徑,一般還會將optimizer和learning rate放入,這邊不多加贅述,僅節錄類別DefaultTrainer中的一段code:

self.checkpointer = DetectionCheckpointer(             
model,
cfg.OUTPUT_DIR,
optimizer=optimizer,
scheduler=self.scheduler,
)

具體來說,要使用特定的某個模型,可以先去Model Zoo裡面看一看,他都有對應的config file(.yaml檔案),要使用也很簡單,只要下對路徑就好,如官方提供的Colab教學中的Mask R-CNN為例:

from detectron2.config import get_cfg
cfg = get_cfg()
cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml") # Let training initialize from model zoo

其餘可再自行閱讀各個模型會利用的參數進行調整,以符合自身任務,同樣是存在cfg裡頭!

客製化模型 Customized Model

若是自定義模型,可以先從拆解模型架構看detectron2.modeling底下有沒有對應的類別作基礎,繼承之後再開始變化,如Backbone或CNNBlockBase,可能之後突然要新增什麼功能比較好改(?),或是也可從nn.Module開始全部重刻。只是要注意,若是屬於這種情況,因為Detectron2把模型建置包裝成只需要build_model方法和config變數,所以需要將新定義的模型做一個註冊的動作(之後會發現很多新加的東西都要”register”),這都是為了迎合前面CfgNode的設計思維:

@META_ARCH_REGISTRY.register()
class NewModel(nn.Module):
...

detectron2.modeling.build.py中的build_model方法

def build_model(cfg):
"""
Build the whole model architecture, defined by ``cfg.MODEL.META_ARCHITECTURE``.
Note that it does not load any weights from ``cfg``.
"""
meta_arch = cfg.MODEL.META_ARCHITECTURE
model = META_ARCH_REGISTRY.get(meta_arch)(cfg)
model.to(torch.device(cfg.MODEL.DEVICE))
return model

若今天客製模型只有部分有些差異,以TridenNet為例,因為和Detectron2的GeneralizedRCNN類別架構一樣,故只需實作該類別中需要替換的部分,以backbone為例:

# refers to TridenNet
@BACKBONE_REGISTRY.register()

def build_trident_resnet_backbone(cfg, input_shape: ShapeSpec):
...
return ResNet(stem, stages, out_features=out_features)

設定好cfg.MODEL.BACKBONE.NAME,這邊使用的例子是定義在Base-TridentNet-Fast-C4.yaml內,則在建置時就會自動抓取這個自訂方法:

self.backbone = build_backbone(cfg)
def build_backbone(cfg, input_shape=None):
"""
Build a backbone from `cfg.MODEL.BACKBONE.NAME`.
Returns:
an instance of :class:`Backbone`
"""
if input_shape is None:
input_shape = ShapeSpec(channels=len(cfg.MODEL.PIXEL_MEAN))
backbone_name = cfg.MODEL.BACKBONE.NAME
backbone = BACKBONE_REGISTRY.get(backbone_name)(cfg, input_shape)
assert isinstance(backbone, Backbone)
return backbone

(其餘可再參考document或projects內的程式碼)

(2) 資料集 Dataset

既然是宣稱提供快速訓練平台,一些常用的資料集格式基本上都已經建立好,如果不是有特別自訂模型或有特別需求的話,通常是把自己的資料轉存成符合上列的資料格式之一比較方便,才不用什麼都要從頭刻一遍,不然一開始就直接打開Pytorch開始刻Dataset好了,轉換的程式相較之下比較好寫,且通常只是跑給自己用的,所以可以寫醜一點(誤)。

在介紹資料集的運作邏輯前,要先介紹介紹DatasetCatalogMetadataCatalog這兩個左右護法,前者用來管理主要資料的存取,像是每張圖片和對應的標籤;後者則是存放一些對整個資料集的描述,如資料集中標籤所對應的名稱。同理前述模型的管理,所有的資料也都是通過註冊的方法,來做管理和存取的動作:

from detectron2.data import DatasetCatalog, MetadataCatalog
DatasetCatalog.register(dataset_name, dataset_dicts)
dicts = DatasetCatalog.get(dataset_name)
MetadataCatalog.get("my_dataset").thing_classes = ["person", "dog"]

DatasetCatalog管理的dataset_dicts為一組字典,每個字典對應的是一張圖片的資訊,若是做一些常見的任務,則可以參考文件中訂定字典中應有的key(但不限於),會減少之後改寫其他模塊的工作,如模型或訓練方式。而MetadataCatalog則管理一個字典,可以加入任何需要共享於整個資料集的資訊,同樣若想在之後取用某些Detectron2的方法,也有一些必須設定好的鍵值。而針對使用情境可分為以下兩項說明:

  1. 直接使用某個public dataset的資料,則把對應的資料集下載下來,再將路徑放到環境變數DETECTRON2_DATASETS就好,在程式運行前先於終端機上輸入,若不更動的話預設為./datasets上:
export DETECTRON2_DATASETS=/path/to/datasets

再於程式上頭加上,就會自動檢查該目錄並進行執行註冊:

from .data.datasets import builtin

目前支援的資料集(即I/O都幫你寫好)為COCO instance/keypoint detection, PanopticFPN, LVIS instance segmentation, cityscapes, Pascal VOC,對應的資料結構可再去參考Github上的文件說明。若想在自己的資料上使用,也可以把資料擺成相同的結構,然後呼叫對應的註冊方法,若還需要做其他處理,也可以只利用其他方法,之後再自己額外做註冊:

from detectron2.data.datasets import register_coco_instances
register_coco_instances("my_dataset", {}, "json_annotation.json", "path/to/image/dir")
# generate json_annotation.json only
data_dicts = load_coco_json(json_file, image_root, name)
# after some processing ...
DatasetCatalog.register('my-coco', new_data_dicts)

2. 有自己資料的儲存結構,或是label格式不屬於任何一種,這邊就需要自行宣告讀寫的方法並製作相應的字典,最後同樣回傳一個字典組成的陣列,如官方提供的Colab教學,裡頭僅用一個function把資料處理完,最後再放入DataCatalog裡面:

def get_balloon_dicts(img_dir):
...
return dataset_dicts
for d in ["train", "val"]:
DatasetCatalog.register("balloon_" + d, lambda d=d: get_balloon_dicts("balloon/" + d))
MetadataCatalog.get("balloon_" + d).set(thing_classes=["balloon"])

最後,該如何讓之後訓練能用到這些定義好的資料呢?沒錯,一樣是在cfg裡面做設定,把之前註冊好的名字放入DATASETS.TRAIN和DATASETS.TEST中,在之後相關方法,如build_detection_train_loader,裡面會再透過DataCatalog去存取,不管是用內建或是自己定義的方法都是一樣,把字典取出來後再根據需求自行處理,最後建立一個DataLoader類別的變數回傳即可。

Detectron2根據現行任務要求對資料格式有了一定的規格,若萬不得已有新的任務或其他特別格式要進行改寫,只能說是牽一髮動全身,通常模型的I/O也得改寫一番,老實說那應該不如直接開Pytorch來寫,畢竟開源的Code也非常多,只是多一些copy+paste和整理的功夫,但我想這種情況應該是非常的少,畢竟在projects內也以Detectron2實作其他STOA,都是很好的參考,可以再細讀裡頭的程式碼。

原本是想一口氣寫完,但發現為了盡可能講詳細,內容還真不少,其實只是把document翻成中文(?),下章再來講有關訓練和測試有關的trainer和evaluation該如何建立,以及訓練日誌應如何紀錄,若文章內容對你有幫助也希望能不吝按幾下claps。

(以上是自己閱讀程式碼整理的心得,若對文章內容有疑慮或觀念錯誤的地方也歡迎留言討論/指正)

參考

Yuxin Wu, Alexander Kirillov, Francisco Massa and Wan-Yen Lo, & Ross Girshick. (2019). Detectron2. https://github.com/facebookresearch/detectron2.

--

--