用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)来对真实世界的图像进行分类时,迁移学习是一种有用的方法。

相关推荐