Welcome to Mashykom WebSite




Keras + TensorFlow 2 を用いた画像分類のページ

Kerasはニューラルネットワークの学習を容易にすために開発されたライブラリです。2015年に開発が開始されたKerasはTheanoをベースとして構築され、広く利用されているPythonで書かれたライブラリになっている。現在、Theano、Tensorflow及びCNTKなどをバックエンドとして利用できます。Kerasの標準インストールで、デフォルトでは、Tensorflowがバックエンドになっています。

 KerasはPythonで書かれていますので、Pythonの基礎的な知識を持っている人を想定します。なお、anaconda等でPythonのモジュール一式がインストールされていることも前提とします。deep-learningの理論的な説明は、例えば、3層パーセプトロン(MLP)や畳み込みニューラルネットワーク(CNN)の説明は、Deep Learningと人工知能のページにありますので、併せて読むと理解が深まると思います。

 GoogleのTensorFlow開発チームは2019年10月1日、オープンソースの機械学習ライブラリ TensorFlow 2 を公開しました。これにより、TensorFlow が大幅に改訂されて、version 1.x からversion 2.xに変更されました。Keras がTensorFLowのモジュールに統合されました。そして、それに対応しての関係でしょうが、TensorFlow 1.xで使用されていたいくつかの重要なモジュールが廃止されました。とりわけ、TensorFlow 2で、tf.contribが廃止された影響は大きいです。また、Keras がTensorFLowのモジュールに統合されましたので、従来のKeras単体でのコードが正常に動作しません。

 このページでは、TensorFlow 2のもとで正常に作動するKerasを用いて、Deep Learning(CNN)のPython実装の例を説明します。特に、画像識別のアルゴリズムをPythonでコード化したライブラリの代表的な事例を取り上げて、機械学習プログラムの実装と実行について説明します。

 python関連のパッケージなどの環境はセットアップされているとします。ここでは、macOS で実行していますが、TensorFlow 2 がインストールされていれば、Ubuntu および Windowsでも正常に作動すると思います。TensorFlow の公式ページにおけるPython開発環境の設定では、'virtualenv' を用いた 'Python virtual environments' を構築することが推奨されています。

 プログラミング経験がまったくなく、とりあえず Tensorflow を体験してみたいという場合には、自身のPCにPythonをインストールする必要はありません。 Google Colaboratoryを使うと、WebブラウザからPythonを使えます。Google のアカウントが必要です。ただし、Jupyter Notebook 形式になっていますので、Jupyter Notebookの操作で戸惑うかもしれません。その場合には、Jupyter Notebook のページを読むことをお勧めします。

 ここでは、GPUを搭載していないPCを前提にしているので、リアルタイムでの物体検出では、処理速度が遅く、画像の切り替わりがスムーズでないことはやむを得ません。また、動作確認は以下に明記されたTensorflowやKerasなどのバージョンで行っています。ソフトウエ環境は以下の通りです。

numpyのバージョンは1.20.3です
scipyのバージョンは1.6.3です
matplotlibのバージョンは3.4.2です
PIL(Pillow)のバージョンは8.2.0です
kerasのバージョンは2.8.0です
tensorflowのバージョンは2.8.0です
pandasのバージョンは1.2.4です
sklearnのバージョンは0.24.2です

このページに説明されるコードはすべて、私のGitHubのrepoにあります。そこにあるプログラムはすべて Google Colab にて実行可能です。


Last updated: 2022.9.20( First uploaded 2018.4.22)


Tensorflow 2.0のインストール

 Tensorflow のインストールでは、Python の仮想環境を構築することが推奨されていますが、ここでは仮想環境の構築は済んでいるとします。Python の仮想環境の構築についての説明は、このページを参照ください。

 システムPython を使用するケース、または、仮想環境を利用すケースを含めて、Python 3.9.4 がインストールされているとします。python を起動します。

$ python

Python 3.9.4 (default, May 15 2021, 14:49:04) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 

 以下のようなコマンドを入力してtensorflow 2 をインストールします。


$ pip install --upgrade tensorflow

 この入力で、TensorFlowのパッケージは ~/site-packages/tensorflow/にインストールされます。

 TensorFlow のバージョン確認をします。


$ python -c "import tensorflow as tf; print( tf.__version__ )"

 次に、Keras のバージョン確認もします。


$ python -c "import keras; print( keras.__version__ )"

 kerasでモデルの保存を行うためにはHDF5が必要となるので、そのためのパッケージもインストールします。


$ pip install h5py

 さらに、構成したモデルを可視化して、そのグラフを描画するためには、graphvizとpydotを利用する必要があります。それらのパッケージは


$ pip install graphviz
$ pip install pydot

とインストールします。


keras.Sequentialモデルを用いた手書き数値の識別


 Tensorflowの公式サイトにあるtutorialsに沿って説明します。TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである tf.kerasを使用します。tf.keras.Sequential モデルを用いた最も単純な例は以下のように書けます。第1行目の、
from __future__ import absolute_import, division, print_function, unicode_literals
はこうするのが慣例だと思って下さい。

from __future__ import absolute_import, division, print_function, unicode_literals
from tensorflow.keras import datasets, layers, models

mnist = datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = models.Sequential()
model.add(layers.Flatten(input_shape=(28, 28)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dropout(0.2))
model.add(layers.Dense(10, activation='softmax'))


model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)

model.evaluate(x_test,  y_test, verbose=2)

 ネットワークは4層のlayersから構成されます。model = Sequential()で、Sequentialモデルを用いた具体的なモデル名をmodelとします。簡単にネットワークのレイヤーを積み重ねて、モデルを構築します。Flatten(input_shape=(28, 28))は入力サイズが28✖️28の2次元データを1次元にフラットにします。Dense(128, activation='relu')は、128個のニューロンが隣接する層と全結合されたレイヤで、活性化関数がRelu関数であることを意味します。Dense(units=10, activation='softmax')は、追加される第4層が10個のニューロンからなる全結合層で、活性化関数がsoftmax関数であることを示します。Dropoutはディープラーニングの過学習を防ぐために行います。Dropoutは、隣接層のニューロンとのつながりをランダムに切断してあげることで、過学習を防ぎます。0.2という数字は、その切断するニューロンの割合を示しています。訓練時には、データが読み込まれるごとにニューロンはランダムに切断されます。

 compile()で、以上で指定された4層のレイヤをもつニューラルネットワークに損失関数と学習方法を指定して、モデルを完成させ、このモデルを用いた訓練(学習)プロセスを設定します。損失関数は交差エントロピー、最適化手法はadamを指定しています。loss='mean_squared_error'と指定すると二乗和誤差関数を使用されます。この例で指定されている'categorical_crossentropy'を使用するためには、データ(x_train,y_train)を'to_categorical'でone-hot形式にする前処理が必要です。損失関数の最小点を検索するための最適化手法にはいくつかの種類があります。SGD(stochastic Gradient Descent)は最も単純なアルゴリズムで、RMSprop、Adagrad, Adadelta、Adamなどのアルゴリズムが利用可能です。これらの詳細については、ディープラーニングの参考書などを参照ください。

 この後、fit()メソッドを用いて、epochs数とbatch_sizeの大きさを与えて、モデルのパラメータ値の訓練(学習)をします。訓練データは教師ラベル付きの(x_train,y_train)です。y_trainが教師ラベルです。mnistデータはmnist.load_data() で読み込んでいます。

 上記のコードを実行すると、以下のように、エポックごとに学習の計算結果が表示されます。


Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 4s 73us/sample - loss: 0.2952 - accuracy: 0.9145
Epoch 2/5
60000/60000 [==============================] - 5s 82us/sample - loss: 0.1409 - accuracy: 0.9575
Epoch 3/5
60000/60000 [==============================] - 4s 66us/sample - loss: 0.1086 - accuracy: 0.9672
Epoch 4/5
60000/60000 [==============================] - 4s 72us/sample - loss: 0.0861 - accuracy: 0.9730
Epoch 5/5
60000/60000 [==============================] - 4s 72us/sample - loss: 0.0743 - accuracy: 0.9766
10000/1 - 0s - loss: 0.0415 - accuracy: 0.9761

[0.07995059116524644, 0.9761]

 次に、MNIST データを用いて、手書き数値の分類をするための、上記のネットワークよりは少し高度な畳み込みニューラルネットワーク (CNN: Convolutional Neural Network) を用いた学習について説明します。このネットワークは MNIST テストセットにおいて、99%以上の精度を達成します。tf.Keras.Sequential APIを使用するため、ほんの数行のコードでモデルの作成と学習を行うことができます。以下にコードを紹介します。モデルは8層のネットワーク層から構成されています。


from __future__ import absolute_import, division, print_function, unicode_literals
from tensorflow.keras import datasets, layers, models

(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()

train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))

# ピクセルの値を 0~1 の間に正規化
train_images, test_images = train_images / 255.0, test_images / 255.0

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

 ネットワーク層のモデルを完成するために、Conv2D(64, (3, 3), activation='relu'))層の後に、ネットワーク層が3層追加されています。(shape (3, 3, 64) の) 畳み込みの基礎部分からの最後の出力テンソルを、1つ以上の Dense レイヤーに入れて分類を実行します。現在の出力は 3D テンソルですが、Dense レイヤーは入力としてベクトル (1D) を取ります。まず、3D 出力を 1D に平滑化 (または展開) してから、最上部に1つ以上の Dense レイヤーを追加します。MNIST は 10 個の出力クラスを持ちます。そのため、我々は最後の Dense レイヤーの出力を 10 にし、softmax関数を使用します。


model.summary()

 # opuput

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 576)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                36928     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________

 モデルの構造がよく理解できます。以下の通りに、モデルをコンパイルして、学習する。


model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_images, train_labels, epochs=5)

 結果は次の通りです。

Train on 60000 samples
Epoch 1/5
60000/60000 [==============================] - 39s 643us/sample - loss: 0.1437 - accuracy: 0.9553
Epoch 2/5
60000/60000 [==============================] - 43s 718us/sample - loss: 0.0470 - accuracy: 0.9851
Epoch 3/5
60000/60000 [==============================] - 48s 800us/sample - loss: 0.0334 - accuracy: 0.9897
Epoch 4/5
60000/60000 [==============================] - 47s 776us/sample - loss: 0.0257 - accuracy: 0.9920
Epoch 5/5
60000/60000 [==============================] - 44s 734us/sample - loss: 0.0209 - accuracy: 0.9932


 シンプルなモデルですが、相当に正確な識別ができます。



keras.Sequentialモデルを用いた画像の識別


 ここでは、tf.keras.Sequential modelを用いてイメージ画像から動物を識別する方法について説明します。 Convolutional Neural Network (CNN) を使用して、CIFAR images の分類問題を取り上げます。 CIFAR-10は、airplane(飛行機)、automobile(自動車)、bird(鳥)、cat(猫)、deer(鹿)、dog(犬)、frog(カエル)、horse(馬)、ship(船)、truck(トラック)の10種類のいずれかが写っている写真を集めて、ラベルをつけたデータセットです。各種類(クラス)当たり60,000 color images 画像があります。詳しくは、https://www.cs.toronto.edu/~kriz/cifar.htmlを参照ください。

 データセットには、 訓練用として 50,000 training images 、検証用とし 10,000 testing imagesが用意されています。Kerasにはこのデータセットを読み込むコードが付録しています


from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf

from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt

(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()

# Normalize pixel values to be between 0 and 1
train_images, test_images = train_images / 255.0, test_images / 255.0

 と入力すると、cifar10のデータセットが読み込まれます。内容を見てみるために、以下のように入力します。


class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
               'dog', 'frog', 'horse', 'ship', 'truck']

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    # The CIFAR labels happen to be arrays, 
    # which is why you need the extra index
    plt.xlabel(class_names[train_labels[i][0]])
plt.show()

 25枚の画像が表示されます。画像はかなり不鮮明です。CNNモデルを作成し、学習するために、以下のコード作成します。cifarの画像のshapeは(32, 32, 3)です。


model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

history = model.fit(train_images, train_labels, epochs=10, 
                    validation_data=(test_images, test_labels))

Train on 50000 samples, validate on 10000 samples
Epoch 1/10
50000/50000 [==============================] - 11s 222us/sample - loss: 1.5684 - accuracy: 0.4266 - val_loss: 1.2367 - val_accuracy: 0.5543
Epoch 2/10
50000/50000 [==============================] - 7s 130us/sample - loss: 1.1648 - accuracy: 0.5879 - val_loss: 1.1616 - val_accuracy: 0.5853
Epoch 3/10
50000/50000 [==============================] - 6s 130us/sample - loss: 1.0314 - accuracy: 0.6379 - val_loss: 1.0190 - val_accuracy: 0.6374
Epoch 4/10
50000/50000 [==============================] - 6s 129us/sample - loss: 0.9382 - accuracy: 0.6711 - val_loss: 0.9656 - val_accuracy: 0.6617
Epoch 5/10
50000/50000 [==============================] - 6s 129us/sample - loss: 0.8650 - accuracy: 0.6970 - val_loss: 0.9005 - val_accuracy: 0.6870
Epoch 6/10
50000/50000 [==============================] - 6s 129us/sample - loss: 0.8054 - accuracy: 0.7183 - val_loss: 0.9217 - val_accuracy: 0.6748
Epoch 7/10
50000/50000 [==============================] - 7s 131us/sample - loss: 0.7557 - accuracy: 0.7356 - val_loss: 0.9336 - val_accuracy: 0.6858
Epoch 8/10
50000/50000 [==============================] - 7s 132us/sample - loss: 0.7145 - accuracy: 0.7514 - val_loss: 0.9107 - val_accuracy: 0.6906
Epoch 9/10
50000/50000 [==============================] - 7s 134us/sample - loss: 0.6758 - accuracy: 0.7647 - val_loss: 0.8696 - val_accuracy: 0.7077
Epoch 10/10
50000/50000 [==============================] - 6s 130us/sample - loss: 0.6410 - accuracy: 0.7757 - val_loss: 0.8684 - val_accuracy: 0.7076

 MNIST分類で採用した前項のモデルと同一ですが、学習の結果はそれほど高精度のモデルにはなっていません。MNISTのデータは白黒でしたので、このような結果となりました。


学習済みモデルを用いた画像からの物体の識別


 通常のCPUを搭載したPCでは、正答率が90%を超えるまでニューラルネットワークの学習を行うためには、数時間から数日も必要とされます。Kerasには、学習済みモデルが用意されています。ImageNetで学習した重みをもつ画像分類のモデルとして、以下のものが用意されています。


Xception
VGG16
VGG19
ResNet50
InceptionV3
InceptionResNetV2
MobileNet
DenseNet
NASNet


 ここでは、ディープラーニングでよく使用されるニューラルネットワークであるVGG、ResNet及びInceptionV3を使用します。VGGは、畳み込み層とプーリング層から構成される基本的なCNNです。畳み込み層と全結合層を全部で16もしくは19層にしてディープにしています。前者をVGG16、後者をVGG19と呼びます。VGGは、2014年に開催された大規模画像認識のコンペILSVRC(ImageNet Large Scale Visual Recognition Challenge)で2位の成績に終わりましたが、シンプルな構成なので応用面ではよく使用されます。(ちなみに、優勝者はGoogLeNetでした)VGGは3x3のフィルターによる畳み込み層を2回から4回連続して、プーリング層でサイズを半分にするという処理を繰り返します。

 ResNetは2015年Microsoftのチームによって開発されたニューラルネットワークです。その特徴は、層を深くしすぎると過学習などの問題が起きるのを回避するために、スキップ構造(ショートカット)と呼ばる概念を導入した点です。ResNetは、VGGのネットワークをベースにして、スキップ構造を取り入れて、層を深くしています。具体的には、畳み込み層を2層おきにスキップしてつなぎ、層を150層以上まで深くしています。ILSVRCのコンペでは、誤認識率が3.5%という驚異的な成果を出しています。

 最初に、この中で最も単純なVGG16モデルを取り上げます。上で触れたとおり、VGG16は2014年のILSVRCで提案された畳み込み13層とフル結合3層の計16層から成る畳み込みニューラルネットワークです。層の数が多いだけで一般的な畳み込みニューラルネットと大きな違いはなく、同時期に提案されたGoogLeNetに比べるとシンプルでわかりやすい。ImageNetと呼ばれる大規模な画像データセットを使って訓練したモデルが公開されています。VGG16の出力層は1000ユニットあり、ImageNetの1000クラスを分類するニューラルネットです。

 KerasではVGG16モデルがkeras.applications.vgg16モジュールに実装されているため簡単に使えます。ImageNetの大規模画像セットで学習済みのモデルなので自分で画像を集めて学習する必要がなくて、簡単に利用できて便利です。能です。

学習済みモデルVGG16を用いた画像識別のコードは以下のようになります。


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

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

img_path = 'images/elephant.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)

model.summary()

preds = model.predict(x)

# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)


print('Predicted:', decode_predictions(preds, top=3)[0])

 一般的に、VGG16クラス、VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None)は4つの引数を取ります。include_topはVGG16のトップにある1000クラス分類するフル結合層(FC)を含むか含まないかを指定します。ここでは画像分類を行いたいためFCを含んだ状態で使いますので、Trueがデフォルトです。weightsはVGG16の重みの種類を指定します。VGG16は単にモデル構造であるため必ずしもImageNetを使って学習しなければいけないわけではありません。しかし、現状ではImageNetで学習した重みしか提供されていないので、これを採用します。input_tensorは自分でモデルに画像を入力したいときに使うので、ここでは必要ありませんので、デフォルトでいいです。input_shapeは入力画像の形状を指定します。include_top=Trueにして画像分類器として使う場合は 、デフォルトにしておきます。このとき、入力のshapeは(224, 224, 3) ('channels_last'データフォーマットのとき) または (3, 224, 224) ('channels_first'データフォーマットのとき)となっています。
 この例では以下にある像の画像'elephant.jpg'を用いています。

elephant.jpg

この画像はホームディレクトリに作成されたimagesという名前のディレクトリの下に配置されています。model.summary()するとモデル構造が見られます。 このコードを実行すると、

Model: "vgg16"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 224, 224, 3)]     0         
_________________________________________________________________
block1_conv1 (Conv2D)        (None, 224, 224, 64)      1792      
_________________________________________________________________
block1_conv2 (Conv2D)        (None, 224, 224, 64)      36928     
_________________________________________________________________
block1_pool (MaxPooling2D)   (None, 112, 112, 64)      0         
_________________________________________________________________
block2_conv1 (Conv2D)        (None, 112, 112, 128)     73856     
_________________________________________________________________
block2_conv2 (Conv2D)        (None, 112, 112, 128)     147584    
_________________________________________________________________
block2_pool (MaxPooling2D)   (None, 56, 56, 128)       0         
_________________________________________________________________
block3_conv1 (Conv2D)        (None, 56, 56, 256)       295168    
_________________________________________________________________
block3_conv2 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_conv3 (Conv2D)        (None, 56, 56, 256)       590080    
_________________________________________________________________
block3_pool (MaxPooling2D)   (None, 28, 28, 256)       0         
_________________________________________________________________
block4_conv1 (Conv2D)        (None, 28, 28, 512)       1180160   
_________________________________________________________________
block4_conv2 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_conv3 (Conv2D)        (None, 28, 28, 512)       2359808   
_________________________________________________________________
block4_pool (MaxPooling2D)   (None, 14, 14, 512)       0         
_________________________________________________________________
block5_conv1 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv2 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_conv3 (Conv2D)        (None, 14, 14, 512)       2359808   
_________________________________________________________________
block5_pool (MaxPooling2D)   (None, 7, 7, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 25088)             0         
_________________________________________________________________
fc1 (Dense)                  (None, 4096)              102764544 
_________________________________________________________________
fc2 (Dense)                  (None, 4096)              16781312  
_________________________________________________________________
predictions (Dense)          (None, 1000)              4097000   
=================================================================
Total params: 138,357,544
Trainable params: 138,357,544
Non-trainable params: 0
_________________________________________________________________
Predicted: [('n02504458', 'African_elephant', 0.7639979), ('n01871265', 'tusker', 0.17359596), ('n02504013', 'Indian_elephant', 0.062363148)]

という結果が表示されます。重み(#Param)がある層を数えていくと全部で16個あることがわかります。include_top=Trueなのでfc1、fc2、predictionsという層が追加されているのが確認できます。また、最後のpredictions層の形状が (None, 1000) で1000クラスの分類であることもわかります。Noneはサイズが決まっていないことを意味し、ここでは入力サンプル数を意味します。
 load_img()でロードした画像は (rows, cols, channels) の3Dテンソルなので、これにサンプル数 samples を追加した4Dテンソルに変換する必要があります。img_to_array()でPIL形式の画像をNumPy array形式に変換して、np.expand_dimsで4次元に変換します。
 クラスの予測はpredict()で行います。VGG16用の平均を引く前処理 preprocess_input() を通した4Dテンソルを入力データとします。predict()の戻り値は、1000クラスの確率値です。VGG16用のdecode_predictions()を使うと確率値が高い順にクラス名を出力してくれます。
  確率76.39%で画像が'African_elephant'の画像であると識別しました。このケースは正答率が比較的高く出る画像です。画像を色々と変えて試して見てください。例えば、以下のひまわりの画像を用いてください。

flower.jpg

手元に画像がないときは、ImageNetを検索して、そこからダウンロードしてください。ImageNetとはスタンフォード大学がインターネット上から画像を集め分類したデータセットのことです。一般画像認識用に用いられる。ImageNetを利用して画像検出・識別精度を競うILSVRCなどコンテストも開かれています。ダウンロードできる画像は、著作権フリーというのではないので、自己学習用のものです。
  次に、ResNet50を取り上げます。このモデルは,TheanoとTensorFlowの両方のbackendで利用でき, 'channels_first'データフォーマット (channels, height, width) 、または、'channels_last'データフォーマット (height, width, channels)の両方で構築可能です。 'channels_first'データフォーマットのとき、入力のshapeは(3, 224, 224)で、'channels_last'データフォーマットのとき入力のshapeは(224, 224, 3)です。 デフォルトの入力サイズは224x224です。学習済みモデルResNet50を用いた物体識別のプログラムは以下のようになります。畳み込み層の深さは168です。VGG16に比較して、相当にディープなニューラルネットワークとなっています。ResNet50は、
ResNet50(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000)
で表現される通り、6つの引数を持ちますが、ここでは、weights以外をデフォルトにしているので、省略しています。

from tensorflow.keras.applications.resnet50 import ResNet50
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions
import numpy as np

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

img_path = 'images/elephant.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)

preds = model.predict(x)

# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)
print('Predicted:', decode_predictions(preds, top=3)[0])


この例では像の画像'elephant.jpg'を用いています。この画像はホームディレクトリに作成されたimagesという名前のディレクトリの下に配置されています。このコードを実行すると、

Predicted: [('n02504458', 'African_elephant', 0.8259493), ('n01871265', 'tusker', 0.09973397), ('n02504013', 'Indian_elephant', 0.07116784)]

という結果が表示されます。 確率82.59%で対象物が'African_elephant'であると識別しました。このケースは正答率が比較的高く出る画像です。VGG16モデルに比較して識別能力が高いと思われます。画像を色々と変えて試して見てください。上で表示した'flower.jpg'を用いて識別を行って見てください。この画像はヒマワリですが、'daisy'であるという確率が一番高く出ます。ImageNetの1000クラス分類にはヒマワリの分類がありません。

 GoogleのチームがTensorFlow向けに開発しているInceptionV3(GoogLeNet)を取り上げます。畳み込み層の深さは159と、ResNet50の深さと同じ程度です。このモデルは,TheanoとTensorFlowの両方のbackendで利用でき, 'channels_first'データフォーマット (channels, height, width) か'channels_last'データフォーマット (height, width, channels)の両方で構築可能です。デフォルトの入力サイズは299x299です。この入力サイズはVGG16及びResNet50と異なります。

InceptionV3による画像識別のスクリプトを以下のように作成します。


from tensorflow.keras.applications.inception_v3 import InceptionV3
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
import numpy as np


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

img_path = 'images/elephant.jpg'
img = image.load_img(img_path, target_size=(299, 299))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

#model.summary()

preds = model.predict(x)

# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)
print('Predicted:', decode_predictions(preds, top=3)[0])

このコードを実行すると、以下の結果となります。

Predicted: [('n02504458', 'African_elephant', 0.61769515), ('n01871265', 'tusker', 0.31645632), ('n02504013', 'Indian_elephant', 0.032580543)]


ResNet50の正答率を比較すると、相対的に低い値です。画像が'flower.jpg'のケースでは正答率が比較的高くなります。画像に依存して、正答率が上下することがわかります。


このページの先頭に戻る

Tensorflow 2 で Object Detection のページに行く

github webページへ