用Keras在TensorFlow上進行遷移學習示例詳解

用Keras在TensorFlow上進行遷移學習示例詳解

本文將展示如何通過實現圖像分類應用程序在TensorFlow上使用Keras進行遷移學習。當您實施卷積神經網絡(CNN)來對某些圖像進行分類時,轉移學習是一種有用的方法。通過使用該方法,您可以節省大量寶貴時間!

簡介

我們將邁出開發可用作移動或Web應用程序一部分的算法的第一步。目標是根據狗的品種對狗的圖像進行分類。如果在圖像中檢測到狗,它將提供狗的品種的估計。如果檢測到人類,它將提供對應的相似估計。

用Keras在TensorFlow上進行遷移學習示例詳解

實現步驟

  • 第0步:導入數據集
  • 第1步:檢測人
  • 第2步:檢測狗
  • 第3步:創建CNN以對狗品種進行分類(來自Scratch)
  • 步驟4:創建CNN以對狗品種進行分類(使用遷移學習)
  • 第5步:測試算法

第0步:導入數據集

首先,我們導入狗狗圖像的數據集。我們通過使用scikit-learn庫中的load_files函數來填充一些變量:

from sklearn.datasets import load_files 
from keras.utils import np_utils
import numpy as np
from glob import glob
# define function to load train, test, and validation datasets
def load_dataset(path):
data = load_files(path)
dog_files = np.array(data['filenames'])
dog_targets = np_utils.to_categorical(np.array(data['target']), 133)
return dog_files, dog_targets

# load train, test, and validation datasets
train_files, train_targets = load_dataset('dog_images/train')
valid_files, valid_targets = load_dataset('dog_images/valid')
test_files, test_targets = load_dataset('dog_images/test')
# load list of dog names
dog_names = [item[20:-1] for item in sorted(glob("dog_images/train/*/"))]

用Keras在TensorFlow上進行遷移學習示例詳解

下載狗數據集(https://s3-us-west-1.amazonaws.com/udacity-aind/dog-project/dogImages.zip)。解壓縮文件,路徑示例`path / to / dog_images`。

接下來,我們導入一個人圖像數據集,其中文件路徑存儲在numpy數組human_files中。

import random
random.seed(8675309)
# load filenames in shuffled human dataset
human_files = np.array(glob("lfw/*/*"))
random.shuffle(human_files)
用Keras在TensorFlow上進行遷移學習示例詳解

下載人數據集(https://s3-us-west-1.amazonaws.com/udacity-aind/dog-project/lfw.zip)。解壓縮文件,路徑示例:`path / to / lfw`。

第1步:檢測人

我們使用OpenCV實現的基於Haar特徵的級聯分類器來檢測圖像中的人臉。OpenCV提供了許多預訓練好的面部檢測器,作為XML文件存儲在github上(https://github.com/opencv/opencv/tree/master/data/haarcascades)。我們下載其中一個探測器並將其存儲在haarcascades目錄中。

我們可以使用Python編寫一個函數,如果在圖像中檢測到人臉返回True,否則返回False。

# returns "True" if face is detected in image stored at img_path
def face_detector(img_path):
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray)
return len(faces) > 0
用Keras在TensorFlow上進行遷移學習示例詳解

測試face_detector函數的性能。

human_files_short = human_files[:100]
dog_files_short = train_files[:100]
print("human_files: " + str(sum([1 for human_file in human_files_short if face_detector(human_file) == True])) + "%")
print("dog_files: " + str(sum([1 for dog_file in dog_files_short if face_detector(dog_file) == True])) + "%")
用Keras在TensorFlow上進行遷移學習示例詳解

輸出為human_files:100%和dog_files:11%

注:人臉檢測使用基於Haar特徵的級聯分類器時,使用了大量的Haar特徵,但這些特徵都是經驗定義的。如果我們有大量的人臉圖像數據,並且有時間搜索超參數,則卷積神經網絡(CNN)是檢測人臉的最佳選擇。因為卷積神經網絡(CNN)會自動查找特徵。

第2步:檢測狗

我們使用預訓練的ResNet-50模型來檢測圖像中的狗。我們的第一行代碼下載了ResNet-50模型,以及在ImageNet上訓練的權重,ImageNet是一個非常大的,非常受歡迎的數據集,用於圖像分類和其他視覺任務。ImageNet包含超過1000萬個URL,每個URL鏈接到包含1000個類別之一的對象的圖像。給定圖像,該預訓練的ResNet-50模型返回包含在圖像中的對象的預測(從ImageNet中的可用類別導出)。

from keras.applications.resnet50 import ResNet50
# define ResNet50 model
ResNet50_model = ResNet50(weights='imagenet')
用Keras在TensorFlow上進行遷移學習示例詳解

當使用TensorFlow作為後端時,Keras CNN需要一個4D數組(我們也將其稱為4D張量)作為輸入,具有形狀

(nb_samples,rows,columns,channels)

其中nb_samples對應於圖像的總數,rows,columns和channels分別對應每個圖像的行,列,和通道的數量。

下面的函數path_to_tensor將圖像文件路徑作為輸入,並返回適合提供給Keras CNN的4D張量。該函數首先加載圖像並將其大小調整為224×224像素的正方形圖像。接下來,將圖像轉換為數組,然後將其調整為4D張量。在這種情況下,由於我們正在處理彩色圖像,因此每個圖像都有三個通道。同樣,由於我們正在處理單個圖像(或樣本),因此返回的張量將始終具有形狀

(nb_samples,224,224,3)

這裡nb_samples是所提供的圖像路徑陣列中的樣本數或圖像數。最好將其nb_samples視為數據集中3D張量的數量!

from keras.preprocessing import image 
from tqdm import tqdm
def path_to_tensor(img_path):
"""The path_to_tensor function below takes a string-valued file path to a color
image as input and returns a 4D tensor suitable for supplying to a Keras CNN.

Args:
img_path: string. a file path to a color images.

Retruns:
numpy.array, a 4D tensor suitable for supplying to a Keras CNN.

Output shape is (1,224,224,3).
"""
# loads RGB image as PIL.Image.Image type
img = image.load_img(img_path, target_size=(224, 224))
# convert PIL.Image.Image type to 3D tensor with shape (224, 224, 3)
x = image.img_to_array(img)
# convert 3D tensor to 4D tensor with shape (1, 224, 224, 3) and return 4D tensor
return np.expand_dims(x, axis=0)
def paths_to_tensor(img_paths):
"""The paths_to_tensor function takes a numpy array of string-valued image paths
as input and returns a 4D tensor with shape.

Args:
img_path: string. a file path to a color images.

Retruns:
numpy.array, a 4D tensor. Output shape is (n_samples,224,224,3).
"""
list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
return np.vstack(list_of_tensors)
用Keras在TensorFlow上進行遷移學習示例詳解

要使4D張量為ResNet-50以及Keras中任何其他預先訓練的模型做好準備,需要一些額外的處理。首先,通過重新排序通道將RGB圖像轉換為BGR。所有預訓練的模型都具有額外的歸一化步驟,即必須從每個圖像中的每個像素中減去平均像素(以RGB表示為[103.939,116.779,123.68],從ImageNet中的所有圖像中的所有像素計算出)。這是在導入函數preprocess_input中實現的。

現在我們有了一種格式化圖像以供應給ResNet-50的方法,現在我們已經準備好使用該神教網絡模型來提取預測。這是通過該predict方法實現的,該方法返回一個數組,其第i個條目是模型的預測概率,即圖像屬於第i個ImageNet類別。這是在下面的resnet50_predict_tags函數中實現的。

from keras.applications.resnet50 import preprocess_input, decode_predictions
def ResNet50_predict_labels(img_path):
"""This is accomplished with the predict method, which returns an array
whose i-th entry is the model's predicted probability that the image
belongs to the i-th ImageNet category.

Args:
img_path: string. a file path to a color images.

Returns:
string, an ImageNet category.
"""
# returns prediction vector for image located at img_path
img = preprocess_input(path_to_tensor(img_path))
return np.argmax(ResNet50_model.predict(img))
用Keras在TensorFlow上進行遷移學習示例詳解

在查看字典時,您會注意到與狗對應的類別以不間斷的順序出現,並與字典鍵151-268對應,包括從“Chihuahua”到“Mexican hairless”的所有類別。因此,為了檢查預訓練的ResNet-50模型是否預測圖像包含狗,我們只需要檢查上面的resnet50_predict_label函數是否返回151到268之間的值(包括)。

我們使用這些想法來完成下面的dog_detector函數,如果在圖像中檢測到狗,則返回True(如果沒有檢測到,則返回False)。

### returns "True" if a dog is detected in the image stored at img_path
def dog_detector(img_path):
"""The function returns True if a dog is detected in an image (and False if not).
In order to check to see if an image is predicted to contain a dog by the
pre-trained ResNet-50 model, we need only check if the ResNet50_predict_labels
function above returns a value between 151 and 268 (inclusive).

Args:
img_path: string. a file path to a color images.

Returns:
boolean, images show a dog or not.
"""
prediction = ResNet50_predict_labels(img_path)
return ((prediction <= 268) & (prediction >= 151))
用Keras在TensorFlow上進行遷移學習示例詳解

測試dog_detector函數的性能

human_files_short = human_files[:100]
dog_files_short = train_files[:100]
print('human_files: ' + str(sum([1 for human_file in human_files_short if dog_detector(human_file) == True])) + '%')
print('dog_files: ' + str(sum([1 for dog_file in dog_files_short if dog_detector(dog_file) == True])) + '%')
用Keras在TensorFlow上進行遷移學習示例詳解

輸出為human_files:0%和dog_files:100%

第3步:創建卷積神經網絡(CNN)以對狗品種進行分類(from Scratch)

現在我們已經有了在圖像中檢測人和狗的函數,我們需要一種方法來預測圖像的品種。在此步驟中,我們將創建一個對狗品種進行分類的卷積神經網絡(CNN)。

from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential
model = Sequential()
model.add(Conv2D(32, 3, padding='same', activation='relu', input_shape=(224, 224, 3)))
model.add(Conv2D(32, 3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.2))
model.add(Conv2D(64, 3, padding='same', activation='relu'))
model.add(Conv2D(64, 3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.2))
model.add(GlobalAveragePooling2D())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(133, activation='softmax'))
用Keras在TensorFlow上進行遷移學習示例詳解

架構如下

用Keras在TensorFlow上進行遷移學習示例詳解

定義了Keras CNN的訓練和測試

from keras.callbacks import ModelCheckpoint
def train_model(model, train_tensors, train_targets, valid_tensors, valid_targets, save_filepath, epochs=20, batch_size=20):
"""Fit model to train dataset, and check accuracy for valid dataset.

Args:
model: complied keras model
train_tensors: train datatset
train_targets: train targets
valid_tensors: valid dataset
valid_targets: valid targts
save_filepath: save filepath
epochs: epochs, default 20
batch_size: batch size, default 20
"""
checkpointer = ModelCheckpoint(filepath=save_filepath, verbose=1, save_best_only=True)

model.fit(
train_tensors,
train_targets,
validation_data=(valid_tensors, valid_targets),
epochs=epochs,
batch_size=20,
callbacks=[checkpointer],
verbose=2)
def test_model(model, test_tensors, test_targets):
"""Test model for test datasets.

Args:
model: complied keras model
test_tensors: test datasets
test_targets: test targets
"""
# get index of predicted dog breed for each image in test set
predictions = [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]

# report test accuracy
test_accuracy = 100*np.sum(np.array(predictions)==np.argmax(test_targets, axis=1))/len(predictions)

print('Test accuracy: %.4f%%' % test_accuracy)
用Keras在TensorFlow上進行遷移學習示例詳解

為了編譯Keras模型,我們執行編譯方法。

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

我們對模型進行了訓練,測試精度為7.4163%。這種精度優於隨機的,但不是很好。通過使用遷移學習,這種準確性得到了驚人的提高!

步驟4:創建卷積神經網絡(CNN)以對狗品種進行分類(使用遷移學習)

首先,定義了提取ResNet-50的bottleneck 特徵以用作我們的狗品種分類模型的輸入的函數。該include_top參數是是否包含在該網絡的頂部的全連接層,這裡是False。因為在我們的應用程序中,網絡頂部的全連接層是唯一用來對犬種進行分類的。

注意:沒有全連接層的ResNet50會返回由ImageNet數據集預先訓練的bottleneck 特徵。

from keras.applications.resnet50 import ResNet50, preprocess_input
def extract_Resnet50(tensor):
"""Extracting bottleneck features of ResNet-50 to use as input our classifying model.

Args:
tensor: numpy.array. a 4D tensor suitable for supplying to a Keras CNN.

Retruns:
numpy.array, bottleneck features of ResNet-50
"""
return ResNet50(weights='imagenet', include_top=False).predict(preprocess_input(tensor))
用Keras在TensorFlow上進行遷移學習示例詳解

然後,我們提取bottleneck 特徵

train_Resnet50 = extract_Resnet50(paths_to_tensor(train_files).astype('float32'))
valid_Resnet50 = extract_Resnet50(paths_to_tensor(valid_files).astype('float32'))
test_Resnet50 = extract_Resnet50(paths_to_tensor(test_files).astype('float32'))
用Keras在TensorFlow上進行遷移學習示例詳解

我們的狗品種分類模型被定義為網絡頂部的全連接層

Resnet50_model = Sequential()
Resnet50_model.add(GlobalAveragePooling2D(input_shape=train_Resnet50.shape[1:]))
Resnet50_model.add(Dense(133, activation='softmax'))
用Keras在TensorFlow上進行遷移學習示例詳解

架構如下

用Keras在TensorFlow上進行遷移學習示例詳解

這就是我們使用遷移學習所做的一切!然後我們編譯我們的狗品種分類模型,並用bottleneck 特徵訓練和測試模型。儘管在步驟3中使用相同參數進行了訓練,但最終測試精度為82.1770%。

注意:ResNet-50遷移學習比以前的卷積神經網絡(CNN)架構要好得多。ResNet-50是長時間訓練的,而之前的CNN在短時間內接受訓練。ResNet-50通過在ImageNet上使用包含大量類別的各種圖像進行訓練,而之前的CNN僅使用狗和人類圖像進行訓練。它只能提取一些圖像模式。因此通過使用遷移學習具有最佳性能。

第5步:測試算法

我們將算法編寫在一起。

def Resnet50_predict_breed(img_path):
"""Return the dog breed that is predicted by the transer learing model.
Args:
img_path: string. a file path to a color images.
Returns:
dog breed: string, and probability: float.
"""
bottleneck_feature = extract_Resnet50(path_to_tensor(img_path))
predicted_vector = Resnet50_model.predict(bottleneck_feature)
idx = np.argmax(predicted_vector)
return dog_names[idx], (predicted_vector.flatten())[idx]
def img_show(img_path):
"""Show images by using matplotlib.
Args:
img_path: string. a file path to a color images.
"""
cv_rgb = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
plt.imshow(cv_rgb)
plt.show()

def show_dog(img_path):
"""Show dogs with breed by using ResNet-50.
Args:
img_path: string. a file path to a color images.
"""
name, prob = Resnet50_predict_breed(img_path)
print("hello, dog!")
img_show(img_path)
print("The dog looks like a ...")
print(name + " (Probability: " + str(round(prob, 2)) + ")")
def show_human(img_path):
"""Show humans with dog breed by using ResNet-50.
Args:
img_path: string. a file path to a color images.
"""
name, prob = Resnet50_predict_breed(img_path)
print("hello, human!")
img_show(img_path)
print("You look like a ...")
print(name + " (Probability: " + str(round(prob, 2)) + ")")

def show_error():
"""Show error.
"""
print("sorry, you look like neither dog or human...")

def main(img_path):
"""Main function of our apllication.
Args:
img_path: string. a file path to a color images.
"""
if dog_detector(img_path):
show_dog(img_path)
elif face_detector(img_path):
show_human(img_path)
else:
show_error()
用Keras在TensorFlow上進行遷移學習示例詳解

測試一些圖像,

用Keras在TensorFlow上進行遷移學習示例詳解

用Keras在TensorFlow上進行遷移學習示例詳解

用Keras在TensorFlow上進行遷移學習示例詳解

在TensorFlow上使用Keras實現卷積神經網絡(CNN)作為遷移學習非常容易。當您實施卷積神經網絡(CNN)來對真實世界的圖像進行分類時,遷移學習是一種有用的方法。


分享到:


相關文章: