“AndroidスマホのTermux上でPythonを動かして、Tensorflow liteのコードを実行するまで”
(はてなブログから引っ越し: '24/8/16)
しょくぶつ(^^)です。
前回までで書いたように、androidスマホでTensorflowを使って機械学習を使う準備はなかなか大変ですね。
私も書いていて、ちょっと飽きてしまいました。
そこで 一番楽しい WindowsでTensorflowを使っていたコードを、androidスマホでTensorflow liteを使って動かすための改変方法を先に書いてしまいます。
TensorflowからTensorflow liteへの変換については、学習モデルの変換の記事はすぐ見つかるのですが、コード自体の変換の記事はなかなかないのではないでしょうか?
Tensorflow lite 、つかってみた、だけじゃないです。バッチリ動いていますよ~っと伝えたくて。
なお、コードを動かしたいだけならば、この回は読み飛ばしてかまいません。
コードの改変について
以前に紹介したWindows上で動いていたtensorflowのコードを、termux上のtensorflow liteで動くように改変します。
Windowsで動いていたコードをTermux環境で動かすに当たり、工夫したのは次の2点です。
-
TersorFlowのコードをlite版に書き換え
何カ所か書き換えます。
なお、学習モデルは予めWindowsパソコンの方で変換します。 -
キャプチャー機器からの画像取り込み python単体ではできないので、「USBカメラ」というアプリと連携します。
次のURLに私が作成したファイルをアップロードしました。
- CNN-predict-public.py
Windows上で動いていたTensorflowのコードです。 - TFlite-predictV0-in-phone.py
termux上のtensorflow liteで動くように改変したコードです。
TersorFlowのコードをlite版に書き換え !!
次のサイト様を参考にしました。
【丁寧解説】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というリストに追記をしています。
TersorFlow学習モデルをlite用へ変換
次のサイト様を参考にしました。
【丁寧解説】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)
キャプチャー機器からの画像取り込み
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 スペシャルは")
次回こそは、上記のように変更したコードで機械学習予測モデルを実行して、「スプラの敵のスペシャルを声でお知らせ」までたどり着きたいと思います。
コメント