mxnet-finetunerの使い方

ディープラーニングによる画像分類モデルをほぼ自動的に作成できるツールを作ったので、簡単に使い方を紹介します。

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からソースコードをクローンしてセットアップします。

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サーバを起動するまで、このツールで一通り実施できます。

以降の手順は、mxnet-finetunerをcloneしたディレクトリで作業することを想定しています。

学習用画像の準備

学習用画像を images/train, images/valid, images/test 配下に以下のような構造で用意します。
下記は、 ariplaneswatch の2種類の画像を用意した例です。

お試し用の画像を使う

画像を用意するのは面倒だが、とりあえず試してみたいという場合には、 util/caltech101_prepare.sh を実行することで、 example_images ディレクトリ配下に、Caltech101データセットから切り出した10クラスの学習用画像を準備できます。

作成されるディレクトリの構成は以下の通りです

  • example_images/train 配下に学習用画像が各クラス60枚、計600枚
  • example_images/valid 配下にバリデーション用画像が各クラス20枚、計200枚
  • example_images/test 配下にテスト用画像が各クラス20枚、計200枚

元々ある images ディレクトリを example_images ディレクトリに置き換えることで学習を始めるための画像の準備が整います。

モデルの学習(ファインチューニング)の実施

モデルの学習は docker-compose で以下のように行います。

コマンドを実行すると、自動的に学習用画像の加工・整形を行い、ImageNetで学習済みのモデルをダウンロードした後、ファインチューニング(転移学習)を行います。
学習時のログやテスト結果等は logs ディレクトリ配下に保存され、学習済みのモデルファイルは学習エポック毎に model ディレクトリに保存されます。

config.yml は、以下のような感じで記載します。

自動生成される config.yml にコメントアウトした状態で各種設定項目を記載しているので、ディープラーニングの経験がある方は、だいたい雰囲気でわかるかと思います。
利用できる学習済みモデルの一覧についてはこちらに、データ拡張の詳細についてはこちらに詳細を記載しています。

学習したモデルをテストする

images/test 配下に保存した画像を使って、モデルの識別精度をテストします。

config.ymltest.model に利用したい学習済みのモデルを指定します。

学習済みモデル model/201705292200-imagenet1k-nin-sgd-0001.params をテストに利用したい場合には、 config.yml に以下のように記載します。

モデル名を指定せず、最後に学習したモデル(この場合にはvalidationの精度が最も高いものが使われます)を利用することもできます。

テストは以下のコマンドで実行します。

テストの結果および識別精度のレポートが logs ディレクトリに保存されます。

学習したモデルのエクスポート

学習済みのモデルをエクスポートできます。
エクスポートしたモデルは、MXNetやONNXの学習済みモデルをAPIサーバとして利用可能な mxnet-model-server で利用できる形式で保存されます。

テストの際と同様な方法で、 config.yml に以下のように学習済みモデルを指定し、

最後に学習したモデルをエクスポートする場合

次のコマンドでモデルをエクスポートできます。

エクスポートされたモデルは拡張子 .model というファイル名で model ディレクトリに保存されます。

エクスポートしたモデルをAPIサーバとして起動

先ほど紹介した mxnet-model-server のビルド済みDockerイメージを利用して、エクスポートしたモデルをAPIサーバとして起動できます.

デフォルトでは、最後にエクスポートしたモデルを自動的に読み込んで起動します。
ローカルホストの8080番ポートに以下のようにアクセスすることでAPIを利用可能です。

以下のような結果が返却されます。

学習済みモデルのエクスポート時に、APIサーバ起動用の設定ファイルを、CPU環境では model/mms_app_cpu.conf に、GPU環境では model/mms_app_gpu.conf に出力します。起動するモデルを変更したい場合には、適宜この設定ファイルを修正してください。

その他

学習結果・識別精度のslackへの自動アップロード

config.yml に以下の設定を追加することで、学習結果および学習済みモデルの評価(混同行列による評価)をslackへ自動アップロードできます。

この機能を利用する場合には、以下のように、slackのAPIトークンを発行し環境変数 SLACK_API_TOKEN を設定しておく必要があります。

学習結果のアップロード

config.ymlfinetune.train_accuracy_graph_slack_upload1 を設定します。
アップロードするチャンネルを指定したい場合は finetune.train_accuracy_graph_slack_channels にチャンネル名を指定します。複数指定すると複数チャンネルに同時に投稿します。

上記の設定で、以下のような画像がslackのgeneralチャンネルにアップロードされます。

学習結果の例

モデルの評価結果のアップロード(混同行列による評価)

config.ymltest.confusion_matrix_slack_upload1 を設定します。
アップロードするチャンネルを指定したい場合は test.confusion_matrix_slack_channels にチャンネル名を指定します。複数指定すると複数チャンネルに同時に投稿します。

上記の設定で、以下のような画像がslackのgeneralチャンネルにアップロードされます

混同行列の例

スクラッチからの学習

開発当初は mxnet-finetuner という名前にもあるとおり、学習済みモデルのファインチューニングを前提にしていましたが、開発途中から各種モデルのスクラッチからの学習にも対応しました。

モデルの指定を下記のように scratch プレフィックス付きのものを指定することで学習済みモデルを利用せず、スクラッチからの学習になります。

スクラッチからの学習に利用できるモデルについてはこちらに記載しています。

その他にも、モデルのレイヤーを一部フリーズしての学習や、複数の学習済みモデルのアンサンブル、各モデルの識別結果のdiversityの確認、等が可能ですが、長くなったので、また別の記事で紹介したいと思います。

各機能の詳細については mxnet-finetuner を御覧ください。

コメント

  1. anon より:

    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/
       …

    (… = 全く同じ画像・枚数)

    という内容でした。
    お手数をお掛けしますが、ご対応頂けましたら幸いです。