サイトアイコン RのWeb制作

[Python] ディープラーニングのモデル「VGG16」を使って画像認識をし、判断した理由の可視化をする。

今日はデータ分析から趣向を変えて画像認識を行います。

やることは簡単。

1.撮った写真を使って画像認識させ、何が写っているか判断させる。
2.何が写っているかを判断した理由(位置)を可視化する。

以上です。

今回使う画像はこちら↓

うちの犬です。
トイプードルですが、トイプードルと判定されない子です。

KerasとOpenCVを使っています。
OpenCVのインストールはターミナルから以下のコマンドを~。

pip install opencv-python

では始めます。
まず手始めに、VGG16という学習済みのディープラーニングのモデルをインポートします。
学習が不要なのでノートPCでも簡単に予測ができます。

最新ではないですが、かなり簡単な構造をしているので理解しやすいモデルです。
構造はmodel.summary()で出力できます。

コーディングに使うのでおすすめはJupyter NotebookかGoogle colab.です。
ミスってもやり直しが効くのでとてもやりやすいです。

# VGG1616を読み込み
from keras.applications.vgg16 import VGG16
model = VGG16(weights = 'imagenet')

# 必要なモノをインポート
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input, decode_predictions
import numpy as np

次に画像を読み込みます。
224×224にサイズを変更するのは、VGG16の受け入れ可能な画像サイズが224×224だからです。
縦長や横長の画像はちょっと伸び縮みさせて無理やり突っ込まれます

# 画像パス(必要に応じて変更してください)
img_path = '/Users/saito/Desktop/dog190720_1.jpeg'
# 画像を読み込む
img = image.load_img(img_path, target_size=(224, 224))
# 画像の確認
img

あとはVGG16に合わせて前処理を行います。
写真はRGBで読み込まれるので(224, 224, 3)の形式になりますが、
VGG16は(写真No., X, Y, 次元数)の形式で読み込まれるのでそれに合わせます。

# np配列に変換
x = image.img_to_array(img)
# VGG16に合わせるため次元追加
x = np.expand_dims(x, axis=0)
# バッチの前処理
x = preprocess_input(x)

予測させます。

preds = model.predict(x)
print('Predicted:', decode_predictions(preds, top=3)[0])
# miniature_poodleの番号を取得
np.argmax(preds[0])
preds[0][266]

↑の写真を使っていると、miniature_poodleの予測値が最大になります。
miniature_poodleの番号は266なので、それを下記で使います。

写真にどこで判定をしたのか書き込みます。

from keras import backend as K

# 最後の畳み込み層の出力マップを出力
last_conv_layer = model.get_layer('block5_conv3')

# その層でプードルのクラスの勾配を確認
poodle_output = model.output[:, 266]
grads = K.gradients(poodle_output, last_conv_layer.output)[0]

# 平均勾配
pooled_grads = K.mean(grads, axis=(0, 1, 2))

# 出力関数を規定
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])

# 2つの値をnumpy配列として取得
pooled_grads_value, conv_layer_output_value = iterate([x])

# 重要度を記入する
for i in range(512):
    conv_layer_output_value[:, :, i] *= pooled_grads_value[i]

# ヒートマップ化する
heatmap = np.mean(conv_layer_output_value, axis=-1)

# ヒートマップの後処理(正規化0~1)
heatmap = np.maximum(heatmap, 0)
heatmap /= np.max(heatmap)
plt.matshow(heatmap)

OpenCVの登場です。
今回は写真の上から重要だった部分を書くことに使います。

# OpenCVスーパーインポーズ用
import cv2

# cv2で画像を読み込む
img = cv2.imread(img_path)
# サイズを戻す
heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))
# RGBに変換
heatmap = np.uint8(255 * heatmap)
# 元の画像に適用
heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)

superimposed_img = heatmap * 0.4 + img

# 保存先を指定
img_path2 = '/Users/saito/Desktop/dog190720_1H.jpeg'
cv2.imwrite(img_path2, superimposed_img)

# 画像を読み込む
img2 = image.load_img(img_path2, target_size=(224, 224))
img2

非常に簡単に処理が終わりました。
出来た画像は以下の通りです。

miniature_poodleと判定したのは、耳や胴体が特徴的であったことがわかります。
このようにディープラーニングも判断内容が可視化でき、完全にブラックボックスではないことがわかります。

ちなみに、自分で撮ったどんな写真でも判定可能です。
img_pathとimg_path2の設定フォルダ(デスクトップ)を間違えなければ誰でも可能!

ちょっとやってみたいなーと思ったら、Google colab.へ。
ファイルの読み込み方法が少し違い、下記のコードを使う必要があります。

from google.colab import files
uploaded = files.upload()
uploaded_file_name = next(iter(uploaded_file))
img_path = uploaded_file_name

非常に簡単ですね~(^^)。

モバイルバージョンを終了