Rotation-InvariantモデルをDOTAで動かす

今回はRotation-InvariantというDOTAデータセットを使って研究開発されているモデルを動かしてみたいと思います。

Rotation-Invariantモデルはこんなモデル
  • 深層学習の物体検出モデル
  • 出力に回転も考慮出来る
  • DOTAというデータセットを使って研究されている

目次

DOTAデータセットについて

物体検出では画像に対して水平なバウンディングボックスで出力されることがおおいのですが、衛星画像だと水平だと一部の情報が失われてしまいます。例えば船であれば船首の向きがわかれば、どこに向かっているか方向がわかります。飛行機もそうですね。

こういったターゲットの回転に対応したRotation-Invariantなモデルが研究されていて、コードが公開されています。

なかでもDOTAデータセットはRotation-Invariantの学習が可能なデータセットになっています。DOTAデータセットは、15種類のターゲットに対応しているオープンデータセットです。詳しくは本家サイトをご確認ください。

出所:DOTA(https://captain-whu.github.io/DOTA/index.html)

今回はこのRotation-Invariantモデルを動かしてみます。

環境は過去記事で使ってきた環境を使います。

EC2は立ち上げているとどんどんお金が請求されていきます!

DOTAデータのダウンロード

先ずはDOTAのデータ(DOTA-v1.0)をダウンロードします。データはこちらからダウンロードできます。

GoogleDriveはコマンドからダウンロードできるみたいですが、色々めんどくさそうなので、私はとりあえずブラウザからダウンロードしました。

ダウンロードした後はzipを解凍して、以下のようなディレクトリ配置にしておきます。

./DOTA/
├── test
│   └── images
├── train
│   ├── images
│   └── labelTxt
│       ├── DOTA-v1.5_train
│       ├── DOTA-v1.5_train_hbb
│       ├── labelTxt
│       └── trainset_reclabelTxt
└── val
    ├── images
    └── labelTxt
        ├── DOTA-v1.5_val
        ├── DOTA-v1.5_val_hbb
        ├── labelTxt
        └── valset_reclabelTxt

これのディレクトリを丸ごとzipに固めて、今度は自分のS3に上げました。

リポジトリのfork

今回使うリポジトリforkしておきました。

forkしたリポジトリは自分の環境にクローン(/home/ubuntu/work/以下)しておきました。

学習済モデルのダウンロード

学習済のモデルをダウンロードしておきます。

ダウンロード先はリポジトリのREADMEに以下のような記載があるので、それに従ってダウンロードすればOKです。

モデル取得先
resnet_v1dGoogleDrive
resnet_v1bresnet_v1dと同じ
resnet50_v1GoogleDrive
resnet101_v1resnet50_v1と同じ
resnet152_v1resnet50_v1と同じ
efficientnetresnet50_v1と同じ
※BO0からBO5までダウンロード
mobilenet_v2resnet50_v1と同じ
darknet53resnet50_v1と同じ
必要な学習済モデルと取得先

元のリポジトリにはオプション的な書かれ方をしていますが、tensorflow版だとresnet50_v1dがなかったりとpytorch以外は全部入れておいた方が良さげです。

保存した重みはpretrained_weightsというディレクトリに以下のディレクトリ構成で入れておきます。

├── pretrained_weight
│    ├── darknet
│       ├── checkpoint
│       ├── darknet.ckpt.data-00000-of-00001
│       ├── darknet.ckpt.index
│       ├── darknet.ckpt.meta
│    ├── efficientnet
│       ├── efficientnet-b0
│           ├── checkpoint
│           ├── model.ckpt.data-00000-of-00001
│           ├── model.ckpt.index
│           ├── model.ckpt.meta
│    ├── mobilenet
│       ├── mobilenet_v1_0.25_128.ckpt.data-00000-of-00001
│       ├── mobilenet_v1_0.25_128.ckpt.index
│       ├── mobilenet_v1_0.25_128.ckpt.meta
│       ├── mobilenet_v1_0.25_128.tflite
│       ├── mobilenet_v1_0.25_128_eval.pbtxt
│       ├── mobilenet_v1_0.25_128_frozen.pb
│       ├── mobilenet_v1_0.25_128_info.txt
│    ├── resnet_v1_50.ckpt    
│    ├── resnet50_v1d.ckpt.index    
│    ├── resnet50_v1d.ckpt.data-00000-of-00001    
│    ├── resnet50_v1d.ckpt.meta
│    ├── resnet50.npy
… 

全体の中でこことDOTAデータセットの配置が一番面倒だったかも。

Python仮想環境の構築

今回必要な環境はtensorflowの1系なので新しく環境を作成します。

conda create -n rotationdetection --clone tensorflow_p37

作成したらactivateして、必要なライブラリを入れていきます。

conda install -c conda-forge opencv
conda install -c conda-forge tqdm
conda install -c conda-forge shapely
pip install git+https://github.com/wookayin/tensorflow-plot.git@master

設定ファイルを準備する

このリポジトリは沢山モデルが実装されていますが、今回はRetinaNet-Rを使ってみることにしました。

./RotationDetection/libs/configs/DOTA/retinanet/cfgs_res50_dota_v8.pyにあるデータをコピーしておきます。

cp ./RotationDetection/libs/configs/DOTA/retinanet/cfgs_res50_dota_v8.py ./RotationDetection/libs/configs/DOTA/retinanet/kazushi_cfgs_res50_dota_v8.py

動作確認したいだけなので、中身20行目あたりの以下だけ変更します。

# dataset
DATASET_NAME = 'DOTA1.5'

これを

# dataset
DATASET_NAME = 'DOTA1.0'

こんな感じで変更。

cfgファイルを所定のディレクトリに入れますが、その前にオリジナルファイルを保存しておきます。

mv ./RotationDetection/libs/configs/cfgs.py ./RotationDetection/libs/configs/cfgs_ori.py
cp ./RotationDetection/libs/configs/DOTA/retinanet/kazushi_cfgs_res50_dota_v8.py ./RotationDetection/libs/configs/cfgs.py

データをEC2にもってくる

先ほど用意したDOTAデータをEC2に持ってきて、以下のように配置します。

./RotationDetection/dataloader/dataset/DOTA/
├── data_crop.py
├── data_slicer.py
├── test
│   ├── images
│   └── test_info.json
├── train
│   ├── images
│   └── labelTxt
├── val
│   ├── images
│   └── labelTxt
└── val_set.txt

データを分割する

DOTA元データは大きいのでデータをcropする。

./RotationDetection/dataloader/dataset/DOTA/data_crop.pyにあるファイルの211行目あたりの

raw_data = '/data/dataset/DOTA/val/'
raw_images_dir = os.path.join(raw_data, 'images', 'images')
raw_label_dir = os.path.join(raw_data, 'labelTxt', 'labelTxt')
save_dir = '/data/dataset/DOTA/DOTA1.0/trainval_easy/'

これを自分の環境に合わせて変更しておきます。

raw_data = '/home/ubuntu/work/RotationDetection/dataloader/dataset/DOTA/val/'
raw_images_dir = os.path.join(raw_data, 'images')
raw_label_dir = os.path.join(raw_data, 'labelTxt', 'labelTxt')
save_dir = '/home/ubuntu/work/RotationDetection/dataloader/dataset/DOTA/DOTA1.0/val/'

変更したらrotationdetectionに実行環境を変更して実行します。

cd /home/ubuntu/work/RotationDetection/dataloader/dataset/DOTA
conda activate rotationdetection
python data_crop.py

上記はvalだけですが、trainも同様に処理しておきます。

DOTAデータの前処理

DOTAデータをモデルに入力可能な形に前処理していきます。

前処理コードは./RotationDetection/dataloader/dataset/convert_data_to_tfrecord.pyにあります。

実行時の引数が多かったのでシェルスクリプトを作成しました。

python /home/ubuntu/work/RotationDetection/dataloader/dataset/convert_data_to_tfrecord.py \
    --root_dir='/home/ubuntu/work/RotationDetection/dataloader/dataset/DOTA/DOTA1.0/' \
    --xml_dir='train/labeltxt' \
    --image_dir='train/images' \
    --save_name='train' \
    --img_format='.png' \
    --dataset='DOTA1.0'

これを./RotationDetection/dataloader/dataset/に移動して実行。

Cythonのコンパイル

Cpythonのコンパイルをしておきます。本家のReadmeにある通りにすればOK。

cd $PATH_ROOT/libs/utils/cython_utils
rm *.so
rm *.c
rm *.cpp
python setup.py build_ext --inplace (or make)

cd $PATH_ROOT/libs/utils/
rm *.so
rm *.c
rm *.cpp
python setup.py build_ext --inplace

Tensorbord用のポート開放設定をする

このリポジトリはTensorbordがサポートされているみたいなので、実行してみたい。

EC2のセキュリティグループのインバウンドルールを以下のように設定しておきます。

EC2インバウンドルールの変更

学習の実行

./RotationDetection/tools/retinanetで以下を実行します。

python train.py

するとこんな感じで学習が進む。

************************************************************************
2021-08-19 02:25:06: global_step:20  current_step:20
speed: 6.735s, remaining training time: 20:22:43:58
total_losses:2.039
cls_loss:1.305
reg_loss:0.734

************************************************************************
2021-08-19 02:25:32: global_step:40  current_step:40
speed: 1.022s, remaining training time: 03:04:15:48
total_losses:1.576
cls_loss:1.139
reg_loss:0.437

************************************************************************
2021-08-19 02:25:53: global_step:60  current_step:60
speed: 0.986s, remaining training time: 03:01:36:59
total_losses:1.591
cls_loss:1.146
reg_loss:0.445

************************************************************************

Tensorbordの起動

Tensorbordは./RotationDetection/output/summaryに移動して、以下を実行するだけ。

tensorboard --logdir=.

これで出てくるURLではアクセスできない。

私はIPを固定してるので、http://ip-XXX-XX-XX-XX:6006/となっているURLをhttp://XXX.XXX.XXX.XXX:6006/に書き換えてアクセス。

しばし待つ

しばし待つ。

このリポジトリはEary Stoppingの設定がされておらず、学習が無限に回る様子。

設定ファイル/RotationDetection/libs/configs/DOTA/retinanet/kazushi_cfgs_res50_dota_v8.pyの以下のエポック回れば、/RotationDetection/output/trained_weights以下のディレクトリに学習結果が保存される。

SAVE_WEIGHTS_INTE = 30000

回ったのを確認して止めた。

テストする

学習が終わったのでテストする。

テストは、/RotationDetection/tools/retinanet/に移動して、以下のコマンドで実行できます。

/RotationDetection/dataloader/dataset/DOTA/test/images_pickup/にtrainから2枚画像を選びました。

(ちなみにtestから選んだりもしたが、検出性能がそこまででてなかったので、動作確認の意味でtrainから選んだ。)

python test_dota.py --test_dir='/home/ubuntu/work/RotationDetection/dataloader/dataset/DOTA/test/images_pickup/' \
                    --gpus=0 \
                    -s

毎回打ちたくないので、私はまたbashを書いた。

テスト結果

テスト結果は/RotationDetection/tools/retinanet/test_dota以下に保存される。

image from DOTA(https://captain-whu.github.io/DOTA/index.html)

RarePlanesの画像でも試してみる。

image from RarePlanes(https://www.cosmiqworks.org/rareplanes/)

学習データに含まれていないのでちょっと厳しいか。

おまけ:描画メモ

/RotationDetection/libs/utils/draw_box_in_img.pyの241行目あたりをコメントアウトするとスコアを消せる。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です