今回は VGG16 を試してみる。 入門編ということで単に Keras から VGG16 を利用する方法を学ぶ。

これまでの Keras シリーズは以下。

1. VGG16

VGG というのは、Visual Geometry Groupの略らしい。

オックスフォード大学で深層学習を使った画像認識を研究しているグループのようだ。

VGG16 というのは彼らが作った有名な多層ニューラルネットで、VGG_ILSVRC_16_layersとしても公開されている。 畳み込み13層、全結合層3層で計16層あるネットワークで VGG が考案したものなので VGG16 と呼ぶようだ。

VGG16 で扱う入力は 224x224 の RGB カラーの画像である。

2. ImageNet

ImageNet で訓練済みの VGG16 重みデータが VGG により公開されており、 Keras ライブラリでもそれを簡単にロードして使う機能がある。

ImageNet は画像のデータセット(またはそれを収集するプロジェクト)で、 現時点で 1,400 万枚の画像があるらしい。

3. 事前準備

いつもの。 今回は自分でモデル構築をしないので少なめ。

# Numpy
import numpy as np

# Matplotlib
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

# Keras
from keras.utils import plot_model

あとで動作確認用に独自に用意した画像を認識させてみるため、 Keras の image ライブラリをロードし、読み込み用の関数を作った。

from keras.preprocessing import image

def load_image(filename):
  img = image.load_img(filename, target_size=(224, 224))
  return image.img_to_array(img)

さらに VGG16 関係のライブラリをインポートする。

# Keras VGG16
from keras.applications.vgg16 import VGG16, preprocess_input, decode_predictions

4. VGG16 のモデルと重みのロード

VGG16 のモデルと重みをロードする。

以下のコードを実行するだけでモデルと、 ImageNet 向けに訓練済みの重みが一緒に読み込まれる。

ローディングには30分くらいかかるときがある。

model = VGG16(include_top=True, weights='imagenet',
              input_tensor=None, input_shape=None)

モデルを可視化してみた。

plot_model(model, to_file='vgg16.png', show_shapes=True)

事前の情報通り、Conv2D が13層、Dense が3層あることが見てとれる。

VGG16

VGG16 Model

5. 画像認識

6枚の画像について認識させてみる。

まずはローカルに保存された画像をロードする。

images = [
  '224x224/rabbit1.jpg',
  '224x224/rabbit2.jpg',
  '224x224/rabbit3.jpg',
  '224x224/rabbit4.jpg',
  '224x224/miku1.jpg',
  '224x224/miku2.jpg'
]

x = np.zeros((len(images), 224, 224, 3))

for i in range(0, len(images)):
  x[i] = load_image(images[i])

認識させてみる。下記のようにするだけでよい。

pred = model.predict(preprocess_input(x))
print('pred.shape={shape}'.format(shape=pred.shape))
# pred.shape=(6, 1000)

ここで preprocess_input というのが挟まっており、何者か気になる。 ヘルプを見ると、下記のように書いている(抜粋)。

>>> help(preprocess_input)
Help on function preprocess_input in module keras.applications.imagenet_utils:

preprocess_input(x, data_format=None, mode='caffe')
    Preprocesses a tensor or Numpy array encoding a batch of images.
    
    # Arguments
        x: Input Numpy or symbolic tensor, 3D or 4D.
        data_format: Data format of the image tensor/array.
        mode: One of "caffe", "tf" or "torch".
            - caffe: will convert the images from RGB to BGR,
                then will zero-center each color channel with
                respect to the ImageNet dataset,
                without scaling.

さっき読み込んだ重みデータは Caffe 用として公開されているもののため、 Keras 上のテンソルとはチャネルの順番とかが違う。 そのため、RGB 形式で読み込んだ画像データを BGR 形式に変換し、 BGR それぞれについて平均が 0 になるようにする。

…ということか?

6. 認識結果

認識結果を表示するにも、便利な decode_predictions という関数が用意されている。

top = decode_predictions(pred, top=5)
for i in range(0, len(top)):
  print('{i}:'.format(i=i))
  for j in range(0, len(top[i])):
    name, desc, score = top[i][j]
    print('  {rank}    {desc} {score:02.1f}%'
	      .format(rank=j+1, desc=desc, score=score*100))

結果を見る前に、decode_predictions について補足しておく。

この関数は内部で imagenet_class_index.json をダウンロードする。 この JSON ファイルには、予測結果ベクタの各インデックスに相当するクラス名が書かれている。

例えば np.argmax(pred[0]) を実行すると 330 が得られるので、 JSON の中から 330 のクラスを探すと次のように書かれており、 wood_rabbit と認識された、ということがわかる。

"330": ["n02325366", "wood_rabbit"],

では、認識結果を見てみる。

その1 ウサギ正面上

Rabbit1

貰いもののウサギ写真である。

  1. wood_rabbit (ウサギ) 24.0%
  2. Angora (アンゴラウサギ) 23.4%
  3. hare (野ウサギ) 22.1%
  4. hamster (ハムスター) 17.2%
  5. guinea_pig (モルモット) 6.1%

ウサギ系の結果が TOP3 に並び、以下も似たような外見の動物が来た。 ちゃんと認識できたと言えよう。

その2 ウサギ側面

Rabbit2

同じウサギ。

  1. hamper (洗濯カゴ) 11.2%
  2. computer_keyboard (PC キーボード) 8.2%
  3. hamster (ハムスター) 7.5%
  4. mouse (ネズミ) 7.1%
  5. tiger_cat (とら猫) 4.0%

残念な結果だ。 写真の6割ほどをケージが占めているからか、 1位は似たような構造を持つであろう洗濯カゴになってしまった。 確かに先入観無しにこの写真を見た場合、ケージの写真であると言えるかもしれない。 逆になぜ、我々はこの写真を見たときにウサギを撮ったものだと思うのだろう?

2位はキーボード。これもケージに引きずられてしまったのだろう。

耳がほぼ映っていないからか、ウサギ判定が下されなかった。

その3 ウサギ上面

Rabbit3

これも同じウサギ。

  1. Angora (アンゴラウサギ) 73.2%
  2. wood_rabbit (ウサギ) 24.5%
  3. hamster (ハムスター) 0.7%
  4. hare (野ウサギ) 0.5%
  5. dishwasher (食器洗い機) 0.4%

正しく当てられた。

その4 ウサギ正面左

Rabbit4

これも同じウサギ。

  1. wood_rabbit (ウサギ) 36.6%
  2. Angora (アンゴラウサギ) 28.6%
  3. hare (野ウサギ) 18.1%
  4. guinea_pig (モルモット) 1.6%
  5. hamster (ハムスター) 1.6%

色んな方向から撮った写真でもちゃんとウサギと認識してくれているみたいだ。 すごいぞ、VGG16。

その5 ミクさん 公式衣装 ver.

Miku1

この作品は piapro から 竹籽さんgood morning をお借りした。

初音ミクさんの画像である。 これは VGG16 で学習したどのクラスともあまり近くない画像と思われる。 だから当然ニューラルネットがこれが何の画像が当てられるはずが無いのだが、 無理矢理分類させてみるとどうなったかというと、以下のようになった。

  1. bubble (泡) 14.6%
  2. comic_book (漫画) 12.0%
  3. oxygen_mask (酸素マスク) 10.0%
  4. stethoscope (聴診器) 3.6%
  5. shower_curtain (シャワーカーテン) 3.5%

1位の泡は何に反応したかわからない。 淡い色合いが泡っぽいとニューラルネットは思ったのかもしれない。 2位の漫画は、漫画、アニメ方面の絵と考えるとまあ当っている。

その6 ミクさん マジカルミライ2017 ver.

Miku2

この作品は piapro から 駒鳥ういさん✨マジカルミライ✨ をお借りした。 勝手に 224x224 にトリミングさせて頂いた。

マジカルミライ 2017 のミクさんである。

  1. pinwheel (風車) 36.2%
  2. whistle (ホイッスル) 6.7%
  3. safety_pin (安全ピン) 5.3%
  4. comic_book (漫画) 4.5%
  5. stethoscope (聴診器) 3.8%

風車は少しわかる。 大きなリボンと広がるツインテールの造形の美しさは風車のそれに近いかもしれない。

やや面白いのは、その5のミクさんと共に判定されたものとして、 漫画の他に聴診器があることである。 ミクさんと聴診器が似ている、これはニューラルネットならではの発想ではないか。

7. まとめ

Keras で VGG16 を使って、画像認識をやってみた。 自分で用意した画像を分類させてみたところ、 VGG16 で学習済みのクラスについては適当に思える結果が得られた。

次回は Fine-tuning を試してミクさんを認識できるようにしてみたい。

8. 参考