LoginSignup
2
0

More than 1 year has passed since last update.

TensorFlowを使ったモデルの構築と転移学習

Posted at

ディープラーニングを使用した画像分類

ディープラーニングを用いて画像の分類を行います。
今回は画像分類のチュートリアルとしてよく用いられる猫と犬の画像の分類を実践してみます。

ライブラリのインポート

Pythonにおけるディープラーニングのライブラリとしては以下が有名です。
- TensorFlow および Keras
- PyTorch

今回の例ではTensorFlowおよびKerasを使用します。

import os
import numpy as np
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image

import tensorflow as tf
from tensorflow.keras import utils as np_utils
from tensorflow.keras.preprocessing import image_dataset_from_directory
from tensorflow.keras.layers import Input, Dense, Conv2D, Flatten, Dropout, MaxPooling2D

データの取得

使用するデータを取得します。
今回使用するデータはTensorFlowのチュートリアルでも用いられている猫と犬の分類済み画像になります。

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'

path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

train_cats_dir = os.path.join(train_dir, 'cats')  # 学習用の猫画像のディレクトリ
train_dogs_dir = os.path.join(train_dir, 'dogs')  # 学習用の犬画像のディレクトリ
validation_cats_dir = os.path.join(validation_dir, 'cats')  # 検証用の猫画像のディレクトリ
validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # 検証用の犬画像のディレクトリ
Downloading data from https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip
68608000/68606236 [==============================] - 1s 0us/step
68616192/68606236 [==============================] - 1s 0us/step

犬と猫の画像はそれぞれtrainという学習用の画像セットとvalidationという検証用の画像のセットに分類されています。

では、それぞれの画像の数を確認してみましょう。

num_cats_tr = len(os.listdir(train_cats_dir))
num_dogs_tr = len(os.listdir(train_dogs_dir))

num_cats_val = len(os.listdir(validation_cats_dir))
num_dogs_val = len(os.listdir(validation_dogs_dir))

total_train = num_cats_tr + num_dogs_tr
total_val = num_cats_val + num_dogs_val

print('total training cat images:', num_cats_tr)
print('total training dog images:', num_dogs_tr)

print('total validation cat images:', num_cats_val)
print('total validation dog images:', num_dogs_val)
print("--")
print("Total training images:", total_train)
print("Total validation images:", total_val)
total training cat images: 1000
total training dog images: 1000
total validation cat images: 500
total validation dog images: 500
--
Total training images: 2000
Total validation images: 1000

一旦ここで、この後に使用するネットワークの学習用の定数を設定しておきます。

BATCH_SIZE = 128
epochs = 10
IMG_SIZE = (160, 160)
IMG_SHAPE = IMG_SIZE + (3,)

データの準備

取得したデータを元にimage_dataset_from_directory関数を使用してデータセットを作成します。
教師データ、検証データそれぞれで用意します。

また、学習後のモデルを評価するためにテストデータセットも用意します。
今回、テストデータセットは検証データセットの一部を使用します。

# 教師データ
train_dataset = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)
# 検証データ
validation_dataset = image_dataset_from_directory(validation_dir,
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  image_size=IMG_SIZE)

val_batches = tf.data.experimental.cardinality(validation_dataset)

# テストデータセット
test_dataset = validation_dataset.take(val_batches // 5)

validation_dataset = validation_dataset.skip(val_batches // 5)
Found 2000 files belonging to 2 classes.
Found 1000 files belonging to 2 classes.

試しに画像を表示してみます。

class_names = train_dataset.class_names

plt.figure(figsize=(10, 10))
for images, labels in train_dataset.take(1):
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")

output_11_0.png

モデルの作成

画像を分類するためのモデルを作成しましょう。

def sample_model():
    # モデルの構成を定義します。
    inputs = Input(shape=IMG_SHAPE)
    x = tf.keras.layers.experimental.preprocessing.Rescaling(1./255, offset= -1)(inputs) # データを0-1の範囲に正規化
    x = Conv2D(16, 3, padding='same', activation='relu')(x)
    x = MaxPooling2D()(x)
    x = Conv2D(32, 3, padding='same', activation='relu')(x)
    x = MaxPooling2D()(x)
    x = Conv2D(64, 3, padding='same', activation='relu')(x)
    x = MaxPooling2D()(x)
    x = Flatten()(x)
    x = Dense(512, activation='relu')(x)
    outputs = Dense(1, activation='sigmoid')(x)
    model = tf.keras.Model(inputs=inputs, outputs=outputs)

    # モデルのコンパイル
    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    return model

# モデルを作成
model = sample_model()

# モデルの構造を確認します
model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 160, 160, 3)]     0         
_________________________________________________________________
rescaling (Rescaling)        (None, 160, 160, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 160, 160, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 80, 80, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 80, 80, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 40, 40, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 40, 40, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 20, 20, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 25600)             0         
_________________________________________________________________
dense (Dense)                (None, 512)               13107712  
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 13,131,809
Trainable params: 13,131,809
Non-trainable params: 0
_________________________________________________________________

モデルの学習

それでは作成したモデルを学習してみましょう。

history = model.fit(train_dataset,
                    epochs=epochs,
                    validation_data=validation_dataset)
Epoch 1/10
16/16 [==============================] - 38s 335ms/step - loss: 1.0439 - accuracy: 0.5030 - val_loss: 0.6913 - val_accuracy: 0.5023
Epoch 2/10
16/16 [==============================] - 5s 265ms/step - loss: 0.6854 - accuracy: 0.5725 - val_loss: 0.6683 - val_accuracy: 0.6170
Epoch 3/10
16/16 [==============================] - 5s 257ms/step - loss: 0.6680 - accuracy: 0.5955 - val_loss: 0.6479 - val_accuracy: 0.6330
Epoch 4/10
16/16 [==============================] - 5s 256ms/step - loss: 0.6248 - accuracy: 0.6640 - val_loss: 0.6236 - val_accuracy: 0.6720
Epoch 5/10
16/16 [==============================] - 5s 258ms/step - loss: 0.5696 - accuracy: 0.7125 - val_loss: 0.5892 - val_accuracy: 0.7099
Epoch 6/10
16/16 [==============================] - 5s 259ms/step - loss: 0.5009 - accuracy: 0.7555 - val_loss: 0.6136 - val_accuracy: 0.6537
Epoch 7/10
16/16 [==============================] - 5s 261ms/step - loss: 0.4441 - accuracy: 0.8045 - val_loss: 0.6165 - val_accuracy: 0.7053
Epoch 8/10
16/16 [==============================] - 5s 261ms/step - loss: 0.3926 - accuracy: 0.8205 - val_loss: 0.6289 - val_accuracy: 0.6984
Epoch 9/10
16/16 [==============================] - 5s 259ms/step - loss: 0.3158 - accuracy: 0.8625 - val_loss: 0.6914 - val_accuracy: 0.6732
Epoch 10/10
16/16 [==============================] - 5s 260ms/step - loss: 0.2574 - accuracy: 0.8920 - val_loss: 0.7377 - val_accuracy: 0.6881

学習結果を可視化してみましょう。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

output_17_0.png

モデルの転移学習

先程のように何もない状態からニューラルネットワークのモデルを構築することは実際の業務ではあまりなく、基本的には公開されている既に構築済み/事前学習済みのモデルをベースとすることが多いです。

今回はMobileNetV2というモデルをImageNetというデータセットで事前学習済みのモデルをベースとし、転移学習しましょう。

# ベースモデルを用意
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')
base_model.trainable = False
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_160_no_top.h5
9412608/9406464 [==============================] - 0s 0us/step
9420800/9406464 [==============================] - 0s 0us/step

画像データの準備

先程のモデルでは画像のデータに対して1~0の範囲で正規化を行いました。

使用するモデルによって、データの範囲が異なります。

使用するライブラリによっては前処理用のメソッドが用意されています。今回はその前処理用のメソッドを利用します。

# モデル用の画像前処理のレイヤーを用意。
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input

モデルの構築

def create_model():
    # モデルの構成を定義します。
    inputs = tf.keras.Input(shape=(160, 160, 3))
    x = preprocess_input(inputs)
    x = base_model(x, training=False)
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    x = tf.keras.layers.Dropout(0.2)(x)
    outputs = Dense(1, activation='sigmoid')(x)
    model = tf.keras.Model(inputs, outputs)

    # モデルのコンパイル
    base_learning_rate = 0.0001
    model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
                loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                metrics=['accuracy'])
    return model

model = create_model()
model.summary()
Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         [(None, 160, 160, 3)]     0         
_________________________________________________________________
tf.math.truediv (TFOpLambda) (None, 160, 160, 3)       0         
_________________________________________________________________
tf.math.subtract (TFOpLambda (None, 160, 160, 3)       0         
_________________________________________________________________
mobilenetv2_1.00_160 (Functi (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dropout (Dropout)            (None, 1280)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,281
Non-trainable params: 2,257,984
_________________________________________________________________


/usr/local/lib/python3.7/dist-packages/keras/optimizer_v2/optimizer_v2.py:356: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.
  "The `lr` argument is deprecated, use `learning_rate` instead.")

ではこのモデルを転移学習してみましょう。

history = model.fit(train_dataset,
                    epochs=epochs,
                    validation_data=validation_dataset)
Epoch 1/10


/usr/local/lib/python3.7/dist-packages/keras/backend.py:4994: UserWarning: "`binary_crossentropy` received `from_logits=True`, but the `output` argument was produced by a sigmoid or softmax activation and thus does not represent logits. Was this intended?"
  '"`binary_crossentropy` received `from_logits=True`, but the `output`'


16/16 [==============================] - 14s 528ms/step - loss: 0.7151 - accuracy: 0.5650 - val_loss: 0.6436 - val_accuracy: 0.6330
Epoch 2/10
16/16 [==============================] - 7s 369ms/step - loss: 0.6210 - accuracy: 0.6730 - val_loss: 0.5553 - val_accuracy: 0.7248
Epoch 3/10
16/16 [==============================] - 7s 372ms/step - loss: 0.5470 - accuracy: 0.7365 - val_loss: 0.4899 - val_accuracy: 0.7844
Epoch 4/10
16/16 [==============================] - 7s 369ms/step - loss: 0.4960 - accuracy: 0.7705 - val_loss: 0.4400 - val_accuracy: 0.8314
Epoch 5/10
16/16 [==============================] - 7s 374ms/step - loss: 0.4285 - accuracy: 0.8305 - val_loss: 0.3819 - val_accuracy: 0.8693
Epoch 6/10
16/16 [==============================] - 7s 373ms/step - loss: 0.3840 - accuracy: 0.8590 - val_loss: 0.3464 - val_accuracy: 0.8956
Epoch 7/10
16/16 [==============================] - 7s 376ms/step - loss: 0.3640 - accuracy: 0.8735 - val_loss: 0.3203 - val_accuracy: 0.9071
Epoch 8/10
16/16 [==============================] - 7s 381ms/step - loss: 0.3256 - accuracy: 0.8925 - val_loss: 0.2858 - val_accuracy: 0.9278
Epoch 9/10
16/16 [==============================] - 7s 373ms/step - loss: 0.3026 - accuracy: 0.9060 - val_loss: 0.2601 - val_accuracy: 0.9427
Epoch 10/10
16/16 [==============================] - 7s 374ms/step - loss: 0.2787 - accuracy: 0.9130 - val_loss: 0.2469 - val_accuracy: 0.9369

学習結果を表示してみます。

先程のモデルを1から構築した場合よりも精度が向上している事がわかります。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

output_30_1.png

output_27_0.png

モデルの評価

テストデータセットを使用してモデルの性能を検証しましょう。

loss, accuracy = model.evaluate(test_dataset)
print('Test accuracy :', accuracy)
1/1 [==============================] - 1s 1s/step - loss: 0.2511 - accuracy: 0.9219
Test accuracy : 0.921875
image_batch, label_batch = test_dataset.as_numpy_iterator().next()
predictions = model.predict_on_batch(image_batch).flatten()

predictions = tf.nn.sigmoid(predictions)
predictions = tf.where(predictions < 0.5, 0, 1)

print('予測結果:\n', predictions.numpy())
print('正解のラベル:\n', label_batch)

# 試しに9枚表示
plt.figure(figsize=(10, 10))
for i in range(9):
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(image_batch[i].astype("uint8"))
  plt.title(class_names[predictions[i]])
  plt.axis("off")
予測結果:
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
正解のラベル:
 [0 0 1 0 0 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 0 0
 0 0 1 0 0 1 1 1 0 1 1 1 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 1 1 0 1
 1 0 1 1 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 0 0 1 1
 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 0]

ディープラーニングを使用した画像分類の実例

ディープラーニングを用いて画像の分類を行います。

今回は画像分類のチュートリアルにて猫と犬の画像の分類を実践してみます。

ライブラリのインポート

Pythonにおけるディープラーニングのライブラリとしては以下が有名です。
- TensorFlow および Keras
- PyTorch

今回の例ではTensorFlowおよびKerasを使用してみましょう。

import os
import numpy as np
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image

import tensorflow as tf
from tensorflow.keras import utils as np_utils
from tensorflow.keras.preprocessing import image_dataset_from_directory
from tensorflow.keras.layers import Input, Dense, Conv2D, Flatten, Dropout, MaxPooling2D

データの取得

使用するデータを取得します。

今回の例ではTensorFlowのチュートリアルでも使用されている犬と猫の分類済みのデータセットを使用します。

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'

path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

train_cats_dir = os.path.join(train_dir, 'cats')  # 学習用の猫画像のディレクトリ
train_dogs_dir = os.path.join(train_dir, 'dogs')  # 学習用の犬画像のディレクトリ
validation_cats_dir = os.path.join(validation_dir, 'cats')  # 検証用の猫画像のディレクトリ
validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # 検証用の犬画像のディレクトリ
Downloading data from https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip
68608000/68606236 [==============================] - 1s 0us/step
68616192/68606236 [==============================] - 1s 0us/step

犬と猫の画像はそれぞれtrainという学習用の画像セットとvalidationという検証用の画像のセットに分類されています。

では、それぞれの画像の数を確認してみましょう。

num_cats_tr = len(os.listdir(train_cats_dir))
num_dogs_tr = len(os.listdir(train_dogs_dir))

num_cats_val = len(os.listdir(validation_cats_dir))
num_dogs_val = len(os.listdir(validation_dogs_dir))

total_train = num_cats_tr + num_dogs_tr
total_val = num_cats_val + num_dogs_val

print('total training cat images:', num_cats_tr)
print('total training dog images:', num_dogs_tr)

print('total validation cat images:', num_cats_val)
print('total validation dog images:', num_dogs_val)
print("--")
print("Total training images:", total_train)
print("Total validation images:", total_val)
total training cat images: 1000
total training dog images: 1000
total validation cat images: 500
total validation dog images: 500
--
Total training images: 2000
Total validation images: 1000

一旦ここで、この後に使用するネットワークの学習用の定数を設定しておきます。

BATCH_SIZE = 128
epochs = 10
IMG_SIZE = (160, 160)
IMG_SHAPE = IMG_SIZE + (3,)

データの準備

画像データを準備します。

先程取得した画像データを元にimage_dataset_from_directoryという関数を使用してデータセットを作成します。

また、テスト用のデータセットを用意しましょう。
今回は検証用のデータセットの一部をテストデータセットとして利用します。
学習後にデータの性能を確認するためテスト用のデータセットを使用して推測を行います。

# 教師データ
train_dataset = image_dataset_from_directory(train_dir,
                                             shuffle=True,
                                             batch_size=BATCH_SIZE,
                                             image_size=IMG_SIZE)
# 検証データ
validation_dataset = image_dataset_from_directory(validation_dir,
                                                  shuffle=True,
                                                  batch_size=BATCH_SIZE,
                                                  image_size=IMG_SIZE)

val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)

# テストデータセット
validation_dataset = validation_dataset.skip(val_batches // 5)
Found 2000 files belonging to 2 classes.
Found 1000 files belonging to 2 classes.

試しに画像を表示してみます。

class_names = train_dataset.class_names

plt.figure(figsize=(10, 10))
for images, labels in train_dataset.take(1):
  for i in range(9):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[labels[i]])
    plt.axis("off")

output_11_0.png

モデルの作成

画像を分類するためのモデルを作成します。
今回は3層の畳み込み層を持つシンプルなモデルを作成します。

def sample_model():
    # モデルの構成を定義します。
    inputs = Input(shape=IMG_SHAPE)
    x = tf.keras.layers.experimental.preprocessing.Rescaling(1./255, offset= -1)(inputs) # データを0-1の範囲に正規化
    x = Conv2D(16, 3, padding='same', activation='relu')(x) # 1つ目の畳み込み層
    x = MaxPooling2D()(x)
    x = Conv2D(32, 3, padding='same', activation='relu')(x) # 2つ目の畳み込み層
    x = MaxPooling2D()(x)
    x = Conv2D(64, 3, padding='same', activation='relu')(x) # 3つ目の畳み込み層
    x = MaxPooling2D()(x)
    x = Flatten()(x) 
    x = Dense(512, activation='relu')(x)
    outputs = Dense(1, activation='sigmoid')(x)
    model = tf.keras.Model(inputs=inputs, outputs=outputs)

    # モデルのコンパイル
    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    return model

# モデルを作成
model = sample_model()

# モデルの構造を確認します
model.summary()
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 160, 160, 3)]     0         
_________________________________________________________________
rescaling (Rescaling)        (None, 160, 160, 3)       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 160, 160, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 80, 80, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 80, 80, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 40, 40, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 40, 40, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 20, 20, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 25600)             0         
_________________________________________________________________
dense (Dense)                (None, 512)               13107712  
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 13,131,809
Trainable params: 13,131,809
Non-trainable params: 0
_________________________________________________________________

モデルの学習

それでは作成したモデルを学習してみましょう。

history = model.fit(train_dataset,
                    epochs=epochs,
                    validation_data=validation_dataset)
Epoch 1/10
16/16 [==============================] - 38s 335ms/step - loss: 1.0439 - accuracy: 0.5030 - val_loss: 0.6913 - val_accuracy: 0.5023
Epoch 2/10
16/16 [==============================] - 5s 265ms/step - loss: 0.6854 - accuracy: 0.5725 - val_loss: 0.6683 - val_accuracy: 0.6170
Epoch 3/10
16/16 [==============================] - 5s 257ms/step - loss: 0.6680 - accuracy: 0.5955 - val_loss: 0.6479 - val_accuracy: 0.6330
Epoch 4/10
16/16 [==============================] - 5s 256ms/step - loss: 0.6248 - accuracy: 0.6640 - val_loss: 0.6236 - val_accuracy: 0.6720
Epoch 5/10
16/16 [==============================] - 5s 258ms/step - loss: 0.5696 - accuracy: 0.7125 - val_loss: 0.5892 - val_accuracy: 0.7099
Epoch 6/10
16/16 [==============================] - 5s 259ms/step - loss: 0.5009 - accuracy: 0.7555 - val_loss: 0.6136 - val_accuracy: 0.6537
Epoch 7/10
16/16 [==============================] - 5s 261ms/step - loss: 0.4441 - accuracy: 0.8045 - val_loss: 0.6165 - val_accuracy: 0.7053
Epoch 8/10
16/16 [==============================] - 5s 261ms/step - loss: 0.3926 - accuracy: 0.8205 - val_loss: 0.6289 - val_accuracy: 0.6984
Epoch 9/10
16/16 [==============================] - 5s 259ms/step - loss: 0.3158 - accuracy: 0.8625 - val_loss: 0.6914 - val_accuracy: 0.6732
Epoch 10/10
16/16 [==============================] - 5s 260ms/step - loss: 0.2574 - accuracy: 0.8920 - val_loss: 0.7377 - val_accuracy: 0.6881

学習結果を可視化してみましょう。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

output_17_0.png

モデルの転移学習

先程のように何もない状態からニューラルネットワークのモデルを構築することは実際の業務ではあまりなく、基本的には公開されている既に構築済み/事前学習済みのモデルをベースとすることが多いです。

今回はMobileNetV2というモデルをImageNetというデータセットで事前学習済みのモデルをベースとし、転移学習しましょう。

# ベースモデルを用意
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')
base_model.trainable = False
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_160_no_top.h5
9412608/9406464 [==============================] - 0s 0us/step
9420800/9406464 [==============================] - 0s 0us/step

画像データの準備

先程のモデルでは画像のデータに対して1~0の範囲で正規化を行いました。

使用するモデルによって、データの範囲が異なります。

使用するライブラリによっては前処理用のメソッドが用意されています。今回はその前処理用のメソッドを利用します。

# モデル用の画像前処理のレイヤーを用意。
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input

モデルの構築

def create_model():
    # モデルの構成を定義します。
    inputs = tf.keras.Input(shape=(160, 160, 3))
    x = preprocess_input(inputs)
    x = base_model(x, training=False)
    x = tf.keras.layers.GlobalAveragePooling2D()(x)
    x = tf.keras.layers.Dropout(0.2)(x)
    outputs = Dense(1, activation='sigmoid')(x)
    model = tf.keras.Model(inputs, outputs)

    # モデルのコンパイル
    base_learning_rate = 0.0001
    model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
                loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
                metrics=['accuracy'])
    return model

model = create_model()
model.summary()
Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_3 (InputLayer)         [(None, 160, 160, 3)]     0         
_________________________________________________________________
tf.math.truediv (TFOpLambda) (None, 160, 160, 3)       0         
_________________________________________________________________
tf.math.subtract (TFOpLambda (None, 160, 160, 3)       0         
_________________________________________________________________
mobilenetv2_1.00_160 (Functi (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dropout (Dropout)            (None, 1280)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,281
Non-trainable params: 2,257,984
_________________________________________________________________


/usr/local/lib/python3.7/dist-packages/keras/optimizer_v2/optimizer_v2.py:356: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.
  "The `lr` argument is deprecated, use `learning_rate` instead.")

ではこのモデルを転移学習してみましょう。

history = model.fit(train_dataset,
                    epochs=epochs,
                    validation_data=validation_dataset)
Epoch 1/10


/usr/local/lib/python3.7/dist-packages/keras/backend.py:4994: UserWarning: "`binary_crossentropy` received `from_logits=True`, but the `output` argument was produced by a sigmoid or softmax activation and thus does not represent logits. Was this intended?"
  '"`binary_crossentropy` received `from_logits=True`, but the `output`'


16/16 [==============================] - 14s 528ms/step - loss: 0.7151 - accuracy: 0.5650 - val_loss: 0.6436 - val_accuracy: 0.6330
Epoch 2/10
16/16 [==============================] - 7s 369ms/step - loss: 0.6210 - accuracy: 0.6730 - val_loss: 0.5553 - val_accuracy: 0.7248
Epoch 3/10
16/16 [==============================] - 7s 372ms/step - loss: 0.5470 - accuracy: 0.7365 - val_loss: 0.4899 - val_accuracy: 0.7844
Epoch 4/10
16/16 [==============================] - 7s 369ms/step - loss: 0.4960 - accuracy: 0.7705 - val_loss: 0.4400 - val_accuracy: 0.8314
Epoch 5/10
16/16 [==============================] - 7s 374ms/step - loss: 0.4285 - accuracy: 0.8305 - val_loss: 0.3819 - val_accuracy: 0.8693
Epoch 6/10
16/16 [==============================] - 7s 373ms/step - loss: 0.3840 - accuracy: 0.8590 - val_loss: 0.3464 - val_accuracy: 0.8956
Epoch 7/10
16/16 [==============================] - 7s 376ms/step - loss: 0.3640 - accuracy: 0.8735 - val_loss: 0.3203 - val_accuracy: 0.9071
Epoch 8/10
16/16 [==============================] - 7s 381ms/step - loss: 0.3256 - accuracy: 0.8925 - val_loss: 0.2858 - val_accuracy: 0.9278
Epoch 9/10
16/16 [==============================] - 7s 373ms/step - loss: 0.3026 - accuracy: 0.9060 - val_loss: 0.2601 - val_accuracy: 0.9427
Epoch 10/10
16/16 [==============================] - 7s 374ms/step - loss: 0.2787 - accuracy: 0.9130 - val_loss: 0.2469 - val_accuracy: 0.9369

学習結果を表示してみます。

先程のモデルを1から構築した場合よりも精度が向上している事がわかります。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

output_27_0.png

モデルの評価

テストデータセットを使用してモデルの性能を検証しましょう。

loss, accuracy = model.evaluate(test_dataset)
print('Test accuracy :', accuracy)
1/1 [==============================] - 1s 1s/step - loss: 0.2511 - accuracy: 0.9219
Test accuracy : 0.921875
image_batch, label_batch = test_dataset.as_numpy_iterator().next()
predictions = model.predict_on_batch(image_batch).flatten()

predictions = tf.nn.sigmoid(predictions)
predictions = tf.where(predictions < 0.5, 0, 1)

print('予測結果:\n', predictions.numpy())
print('正解のラベル:\n', label_batch)

# 試しに9枚表示
plt.figure(figsize=(10, 10))
for i in range(9):
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(image_batch[i].astype("uint8"))
  plt.title(class_names[predictions[i]])
  plt.axis("off")
予測結果:
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]
正解のラベル:
 [0 0 1 0 0 1 1 1 0 1 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 0 0 0 1 0 1 1 0 0 0
 0 0 1 0 0 1 1 1 0 1 1 1 0 0 1 0 1 0 1 0 1 0 1 0 0 0 1 0 1 1 0 0 1 1 1 0 1
 1 0 1 1 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 0 0 1 1
 0 1 1 1 0 0 0 1 1 0 1 1 0 0 0 1 0]

output_30_1.png

参考資料

TensorFlow チュートリアル
- 画像分類
- 転移学習とファインチューニング

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0