深度學習之影象分類ResNet50學習

2021-05-10 21:01:08

深度學習之影象分類ResNet50

此次採用遷移學習並微調。一般的建議是:

使用預先訓練的模型進行特徵提取:使用小型資料集時,通常的做法是利用在相同域中的較巨量資料集上訓練的模型中學習的特徵。這是通過範例化預訓練的模型並在頂部新增完全連線的分類器來完成的。預先訓練的模型是「凍結的」,訓練過程中僅更新分類器的權重。在這種情況下,折積基礎提取了與每個影象關聯的所有特徵,而您剛剛訓練了一個分類器,該分類器根據給定的提取特徵集確定影象類。

對預訓練模型進行微調:為了進一步提高效能,可能需要通過微調將預訓練模型的頂層重新用於新的資料集。在這種情況下,您需要調整權重,以使模型學習到特定於資料集的高階功能。通常在訓練資料集很大並且與訓練前的模型非常相似的原始資料集非常相似時,建議使用此技術。

ResNet50實戰

官方範例程式碼用的二分類,貓狗分類。
另外遷移學習使用的是MobileNet V2 model。

所以這次的更改無非就是更改一下分類層,和引入ResNet即可了。

環境準備

可能有些沒有用到!

from tensorflow.keras.applications import ResNet50
from tensorflow.keras import layers
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications.resnet50 import preprocess_input
from tensorflow.keras.preprocessing import image
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
import matplotlib.pyplot as plt
import PIL
import numpy as np

資料集

使用的是官方的花朵影象的分類。官方也很無語,影象分類用的是花朵影象,到遷移學習的時候又換成了狗和貓的分類。

import pathlib
dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz"
data_dir = tf.keras.utils.get_file('flower_photos', origin=dataset_url, untar=True)
data_dir = pathlib.Path(data_dir)

拆分資料集

batch_size = 32
img_height = 180
img_width = 180

train_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="training",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)

val_ds = tf.keras.preprocessing.image_dataset_from_directory(
  data_dir,
  validation_split=0.2,
  subset="validation",
  seed=123,
  image_size=(img_height, img_width),
  batch_size=batch_size)

class_names = train_ds.class_names
print(class_names)

AUTOTUNE = tf.data.AUTOTUNE

train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

檢視資料集

plt.figure(figsize=(10, 10))
for images, labels in train_ds.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")

定義model

data_augmentation = tf.keras.Sequential([
  tf.keras.layers.experimental.preprocessing.RandomFlip('horizontal'),
  tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
])
for image, _ in train_ds.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')

base_model = ResNet50(include_top=False, weights='imagenet',input_shape=(180,180,3))
base_model.trainable = False
inputs = tf.keras.Input(shape=(180,180,3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x,training=False)
print(base_model.output.shape)
x = GlobalAveragePooling2D()(x)
y = Dense(5, activation='softmax')(x) #final layer with softmax activation
model = Model(inputs=inputs, outputs=y, name="ResNet50")
model.summary()

編譯模型

loss = tf.keras.losses.SparseCategoricalCrossentropy()
metrics = tf.metrics.SparseCategoricalAccuracy()
model.compile(optimizer='Adam', loss=loss, metrics=metrics)
len(model.trainable_variables)

訓練模型

history = model.fit(train_ds,
                    epochs=10,
                    validation_data=val_ds)

基本上就能達到92的準確率。
92/92 [==============================] - 19s 206ms/step - loss: 0.0186 - sparse_categorical_accuracy: 1.0000 - val_loss: 0.2470 - val_sparse_categorical_accuracy: 0.9292

微調遷移學習模型

按理說資料量不大,微調可能給不了太大提升。試一下就是了。

175層凍結前面100層

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

重新編譯模型和訓練模型

從未微調的最後一次訓練接著訓練10個epochs。

loss = tf.keras.losses.SparseCategoricalCrossentropy()
metrics = tf.metrics.SparseCategoricalAccuracy()
model.compile(optimizer='Adam', loss=loss, metrics=metrics)
len(model.trainable_variables)

fine_tune_epochs = 10
total_epochs =  10 + fine_tune_epochs

history_fine = model.fit(train_ds,
                         epochs=total_epochs,
                         initial_epoch=history.epoch[-1],
                         validation_data=val_ds)

結果反而還降低了。
92/92 [==============================] - 31s 341ms/step - loss: 0.1780 - sparse_categorical_accuracy: 0.9440 - val_loss: 0.3261 - val_sparse_categorical_accuracy: 0.9183

預測下資料

image_batch,label_batch = val_ds.as_numpy_iterator().next()
print(label_batch)
predictions = model.predict_on_batch(image_batch)

for i in range(0,predictions.shape[0]):
  print(np.argmax(predictions[i]))
  prediction = np.argmax(predictions[i])
  
  if (prediction != label_batch[i]):
    plt.figure(figsize=(10, 10))
    plt.imshow(images[i].numpy().astype("uint8"))
    plt.title(class_names[label_batch[i]] + "-" + class_names[prediction])
    plt.show()

附錄