![]() | ![]() | ![]() | ![]() |
このチュートリアルでは、事前にトレーニングされたネットワークからの転移学習を使用して、猫と犬の画像を分類する方法を学習します。
事前トレーニング済みモデルは、大規模なデータセット、通常は大規模な画像分類タスクで以前にトレーニングされた保存済みネットワークです。事前トレーニング済みのモデルをそのまま使用するか、転移学習を使用してこのモデルを特定のタスクに合わせてカスタマイズします。
画像分類の伝達学習の背後にある直感は、モデルが十分に大きく一般的なデータセットでトレーニングされている場合、このモデルは視覚世界の一般的なモデルとして効果的に機能するということです。その後、大きなデータセットで大きなモデルをトレーニングすることにより、最初から始めることなく、これらの学習された特徴マップを利用できます。
このノートブックでは、事前にトレーニングされたモデルをカスタマイズする2つの方法を試します。
特徴抽出:以前のネットワークで学習した表現を使用して、新しいサンプルから意味のある特徴を抽出します。事前にトレーニングされたモデルの上に、最初からトレーニングされる新しい分類子を追加するだけで、データセットに対して以前に学習したフィーチャマップを再利用できます。
モデル全体を(再)トレーニングする必要はありません。基本畳み込みネットワークには、画像の分類に一般的に役立つ機能がすでに含まれています。ただし、事前トレーニング済みモデルの最後の分類部分は、元の分類タスクに固有であり、その後、モデルがトレーニングされたクラスのセットに固有です。
微調整:フリーズしたモデルベースの最上位レイヤーのいくつかをフリーズ解除し、新しく追加された分類子レイヤーとベースモデルの最後のレイヤーの両方を共同でトレーニングします。これにより、基本モデルの高次の特徴表現を「微調整」して、特定のタスクとの関連性を高めることができます。
一般的な機械学習ワークフローに従います。
- データを調べて理解する
- この場合はKerasImageDataGeneratorを使用して入力パイプラインを構築します
- モデルを作成します
- 事前にトレーニングされたベースモデル(および事前にトレーニングされたウェイト)をロードします
- 分類レイヤーを上にスタックします
- モデルをトレーニングする
- モデルを評価する
import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf
from tensorflow.keras.preprocessing import image_dataset_from_directory
データの前処理
データのダウンロード
このチュートリアルでは、猫と犬の数千枚の画像を含むデータセットを使用します。画像を含むzipファイルをダウンロードして抽出し、 tf.keras.preprocessing.image_dataset_from_directory
ユーティリティを使用してトレーニングと検証用のtf.data.Dataset
を作成します。このチュートリアルでは、画像の読み込みについて詳しく知ることができます。
_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')
BATCH_SIZE = 32
IMG_SIZE = (160, 160)
train_dataset = image_dataset_from_directory(train_dir,
shuffle=True,
batch_size=BATCH_SIZE,
image_size=IMG_SIZE)
Downloading data from https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip 68608000/68606236 [==============================] - 1s 0us/step Found 2000 files belonging to 2 classes.
validation_dataset = image_dataset_from_directory(validation_dir,
shuffle=True,
batch_size=BATCH_SIZE,
image_size=IMG_SIZE)
Found 1000 files belonging to 2 classes.
トレーニングセットの最初の9つの画像とラベルを表示します。
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")
元のデータセットにはテストセットが含まれていないため、テストセットを作成します。これを行うには、 tf.data.experimental.cardinality
使用して、検証セットで使用可能なデータのバッチ数を決定し、それらの20%をテストセットに移動します。
val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)
print('Number of validation batches: %d' % tf.data.experimental.cardinality(validation_dataset))
print('Number of test batches: %d' % tf.data.experimental.cardinality(test_dataset))
Number of validation batches: 26 Number of test batches: 6
パフォーマンスのためにデータセットを構成する
バッファリングされたプリフェッチを使用して、I / Oがブロックされることなくディスクからイメージをロードします。この方法の詳細については、データパフォーマンスガイドを参照してください。
AUTOTUNE = tf.data.AUTOTUNE
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)
validation_dataset = validation_dataset.prefetch(buffer_size=AUTOTUNE)
test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)
データ拡張を使用する
大きな画像データセットがない場合は、回転や水平反転など、ランダムでありながら現実的な変換をトレーニング画像に適用して、サンプルの多様性を人為的に導入することをお勧めします。これは、モデルをトレーニングデータのさまざまな側面に公開し、過剰適合を減らすのに役立ちます。このチュートリアルでは、データ拡張について詳しく学ぶことができます。
data_augmentation = tf.keras.Sequential([
tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'),
tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
])
これらのレイヤーを同じ画像に繰り返し適用して、結果を見てみましょう。
for image, _ in train_dataset.take(1):
plt.figure(figsize=(10, 10))
first_image = image[0]
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
augmented_image = data_augmentation(tf.expand_dims(first_image, 0))
plt.imshow(augmented_image[0] / 255)
plt.axis('off')
ピクセル値を再スケーリングします
tf.keras.applications.MobileNetV2
に、ベースモデルとして使用するためにtf.keras.applications.MobileNetV2
をダウンロードします。このモデルは[-1,1]
のピクセル値を想定していますが、この時点では、画像のピクセル値は[0-255]
ます。それらを再スケーリングするには、モデルに含まれている前処理方法を使用します。
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
rescale = tf.keras.layers.experimental.preprocessing.Rescaling(1./127.5, offset= -1)
事前にトレーニングされたconvnetからベースモデルを作成します
Googleで開発されたMobileNetV2モデルから基本モデルを作成します。これは、140万の画像と1000のクラスで構成される大規模なデータセットであるImageNetデータセットで事前にトレーニングされています。 ImageNetは、 jackfruit
やsyringe
などのさまざまなカテゴリの研究トレーニングデータセットです。この知識の基盤は、特定のデータセットから猫と犬を分類するのに役立ちます。
まず、特徴抽出に使用するMobileNetV2のレイヤーを選択する必要があります。最後の分類レイヤー(機械学習モデルのほとんどの図は下から上に移動するため、「上」)はあまり役に立ちません。代わりに、一般的な方法に従って、フラット化操作の前の最後のレイヤーに依存します。この層は「ボトルネック層」と呼ばれます。ボトルネックレイヤーの機能は、最終/トップレイヤーと比較してより一般性を保持します。
まず、ImageNetでトレーニングされたウェイトがプリロードされたMobileNetV2モデルをインスタンス化します。 include_top = False引数を指定することにより、上部に分類レイヤーを含まないネットワークをロードします。これは、特徴抽出に最適です。
# Create the base model from the pre-trained model MobileNet V2
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
include_top=False,
weights='imagenet')
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
この特徴抽出器は、各160x160x3
画像を5x5x1280
の特徴ブロックに変換します。画像のバッチの例に対してどのように機能するかを見てみましょう。
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
print(feature_batch.shape)
(32, 5, 5, 1280)
特徴抽出
このステップでは、前のステップで作成した畳み込みベースをフリーズし、特徴抽出器として使用します。さらに、その上に分類器を追加し、最上位の分類器をトレーニングします。
畳み込みベースをフリーズします
モデルをコンパイルしてトレーニングする前に、畳み込みベースをフリーズすることが重要です。フリーズ(layer.trainable = Falseの設定による)は、トレーニング中に特定のレイヤーの重みが更新されないようにします。 MobileNet V2には多くのレイヤーがあるため、モデル全体のtrainable
フラグをFalseに設定すると、すべてのレイヤーがフリーズします。
base_model.trainable = False
BatchNormalizationレイヤーに関する重要な注意事項
多くのモデルには、 tf.keras.layers.BatchNormalization
レイヤーが含まれてtf.keras.layers.BatchNormalization
ます。このレイヤーは特殊なケースであり、このチュートリアルの後半で示すように、微調整のコンテキストで予防策を講じる必要があります。
layer.trainable = False
を設定すると、 BatchNormalization
レイヤーは推論モードで実行され、平均と分散の統計は更新されません。
微調整を行うためにBatchNormalizationレイヤーを含むモデルのフリーズを解除するときは、ベースモデルを呼び出すときにtraining = False
を渡して、BatchNormalizationレイヤーを推論モードに保つ必要があります。そうしないと、訓練不可能な重みに適用された更新により、モデルが学習した内容が破壊されます。
# Let's take a look at the base model architecture
base_model.summary()
Model: "mobilenetv2_1.00_160" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_1 (InputLayer) [(None, 160, 160, 3) 0 __________________________________________________________________________________________________ Conv1 (Conv2D) (None, 80, 80, 32) 864 input_1[0][0] __________________________________________________________________________________________________ bn_Conv1 (BatchNormalization) (None, 80, 80, 32) 128 Conv1[0][0] __________________________________________________________________________________________________ Conv1_relu (ReLU) (None, 80, 80, 32) 0 bn_Conv1[0][0] __________________________________________________________________________________________________ expanded_conv_depthwise (Depthw (None, 80, 80, 32) 288 Conv1_relu[0][0] __________________________________________________________________________________________________ expanded_conv_depthwise_BN (Bat (None, 80, 80, 32) 128 expanded_conv_depthwise[0][0] __________________________________________________________________________________________________ expanded_conv_depthwise_relu (R (None, 80, 80, 32) 0 expanded_conv_depthwise_BN[0][0] __________________________________________________________________________________________________ expanded_conv_project (Conv2D) (None, 80, 80, 16) 512 expanded_conv_depthwise_relu[0][0 __________________________________________________________________________________________________ expanded_conv_project_BN (Batch (None, 80, 80, 16) 64 expanded_conv_project[0][0] __________________________________________________________________________________________________ block_1_expand (Conv2D) (None, 80, 80, 96) 1536 expanded_conv_project_BN[0][0] __________________________________________________________________________________________________ block_1_expand_BN (BatchNormali (None, 80, 80, 96) 384 block_1_expand[0][0] __________________________________________________________________________________________________ block_1_expand_relu (ReLU) (None, 80, 80, 96) 0 block_1_expand_BN[0][0] __________________________________________________________________________________________________ block_1_pad (ZeroPadding2D) (None, 81, 81, 96) 0 block_1_expand_relu[0][0] __________________________________________________________________________________________________ block_1_depthwise (DepthwiseCon (None, 40, 40, 96) 864 block_1_pad[0][0] __________________________________________________________________________________________________ block_1_depthwise_BN (BatchNorm (None, 40, 40, 96) 384 block_1_depthwise[0][0] __________________________________________________________________________________________________ block_1_depthwise_relu (ReLU) (None, 40, 40, 96) 0 block_1_depthwise_BN[0][0] __________________________________________________________________________________________________ block_1_project (Conv2D) (None, 40, 40, 24) 2304 block_1_depthwise_relu[0][0] __________________________________________________________________________________________________ block_1_project_BN (BatchNormal (None, 40, 40, 24) 96 block_1_project[0][0] __________________________________________________________________________________________________ block_2_expand (Conv2D) (None, 40, 40, 144) 3456 block_1_project_BN[0][0] __________________________________________________________________________________________________ block_2_expand_BN (BatchNormali (None, 40, 40, 144) 576 block_2_expand[0][0] __________________________________________________________________________________________________ block_2_expand_relu (ReLU) (None, 40, 40, 144) 0 block_2_expand_BN[0][0] __________________________________________________________________________________________________ block_2_depthwise (DepthwiseCon (None, 40, 40, 144) 1296 block_2_expand_relu[0][0] __________________________________________________________________________________________________ block_2_depthwise_BN (BatchNorm (None, 40, 40, 144) 576 block_2_depthwise[0][0] __________________________________________________________________________________________________ block_2_depthwise_relu (ReLU) (None, 40, 40, 144) 0 block_2_depthwise_BN[0][0] __________________________________________________________________________________________________ block_2_project (Conv2D) (None, 40, 40, 24) 3456 block_2_depthwise_relu[0][0] __________________________________________________________________________________________________ block_2_project_BN (BatchNormal (None, 40, 40, 24) 96 block_2_project[0][0] __________________________________________________________________________________________________ block_2_add (Add) (None, 40, 40, 24) 0 block_1_project_BN[0][0] block_2_project_BN[0][0] __________________________________________________________________________________________________ block_3_expand (Conv2D) (None, 40, 40, 144) 3456 block_2_add[0][0] __________________________________________________________________________________________________ block_3_expand_BN (BatchNormali (None, 40, 40, 144) 576 block_3_expand[0][0] __________________________________________________________________________________________________ block_3_expand_relu (ReLU) (None, 40, 40, 144) 0 block_3_expand_BN[0][0] __________________________________________________________________________________________________ block_3_pad (ZeroPadding2D) (None, 41, 41, 144) 0 block_3_expand_relu[0][0] __________________________________________________________________________________________________ block_3_depthwise (DepthwiseCon (None, 20, 20, 144) 1296 block_3_pad[0][0] __________________________________________________________________________________________________ block_3_depthwise_BN (BatchNorm (None, 20, 20, 144) 576 block_3_depthwise[0][0] __________________________________________________________________________________________________ block_3_depthwise_relu (ReLU) (None, 20, 20, 144) 0 block_3_depthwise_BN[0][0] __________________________________________________________________________________________________ block_3_project (Conv2D) (None, 20, 20, 32) 4608 block_3_depthwise_relu[0][0] __________________________________________________________________________________________________ block_3_project_BN (BatchNormal (None, 20, 20, 32) 128 block_3_project[0][0] __________________________________________________________________________________________________ block_4_expand (Conv2D) (None, 20, 20, 192) 6144 block_3_project_BN[0][0] __________________________________________________________________________________________________ block_4_expand_BN (BatchNormali (None, 20, 20, 192) 768 block_4_expand[0][0] __________________________________________________________________________________________________ block_4_expand_relu (ReLU) (None, 20, 20, 192) 0 block_4_expand_BN[0][0] __________________________________________________________________________________________________ block_4_depthwise (DepthwiseCon (None, 20, 20, 192) 1728 block_4_expand_relu[0][0] __________________________________________________________________________________________________ block_4_depthwise_BN (BatchNorm (None, 20, 20, 192) 768 block_4_depthwise[0][0] __________________________________________________________________________________________________ block_4_depthwise_relu (ReLU) (None, 20, 20, 192) 0 block_4_depthwise_BN[0][0] __________________________________________________________________________________________________ block_4_project (Conv2D) (None, 20, 20, 32) 6144 block_4_depthwise_relu[0][0] __________________________________________________________________________________________________ block_4_project_BN (BatchNormal (None, 20, 20, 32) 128 block_4_project[0][0] __________________________________________________________________________________________________ block_4_add (Add) (None, 20, 20, 32) 0 block_3_project_BN[0][0] block_4_project_BN[0][0] __________________________________________________________________________________________________ block_5_expand (Conv2D) (None, 20, 20, 192) 6144 block_4_add[0][0] __________________________________________________________________________________________________ block_5_expand_BN (BatchNormali (None, 20, 20, 192) 768 block_5_expand[0][0] __________________________________________________________________________________________________ block_5_expand_relu (ReLU) (None, 20, 20, 192) 0 block_5_expand_BN[0][0] __________________________________________________________________________________________________ block_5_depthwise (DepthwiseCon (None, 20, 20, 192) 1728 block_5_expand_relu[0][0] __________________________________________________________________________________________________ block_5_depthwise_BN (BatchNorm (None, 20, 20, 192) 768 block_5_depthwise[0][0] __________________________________________________________________________________________________ block_5_depthwise_relu (ReLU) (None, 20, 20, 192) 0 block_5_depthwise_BN[0][0] __________________________________________________________________________________________________ block_5_project (Conv2D) (None, 20, 20, 32) 6144 block_5_depthwise_relu[0][0] __________________________________________________________________________________________________ block_5_project_BN (BatchNormal (None, 20, 20, 32) 128 block_5_project[0][0] __________________________________________________________________________________________________ block_5_add (Add) (None, 20, 20, 32) 0 block_4_add[0][0] block_5_project_BN[0][0] __________________________________________________________________________________________________ block_6_expand (Conv2D) (None, 20, 20, 192) 6144 block_5_add[0][0] __________________________________________________________________________________________________ block_6_expand_BN (BatchNormali (None, 20, 20, 192) 768 block_6_expand[0][0] __________________________________________________________________________________________________ block_6_expand_relu (ReLU) (None, 20, 20, 192) 0 block_6_expand_BN[0][0] __________________________________________________________________________________________________ block_6_pad (ZeroPadding2D) (None, 21, 21, 192) 0 block_6_expand_relu[0][0] __________________________________________________________________________________________________ block_6_depthwise (DepthwiseCon (None, 10, 10, 192) 1728 block_6_pad[0][0] __________________________________________________________________________________________________ block_6_depthwise_BN (BatchNorm (None, 10, 10, 192) 768 block_6_depthwise[0][0] __________________________________________________________________________________________________ block_6_depthwise_relu (ReLU) (None, 10, 10, 192) 0 block_6_depthwise_BN[0][0] __________________________________________________________________________________________________ block_6_project (Conv2D) (None, 10, 10, 64) 12288 block_6_depthwise_relu[0][0] __________________________________________________________________________________________________ block_6_project_BN (BatchNormal (None, 10, 10, 64) 256 block_6_project[0][0] __________________________________________________________________________________________________ block_7_expand (Conv2D) (None, 10, 10, 384) 24576 block_6_project_BN[0][0] __________________________________________________________________________________________________ block_7_expand_BN (BatchNormali (None, 10, 10, 384) 1536 block_7_expand[0][0] __________________________________________________________________________________________________ block_7_expand_relu (ReLU) (None, 10, 10, 384) 0 block_7_expand_BN[0][0] __________________________________________________________________________________________________ block_7_depthwise (DepthwiseCon (None, 10, 10, 384) 3456 block_7_expand_relu[0][0] __________________________________________________________________________________________________ block_7_depthwise_BN (BatchNorm (None, 10, 10, 384) 1536 block_7_depthwise[0][0] __________________________________________________________________________________________________ block_7_depthwise_relu (ReLU) (None, 10, 10, 384) 0 block_7_depthwise_BN[0][0] __________________________________________________________________________________________________ block_7_project (Conv2D) (None, 10, 10, 64) 24576 block_7_depthwise_relu[0][0] __________________________________________________________________________________________________ block_7_project_BN (BatchNormal (None, 10, 10, 64) 256 block_7_project[0][0] __________________________________________________________________________________________________ block_7_add (Add) (None, 10, 10, 64) 0 block_6_project_BN[0][0] block_7_project_BN[0][0] __________________________________________________________________________________________________ block_8_expand (Conv2D) (None, 10, 10, 384) 24576 block_7_add[0][0] __________________________________________________________________________________________________ block_8_expand_BN (BatchNormali (None, 10, 10, 384) 1536 block_8_expand[0][0] __________________________________________________________________________________________________ block_8_expand_relu (ReLU) (None, 10, 10, 384) 0 block_8_expand_BN[0][0] __________________________________________________________________________________________________ block_8_depthwise (DepthwiseCon (None, 10, 10, 384) 3456 block_8_expand_relu[0][0] __________________________________________________________________________________________________ block_8_depthwise_BN (BatchNorm (None, 10, 10, 384) 1536 block_8_depthwise[0][0] __________________________________________________________________________________________________ block_8_depthwise_relu (ReLU) (None, 10, 10, 384) 0 block_8_depthwise_BN[0][0] __________________________________________________________________________________________________ block_8_project (Conv2D) (None, 10, 10, 64) 24576 block_8_depthwise_relu[0][0] __________________________________________________________________________________________________ block_8_project_BN (BatchNormal (None, 10, 10, 64) 256 block_8_project[0][0] __________________________________________________________________________________________________ block_8_add (Add) (None, 10, 10, 64) 0 block_7_add[0][0] block_8_project_BN[0][0] __________________________________________________________________________________________________ block_9_expand (Conv2D) (None, 10, 10, 384) 24576 block_8_add[0][0] __________________________________________________________________________________________________ block_9_expand_BN (BatchNormali (None, 10, 10, 384) 1536 block_9_expand[0][0] __________________________________________________________________________________________________ block_9_expand_relu (ReLU) (None, 10, 10, 384) 0 block_9_expand_BN[0][0] __________________________________________________________________________________________________ block_9_depthwise (DepthwiseCon (None, 10, 10, 384) 3456 block_9_expand_relu[0][0] __________________________________________________________________________________________________ block_9_depthwise_BN (BatchNorm (None, 10, 10, 384) 1536 block_9_depthwise[0][0] __________________________________________________________________________________________________ block_9_depthwise_relu (ReLU) (None, 10, 10, 384) 0 block_9_depthwise_BN[0][0] __________________________________________________________________________________________________ block_9_project (Conv2D) (None, 10, 10, 64) 24576 block_9_depthwise_relu[0][0] __________________________________________________________________________________________________ block_9_project_BN (BatchNormal (None, 10, 10, 64) 256 block_9_project[0][0] __________________________________________________________________________________________________ block_9_add (Add) (None, 10, 10, 64) 0 block_8_add[0][0] block_9_project_BN[0][0] __________________________________________________________________________________________________ block_10_expand (Conv2D) (None, 10, 10, 384) 24576 block_9_add[0][0] __________________________________________________________________________________________________ block_10_expand_BN (BatchNormal (None, 10, 10, 384) 1536 block_10_expand[0][0] __________________________________________________________________________________________________ block_10_expand_relu (ReLU) (None, 10, 10, 384) 0 block_10_expand_BN[0][0] __________________________________________________________________________________________________ block_10_depthwise (DepthwiseCo (None, 10, 10, 384) 3456 block_10_expand_relu[0][0] __________________________________________________________________________________________________ block_10_depthwise_BN (BatchNor (None, 10, 10, 384) 1536 block_10_depthwise[0][0] __________________________________________________________________________________________________ block_10_depthwise_relu (ReLU) (None, 10, 10, 384) 0 block_10_depthwise_BN[0][0] __________________________________________________________________________________________________ block_10_project (Conv2D) (None, 10, 10, 96) 36864 block_10_depthwise_relu[0][0] __________________________________________________________________________________________________ block_10_project_BN (BatchNorma (None, 10, 10, 96) 384 block_10_project[0][0] __________________________________________________________________________________________________ block_11_expand (Conv2D) (None, 10, 10, 576) 55296 block_10_project_BN[0][0] __________________________________________________________________________________________________ block_11_expand_BN (BatchNormal (None, 10, 10, 576) 2304 block_11_expand[0][0] __________________________________________________________________________________________________ block_11_expand_relu (ReLU) (None, 10, 10, 576) 0 block_11_expand_BN[0][0] __________________________________________________________________________________________________ block_11_depthwise (DepthwiseCo (None, 10, 10, 576) 5184 block_11_expand_relu[0][0] __________________________________________________________________________________________________ block_11_depthwise_BN (BatchNor (None, 10, 10, 576) 2304 block_11_depthwise[0][0] __________________________________________________________________________________________________ block_11_depthwise_relu (ReLU) (None, 10, 10, 576) 0 block_11_depthwise_BN[0][0] __________________________________________________________________________________________________ block_11_project (Conv2D) (None, 10, 10, 96) 55296 block_11_depthwise_relu[0][0] __________________________________________________________________________________________________ block_11_project_BN (BatchNorma (None, 10, 10, 96) 384 block_11_project[0][0] __________________________________________________________________________________________________ block_11_add (Add) (None, 10, 10, 96) 0 block_10_project_BN[0][0] block_11_project_BN[0][0] __________________________________________________________________________________________________ block_12_expand (Conv2D) (None, 10, 10, 576) 55296 block_11_add[0][0] __________________________________________________________________________________________________ block_12_expand_BN (BatchNormal (None, 10, 10, 576) 2304 block_12_expand[0][0] __________________________________________________________________________________________________ block_12_expand_relu (ReLU) (None, 10, 10, 576) 0 block_12_expand_BN[0][0] __________________________________________________________________________________________________ block_12_depthwise (DepthwiseCo (None, 10, 10, 576) 5184 block_12_expand_relu[0][0] __________________________________________________________________________________________________ block_12_depthwise_BN (BatchNor (None, 10, 10, 576) 2304 block_12_depthwise[0][0] __________________________________________________________________________________________________ block_12_depthwise_relu (ReLU) (None, 10, 10, 576) 0 block_12_depthwise_BN[0][0] __________________________________________________________________________________________________ block_12_project (Conv2D) (None, 10, 10, 96) 55296 block_12_depthwise_relu[0][0] __________________________________________________________________________________________________ block_12_project_BN (BatchNorma (None, 10, 10, 96) 384 block_12_project[0][0] __________________________________________________________________________________________________ block_12_add (Add) (None, 10, 10, 96) 0 block_11_add[0][0] block_12_project_BN[0][0] __________________________________________________________________________________________________ block_13_expand (Conv2D) (None, 10, 10, 576) 55296 block_12_add[0][0] __________________________________________________________________________________________________ block_13_expand_BN (BatchNormal (None, 10, 10, 576) 2304 block_13_expand[0][0] __________________________________________________________________________________________________ block_13_expand_relu (ReLU) (None, 10, 10, 576) 0 block_13_expand_BN[0][0] __________________________________________________________________________________________________ block_13_pad (ZeroPadding2D) (None, 11, 11, 576) 0 block_13_expand_relu[0][0] __________________________________________________________________________________________________ block_13_depthwise (DepthwiseCo (None, 5, 5, 576) 5184 block_13_pad[0][0] __________________________________________________________________________________________________ block_13_depthwise_BN (BatchNor (None, 5, 5, 576) 2304 block_13_depthwise[0][0] __________________________________________________________________________________________________ block_13_depthwise_relu (ReLU) (None, 5, 5, 576) 0 block_13_depthwise_BN[0][0] __________________________________________________________________________________________________ block_13_project (Conv2D) (None, 5, 5, 160) 92160 block_13_depthwise_relu[0][0] __________________________________________________________________________________________________ block_13_project_BN (BatchNorma (None, 5, 5, 160) 640 block_13_project[0][0] __________________________________________________________________________________________________ block_14_expand (Conv2D) (None, 5, 5, 960) 153600 block_13_project_BN[0][0] __________________________________________________________________________________________________ block_14_expand_BN (BatchNormal (None, 5, 5, 960) 3840 block_14_expand[0][0] __________________________________________________________________________________________________ block_14_expand_relu (ReLU) (None, 5, 5, 960) 0 block_14_expand_BN[0][0] __________________________________________________________________________________________________ block_14_depthwise (DepthwiseCo (None, 5, 5, 960) 8640 block_14_expand_relu[0][0] __________________________________________________________________________________________________ block_14_depthwise_BN (BatchNor (None, 5, 5, 960) 3840 block_14_depthwise[0][0] __________________________________________________________________________________________________ block_14_depthwise_relu (ReLU) (None, 5, 5, 960) 0 block_14_depthwise_BN[0][0] __________________________________________________________________________________________________ block_14_project (Conv2D) (None, 5, 5, 160) 153600 block_14_depthwise_relu[0][0] __________________________________________________________________________________________________ block_14_project_BN (BatchNorma (None, 5, 5, 160) 640 block_14_project[0][0] __________________________________________________________________________________________________ block_14_add (Add) (None, 5, 5, 160) 0 block_13_project_BN[0][0] block_14_project_BN[0][0] __________________________________________________________________________________________________ block_15_expand (Conv2D) (None, 5, 5, 960) 153600 block_14_add[0][0] __________________________________________________________________________________________________ block_15_expand_BN (BatchNormal (None, 5, 5, 960) 3840 block_15_expand[0][0] __________________________________________________________________________________________________ block_15_expand_relu (ReLU) (None, 5, 5, 960) 0 block_15_expand_BN[0][0] __________________________________________________________________________________________________ block_15_depthwise (DepthwiseCo (None, 5, 5, 960) 8640 block_15_expand_relu[0][0] __________________________________________________________________________________________________ block_15_depthwise_BN (BatchNor (None, 5, 5, 960) 3840 block_15_depthwise[0][0] __________________________________________________________________________________________________ block_15_depthwise_relu (ReLU) (None, 5, 5, 960) 0 block_15_depthwise_BN[0][0] __________________________________________________________________________________________________ block_15_project (Conv2D) (None, 5, 5, 160) 153600 block_15_depthwise_relu[0][0] __________________________________________________________________________________________________ block_15_project_BN (BatchNorma (None, 5, 5, 160) 640 block_15_project[0][0] __________________________________________________________________________________________________ block_15_add (Add) (None, 5, 5, 160) 0 block_14_add[0][0] block_15_project_BN[0][0] __________________________________________________________________________________________________ block_16_expand (Conv2D) (None, 5, 5, 960) 153600 block_15_add[0][0] __________________________________________________________________________________________________ block_16_expand_BN (BatchNormal (None, 5, 5, 960) 3840 block_16_expand[0][0] __________________________________________________________________________________________________ block_16_expand_relu (ReLU) (None, 5, 5, 960) 0 block_16_expand_BN[0][0] __________________________________________________________________________________________________ block_16_depthwise (DepthwiseCo (None, 5, 5, 960) 8640 block_16_expand_relu[0][0] __________________________________________________________________________________________________ block_16_depthwise_BN (BatchNor (None, 5, 5, 960) 3840 block_16_depthwise[0][0] __________________________________________________________________________________________________ block_16_depthwise_relu (ReLU) (None, 5, 5, 960) 0 block_16_depthwise_BN[0][0] __________________________________________________________________________________________________ block_16_project (Conv2D) (None, 5, 5, 320) 307200 block_16_depthwise_relu[0][0] __________________________________________________________________________________________________ block_16_project_BN (BatchNorma (None, 5, 5, 320) 1280 block_16_project[0][0] __________________________________________________________________________________________________ Conv_1 (Conv2D) (None, 5, 5, 1280) 409600 block_16_project_BN[0][0] __________________________________________________________________________________________________ Conv_1_bn (BatchNormalization) (None, 5, 5, 1280) 5120 Conv_1[0][0] __________________________________________________________________________________________________ out_relu (ReLU) (None, 5, 5, 1280) 0 Conv_1_bn[0][0] ================================================================================================== Total params: 2,257,984 Trainable params: 0 Non-trainable params: 2,257,984 __________________________________________________________________________________________________
分類ヘッドを追加する
特徴のブロックから予測を生成するには、 tf.keras.layers.GlobalAveragePooling2D
レイヤーを使用して、空間5x5
空間位置を平均し、特徴を画像ごとに1つの1280要素のベクトルに変換します。
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)
(32, 1280)
tf.keras.layers.Dense
レイヤーを適用して、これらの特徴を画像ごとに1つの予測に変換します。この予測はlogit
または生の予測値として扱われるため、ここでは活性化関数は必要ありません。正の数はクラス1を予測し、負の数はクラス0を予測します。
prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)
(32, 1)
Keras Functional APIを使用して、データ拡張、再スケーリング、base_model、および特徴抽出レイヤーをチェーン化してモデルを構築します。前述のように、モデルにはBatchNormalizationレイヤーが含まれているため、training = Falseを使用します。
inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
モデルをコンパイルする
トレーニングする前にモデルをコンパイルします。 2つのクラスがあるため、モデルは線形出力を提供するため、 from_logits=True
でバイナリクロスエントロピー損失を使用します。
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'])
model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_2 (InputLayer) [(None, 160, 160, 3)] 0 _________________________________________________________________ sequential (Sequential) (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 (Dense) (None, 1) 1281 ================================================================= Total params: 2,259,265 Trainable params: 1,281 Non-trainable params: 2,257,984 _________________________________________________________________
MobileNetの2.5Mパラメーターは凍結されていますが、Denseレイヤーには1.2Kのトレーニング可能なパラメーターがあります。これらは、2つのtf.Variable
オブジェクト、重みとバイアスに分けられます。
len(model.trainable_variables)
2
モデルをトレーニングする
10エポックのトレーニング後、検証セットで最大94%の精度が得られるはずです。
initial_epochs = 10
loss0, accuracy0 = model.evaluate(validation_dataset)
26/26 [==============================] - 2s 30ms/step - loss: 0.7783 - accuracy: 0.4930
print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))
initial loss: 0.77 initial accuracy: 0.50
history = model.fit(train_dataset,
epochs=initial_epochs,
validation_data=validation_dataset)
Epoch 1/10 63/63 [==============================] - 6s 55ms/step - loss: 0.6438 - accuracy: 0.6380 - val_loss: 0.5101 - val_accuracy: 0.7562 Epoch 2/10 63/63 [==============================] - 3s 47ms/step - loss: 0.4784 - accuracy: 0.7505 - val_loss: 0.3731 - val_accuracy: 0.8540 Epoch 3/10 63/63 [==============================] - 3s 45ms/step - loss: 0.3761 - accuracy: 0.8235 - val_loss: 0.2797 - val_accuracy: 0.9146 Epoch 4/10 63/63 [==============================] - 3s 46ms/step - loss: 0.3209 - accuracy: 0.8585 - val_loss: 0.2321 - val_accuracy: 0.9233 Epoch 5/10 63/63 [==============================] - 3s 47ms/step - loss: 0.2857 - accuracy: 0.8795 - val_loss: 0.1939 - val_accuracy: 0.9431 Epoch 6/10 63/63 [==============================] - 3s 46ms/step - loss: 0.2518 - accuracy: 0.8945 - val_loss: 0.1716 - val_accuracy: 0.9418 Epoch 7/10 63/63 [==============================] - 3s 46ms/step - loss: 0.2425 - accuracy: 0.9065 - val_loss: 0.1449 - val_accuracy: 0.9604 Epoch 8/10 63/63 [==============================] - 3s 46ms/step - loss: 0.2194 - accuracy: 0.9110 - val_loss: 0.1406 - val_accuracy: 0.9616 Epoch 9/10 63/63 [==============================] - 3s 45ms/step - loss: 0.2077 - accuracy: 0.9160 - val_loss: 0.1235 - val_accuracy: 0.9641 Epoch 10/10 63/63 [==============================] - 3s 46ms/step - loss: 0.2056 - accuracy: 0.9170 - val_loss: 0.1171 - val_accuracy: 0.9653
学習曲線
MobileNet V2基本モデルを固定特徴抽出器として使用する場合の、トレーニングと検証の精度/損失の学習曲線を見てみましょう。
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()
程度は低いですが、トレーニングメトリックはエポックの平均を報告し、検証メトリックはエポックの後に評価されるため、検証メトリックは少し長くトレーニングされたモデルを参照します。
微調整
特徴抽出実験では、MobileNetV2ベースモデルの上にいくつかのレイヤーをトレーニングするだけでした。事前トレーニングされたネットワークの重みは、トレーニング中に更新されませんでした。
パフォーマンスをさらに向上させる1つの方法は、追加した分類器のトレーニングと並行して、事前トレーニング済みモデルの最上位層の重みをトレーニング(または「微調整」)することです。トレーニングプロセスでは、重みを一般的な特徴マップからデータセットに特に関連付けられた特徴に調整する必要があります。
また、MobileNetモデル全体ではなく、少数の最上位レイヤーを微調整するようにしてください。ほとんどの畳み込みネットワークでは、層が上位にあるほど、より専門化されます。最初の数層は、ほとんどすべてのタイプの画像に一般化する非常に単純で一般的な機能を学習します。上に行くにつれて、機能はモデルがトレーニングされたデータセットにますます固有になります。微調整の目標は、一般的な学習を上書きするのではなく、これらの特殊な機能を新しいデータセットで機能するように適合させることです。
モデルの最上層のフリーズを解除します
あなたがする必要があるのは、 base_model
を解除し、最下層を訓練base_model
に設定することです。次に、モデルを再コンパイルし(これらの変更を有効にするために必要)、トレーニングを再開する必要があります。
base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))
# Fine-tune from this layer onwards
fine_tune_at = 100
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = False
Number of layers in the base model: 154
モデルをコンパイルする
はるかに大きなモデルをトレーニングしていて、事前にトレーニングされた重みを再適応させたい場合は、この段階で低い学習率を使用することが重要です。そうしないと、モデルがすぐに過剰適合する可能性があります。
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
optimizer = tf.keras.optimizers.RMSprop(lr=base_learning_rate/10),
metrics=['accuracy'])
model.summary()
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_2 (InputLayer) [(None, 160, 160, 3)] 0 _________________________________________________________________ sequential (Sequential) (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 (Dense) (None, 1) 1281 ================================================================= Total params: 2,259,265 Trainable params: 1,862,721 Non-trainable params: 396,544 _________________________________________________________________
len(model.trainable_variables)
56
モデルのトレーニングを続ける
以前に収束するようにトレーニングした場合、この手順により、精度が数パーセント向上します。
fine_tune_epochs = 10
total_epochs = initial_epochs + fine_tune_epochs
history_fine = model.fit(train_dataset,
epochs=total_epochs,
initial_epoch=history.epoch[-1],
validation_data=validation_dataset)
Epoch 10/20 63/63 [==============================] - 8s 60ms/step - loss: 0.1665 - accuracy: 0.9338 - val_loss: 0.0613 - val_accuracy: 0.9715 Epoch 11/20 63/63 [==============================] - 3s 50ms/step - loss: 0.1202 - accuracy: 0.9489 - val_loss: 0.0597 - val_accuracy: 0.9703 Epoch 12/20 63/63 [==============================] - 3s 50ms/step - loss: 0.1025 - accuracy: 0.9567 - val_loss: 0.0410 - val_accuracy: 0.9839 Epoch 13/20 63/63 [==============================] - 3s 50ms/step - loss: 0.0864 - accuracy: 0.9623 - val_loss: 0.0522 - val_accuracy: 0.9740 Epoch 14/20 63/63 [==============================] - 3s 46ms/step - loss: 0.0798 - accuracy: 0.9597 - val_loss: 0.0324 - val_accuracy: 0.9889 Epoch 15/20 63/63 [==============================] - 3s 46ms/step - loss: 0.0840 - accuracy: 0.9673 - val_loss: 0.0374 - val_accuracy: 0.9864 Epoch 16/20 63/63 [==============================] - 3s 46ms/step - loss: 0.0747 - accuracy: 0.9700 - val_loss: 0.0262 - val_accuracy: 0.9876 Epoch 17/20 63/63 [==============================] - 3s 47ms/step - loss: 0.0679 - accuracy: 0.9757 - val_loss: 0.0387 - val_accuracy: 0.9814 Epoch 18/20 63/63 [==============================] - 3s 47ms/step - loss: 0.0729 - accuracy: 0.9675 - val_loss: 0.0311 - val_accuracy: 0.9876 Epoch 19/20 63/63 [==============================] - 3s 46ms/step - loss: 0.0601 - accuracy: 0.9744 - val_loss: 0.0281 - val_accuracy: 0.9901 Epoch 20/20 63/63 [==============================] - 3s 47ms/step - loss: 0.0537 - accuracy: 0.9781 - val_loss: 0.0574 - val_accuracy: 0.9728
MobileNet V2ベースモデルの最後の数層を微調整し、その上で分類器をトレーニングするときのトレーニングと検証の精度/損失の学習曲線を見てみましょう。検証の損失はトレーニングの損失よりもはるかに大きいため、過剰適合になる可能性があります。
また、新しいトレーニングセットは比較的小さく、元のMobileNet V2データセットと類似しているため、過剰適合になる可能性があります。
微調整後、モデルは検証セットでほぼ98%の精度に達します。
acc += history_fine.history['accuracy']
val_acc += history_fine.history['val_accuracy']
loss += history_fine.history['loss']
val_loss += history_fine.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.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
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.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1],
plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()
評価と予測
最後に、テストセットを使用して、新しいデータに対するモデルのパフォーマンスを検証できます。
loss, accuracy = model.evaluate(test_dataset)
print('Test accuracy :', accuracy)
6/6 [==============================] - 0s 30ms/step - loss: 0.0339 - accuracy: 0.9792 Test accuracy : 0.9791666865348816
これで、このモデルを使用して、ペットが猫か犬かを予測する準備が整いました。
#Retrieve a batch of images from the test set
image_batch, label_batch = test_dataset.as_numpy_iterator().next()
predictions = model.predict_on_batch(image_batch).flatten()
# Apply a sigmoid since our model returns logits
predictions = tf.nn.sigmoid(predictions)
predictions = tf.where(predictions < 0.5, 0, 1)
print('Predictions:\n', predictions.numpy())
print('Labels:\n', label_batch)
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")
Predictions: [0 0 1 0 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 0 1 1 0 1 0 1] Labels: [1 0 1 0 1 0 0 1 1 1 1 0 1 0 1 0 1 0 0 0 1 1 0 1 0 1 1 1 0 1 0 1]
概要
特徴抽出に事前トレーニング済みモデルを使用する:小さなデータセットを使用する場合、同じドメイン内の大きなデータセットでトレーニングされたモデルによって学習された特徴を利用するのが一般的な方法です。これは、事前にトレーニングされたモデルをインスタンス化し、完全に接続された分類器を上部に追加することによって行われます。事前にトレーニングされたモデルは「凍結」されており、トレーニング中に分類器の重みのみが更新されます。この場合、畳み込みベースは各画像に関連付けられたすべての特徴を抽出し、抽出された特徴のセットを指定して画像クラスを決定する分類器をトレーニングしました。
事前トレーニング済みモデルの微調整:パフォーマンスをさらに向上させるために、事前トレーニング済みモデルの最上位レイヤーを微調整によって新しいデータセットに再利用することをお勧めします。この場合、モデルがデータセットに固有の高レベルの機能を学習するように重みを調整しました。この手法は通常、トレーニングデータセットが大きく、事前トレーニング済みモデルがトレーニングされた元のデータセットと非常に類似している場合に推奨されます。
詳細については、 Transferラーニングガイドをご覧ください。
# MIT License
#
# Copyright (c) 2017 François Chollet # IGNORE_COPYRIGHT: cleared by OSS licensing
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.