第5回: androidスマホだけで機械学習の予測モデルを使おう ! (3) 元のコードをTensorflow lite用に修正
この記事は移転しました。約2秒後に新記事へ移動します。移動しない場合はココをクリックしてください。
しょくぶつ(^^)です。
前回までで書いたように、androidスマホでTensorflowを使って機械学習を使う準備はなかなか大変ですね。
私も書いていて、ちょっと飽きてしまいました。
そこで一番楽しいWindowsでTensorflowを使っていたコードを、androidスマホでTensorflow liteを使って動かすための改変方法を先に書いてしまいます。
Tensorflow lite 、つかってみた、だけじゃないです。バッチリ動いていますよ~っと伝えたくて。
なお、コードを動かしたいだけならば、この回は読み飛ばしてかまいません。
コードの改変を説明します
Windowsで動いていたコードをTermux環境で動かすに当たり、工夫したのは次の2点です。
- TersorFlowのコードをlite版に書き換え
何カ所か書き換えます。
なお、学習データは予めWindowsパソコンの方で変換します。
- キャプチャー機器からの画像取り込み
python単体ではできないので、「USBカメラ」というアプリと連携します。
次のURLに私が作成したファイルをアップロードしました。
TersorFlowのコードをlite版に書き換え !! (1)
※ 次のサイトを参考にしました。 https://tpsxai.com/raspberrypi_tensorflow_lite/
TersorFlowで作成した学習データを、予めWindowsの方で変換します。
私が作成したものは"buki-classification.h5"というファイル名です。
これを次のコードで"buki-classification.tflite"というファイルに変換しました。
# -*- coding: utf-8 -*- """ Created on Tue Nov 31 13:03:26 2023 @author: plant-smile """ import tensorflow as tf from tensorflow.python.keras.models import load_model # モデル読み込み (Tensorflow使用, 畳み込みニューラルネットワーク(CNN)を駆使して訓練したものです) if __name__ == '__main__': model =load_model('buki-classification.h5') #TFliteモデルへの変換 converter = tf.lite.TFLiteConverter.from_keras_model(model) tflite_model = converter.convert() open("buki-classification.tflite", "wb").write(tflite_model)
TersorFlowのコードをlite版に書き換え !! (2)
※ 次のサイトを参考にしました。 https://tpsxai.com/raspberrypi_tensorflow_lite/
いよいよコード自体の書き換えです。
その箇所を挙げていきます。
ライブラリのインポートは次のように変更します。
元 from tensorflow.python.keras.models import load_model
修正 try: # linux import tflite_runtime.interpreter as tflite except ImportError: # win from tensorflow import lite as tflite
お気付きの通り、このように書くことで、このコードをスマホだけでなくWindowsでも動作させることができます。
スマホで動かす前にWindowsで試行する際に便利ですね。
モデルの読み込みは次のように変更します。 これはもう、こういうものだと思ってください。
元 # モデル読み込み (Tensorflow使用, 畳み込みニューラルネットワーク(CNN)を駆使して訓練したものです) #loaded_model =load_model('buki-classification.h5')
修正 # モデルの読み込み interpreter = tflite.Interpreter(model_path="buki-classification.tflite") # モデルのテンソルへの割り当て interpreter.allocate_tensors() # 入力の詳細の取得 input_details = interpreter.get_input_details() # 出力の詳細の取得 output_details = interpreter.get_output_details()
かんじん要の、予測部分です。
元 predicted_labels = loaded_model.predict_on_batch(XPdata)
この1行のコードは、(64,64,3)の画像が4個入った XPdata という(4, 64, 64, 3)のndarrayを入力とします。 そして、予測結果がベクトル4つからなる predicted_labels として出力する、というものです。 これを次のように変更します。
変更 predicted_labels=[] for i in range(4): # バッチ次元の追加と型変換 XPdata_tmp= np.expand_dims(XPdata[i], axis=0).astype("float32") # 入力画像のセット interpreter.set_tensor(input_details[0]['index'], XPdata_tmp) # 処理実行 interpreter.invoke() # 出力の取り出し predicted_labels.append( interpreter.get_tensor(output_details[0]['index']) )
まず、for文で4回ループさせています。 これは、Tensorflow liteでは画像4つをまとめて処理することが(私は)できなかったためです。
次に、「バッチ次元の追加と型変換」を行っています。本コードでは
np.expand_dims(XPdata[i], axis=0).astype("float32")
とすることで、XPdata[i]のaxisを1つ増やして、型をfloat32に変換しています。
ここが大事なので丁寧に説明しますね。
まず、モデルの入力の詳細情報は、すこし前の修正で記載した、
input_details = interpreter.get_input_details()
にて input_details という変数に入っています。 この中身は次の通りです。
[{'name': 'serving_default_conv2d_input:0', 'index': 0, 'shape': array([ 1, 64, 64, 3]), 'shape_signature': array([-1, 64, 64, 3]), 'dtype': numpy.float32, 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}}]
確認するのは3点です。
index "0"です。
shape array([ 1, 64, 64, 3]) です。
dtype 配列の型は float32 です。
その一方、入力データXPdata[i]は(64, 64, 3)と次元が1つ足りません。また、型を調べたところfloat32ではなくfloat64でした。
だから「バッチ次元の追加と型変換」したわけです。
(逆に、lite版ではない tensorflow は、これを自動でやってくれるんですね)
その次は「入力画像のセット」です。これは、さっき書いたようにindexが"0"と分かったので、その通りに
interpreter.set_tensor(input_details[0]['index'], XPdata_tmp)
と記載しました。
最後の「出力の取り出し」は、
predicted_labels.append( interpreter.get_tensor(output_details[0]['index']) )
とすることで、predicted_labelsというリストに追記をしています。
キャプチャー機器からの画像取り込み
python単体ではできないので、「USBカメラ」というアプリと連携します。
キャプチャー機器をUSB端子に接続して「USBカメラ」を起動して、キャプチャー画像は見られるでしょうか?
次は好みですが、私は 全画面表示 にしました。
そうしたら、電源ボタン + 音量ダウンボタン などでスクリーンショットを撮ってください。
そして、スクリーンショット自体の画像サイズと、その画像内のキャプチャー画面のサイズを、パソコンなどで調べて下さい。
そして
# # # # # #cap_dev_no = 1 cap_W = 1920 # キャプチャー画像の横幅 cap_H = 1080 # キャプチャー画像の縦幅 cap_Left = 215 # キャプチャー画像の横の原点。 cap_Right = cap_Left + 1920 cap_Top = 0 #キャプチャー画像の縦の原点 cap_Bottom = 1080 # # # # #
に記載して下さい。
そして、実際に使うときは、「USBカメラ」でキャプチャーしつつ、画面上にブキ一覧が表示されたら、スクリーンショットを撮ってください。
スクリーンショットは /sdcard/Pictures/Screenshots/ に保管されるので、この場所のファイルが増えたことをpython側でglob関数とmax関数を使って検知し、そのスクリーンショットを取り込みます。
フォルダ更新監視には Watchdog を使う方法 (ちょっと難しい) がよく使われていますが、本コードではこれで十分でしょう。
これをpython側で実現するために、次のようにコードを変更しました。
なお、sleep文はシステム側でスクリーンショットが書き込み終わる前にpython側が読み込みを開始してしまうのを防ぐためです。
とりあえず0.9秒としましたが、もう少し短めでもいいかも。
元 img = cap_camera_to_img() ※ 「cap_camera_to_img」はopencvを使ってカメラ画像を読み込む自作関数
変更 list_of_files =glob.glob('/sdcard/Pictures/Screenshots/*') latest_file = max(list_of_files, key=os.path.getctime) old_l_file=""+latest_file while (latest_file == old_l_file): old_l_file=""+latest_file list_of_files =glob.glob('/sdcard/Pictures/Screenshots/*') latest_file = max(list_of_files, key=os.path.getctime) time.sleep(0.9)
その他の変更
発音部分
例えば次のように変更して、termux-tts-speakを使うようにしています。
元 engine.say('スペシャルは')
変更 tmp=subprocess.getoutput("termux-tts-speak スペシャルは")
次回こそは、上記のように変更したコードで機械学習予測モデルを実行して、「スプラの敵のスペシャルを声でお知らせ」までたどり着きたいと思います。