Tag Archives: tensorflow

WEBアプリ構築(keras+uwsgi+bottle)編+まとめ&AI構築の悩み(3/3)

前回、前々回から引き続き、植物の写真から病気を判別するサイト(http://www.plant-check.jp/)を作ったときのまとめ。今回はWEBサイトを構築したときのまとめ。

WEBサイトの概要

リンクにあるとおり、WEBサイトはシンプルな作りとなっている。大きなユースケースは下記の2つ。そしてやることはほとんど同じ。

  1. ユーザがサイトの「写真を撮影」ボタンを押すと、自分のスマホから写真を撮ることが出来る。次に「病気をチェック」ボタンを押すと、取った写真の植物が病気か判定される。
  2. ユーザがサイトの「写真を選択」ボタンを押すと、自分のPC/スマホから写真を選択することが出来る。次に「病気をチェック」ボタンを押すと、選んだ写真の植物が病気か判定される。

HTML部分

スマホ経由で写真を取らせるにはcapture=”camera”を使えばOK。

 <input type="file" accept="image/*" capture="camera" id="camera" style="display:none;" name="upload" />

それ以外の部分として、labelタグを使ってボタンを隠したり、若干JavaScriptでボタンの活性・非活性を切り替えたりしているが特殊なことはやっていない。

サーバ部分

サーバ部分の構成は次の通りで、こちらも特殊な構成ではない。

  • sakuraのVPS(ubuntu 16.04 LTS)
  • Docker
  • nginx
  • uwsgi
  • Pythonのアプリ
    • bottleを利用してモデル部分(keras + tensorflow)とWEBアプリ部分をつなげた。

uwsgiとnginxのつなぎはSocketを使用した。(nginx側 uwsgi_pass unix:/path_to/uwsgi.sock、uwsgi側 socket = /path_to/uwsgi.sock )良くわからない場合は、公式サイトのサンプルを見ながら書くのが一番だった記憶がある。

Pythonのアプリ部分はbottleを用いて書いた。毎回毎回モデルをロードするアプローチはさすがに富豪過ぎるので、起動時に判別モデル(植物か否か、病気か否か)をロードしてそれを使いまわすようにしている。

一点はまった点としてuwsgi.confでthreads = 1、enable-threads = falseとしないと動作しなかった。当時forumを確認した感じではkerasのpredict処理がマルチスレッドに対応していないのが原因だったと記憶している(が、モデル読み込み時にフリーズしたような記憶もある)。それと、bottleの制約のようだが、巨大ファイルをアップロードされた場合の対策(chunkごとに読みこんで、一定以上のサイズの場合は処理を停止)を自分で書かないといけないのが面倒だった。

まとめ

モデル構築部分のコード行数は約100行(×2)、サーバ部分のコード行数は約200行だった。「AI(人工知能)が病気を診断!」というサイトがこの程度の行数で書けてしまうのは、便利なライブラリが整備されたおかげで、作者に感謝である。もっとも、モデル構築部分はノウハウ含めて自動化されていくはずで、大手各社もAutoML、Azure Machine Learning、・・・と言った名前でそのような環境をリリースしている。

AIやら人工知能やら何やらの主戦場は、結局何するの?という方向か、ほんまに使えるの?という方向になる気がしていて、クロスバリデーションベースのモデル精度を競っても無意味な時代になりつつあるんだろうなーと思う。(ということで私は自動化大歓迎、その他の思いはその他へ)

今後、情熱が復活すれば他の病気への対応を行う予定。もう少し実験したいことがあるので、コードを公開するのはその後になる見込み。

Read more »

モデル構築(keras/tensorflow+InceptionV3+Data augmentation)編(2/3)

前回から引き続き、植物の写真から病気を判別するサイト(http://www.plant-check.jp/)を作ったときのまとめ。

モデル構築の流れ

前回書いたとおり、今回、(1)植物の葉かそれ以外かを判別する2値分類モデル+(2)植物の葉が病気か否かを判別する多値分類モデルを作成した。構築方法は(1)、(2)ともに同様でkeras, tensorflowを用いて転移学習を行った。モデル構築の流れは下記の通り。バリバリのDeep Learningなので、人工知能(AI)を利用し、植物の病気判定を行った!と言っていいはず(大事なことなので2回目)。

  1. 写真をラベルごと((1)は「植物の葉/それ以外」、(2)は「健康な葉/黒星病/うどん粉病/その他カビ系の病気」)ごとに別のディレクトリに格納する。
  2. kerasを使ってImageNetを学習したInception V3をロードする。
  3. ロードしたモデルの一部(今回は250層以降)を学習可能に設定する。
  4. 問題(2値 or 多値)に応じて、Inecption V3の後段のネットワークを設定する。
  5. 写真データを学習用と検証に分ける。
  6. 写真データをdata augmentationするよう設定する。
  7. 学習、検証する。

モデル構築のコード

上記1.~7.のフローはkeras (+ tensorflow)を用いると簡単に実装できる。2値分類モデルにおける、2.~4.は下記のように実装可能。多値の場合は最終層を「predictions = Dense(クラス数, activation=”softmax”)(x)」てな感じに変えて「loss = “categorical_crossentropy”」とすればよい。ざっくりいうとニューラルネットワークの前段として学習済みのネットワークを用い特徴量抽出等を再利用(=転移学習)し、本件で必要な分類を行うネットワークを追加している。

base_model = InceptionV3(weights='imagenet', include_top=False)
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation="relu")(x)
x = Dropout(0.5)(x)
x = Dense(256, activation="relu")(x)
predictions = Dense(1, activation="sigmoid")(x)

model = Model(inputs=base_model.input, outputs=predictions)

for layer in model.layers[:249]:
   layer.trainable = False
for layer in model.layers[249:]:
   layer.trainable = True

model.compile(loss = "binary_crossentropy", optimizer = optimizers.SGD(lr=0.0001, momentum=0.9), metrics=["accuracy"])

Data augmentationは画像を変形(回転、移動、縮小、拡大などなど)させながらデータを増やして学習する方法である。画像を変形させることで、1つの画像から複数のパターンを生み出す。日本語だとデータ拡張とか呼ばれている(はず)。写真の撮られ方に依存する差異等が吸収できるので、未知データに対する性能の向上に効果がある(と私は思っている)。モデル適用時にも変形させながら何パターンか適用し、その平均を取ると性能向上効果がある(こともある)が、こっちをデータ拡張と言うかはよくわからない。

kerasだと「ImageDataGenerator」を使って簡単に書ける。多値の場合は「class_mode = “categorical”」に変更すればよい。学習データと検証データのsplitを事前に行っていれば、同じようにtest_generatorをかける。

train_datagen = ImageDataGenerator(
    rescale = 1./255,
    horizontal_flip = True,
    fill_mode = "nearest",
    zoom_range = 0.3,
    width_shift_range = 0.3,
    height_shift_range=0.3,
    rotation_range=90)
train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size = (img_height, img_width),
    batch_size = batch_size,
    class_mode = "binary")

学習は下記のように行えばよく、チェックポイントごとにモデルが保存され、精度に応じて自動でストップされる。nvidia-dockerのコンテナでGPU版のtensorflowを入れていれば、GPUを用いた学習が行われる。とても便利。

checkpoint = ModelCheckpoint("/保存用ディレクトリ/モデル名_{epoch:02d}.h5", monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='auto', period=1)
early = EarlyStopping(monitor='val_acc', min_delta=0, patience=10, verbose=1, mode='auto')

model.fit_generator(
    train_generator,
    samples_per_epoch = nb_train_samples,
    epochs = epochs,
    validation_data = validation_generator,
    nb_val_samples = nb_validation_samples,
    callbacks = [checkpoint, early])

今まで紹介したコードは、だいたいkerasのsampleコードをベースにしている。自分でやってみたい方はkerasのチュートリアル(https://keras.io/ja/内のリンクから飛べる)を読むのがお勧めである。

モデル構築のポイント

コード自体は簡単に書け、実行してみると検証データでの精度も良いと見える結果が得られる。が、実際に重要なのは「未知データに対する予測性能」で、思ったとおりの結果にならないことがある。これは学習・検証データの関連が強すぎるから(=独立じゃないから)で、学習結果を評価する上で重要なポイントとなる。この手の問題への対応はとっても難しい。私は人工知能やらAIやらと呼ばれるモノの中には、正しく評価されていない、過大評価なヤツも多いと思っている。

Deep Learningを使った場合(特に本件のような非常に複雑なネットワークを組んでいる場合)、「黒星病の特徴である黒の斑点を見て、黒星病と判断している」など「AIの判定に納得感があるか」はわかりにくい。検証すると、「AIはバラが植わっている鉢の色に注目して病気だと判別する」場合もある(詳しくは後述*1)。

このような動きはモデルの説明可能性の文脈でよく話題になっていて、たとえばKDD 2016の「“Why Should I Trust You?”Explaining the Predictions of Any Classifier」に詳しい。論文中の対応案は、まずまず良く動くが、速度面など使い勝手はイマイチという印象を受けた。説明可能性は重要な分野だが、現時点で決定打となる対応策は存在しない。

本件では写真を撮ったのが自分自身ということもあり、背景や鉢が注目点とならないよう気を使っている。具体的には病気の葉と健康な葉それぞれを同じ木・背景で撮影する、様々なパターンを混ぜるなど、変な場所に注目されないようにデータを作成している。

モデルの学習時には完全に未知のデータで検証がされるようにし、また、手動でAIが間違いやすいパターンの解析・チェックを行っている。割と丁寧に判別モデルを作っているが、それでも、「未知データに対する判別能力」は「検証結果として数学的に求められた能力」ほど高くはないんだろうなーと思っている。

Read more »