tensorflow2.0に付属の「tf.keras.applications.VGG16」モデルを使って、1000種類の画像を識別できるPythonクラスを簡単に作ってみます。
※2019/01/21の記事をTensorflow2.0対応にリライトしました。
はじめに
今回利用するモデルはVGG16です。
精度を追及するなら、もっと新しくて良いモデル(ResNetとかInceptionV3とか)も提供されていますが、CPUオンリーの非力なノートPCで実行するので、軽めのモデルを選んでます。
使用例のサンプルは、何故かTensorflowのサイトにはなくて、こちらのKerasのドキュメントにありました。
VGG16のところを見ると、簡単な画像評価のサンプルが載ってます。
まずは、これを参考にします。
featuresは評価結果ではなく特徴マップであること
でも。
サンプルソース通りにすると、うまくいきません(笑)。
そもそも。
model.predict(x) の左辺「features」は、(1,7,7,512)の特徴マップ(Feature map)であって、評価結果ではありません。
特徴マップって何?と思ったら、こちらを参照ください。
なので。
まず「model.predict(x)」で評価結果「shape (samples, 1000)).」を返してくれるようにオプションを変更する必要があります。
どこを修正するかというと、
model = VGG16(weights='imagenet', include_top=False)
の部分を
model = VGG16(weights='imagenet', include_top=True)
にします。
「include_top=False」だと、predict()は、特徴マップ(Feature Map)を返す。
「include_top=True」だと、評価結果「shape (samples, 1000)).」を返す。
そういう仕様です。
結果を整形してくれるメソッドを使う
あと。
評価結果をそのまま使っても良いですけど、結果を扱いやすくしてくれる「tf.keras.applications.vgg16.decode_predictions()」というメソッドが用意されていますので、こちらを使った方が良いです。
これをつかえば・・例えば。
上記のfeaturesをうけて。
results = decode_predictions(features, top=5)[0]
for result in results:
print(result)
みたいにすると、確率の高い順のベスト5がこんな感じで表示されます。
('n02504458', 'African_elephant', 0.77940494)
('n01871265', 'tusker', 0.19876274)
('n02504013', 'Indian_elephant', 0.021399843)
('n02437312', 'Arabian_camel', 0.00020747511)
('n02113799', 'standard_poodle', 5.550688e-05)
つまり、識別結果をひとつだけ表示させる場合は、「top=1」とすれば良くて、この結果の「results[0][1]」でクラス名がとれるわけです。
ちなみに、前に書いたオプションを「False」のままにしていると、当たり前ですが、以下のようなエラーがでて動きません。
ValueError: decode_predictions expects a batch of predictions (i.e. a 2D array of shape (samples, 1000)). Found array with shape: (1, 7, 7, 512)
クラス化してみます
サンプル通りでは芸がないので、簡単なクラスにしてみました。
tensorflow2.0で動作確認しました。
import tensorflow as tf import numpy as np class Vgg16k: def __init__(self): self.model = tf.keras.applications.VGG16( weights='imagenet', include_top=True) self.IMAGE_SHAPE = (224, 224) def __predict(self, img): x = tf.keras.preprocessing.image.img_to_array(img) x = np.expand_dims(x, axis=0) x = tf.keras.applications.vgg16.preprocess_input(x) features = self.model.predict(x) results = tf.keras.applications.vgg16.decode_predictions(features, top=1)[0] ans = {} ans['pp'] = '{:.2f}'.format(results[0][2] * 100.0) + '%' ans['en'] = results[0][1] return ans def predict_from_path(self, imgpath): img = tf.keras.preprocessing.image.load_img( imgpath, target_size=self.IMAGE_SHAPE) return self.__predict(img) if __name__ == '__main__': m = Vgg16k() a = m.predict_from_path('.\\tf20\\test.jpg') print(a['en'] + 'です。確率は' + a['pp'])
試しに評価してみる画像は、楽器「ファゴット」にしてみました。
ファゴットは英語で「bassoon」です。
画像はこれです。
実行結果はこちら。
ちゃんと、bassoonになってます。
最初の1回だけ、学習済の重みをダウンロードするのに時間がかかります。
自分の環境だと10分くらいでした。
でも、2回目からはローカルに保存されるので速いです。
これはいいですね。
デモにはもってこいです(笑)
ではでは。