"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

tf.kerasの事前学習済VGG16モデルを使った画像の分類のやり方と、躓いたところ

 kerasには、ImageNetで事前学習済の重みを利用可能なモデルがあります。

f:id:arakan_no_boku:20190119213643j:plain

これを使うと簡単に画像を1000クラスに分類できます。

画像分類のデモとかをやるだけなら、自分でガシガシ学習しなくても、これを利用させてもらうのが時間の節約になりそうです。

でも。

自分でやってみて、ちょっとだけハマったので、覚えているうちにメモっておきます。

なお、この記事は以下とセットです。

あわせてどうぞ。

arakan-pgm-ai.hatenablog.com

 

kerasの日本語ドキュメントのソースを参考にする

 

こちらに日本語のドキュメントがあるので、参考にさせてもらいました。

keras.io

VGG16のところを見ると、簡単な画像評価のサンプルが載ってます。

f:id:arakan_no_boku:20190116230614j:plain

とてもシンプルです。

カレントフォルダに画像ファイルを用意すれば、ほぼ、コピペで動きます。

 

サンプルソース通りにやるだけではうまくいかない

 

でも。

ここで、model.predict(x) の左辺「features」に評価結果がnumpy配列で入っているのかなと思って「argmax(features)」なんてやると、例えば「8543」みたいなわけのわからない結果が返ってきます。(1000クラスの8543?・・)

しかも、数字が返ってきても、その数字が何を指すのか?わかりません。

まあ、探せばこんな感じでインデックスとテキストの情報はあるので、これを元に辞書でも作ってしまえば済む話なのですけど。

text: imagenet 1000 class id to human readable labels (Fox, E., & Guestrin, C. (n.d.). Coursera Machine Learning Specialization.) · GitHub

もう少しスマートな方法があるはずだよなあ・・。

そう思って、探すとありました。

 

VGG16の結果を整形してくれるメソッドdecode_predictions

 

kerasの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)

名前もちゃんとでているし、これは良いですね。

で。

早速、上記サンプルソースにその部分を付け加えて実行します。

もちろん、下記のimportも忘れずに。

from tensorflow.keras.applications.vgg16 import decode_predictions

 

Shapeが違うというエラーでしばらくハマる

 

すると。

難儀なことに、以下のようなエラーがでて動きません。

 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)

ここでしばらくハマりました。

結局、サンプルコードの変数名(features)の意味をちゃんと理解せずに、思い込みしてたのが問題でした。

features = model.predict(x)  のfeaturesは(1,7,7,512)の特徴マップ(Feature map)であって、評価結果ではありませんでした。

だから・・featuresなんですねえ・・・。

特徴マップって何?と思ったら、こちらを参照ください。

deepage.net

だから。

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)).」を返す。

そういう仕様だということです。

 

問題を修正したソースでやるとうまくいきました

 

修正したソースはこちらです。

from tensorflow.keras.applications.vgg16 import VGG16,decode_predictions
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.vgg16 import preprocess_input
import numpy as np

model = VGG16(weights='imagenet', include_top=True)

img_path = 'bassoon.jpg'
img = image.load_img(img_path, target_size=(224, 224))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

pred = model.predict(x)
results = decode_predictions(pred, top=3)[0]
for result in results:
    print(result)

試しに評価してみる画像は、ちょっと変わったものをと考えて楽器「ファゴット」にしてみました。 

ファゴットは英語で「bassoon」です。

なので、「bassoon.jpg」で画像検索して、こちらのリンクの画像を保存してやってみました。

www.thomann.de

結果はこうです。

('n02804610', 'bassoon', 0.5258739)
('n03838899', 'oboe', 0.18741602)
('n03372029', 'flute', 0.09341654) 

bassoon(ファゴット)!ばっちり、正解です。

すごいですね。

最初の1回だけ、学習済の重みをダウンロードするのに時間がかかります。

自分の環境だと10分くらいでした。

でも、2回目からはローカルに保存されるので速いです。

これはいいですね。

デモにはもってこいです(笑)