ディープラーニングによる画像分類モデルをほぼ自動的に作成できるツールを作ったので、簡単に使い方を紹介します。
docker環境および、ある程度の枚数の識別したい画像を準備すれば、本記事の手順に添って、簡単なyamlの設定ファイルを記述するだけで、モデルの学習から画像識別APIサーバを起動するところまで、一通り実施できます。
本ツールはコンテナ内部でGPU利用を含んだほとんどの処理を自動実行するため、機械学習フレームワークを利用する際にハマりやすい、GPUドライバやCUDAやcuDNN等のインストールのトラブルやドライバの組み合わせによる相性問題に悩まされることがありません。GPU無しでもCPUで学習できますが、画像枚数が多くなると学習時間が長いためGPU環境をオススメします。また、名前にあるとおり、ツールの内部では Apache MXNet を利用しています。
ソースコードは以下で公開しています。
https://github.com/knjcode/mxnet-finetuner
2017年8月に公開したラーメン二郎全店舗識別bot(@jirou_deep)の開発にもこのツールを活用しています。
目次
特徴
ざっくり、特徴をまとめると以下のとおりです
- docker-composeでお手軽起動(GPU利用も可)
- 学習データの加工・整形の自動実行
- 学習済みモデルの自動ダウンロード
- 学習データのクラス数に応じた学習済みモデルの自動整形
- 各種データ拡張機能(反転、回転、輝度変更、等々)
- 学習結果のグラフ化、混同行列や分類精度のレポート機能
- Slackへの学習結果のアップロード
- 学習済みモデルのエクスポート
- 学習済みモデルをAPIサーバとして起動
必要環境
- docker
- docker-compose
- jq
- wget もしくは curl
NVIDIAのGPUを利用する場合には nvidia-docker
または nvidia-docker2
が必要です
セットアップ
GitHubからソースコードをクローンしてセットアップします。
1 2 3 4 |
$ git clone https://github.com/knjcode/mxnet-finetuner $ cd mxnet-finetuner $ bash setup.sh |
setup.sh
を実行することで、カレントディレクトリにホスト環境に応じた docker-compose.yml
および、学習やテスト等の設定値が記載された config.yml
が作成されます。
ホストに nvidia-docker
または nvidia-docker2
がインストールされている場合にはGPUが利用可能であると判断し、 docker-compose.yml
にホスト側GPUをコンテナ内部から利用できる設定を追加します。
ホストのGPUドライバーをアップデートした場合やホストのGPU数が増減した場合には、 setup.sh
を再度実行し、現在の環境に対応した docker-compose.yml
を再度作成してください。(nvidia-docker2を利用している場合にはこの作業は不要です)
簡単な使い方
詳細はこの後の各セクションで説明しますが、学習対象の画像を用意するだけで、以下のコマンド例にあるように、モデルの学習から画像識別APIサーバを起動するまで、このツールで一通り実施できます。
1 2 3 4 5 6 7 8 9 |
# 画像識別モデルの学習 $ docker-compose run finetuner # 学習したモデルのテスト $ docker-compose run finetuner test # 学習したモデルのエクスポート $ docker-compose run finetuner export # 学習したモデルをAPIサーバとして起動 $ dcoker-compose up -d mms |
以降の手順は、mxnet-finetunerをcloneしたディレクトリで作業することを想定しています。
学習用画像の準備
学習用画像を images/train
, images/valid
, images/test
配下に以下のような構造で用意します。
下記は、 ariplanes
と watch
の2種類の画像を用意した例です。
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 |
images/ train/ airplanes/ airplane001.jpg airplane002.jpg ... watch/ watch001.jpg watch002.jpg ... valid/ airplanes/ airplane101.jpg airplane102.jpg ... watch/ watch101.jpg watch102.jpg ... test/ airplanes/ airplane201.jpg airplane202.jpg ... watch/ watch201.jpg watch202.jpg ... |
お試し用の画像を使う
画像を用意するのは面倒だが、とりあえず試してみたいという場合には、 util/caltech101_prepare.sh
を実行することで、 example_images
ディレクトリ配下に、Caltech101データセットから切り出した10クラスの学習用画像を準備できます。
1 2 |
$ util/caltech101_prepare.sh |
作成されるディレクトリの構成は以下の通りです
example_images/train
配下に学習用画像が各クラス60枚、計600枚example_images/valid
配下にバリデーション用画像が各クラス20枚、計200枚example_images/test
配下にテスト用画像が各クラス20枚、計200枚
元々ある images
ディレクトリを example_images
ディレクトリに置き換えることで学習を始めるための画像の準備が整います。
1 2 3 |
$ rm -rf images $ mv example_images images |
モデルの学習(ファインチューニング)の実施
モデルの学習は docker-compose
で以下のように行います。
1 2 |
$ docker-compose run finetuner |
コマンドを実行すると、自動的に学習用画像の加工・整形を行い、ImageNetで学習済みのモデルをダウンロードした後、ファインチューニング(転移学習)を行います。
学習時のログやテスト結果等は logs
ディレクトリ配下に保存され、学習済みのモデルファイルは学習エポック毎に model
ディレクトリに保存されます。
config.yml
は、以下のような感じで記載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
common: num_threads: 8 # CPUを8コア使用 gpus: 0,1 # IDが0と1の2つのGPUを使用 finetune: models: - imagenet1k-resnet-50 # 学習済みのResNet-50を利用 optimizers: - sgd # optimizerにSGDを利用 num_epochs: 10 # 10エポック学習 lr: 0.0001 # 学習率は 0.0001 mom: 0.9 # モメンタムは 0.9 wd: 0.00001 # Weight Decayは 0.00001 batch_size: 32 # バッチサイズは 32 data_aug_level: 3 # データ拡張(強め)実施 train_accuracy_graph_output: 1 # 学習結果のグラフを出力 |
自動生成される config.yml
にコメントアウトした状態で各種設定項目を記載しているので、ディープラーニングの経験がある方は、だいたい雰囲気でわかるかと思います。
利用できる学習済みモデルの一覧についてはこちらに、データ拡張の詳細についてはこちらに詳細を記載しています。
学習したモデルをテストする
images/test
配下に保存した画像を使って、モデルの識別精度をテストします。
config.yml
の test.model
に利用したい学習済みのモデルを指定します。
学習済みモデル model/201705292200-imagenet1k-nin-sgd-0001.params
をテストに利用したい場合には、 config.yml
に以下のように記載します。
1 2 3 |
test: model: 201705292200-imagenet1k-nin-sgd-0001 |
モデル名を指定せず、最後に学習したモデル(この場合にはvalidationの精度が最も高いものが使われます)を利用することもできます。
1 2 3 |
test: use_latest: 1 |
テストは以下のコマンドで実行します。
1 2 |
$ docker-compose run finetuner test |
テストの結果および識別精度のレポートが logs
ディレクトリに保存されます。
学習したモデルのエクスポート
学習済みのモデルをエクスポートできます。
エクスポートしたモデルは、MXNetやONNXの学習済みモデルをAPIサーバとして利用可能な mxnet-model-server で利用できる形式で保存されます。
テストの際と同様な方法で、 config.yml
に以下のように学習済みモデルを指定し、
1 2 3 |
export: model: 201705292200-imagenet1k-nin-sgd-0001 |
最後に学習したモデルをエクスポートする場合
1 2 3 |
export: use_latest: 1 |
次のコマンドでモデルをエクスポートできます。
1 2 |
$ docker-compose run finetuner export |
エクスポートされたモデルは拡張子 .model
というファイル名で model
ディレクトリに保存されます。
エクスポートしたモデルをAPIサーバとして起動
先ほど紹介した mxnet-model-server のビルド済みDockerイメージを利用して、エクスポートしたモデルをAPIサーバとして起動できます.
1 2 |
$ docker-compose up -d mms |
デフォルトでは、最後にエクスポートしたモデルを自動的に読み込んで起動します。
ローカルホストの8080番ポートに以下のようにアクセスすることでAPIを利用可能です。
1 2 |
以下のような結果が返却されます。
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 |
{ "prediction": [ [ { "class": "airplane", "probability": 0.9950716495513916 }, { "class": "watch", "probability": 0.004928381647914648 } ] ] } { "prediction": [ [ { "class": "airplane", "probability": 0.9950716495513916 }, { "class": "watch", "probability": 0.004928381647914648 } ] ] } |
学習済みモデルのエクスポート時に、APIサーバ起動用の設定ファイルを、CPU環境では model/mms_app_cpu.conf
に、GPU環境では model/mms_app_gpu.conf
に出力します。起動するモデルを変更したい場合には、適宜この設定ファイルを修正してください。
その他
学習結果・識別精度のslackへの自動アップロード
config.yml
に以下の設定を追加することで、学習結果および学習済みモデルの評価(混同行列による評価)をslackへ自動アップロードできます。
この機能を利用する場合には、以下のように、slackのAPIトークンを発行し環境変数 SLACK_API_TOKEN
を設定しておく必要があります。
1 2 |
$ export SLACK_API_TOKEN="xoxp-123456789-123456789-123456789-123456789" |
学習結果のアップロード
config.yml
に finetune.train_accuracy_graph_slack_upload
に 1
を設定します。
アップロードするチャンネルを指定したい場合は finetune.train_accuracy_graph_slack_channels
にチャンネル名を指定します。複数指定すると複数チャンネルに同時に投稿します。
1 2 3 4 5 |
finetune: train_accuracy_graph_slack_upload: 1 train_accuracy_graph_slack_channels: - general |
上記の設定で、以下のような画像がslackのgeneralチャンネルにアップロードされます。
モデルの評価結果のアップロード(混同行列による評価)
config.yml
に test.confusion_matrix_slack_upload
に 1
を設定します。
アップロードするチャンネルを指定したい場合は test.confusion_matrix_slack_channels
にチャンネル名を指定します。複数指定すると複数チャンネルに同時に投稿します。
1 2 3 4 5 |
test: confusion_matrix_slack_upload: 1 confusion_matrix_slack_channels: - general |
上記の設定で、以下のような画像がslackのgeneralチャンネルにアップロードされます
スクラッチからの学習
開発当初は mxnet-finetuner
という名前にもあるとおり、学習済みモデルのファインチューニングを前提にしていましたが、開発途中から各種モデルのスクラッチからの学習にも対応しました。
モデルの指定を下記のように scratch
プレフィックス付きのものを指定することで学習済みモデルを利用せず、スクラッチからの学習になります。
1 2 3 4 |
finetune: models: - scratch-inception-v3 |
スクラッチからの学習に利用できるモデルについてはこちらに記載しています。
その他にも、モデルのレイヤーを一部フリーズしての学習や、複数の学習済みモデルのアンサンブル、各モデルの識別結果のdiversityの確認、等が可能ですが、長くなったので、また別の記事で紹介したいと思います。
各機能の詳細については mxnet-finetuner を御覧ください。
コメント
docker-compose run finetuner を実行すると logフォルダに xxx-train_accuracy.png は作成されるのですが
その次の xxx-valid-confusion_matrix.png の生成でエラーが発生してしまいます。
Ubuntu 18.04.1 (CPU利用)
docker 18.09.0-beta3
docker-compose version 1.21.1 です。
INFO:root:Epoch[9] Batch [290] Speed: 8.82 samples/sec accuracy=1.000000
INFO:root:Epoch[9] Batch [300] Speed: 8.65 samples/sec accuracy=0.980000
INFO:root:Epoch[9] Batch [310] Speed: 8.63 samples/sec accuracy=0.990000
INFO:root:Epoch[9] Batch [320] Speed: 8.91 samples/sec accuracy=0.990000
INFO:root:Epoch[9] Train-accuracy=1.000000
INFO:root:Epoch[9] Time cost=374.208
INFO:root:Saved checkpoint to “model/20181008223329-imagenet1k-nin-sgd-0010.params”
INFO:root:Epoch[9] Validation-accuracy=0.992331
Train all the layers
Saved train accuracy graph to “logs/20181008223329-imagenet1k-nin-sgd-train_accuracy.png”
Start auto test using fine-tuned model with validation data
model_prefix: 20181008223329-imagenet1k-nin-sgd
model_epoch: 10
[23:58:48] src/io/iter_image_recordio_2.cc:170: ImageRecordIOParser2: /data/valid/images-valid-224.rec, use 1 threads for decoding..
Saved predict results to “logs/20181008223329-imagenet1k-nin-sgd-0010-valid-results.txt”
Saved config file to “logs/20181008223329-imagenet1k-nin-sgd-0010-valid-config.yml”
Traceback (most recent call last):
File “util/confusion_matrix.py”, line 79, in
y_true = [labels[int(i[1])] for i in results]
File “util/confusion_matrix.py”, line 79, in
y_true = [labels[int(i[1])] for i in results]
ValueError: invalid literal for int() with base 10: ‘-‘
Traceback (most recent call last):
File “util/classification_report.py”, line 47, in
y_true = [labels[int(i[1])] for i in results]
File “util/classification_report.py”, line 47, in
y_true = [labels[int(i[1])] for i in results]
ValueError: invalid literal for int() with base 10: ‘-‘
Error: classification report does not exist.
images/test, train, valid には2カテゴリ分、それぞれ1600枚と1800枚の画像が入っています。
ちなみに、お試し画像(util/caltech101_prepare.sh) では問題なく完走しました。
config.ymlのパラメータは変更していません。
今回 test,train,valid には2カテゴリ分のフォルダを全て同じようにコピーしましたが
厳密に画像が被らないよう仕分けする必要があるのでしょうか。
例:train 600枚, valid 1200枚(trainに入れた画像はvalidに入れないようにする)など。
<今回の場合>
images/
test/
…
train/
…
valid/
…
(… = 全く同じ画像・枚数)
という内容でした。
お手数をお掛けしますが、ご対応頂けましたら幸いです。