福岡人データサイエンティストの部屋

データサイエンスを極めるため、日々の学習を綴っています。

CNNの転移学習【図解速習DEEP LEARNING】#005

こんにちは!こーたろーです。
今日も図解速習DEEP LEARNINGの続きをやっていきます!

今回の実践では、事前学習済みのモデルと、その転移学習(ファインチューニング)を使って、犬猫画像の分類を行いました。
長くなったため、ブログのパートを二つに分けます。

学習済みモデルをカスタマイズして使う方法として、

  1. 学習済みモデルを特徴抽出に使う
  2. 学習済みモデルのファインチューニング


の二通りをやってみます。
今回は「1.学習済みモデルを特徴抽出に使う」方法を試してみました。



1.必要なライブラリーのインストール・インポート

import os

import tensorflow as tf
from tensorflow import keras

import numpy as np
import matplotlib.pyplot as plt

2.データセットの準備

try:
  import tensorflow_datasets as tfds
except:
  !python -m pip install tensorflow-datasets
  import tensorflow_datasets as tfds
tfds.disable_progress_bar()

Google Colabratoryには、Tensorflow_datasetsが予めインストールさせていないことがあるため、Try-Except文で、読み込ませる。
「!」を構文の頭に入れることによって、ターミナルと同様にpipコマンドが扱えます。

(raw_train, raw_validation, raw_test), metadata = tfds.load(
    'cats_vs_dogs',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

splitを使用し、データを学習データ80%、検証データ10%、テストデータ10%として、データセットを分割します。


データセットの中身を確認してみましょう。

get_label_name = metadata.features['label'].int2str

for image, label in raw_train.take(2):
  plt.figure()
  plt.imshow(image)
  plt.title(get_label_name(label))

out
f:id:dsf-kotaro:20210125165150p:plain


画像サイズも160×160とし、ピクセルの値(0-255)を-1~1の値に変更していきます。

IMG_SIZE = 160 

def format_example(image, label):
  image = tf.cast(image, tf.float32)
  image = (image/127.5) - 1
  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image, label

上記のような関数を全てのピクセルに適用させるため、map( )を使用します。
「train」「validation」「test」のデータセット各々を処理していきます。

train = raw_train.map(format_example)
validation = raw_validation.map(format_example)
test = raw_test.map(format_example)

データセットをシャッフルして、バッチにまとめていきます。

BATCH_SIZE = 32
SHUFFLE_BUFFER_SIZE = 1000
train_batches = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE)
validation_batches = validation.batch(BATCH_SIZE)
test_batches = test.batch(BATCH_SIZE)


3.モデルの作成
Googleが開発したMobileNetV2からモデルを作成します。
ImageNetで学習済みの重み(weight=’imagenet’)をMobileNetV2でモデルとして呼び出し、特徴抽出を行います。
この時、Include_top=False引数をしていすることで、一番上の分類レイヤ(最終層)を除いたモデルが読み込まれます。
特徴抽出においては、この最終層は分類においてはあまり役に立たないため、除いて読み込むことをお勧めします。

IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)

base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False, 
                                               weights='imagenet')

この特徴抽出は、160×160×3の画像から、5×5×1280の特徴マップを取り出します。

特徴を抽出したレイヤーの重みが更新されないように設定する。

base_model.trainable = False

特徴ブロックから予測を生成するため、tf.keras.layers.GlobalAveragePooling2D層をつかって、5×5ごとに平均化とプーリングを行い、特徴を1画像あたり1280要素をもつ1次元ベクトルに変換していきます。

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)

f:id:dsf-kotaro:20210125165526p:plain

予測を出力するための層を作成します。正=1、負=0として、出力するように設定します。

prediction_layer = keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)

out
f:id:dsf-kotaro:20210125165654p:plain

kerasのSequentialで各層を連結していきます。

model = tf.keras.Sequential([
  base_model,
  global_average_layer,
  prediction_layer
])


4.モデルのコンパイル

2クラス分類のため、バイナリクロスエントロピー損失を使いました。

base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate), 
              loss='binary_crossentropy', 
              metrics=['accuracy'])


できたモデルを見てみましょう。

model.summary()

out
f:id:dsf-kotaro:20210125165740p:plain

len(model.trainable_variables)

最終のDense送の1281のパラメータは、固定されていないため、学習の余地があります。
そこで、モデルを学習させていきます。


5.モデルの学習

initial_epochs = 10
validation_steps = 20
loss0,accuracy0 = model.evaluate(validation_batches, steps = validation_steps)

out
f:id:dsf-kotaro:20210125165814p:plain

損失・正解率を確認

print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))

out
f:id:dsf-kotaro:20210125165839p:plain

history = model.fit(train_batches,
                    epochs=initial_epochs,
                    validation_data=validation_batches)

out
f:id:dsf-kotaro:20210125165922p:plain

6.学習結果の表示

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

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

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

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

out
f:id:dsf-kotaro:20210125170024p:plain



とりあえず今日はここまでできました。
次は、学習済みモデルのファインチューニング


今日のまとめ。

学習が完了している層をもってきて、そこにCNNらしくプーリング層、それから予測用の層を追加。
追加した層の重みだけを学習で更新させて、学習済みの層と併せてモデルを構築。