Detectron2での推論について前回整理したので、今回は訓練について整理していこうと思います。
・問題設定
・Detectron2での学習の流れ
データセットについて
BCCD Dataset は、血液の顕微鏡写真の3~400枚程度の物体検出用データセットです。
赤血球(RBC)・白血球(WBC)・血小板(Platelet)の3種類についてアノテーションがされています。
アノテーションデータは Pascal VOC形式で、[x0, y0, x1, y1]で表現されるバウンディングボックスが付与されています。
データはこちらのレポジトリからダウンロードできます。
1 2 |
# データを取得 !git clone https://github.com/Shenggan/BCCD_Dataset.git |
Detectron2での学習の流れ
流れは下記のとおりです。
- データをCOCOフォーマットに変換する
- dataset_dictsを用意する
- DatasetCatalog(とMetadataCatalog)を設定する
- モデルを構築・学習
- 結果をCOCO APIで評価
0. 環境合わせ&ライブラリのインポート
Colabのチュートリアル
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# install dependencies: (use cu101 because colab has CUDA 10.1) !pip install -U torch==1.5 torchvision==0.6 -f https://download.pytorch.org/whl/cu101/torch_stable.html !pip install cython pyyaml==5.1 !pip install -U 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI' import torch, torchvision print(torch.__version__, torch.cuda.is_available()) !gcc --version # opencv is pre-installed on colab # install detectron2: !pip install detectron2==0.1.3 -f https://dl.fbaipublicfiles.com/detectron2/wheels/cu101/torch1.5/index.html # You may need to restart your runtime prior to this, to let your installation take effect # Some basic setup: # Setup detectron2 logger import detectron2 from detectron2.utils.logger import setup_logger setup_logger() # import some common libraries import numpy as np import cv2 import random from google.colab.patches import cv2_imshow # import some common detectron2 utilities from detectron2 import model_zoo from detectron2.engine import DefaultPredictor from detectron2.config import get_cfg from detectron2.utils.visualizer import Visualizer from detectron2.data import MetadataCatalog |
1. データをCOCOフォーマットに変換する
COCOフォーマットが扱いやすいため、変換します。
Pascal VOC→COCOは、こちらのレポジトリを使わせてもらって簡単に変換できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
!git clone https://github.com/yukkyo/voc2coco.git %cd voc2coco/ !python voc2coco.py --ann_dir sample/Annotations \ --ann_ids sample/dataset_ids/train.txt \ --labels sample/labels.txt \ --output sample/bccd_train_cocoformat.json \ --ext xml !python voc2coco.py --ann_dir sample/Annotations \ --ann_ids sample/dataset_ids/test.txt \ --labels sample/labels.txt \ --output sample/bccd_test_cocoformat.json \ --ext xml |
2. dataset_dicts を用意する
Detectron2で学習させる場合は、アノテーションデータをCOCOフォーマットに似た list[dict]
のかたちで用意する必要があります。詳細は、こちらの Standard Dataset Dicts の箇所を見るとわかります。
イメージとしては、一枚の画像ごとに下記のような辞書を用意して、リストに入れてあげます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{"file_name": "image/path", "height": 100, "width": 100, "image_id": 1, "annotations": [ [ {"bbox": [x0, y0, w, h], "bbox_mode": BoxMode.XYWH_ABS, "category_id": 0, "segmentation": []} ], [ {"bbox": [x0, y0, w, h], "bbox_mode": BoxMode.XYWH_ABS, "category_id": 0, "segmentation": []} ] ] } |
“bbox_mode” でバウンディングボックスの表現の仕方を指定することができます。
[x0, y0, x1, y1] の場合は、BoxMode.XYXY_ABS
[x0, y0, w, h] の場合は、BoxMode.XYWH_ABS
といったようにします。他にも指定できこちらから確認できます。
ここは、チュートリアルのものをそのまま使うことはできないので、書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
def get_bccd_dicts(img_dir, json_path): with open(json_path) as f: imgs_anns = json.load(f) images = imgs_anns["images"] annotations = imgs_anns["annotations"] dataset_dicts = [] ids = [] for im in images: record = {} filename = os.path.join(img_dir, im["file_name"]) record["file_name"] = filename record["height"] = im["height"] record["width"] = im["width"] record["image_id"] = im["id"] dataset_dicts.append(record) ids.append(im["id"]) annos_dict = {} for id in ids: objs = [] for anno in annotations: if anno["image_id"] == id: obj = { "bbox": anno["bbox"], "bbox_mode": BoxMode.XYWH_ABS, "category_id": anno["category_id"], "segmentation": anno["segmentation"] } objs.append(obj) annos_dict[id] = objs for dic in dataset_dicts: for id in annos_dict.keys(): if dic["image_id"] == id: dic["annotations"] = annos_dict[id] return dataset_dicts img_dir = "BCCD_Dataset/BCCD/JPEGImages/" train_json_path = "voc2coco/sample/bccd_train_cocoformat.json" test_json_path = "voc2coco/sample/bccd_test_cocoformat.json" train_dataset_dicts = get_bccd_dicts(img_dir, train_json_path) test_dataset_dicts = get_bccd_dicts(img_dir, test_json_path) |
3. DatasetCatalog(とMetadataCatalog)を設定する
データセットに名前をつけて、DatasetCatalogに登録してあげる必要があります。MetadataCatalogへのメタデータの登録は必要に応じて、だそうですが、このあたりよくわかっていません。
とりあえず、COCOフォーマットのJSONファイルがある場合は、register_coco_instances
というもので登録してあげればOKのようです。
1 2 3 |
from detectron2.data.datasets import register_coco_instances register_coco_instances("bccd_train", {}, train_json_path, img_dir) register_coco_instances("bccd_test", {}, test_json_path, img_dir) |
ここでいくつか教師データを可視化して確認します。
1 2 3 4 5 6 7 |
bccd_train = MetadataCatalog.get("bccd_train") for d in random.sample(train_dataset_dicts, 3): img = cv2.imread(d["file_name"]) visualizer = Visualizer(img[:, :, ::-1], metadata=bccd_train, scale=0.5) out = visualizer.draw_dataset_dict(d) cv2_imshow(out.get_image()[:, :, ::-1]) |
4. モデルを構築・学習
ここまでできたら後は楽です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from detectron2.config import get_cfg cfg = get_cfg() # Faster RCNN cfg.merge_from_file(model_zoo.get_config_file("COCO-Detection/faster_rcnn_R_101_FPN_3x.yaml")) cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Detection/faster_rcnn_R_101_FPN_3x.yaml") cfg.MODEL.ROI_HEADS.NUM_CLASSES = 3 cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 128 # Dataset( "," がないとエラー吐かれる) cfg.DATASETS.TRAIN = ("bccd_train",) cfg.DATASETS.TEST = ("bccd_test", ) # ハイパーパラメータ cfg.SOLVER.IMS_PER_BATCH = 2 cfg.SOLVER.BASE_LR = 0.001 cfg.SOLVER.MAX_ITER = 500 |
前回の推論の箇所でも書きましたが、こちらのConfig Reference でモデル独自のハイパーパラメータを探して設定することが可能です。
1 2 3 4 5 6 |
from detectron2.engine import DefaultTrainer os.makedirs(cfg.OUTPUT_DIR, exist_ok=True) trainer = DefaultTrainer(cfg) trainer.resume_or_load(resume=False) trainer.train() |
Trainer には SimpleTrainer と DefaultTrainer があり、SimpleTrainer は必要最小限の機能(損失を計算して、勾配算出し、更新する)しか備わっていないらしく、DefaultTrainerにしておけば、勝手にTensorBoardへのロギング等いろいろしてくれるみたいです。
500エポック回し終わると、ログは TnesorBoard で確認できます。
1 2 3 |
%load_ext tensorboard %tensorboard --logdir output |
いくつかテストデータを推論してみて可視化をします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# モデルに学習済み重みを読み込ませる cfg.MODEL.WEIGHTS = os.path.join(cfg.OUTPUT_DIR, "model_final.pth") cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.5 # faster rcnn cfg.DATASETS.TEST = ("bccd_test", ) predictor = DefaultPredictor(cfg) for d in random.sample(test_dataset_dicts, 3): im = cv2.imread(d["file_name"]) outputs = predictor(im) v = Visualizer(im[:, :, ::-1], metadata=bccd_test, scale=0.8, #何倍に画像を表示するか ) out = v.draw_instance_predictions(outputs["instances"].to("cpu")) cv2_imshow(out.get_image()[:, :, ::-1]) |
良さそうに見えますが、どれくらい良いのか、最後にCOCOAPIで検証します。
5. 結果をCOCO APIで評価
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from detectron2.evaluation import COCOEvaluator, inference_on_dataset from detectron2.data import build_detection_test_loader evaluator = COCOEvaluator("bccd_test", cfg, False, output_dir="./output/") val_loader = build_detection_test_loader(cfg, "bccd_test") inference_on_dataset(trainer.model, val_loader, evaluator) ``` Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.581 Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.903 Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.650 Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.248 Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.462 Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.450 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.378 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.626 Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.687 Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.620 Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.615 Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.511 ``` |
ということで、諸数値を見ることができました。
Detectron2を使ってみたいどなたかのお役に立てば幸いです。
最後まで読んで頂きありがとうございました。