はじめに
前回は、AIでライオンとネコを検出する!と題してTensorflowを使ってObject Detectionをやってみました。うまく識別はできたが今回はもう少し前に進んでみましょう!。ピクセル単位でライオンとネコを検出をしてみたいと思います。今回はSemantic Segmentationという技術を用います。
前提
下記のGCPのGPUインスタンスを使用します。CPUオンリーの環境でもやってみましたが手順はほとんど変わりませんので問題ないかと思います。ただし、Semantic Segumentationはトレーニングが終息するまで、かなりのステップ数が必要になるようなので、なるべく高速なGPUインスタンスをお使いになることをおススメします。
モデルのダウンロード
まずは、いつも通り、Githubからモデルをダウンロードしてきましょう。あわせて、今回ベースに用いるモデルもダウンロードしてきます。ベースとなるモデルはいくつかありますが、今回はmobilenetv2のtrainvalというものを用いました。
参考:モデルダウンロードページ
1 2 3 4 |
$git clone https://github.com/tensorflow/models.git $cd models/research/deeplab/ $wget http://download.tensorflow.org/models/deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz $ tar xvfz deeplabv3_mnv2_pascal_trainval_2018_01_29.tar.gz |
トレーニング用のデータを準備
Semantic Segmentationはデータの準備が大変です。準備方法はちょっと複雑なので、別途こちらのページで説明します。以下、学習用データが作成された前提で進めます。
データ格納用のフォルダを作成します。
1 2 3 4 |
$ mkdir dataset $ mkdir dataset/img # 元画像のデータ $ mkdir dataset/lbl # ピクセルごとのラベルのデータ $ mkdir dataset/lst # 使用する画像リスト |
画像データフォルダの中身はこのような感じ。トレーニングの際にcropサイズを指定できるので、この時点でサイズは特に意識しないでも大丈夫です。
1 2 3 |
$ ls dataset/img/ lion01.jpg lion03.jpg lion05.jpg lion07.jpg lion09.jpg lion11.jpg lion13.jpg lion15.jpg lion17.jpg lion02.jpg lion04.jpg lion06.jpg lion08.jpg lion10.jpg lion12.jpg lion14.jpg lion16.jpg lion18.jpg |
ラベルデータフォルダの中身はこのような感じ。こちらもサイズは元の画像データに合わせておけばよいです。
1 2 3 |
$ ls dataset/lbl/ lion01.png lion03.png lion05.png lion07.png lion09.png lion11.png lion13.png lion15.png lion17.png lion02.png lion04.png lion06.png lion08.png lion10.png lion12.png lion14.png lion16.png lion18.png |
画像リストフォルダの中身は次の二つで、それぞれの中身は下記のように拡張子抜きのファイル名を列挙します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ ls dataset/lst/ train.txt val.txt $ cat dataset/lst/train.txt lion01 lion02 lion03 lion04 lion05 lion06 lion07 lion08 lion09 lion10 lion11 lion12 lion13 lion14 $ cat dataset/lst/val.txt lion15 lion16 lion17 |
TFRecordの作成
データセットからおなじみのTFRecordを作成します。
1 2 3 4 5 6 7 8 9 10 11 |
$ mkdir dataset/tfrecord $ python3 datasets/build_voc2012_data.py --output_dir=./dataset/tfrecord --image_folder=./dataset/img --semantic_segmentation_folder=./dataset/lbl --list_folder=./dataset/lst --image_format=jpg 2019-03-09 05:41:05.231652: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:998] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero <snip> >> Converting image 1/3 shard 0 >> Converting image 2/3 shard 1 >> Converting image 3/3 shard 2 $ ls dataset/tfrecord/ train-00000-of-00004.tfrecord train-00002-of-00004.tfrecord val-00000-of-00004.tfrecord val-00002-of-00004.tfrecord train-00001-of-00004.tfrecord train-00003-of-00004.tfrecord val-00001-of-00004.tfrecord val-00003-of-00004.tfrecord |
トレーニングの設定
作成したデータセットを使用できるようにdatasets/data_generator.pyを編集します。DatasetDescriptorとして、トレーニングに使用するデータ数やクラス数などを指定してあげます。あとはこのDatasetDescriptorをエントリを_DATASETS_INFORMATIONに追加してあげればよいです。
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 |
... 'ignore_label', # Ignore label value. ]) _LIONL_CAT_INFORMATION = DatasetDescriptor( splits_to_sizes={ 'train': 14, # トレーニング用データ数 'val': 3, # 評価用データ数 }, num_classes=3, # 3 ライオン、ネコ、背景 ignore_label=255, ) _CITYSCAPES_INFORMATION = DatasetDescriptor( splits_to_sizes={ ... _DATASETS_INFORMATION = { 'cityscapes': _CITYSCAPES_INFORMATION, 'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION, 'ade20k': _ADE20K_INFORMATION, 'lioncat': _LIONL_CAT_INFORMATION, # ←追加 } ... |
トレーニングの開始
ではトレーニング開始です。datasetは先ほど編集したスクリプトに記載したデータセット名を指定します。実行してみてわかったのですが、以前少し触っていた時とスクリプトが変わったようで実行ログの表示が以前と違っていました。何となく、参考までに以前の出力も下記に示していますが、現在のステップ数とか表示されなくなってなんかわかりずらくなった気がします。処理時間も sec/stepから step/secにかわってるし。いずれにせよ、依然はCPUでやったので、1stepあたり1秒強かかっていたのがGPUのおかげで0.1秒くらいになっていそうです(step/secの逆数)。
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 |
$export PYTHONPATH=${HOME}/models:${HOME}/models/research:${HOME}/models/research/slim $python3 \ ./train.py \ --dataset=lioncat \ --logtostderr \ --train_split=train \ --model_variant=mobilenet_v2 \ --output_stride=8 \ --train_crop_size=257 \ --train_crop_size=257 \ --train_batch_size=2 \ --training_number_of_steps=100000 \ --train_logdir='./dataset/trainlog' \ --dataset_dir='./dataset/tfrecord' \ --tf_initial_checkpoint=./deeplabv3_mnv2_pascal_trainval/model.ckpt-30000 \ --initialize_last_layer=False <snip> INFO:tensorflow:global_step/sec: 7.7467 Total loss is :[0.395626783] INFO:tensorflow:global_step/sec: 9.25241 Total loss is :[0.576795936] INFO:tensorflow:global_step/sec: 8.76726 Total loss is :[0.420322508] INFO:tensorflow:global_step/sec: 10.0072 Total loss is :[0.644693375] INFO:tensorflow:global_step/sec: 7.85811 Total loss is :[0.380738616] INFO:tensorflow:global_step/sec: 9.53833 Total loss is :[0.462322384] INFO:tensorflow:global_step/sec: 9.24575 Total loss is :[0.474193633] INFO:tensorflow:global_step/sec: 8.47071 Total loss is :[0.404216766] INFO:tensorflow:global_step/sec: 9.19704 Total loss is :[0.384837329] INFO:tensorflow:global_step/sec: 10.0862 Total loss is :[0.517246902] |
■以前、CPUでやった時のログ
1 2 3 4 5 |
I0309 15:07:43.188007 140693782988608 learning.py:505] global step 26330: loss = 0.2596 (1.058 sec/step) I0309 15:07:54.232152 140693782988608 learning.py:505] global step 26340: loss = 0.5139 (1.278 sec/step) I0309 15:08:05.626399 140693782988608 learning.py:505] global step 26350: loss = 0.2908 (1.016 sec/step) I0309 15:08:16.853006 140693782988608 learning.py:505] global step 26360: loss = 0.2974 (1.156 sec/step) I0309 15:08:29.279487 140693782988608 learning.py:505] global step 26370: loss = 0.4406 (1.121 sec/step) |
セグメンテーション実施
ある程度学習が進んだら、試しにセグメンテーションを実施してみましょう。下記のコマンドで実施できます。
1 2 3 4 5 6 7 8 9 10 11 |
python3 vis.py \ --logtostderr \ --vis_split="val" \ --model_variant="mobilenet_v2" \ --output_stride=8 \ --vis_crop_size=1200 \ --vis_crop_size=3000 \ --dataset="lioncat" \ --checkpoint_dir="./dataset/trainlog/" \ --vis_logdir='./dataset/vislog' \ --dataset_dir=./dataset/tfrecord |
以下、実行結果です。上から3万ステップ、4万ステップ、5万ステップ、7万ステップの結果です。最初のうちはネコとライオンを混同しているようですが、最後にはしっかりとネコを見分けられるようになっていますね。学習の効果が出ています。
まとめ
今回は、Semantic Segmentationを用いてライオンとネコをピクセル単位で検出してみました。ちょっと学習時間や学習データの準備でハードルが高いですが、結構な精度で検出ができます。いろいろと面白い活用ができそうなので、もう少し遊んでみたいと思います。
コメント