作ったもの
アプリURL:https://judge-cat-or-dog.herokuapp.com/
ソースコード:https://github.com/oddgai/cat_or_dog/
※サーバー代をケチっているのでアプリ起動までに時間がかかる場合があります。その間に画像を選ぶなどしてください。
MobileNetV2という高精度な画像分類モデルを転移学習・ファインチューニングしたものを使っているため、精度は95%くらいだと思われます。
どうやったか(ざっくり)
ソースコードは上記GitHubにあげているのと基本的には下記を参考にしているため、オリジナルな部分のみ書きます。
TensorFlowでのモデル構築
ここは上記のTensorFlowの犬猫判定チュートリアルをGoogle Colaboratory上でそのままやりました。
理論的な説明こそあまりないものの、簡単なモデル
-> その課題を考察
-> それを解決する発展的なモデル
のような自然な流れで書かれていて、本当に取り組みやすかったです。
モデル構築後に、アプリで使う用にモデルをファイルとして保存しています。
from google.colab import drive
drive.mount("/content/drive")
model.save("drive/My Drive/Colab Notebooks/cat_dog_model.h5")
上記のようにファイルとして保存すると、下記のように読み込むだけで別の環境でモデルを復元でき、推論などが実行できます。
model = tf.keras.models.load_model("src/cat_dog_model.h5")
Streamlitでの実装
Streamlitとは、Pythonだけ(HTML不要)で簡単にいい感じのWebアプリを作ることができるフレームワークです。
Pythonの軽量フレームワークとしてはFlaskが有名ですが、特に理由がない限り個人的にはStreamlitでいいんじゃ…?と思うくらい簡単にアプリが作れます。
どのくらい簡単かというと、今回のアプリの表示部分はこれだけで実装しています(関数などは省略)。
import numpy as np
import streamlit as st
from PIL import Image, ImageOps
uploaded_file = st.file_uploader("判定したい画像を選んでね")
if uploaded_file is not None:
try:
# 画像を読み込む
uploaded_img = Image.open(uploaded_file)
uploaded_img = ImageOps.exif_transpose(uploaded_img) # 画像を適切な向きに補正する
# 犬猫判定
pred = sample_predict(uploaded_img)
# 結果表示
st.info(f"これは**{get_result(pred)}**です!")
score = np.int(np.round(pred, 2)*20)
st.text(f"犬 0 |{'-'*score}*{'-'*(19-score)}| 100 猫")
st.image(uploaded_img, use_column_width=True)
except:
st.error("判定できませんでした・・・適切な画像をアップロードしてください!")
HTMLやCSSなしでモダンっぽいUIにしてくれるのでありがたいですね。
今回はユーザーがアップロードした画像を表示していますが、これ以外にも
- ソートなどがインタラクティブにできるデータフレームを表示
- Matplotlib, Plotly, Bokehなどの基本的なグラフ、地図描画
- 動画、音声ファイル再生
などなど、いろんな機能があります。
Streamlitのギャラリーページには豊富なサンプルが載っているので、ぜひ見てみてください。
細かいエラーと対応
最終的には解決できましたが、いくつか詰まったことがあるのでメモ的に残しておきます。
TensorFlowがMac M1にインストールできない
少し前に奮発して買ったMacBook Air (M1, 2020)
を使いましたが、Pipenv
上でpip install tensorflow
しようとすると下記のエラーが出ました。
❯ pip install tensorflow
ERROR: Could not find a version that satisfies the requirement tensorflow
ERROR: No matching distribution found for tensorflow
調べてみると、
- Mac M1のPython環境構築は現状ではMiniforgeがいいらしい
- AppleがMac M1用のtensorflow-macosをリリースしている
とのことなので、下記を参考にすると無事インストールできました。
https://zenn.dev/karaage0703/articles/0ab9e654cfb0ec
Google Colaboratoryで保存したモデルがロードできない
tf.keras.models.load_model()
しようとすると下記のエラーが出ました。
tensorflow load model 'str' object has no attribute 'decode'
TensorFlowのバージョンがColab(2.5)とローカル(2.4)で違うのが原因っぽかったので、Colab上でバージョンを下げてモデルを保存し直しました。
!pip3 uninstall tensorflow
!pip3 install tensorflow==2.4.0
それをロードすると、また別のエラーが出ました。
NotImplementedError: Cannot convert a symbolic Tensor (gru/strided_slice:0) to a numpy array. This error may indicate that you’re trying to pass a Tensor to a NumPy call, which is not supported
調べたところ、TensorFlow 2.x
とnumpy 1.20.x
の間で発生している不具合のようでした。
下記を参考にnumpy==1.19.5
をインストールし直すと、無事ロードできました。
https://chimesness.com/post-246/
アップロードした画像がたまに90度回転して表示されてしまう
スマホで撮影した画像でよく起こっていたもので、それによって判定にも良くない影響が出ていました(実家の猫の画像が確実に猫
-> ほぼ猫
にランクダウンするなど)。
PILまわりを調べてみると、JPEGにExifという表示方法を制御するタグがあって、それを適切に読み取れてないために起こっているようでした。
下記を参考に、読み込み時に1行追加したら解決しました
https://github.com/python-pillow/Pillow/issues/4703
# 画像を読み込む
uploaded_img = Image.open(uploaded_file)
uploaded_img = ImageOps.exif_transpose(uploaded_img) # 画像を適切な向きに補正する
Herokuにデプロイしようとするとslug size
が500MBを超える
Herokuでは必要なパッケージをslug
という形で保存するらしいのですが、それが500MB以下でないとデプロイできない仕様になっています。
どうやらTensorFlow 2.x
ではGPUをサポートするために容量が大きくなっていて、ほぼ確実に500MBをオーバーするようです。
とはいえ今さら1.x
系のモデルを作り直したくないので、下記を参考にrequirements.txt
を書き直すと300MBくらいに減ってデプロイできました。
https://qiita.com/pi0329/items/22e38713df1e008c6773
tensorflow==2.4.0
↓
tensorflow-cpu==2.4.0
感想
TensorFlowやStreamlitを真面目に触ったのは初めてでしたが、公式サイトが充実していたので非常にやりやすかったです(着手〜リリースまで実質半日くらい)。
また技術ブログ系は今までROM専だったのですが、いろんな方の記事に支えられていることを改めて実感したので自分も書いてみました。
今回はチュートリアルのつぎはぎ的なアプリでしたが、次はアイデアから自分で考えたものを作りたいです。