卷积神经网络(Convolutional Neural Network, CNN)

项目:实现一个狗品种识别算法App

在这个notebook文件中,有些模板代码已经提供给你,但你还需要实现更多的功能来完成这个项目。除非有明确要求,你无须修改任何已给出的代码。以'(练习)'开始的标题表示接下来的代码部分中有你需要实现的功能。这些部分都配有详细的指导,需要实现的部分也会在注释中以'TODO'标出。请仔细阅读所有的提示。

除了实现代码外,你还需要回答一些与项目及代码相关的问题。每个需要回答的问题都会以 '问题 X' 标记。请仔细阅读每个问题,并且在问题后的 '回答' 部分写出完整的答案。我们将根据 你对问题的回答 和 撰写代码实现的功能 来对你提交的项目进行评分。

提示:Code 和 Markdown 区域可通过 Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。

项目中显示为_选做_的部分可以帮助你的项目脱颖而出,而不是仅仅达到通过的最低要求。如果你决定追求更高的挑战,请在此 notebook 中完成_选做_部分的代码。


让我们开始吧

在这个notebook中,你将迈出第一步,来开发可以作为移动端或 Web应用程序一部分的算法。在这个项目的最后,你的程序将能够把用户提供的任何一个图像作为输入。如果可以从图像中检测到一只狗,它会输出对狗品种的预测。如果图像中是一个人脸,它会预测一个与其最相似的狗的种类。下面这张图展示了完成项目后可能的输出结果。(……实际上我们希望每个学生的输出结果不相同!)

在现实世界中,你需要拼凑一系列的模型来完成不同的任务;举个例子,用来预测狗种类的算法会与预测人类的算法不同。在做项目的过程中,你可能会遇到不少失败的预测,因为并不存在完美的算法和模型。你最终提交的不完美的解决方案也一定会给你带来一个有趣的学习经验!

项目内容

我们将这个notebook分为不同的步骤,你可以使用下面的链接来浏览此notebook。

  • Step 0: 导入数据集
  • Step 1: 检测人脸
  • Step 2: 检测狗狗
  • Step 3: 从头创建一个CNN来分类狗品种
  • Step 4: 使用一个CNN来区分狗的品种(使用迁移学习)
  • Step 5: 建立一个CNN来分类狗的品种(使用迁移学习)
  • Step 6: 完成你的算法
  • Step 7: 测试你的算法

在该项目中包含了如下的问题:


步骤 0: 导入数据集

导入狗数据集

在下方的代码单元(cell)中,我们导入了一个狗图像的数据集。我们使用 scikit-learn 库中的 load_files 函数来获取一些变量:

  • train_files, valid_files, test_files - 包含图像的文件路径的numpy数组
  • train_targets, valid_targets, test_targets - 包含独热编码分类标签的numpy数组
  • dog_names - 由字符串构成的与标签相对应的狗的种类

In [7]:
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('/data/dog_images/train')
valid_files, valid_targets = load_dataset('/data/dog_images/valid')
test_files, test_targets = load_dataset('/data/dog_images/test')

# load list of dog names
dog_names = [item[20:-1] for item in sorted(glob("/data/dog_images/train/*/"))]

# print statistics about the dataset
print('There are %d total dog categories.' % len(dog_names))
print('There are %s total dog images.\n' % len(np.hstack([train_files, valid_files, test_files])))
print('There are %d training dog images.' % len(train_files))
print('There are %d validation dog images.' % len(valid_files))
print('There are %d test dog images.'% len(test_files))


There are 133 total dog categories.
There are 8351 total dog images.

There are 6680 training dog images.
There are 835 validation dog images.
There are 836 test dog images.

导入人脸数据集

在下方的代码单元中,我们导入人脸图像数据集,文件所在路径存储在名为 human_files 的 numpy 数组。


In [8]:
import random
random.seed(8675309)

# 加载打乱后的人脸数据集的文件名
human_files = np.array(glob("/data/lfw/*/*"))
random.shuffle(human_files)

# 打印数据集的数据量
print('There are %d total human images.' % len(human_files))


There are 13233 total human images.

步骤1:检测人脸

我们将使用 OpenCV 中的 Haar feature-based cascade classifiers 来检测图像中的人脸。OpenCV 提供了很多预训练的人脸检测模型,它们以XML文件保存在 github。我们已经下载了其中一个检测模型,并且把它存储在 haarcascades 的目录中。

在如下代码单元中,我们将演示如何使用这个检测模型在样本图像中找到人脸。


In [9]:
import cv2                
import matplotlib.pyplot as plt                        
%matplotlib inline                               

# 提取预训练的人脸检测模型
face_cascade = cv2.CascadeClassifier('haarcascades/haarcascade_frontalface_alt.xml')

# 加载彩色(通道顺序为BGR)图像
img = cv2.imread(human_files[3])

# 将BGR图像进行灰度处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 在图像中找出脸
faces = face_cascade.detectMultiScale(gray)

# 打印图像中检测到的脸的个数
print('Number of faces detected:', len(faces))

# 获取每一个所检测到的脸的识别框
for (x,y,w,h) in faces:
    # 在人脸图像中绘制出识别框
    cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    
# 将BGR图像转变为RGB图像以打印
cv_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 展示含有识别框的图像
plt.imshow(cv_rgb)
plt.show()


Number of faces detected: 1

在使用任何一个检测模型之前,将图像转换为灰度图是常用过程。detectMultiScale 函数使用储存在 face_cascade 中的的数据,对输入的灰度图像进行分类。

在上方的代码中,faces 以 numpy 数组的形式,保存了识别到的面部信息。它其中每一行表示一个被检测到的脸,该数据包括如下四个信息:前两个元素 xy 代表识别框左上角的 x 和 y 坐标(参照上图,注意 y 坐标的方向和我们默认的方向不同);后两个元素代表识别框在 x 和 y 轴两个方向延伸的长度 wd

写一个人脸识别器

我们可以将这个程序封装为一个函数。该函数的输入为人脸图像的路径,当图像中包含人脸时,该函数返回 True,反之返回 False。该函数定义如下所示。


In [10]:
# 如果img_path路径表示的图像检测到了脸,返回"True" 
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

【练习】 评估人脸检测模型


问题 1:

在下方的代码块中,使用 face_detector 函数,计算:

  • human_files 的前100张图像中,能够检测到人脸的图像占比多少?
  • dog_files 的前100张图像中,能够检测到人脸的图像占比多少?

理想情况下,人图像中检测到人脸的概率应当为100%,而狗图像中检测到人脸的概率应该为0%。你会发现我们的算法并非完美,但结果仍然是可以接受的。我们从每个数据集中提取前100个图像的文件路径,并将它们存储在human_files_shortdog_files_short中。


In [11]:
human_files_short = human_files[:100]
dog_files_short = train_files[:100]
## 请不要修改上方代码

## TODO: 基于human_files_short和dog_files_short
## 中的图像测试face_detector的表现
human_files_short_detect = 0
dog_files_short_detect = 0

for i in range(100):
    if (face_detector(human_files_short[i])):
        human_files_short_detect += 1
    if (face_detector(dog_files_short[i])):
        dog_files_short_detect += 1

print("The percentage of detecting human faces in human files is:", human_files_short_detect/human_files_short.size)
print("The percentage of detecting human faces in dog files is:", dog_files_short_detect/dog_files_short.size)


The percentage of detecting human faces in human files is: 1.0
The percentage of detecting human faces in dog files is: 0.11

问题 2:

就算法而言,该算法成功与否的关键在于,用户能否提供含有清晰面部特征的人脸图像。 那么你认为,这样的要求在实际使用中对用户合理吗?如果你觉得不合理,你能否想到一个方法,即使图像中并没有清晰的面部特征,也能够检测到人脸?

回答:

不太合理,因为图片的来源不同,不能保证所有的图片的脸部都是清晰的。 如果脸部特征不太清晰,应对图片进行前期的预处理。


选做:

我们建议在你的算法中使用opencv的人脸检测模型去检测人类图像,不过你可以自由地探索其他的方法,尤其是尝试使用深度学习来解决它:)。请用下方的代码单元来设计和测试你的面部监测算法。如果你决定完成这个_选做_任务,你需要报告算法在每一个数据集上的表现。


In [12]:
## (选做) TODO: 报告另一个面部检测算法在LFW数据集上的表现
### 你可以随意使用所需的代码单元数

步骤 2: 检测狗狗

在这个部分中,我们使用预训练的 ResNet-50 模型去检测图像中的狗。下方的第一行代码就是下载了 ResNet-50 模型的网络结构参数,以及基于 ImageNet 数据集的预训练权重。

ImageNet 这目前一个非常流行的数据集,常被用来测试图像分类等计算机视觉任务相关的算法。它包含超过一千万个 URL,每一个都链接到 1000 categories 中所对应的一个物体的图像。任给输入一个图像,该 ResNet-50 模型会返回一个对图像中物体的预测结果。


In [13]:
from keras.applications.resnet50 import ResNet50

# 定义ResNet50模型
ResNet50_model = ResNet50(weights='imagenet')


Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels.h5
102858752/102853048 [==============================] - 1s 0us/step

数据预处理

  • 在使用 TensorFlow 作为后端的时候,在 Keras 中,CNN 的输入是一个4维数组(也被称作4维张量),它的各维度尺寸为 (nb_samples, rows, columns, channels)。其中 nb_samples 表示图像(或者样本)的总数,rows, columns, 和 channels 分别表示图像的行数、列数和通道数。
  • 下方的 path_to_tensor 函数实现如下将彩色图像的字符串型的文件路径作为输入,返回一个4维张量,作为 Keras CNN 输入。因为我们的输入图像是彩色图像,因此它们具有三个通道( channels3)。
    1. 该函数首先读取一张图像,然后将其缩放为 224×224 的图像。
    2. 随后,该图像被调整为具有4个维度的张量。
    3. 对于任一输入图像,最后返回的张量的维度是:(1, 224, 224, 3)
  • paths_to_tensor 函数将图像路径的字符串组成的 numpy 数组作为输入,并返回一个4维张量,各维度尺寸为 (nb_samples, 224, 224, 3)。 在这里,nb_samples是提供的图像路径的数据中的样本数量或图像数量。你也可以将 nb_samples 理解为数据集中3维张量的个数(每个3维张量表示一个不同的图像。

In [14]:
from keras.preprocessing import image                  
from tqdm import tqdm

def path_to_tensor(img_path):
    # 用PIL加载RGB图像为PIL.Image.Image类型
    img = image.load_img(img_path, target_size=(224, 224))
    # 将PIL.Image.Image类型转化为格式为(224, 224, 3)的3维张量
    x = image.img_to_array(img)
    # 将3维张量转化为格式为(1, 224, 224, 3)的4维张量并返回
    return np.expand_dims(x, axis=0)

def paths_to_tensor(img_paths):
    list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
    return np.vstack(list_of_tensors)

基于 ResNet-50 架构进行预测

对于通过上述步骤得到的四维张量,在把它们输入到 ResNet-50 网络、或 Keras 中其他类似的预训练模型之前,还需要进行一些额外的处理:

  1. 首先,这些图像的通道顺序为 RGB,我们需要重排他们的通道顺序为 BGR。
  2. 其次,预训练模型的输入都进行了额外的归一化过程。因此我们在这里也要对这些张量进行归一化,即对所有图像所有像素都减去像素均值 [103.939, 116.779, 123.68](以 RGB 模式表示,根据所有的 ImageNet 图像算出)。

导入的 preprocess_input 函数实现了这些功能。如果你对此很感兴趣,可以在 这里 查看 preprocess_input的代码。

在实现了图像处理的部分之后,我们就可以使用模型来进行预测。这一步通过 predict 方法来实现,它返回一个向量,向量的第 i 个元素表示该图像属于第 i 个 ImageNet 类别的概率。这通过如下的 ResNet50_predict_labels 函数实现。

通过对预测出的向量取用 argmax 函数(找到有最大概率值的下标序号),我们可以得到一个整数,即模型预测到的物体的类别。进而根据这个 清单,我们能够知道这具体是哪个品种的狗狗。


In [15]:
from keras.applications.resnet50 import preprocess_input, decode_predictions
def ResNet50_predict_labels(img_path):
    # 返回img_path路径的图像的预测向量
    img = preprocess_input(path_to_tensor(img_path))
    return np.argmax(ResNet50_model.predict(img))

完成狗检测模型

在研究该 清单 的时候,你会注意到,狗类别对应的序号为151-268。因此,在检查预训练模型判断图像是否包含狗的时候,我们只需要检查如上的 ResNet50_predict_labels 函数是否返回一个介于151和268之间(包含区间端点)的值。

我们通过这些想法来完成下方的 dog_detector 函数,如果从图像中检测到狗就返回 True,否则返回 False


In [16]:
def dog_detector(img_path):
    prediction = ResNet50_predict_labels(img_path)
    return ((prediction <= 268) & (prediction >= 151))

【作业】评估狗狗检测模型


问题 3:

在下方的代码块中,使用 dog_detector 函数,计算:

  • human_files_short中图像检测到狗狗的百分比?
  • dog_files_short中图像检测到狗狗的百分比?

In [17]:
### TODO: 测试dog_detector函数在human_files_short和dog_files_short的表现
human_files_short_detect = 0
dog_files_short_detect = 0

for i in range(100):
    if (dog_detector(human_files_short[i])):
        human_files_short_detect += 1
    if (dog_detector(dog_files_short[i])):
        dog_files_short_detect += 1

print("The percentage of detecting dogs in human files is:", human_files_short_detect/human_files_short.size)
print("The percentage of detecting dogs in dog files is:", dog_files_short_detect/dog_files_short.size)


The percentage of detecting dogs in human files is: 0.0
The percentage of detecting dogs in dog files is: 1.0

步骤 3: 从头开始创建一个CNN来分类狗品种

现在我们已经实现了一个函数,能够在图像中识别人类及狗狗。但我们需要更进一步的方法,来对狗的类别进行识别。在这一步中,你需要实现一个卷积神经网络来对狗的品种进行分类。你需要从头实现你的卷积神经网络(在这一阶段,你还不能使用迁移学习),并且你需要达到超过1%的测试集准确率。在本项目的步骤五种,你还有机会使用迁移学习来实现一个准确率大大提高的模型。

在添加卷积层的时候,注意不要加上太多的(可训练的)层。更多的参数意味着更长的训练时间,也就是说你更可能需要一个 GPU 来加速训练过程。万幸的是,Keras 提供了能够轻松预测每次迭代(epoch)花费时间所需的函数。你可以据此推断你算法所需的训练时间。

值得注意的是,对狗的图像进行分类是一项极具挑战性的任务。因为即便是一个正常人,也很难区分布列塔尼犬和威尔士史宾格犬。

布列塔尼犬(Brittany) 威尔士史宾格犬(Welsh Springer Spaniel)

不难发现其他的狗品种会有很小的类间差别(比如金毛寻回犬和美国水猎犬)。

金毛寻回犬(Curly-Coated Retriever) 美国水猎犬(American Water Spaniel)

同样,拉布拉多犬(labradors)有黄色、棕色和黑色这三种。那么你设计的基于视觉的算法将不得不克服这种较高的类间差别,以达到能够将这些不同颜色的同类狗分到同一个品种中。

黄色拉布拉多犬(Yellow Labrador) 棕色拉布拉多犬(Chocolate Labrador) 黑色拉布拉多犬(Black Labrador)

我们也提到了随机分类将得到一个非常低的结果:不考虑品种略有失衡的影响,随机猜测到正确品种的概率是1/133,相对应的准确率是低于1%的。

请记住,在深度学习领域,实践远远高于理论。大量尝试不同的框架吧,相信你的直觉!当然,玩得开心!

数据预处理

通过对每张图像的像素值除以255,我们对图像实现了归一化处理。


In [18]:
from PIL import ImageFile                            
ImageFile.LOAD_TRUNCATED_IMAGES = True                 

# Keras中的数据预处理过程
train_tensors = paths_to_tensor(train_files).astype('float32')/255
valid_tensors = paths_to_tensor(valid_files).astype('float32')/255
test_tensors = paths_to_tensor(test_files).astype('float32')/255


100%|██████████| 6680/6680 [01:25<00:00, 78.01it/s] 
100%|██████████| 835/835 [00:09<00:00, 86.10it/s] 
100%|██████████| 836/836 [00:09<00:00, 86.80it/s] 

【练习】模型架构

创建一个卷积神经网络来对狗品种进行分类。在你代码块的最后,执行 model.summary() 来输出你模型的总结信息。

我们已经帮你导入了一些所需的 Python 库,如有需要你可以自行导入。如果你在过程中遇到了困难,如下是给你的一点小提示——该模型能够在5个 epoch 内取得超过1%的测试准确率,并且能在CPU上很快地训练。


问题 4:

在下方的代码块中尝试使用 Keras 搭建卷积网络的架构,并回答相关的问题。

  1. 你可以尝试自己搭建一个卷积网络的模型,那么你需要回答你搭建卷积网络的具体步骤(用了哪些层)以及为什么这样搭建。
  2. 你也可以根据上图提示的步骤搭建卷积网络,那么请说明为何如上的架构能够在该问题上取得很好的表现。

回答: 我选择根据上图提示搭建卷积神经网络。首先,搭建三层卷积层可以检测更高级的特征,以达到狗狗品种分类的目的。同时,两个卷积层之间的池化层有效降低了数据的复杂度,使得训练效率得到有效提升


In [19]:
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential

model = Sequential()

### TODO: 定义你的网络架构
model.add(Conv2D(filters=16, kernel_size=2, input_shape=(224, 224, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.2))
model.add(Conv2D(filters=32, kernel_size=2, activation='relu'))
model.add(MaxPooling2D(pool_size=2))

model.add(Dropout(0.2))
model.add(Conv2D(filters=64, kernel_size=2, activation='relu'))
model.add(MaxPooling2D(pool_size=2))
model.add(Dropout(0.2))

model.add(GlobalAveragePooling2D())
model.add(Dense(133, activation='softmax'))
                    
model.summary()


_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_9 (Conv2D)            (None, 223, 223, 16)      208       
_________________________________________________________________
max_pooling2d_10 (MaxPooling (None, 111, 111, 16)      0         
_________________________________________________________________
dropout_8 (Dropout)          (None, 111, 111, 16)      0         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 110, 110, 32)      2080      
_________________________________________________________________
max_pooling2d_11 (MaxPooling (None, 55, 55, 32)        0         
_________________________________________________________________
dropout_9 (Dropout)          (None, 55, 55, 32)        0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 54, 54, 64)        8256      
_________________________________________________________________
max_pooling2d_12 (MaxPooling (None, 27, 27, 64)        0         
_________________________________________________________________
dropout_10 (Dropout)         (None, 27, 27, 64)        0         
_________________________________________________________________
global_average_pooling2d_3 ( (None, 64)                0         
_________________________________________________________________
dense_3 (Dense)              (None, 133)               8645      
=================================================================
Total params: 19,189
Trainable params: 19,189
Non-trainable params: 0
_________________________________________________________________

In [20]:
## 编译模型
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

【练习】训练模型


问题 5:

在下方代码单元训练模型。使用模型检查点(model checkpointing)来储存具有最低验证集 loss 的模型。

可选题:你也可以对训练集进行 数据增强,来优化模型的表现。


In [21]:
from keras.callbacks import ModelCheckpoint  

### TODO: 设置训练模型的epochs的数量

epochs = 5

### 不要修改下方代码

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5', 
                               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=1)


Train on 6680 samples, validate on 835 samples
Epoch 1/5
6660/6680 [============================>.] - ETA: 0s - loss: 4.8817 - acc: 0.0105Epoch 00001: val_loss improved from inf to 4.87374, saving model to saved_models/weights.best.from_scratch.hdf5
6680/6680 [==============================] - 22s 3ms/step - loss: 4.8816 - acc: 0.0106 - val_loss: 4.8737 - val_acc: 0.0120
Epoch 2/5
6660/6680 [============================>.] - ETA: 0s - loss: 4.8539 - acc: 0.0120Epoch 00002: val_loss improved from 4.87374 to 4.84636, saving model to saved_models/weights.best.from_scratch.hdf5
6680/6680 [==============================] - 21s 3ms/step - loss: 4.8536 - acc: 0.0120 - val_loss: 4.8464 - val_acc: 0.0168
Epoch 3/5
6660/6680 [============================>.] - ETA: 0s - loss: 4.8110 - acc: 0.0195Epoch 00003: val_loss improved from 4.84636 to 4.80888, saving model to saved_models/weights.best.from_scratch.hdf5
6680/6680 [==============================] - 22s 3ms/step - loss: 4.8109 - acc: 0.0195 - val_loss: 4.8089 - val_acc: 0.0168
Epoch 4/5
6660/6680 [============================>.] - ETA: 0s - loss: 4.7768 - acc: 0.0201Epoch 00004: val_loss improved from 4.80888 to 4.77889, saving model to saved_models/weights.best.from_scratch.hdf5
6680/6680 [==============================] - 21s 3ms/step - loss: 4.7767 - acc: 0.0202 - val_loss: 4.7789 - val_acc: 0.0251
Epoch 5/5
6660/6680 [============================>.] - ETA: 0s - loss: 4.7451 - acc: 0.0243Epoch 00005: val_loss improved from 4.77889 to 4.75447, saving model to saved_models/weights.best.from_scratch.hdf5
6680/6680 [==============================] - 21s 3ms/step - loss: 4.7449 - acc: 0.0243 - val_loss: 4.7545 - val_acc: 0.0240
Out[21]:
<keras.callbacks.History at 0x7fa0ac6f8390>

In [22]:
## 加载具有最好验证loss的模型

model.load_weights('saved_models/weights.best.from_scratch.hdf5')

测试模型

在狗图像的测试数据集上试用你的模型。确保测试准确率大于1%。


In [23]:
# 获取测试数据集中每一个图像所预测的狗品种的index
dog_breed_predictions = [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]

# 报告测试准确率
test_accuracy = 100*np.sum(np.array(dog_breed_predictions)==np.argmax(test_targets, axis=1))/len(dog_breed_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)


Test accuracy: 2.6316%

步骤 4: 使用一个CNN来区分狗的品种

使用 迁移学习(Transfer Learning)的方法,能帮助我们在不损失准确率的情况下大大减少训练时间。在以下步骤中,你可以尝试使用迁移学习来训练你自己的CNN。

得到从图像中提取的特征向量(Bottleneck Features)


In [24]:
bottleneck_features = np.load('/data/bottleneck_features/DogVGG16Data.npz')
train_VGG16 = bottleneck_features['train']
valid_VGG16 = bottleneck_features['valid']
test_VGG16 = bottleneck_features['test']

模型架构

该模型使用预训练的 VGG-16 模型作为固定的图像特征提取器,其中 VGG-16 最后一层卷积层的输出被直接输入到我们的模型。我们只需要添加一个全局平均池化层以及一个全连接层,其中全连接层使用 softmax 激活函数,对每一个狗的种类都包含一个节点。


In [25]:
VGG16_model = Sequential()
VGG16_model.add(GlobalAveragePooling2D(input_shape=train_VGG16.shape[1:]))
VGG16_model.add(Dense(133, activation='softmax'))

VGG16_model.summary()


_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
global_average_pooling2d_4 ( (None, 512)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 133)               68229     
=================================================================
Total params: 68,229
Trainable params: 68,229
Non-trainable params: 0
_________________________________________________________________

In [26]:
## 编译模型

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

In [27]:
## 训练模型

checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.VGG16.hdf5', 
                               verbose=1, save_best_only=True)

VGG16_model.fit(train_VGG16, train_targets, 
          validation_data=(valid_VGG16, valid_targets),
          epochs=20, batch_size=20, callbacks=[checkpointer], verbose=1)


Train on 6680 samples, validate on 835 samples
Epoch 1/20
6600/6680 [============================>.] - ETA: 0s - loss: 11.8988 - acc: 0.1303Epoch 00001: val_loss improved from inf to 10.57918, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 308us/step - loss: 11.8949 - acc: 0.1307 - val_loss: 10.5792 - val_acc: 0.2132
Epoch 2/20
6500/6680 [============================>.] - ETA: 0s - loss: 9.6765 - acc: 0.2878Epoch 00002: val_loss improved from 10.57918 to 9.51589, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 253us/step - loss: 9.6727 - acc: 0.2880 - val_loss: 9.5159 - val_acc: 0.2862
Epoch 3/20
6460/6680 [============================>.] - ETA: 0s - loss: 8.9693 - acc: 0.3641Epoch 00003: val_loss improved from 9.51589 to 9.13973, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 248us/step - loss: 8.9632 - acc: 0.3645 - val_loss: 9.1397 - val_acc: 0.3341
Epoch 4/20
6520/6680 [============================>.] - ETA: 0s - loss: 8.7415 - acc: 0.4006Epoch 00004: val_loss improved from 9.13973 to 9.10053, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 255us/step - loss: 8.7140 - acc: 0.4024 - val_loss: 9.1005 - val_acc: 0.3485
Epoch 5/20
6600/6680 [============================>.] - ETA: 0s - loss: 8.4844 - acc: 0.4242Epoch 00005: val_loss improved from 9.10053 to 8.91824, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 250us/step - loss: 8.4887 - acc: 0.4241 - val_loss: 8.9182 - val_acc: 0.3593
Epoch 6/20
6520/6680 [============================>.] - ETA: 0s - loss: 8.2685 - acc: 0.4466Epoch 00006: val_loss improved from 8.91824 to 8.75737, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 255us/step - loss: 8.2920 - acc: 0.4454 - val_loss: 8.7574 - val_acc: 0.3701
Epoch 7/20
6620/6680 [============================>.] - ETA: 0s - loss: 8.1670 - acc: 0.4601Epoch 00007: val_loss improved from 8.75737 to 8.58505, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 250us/step - loss: 8.1624 - acc: 0.4605 - val_loss: 8.5851 - val_acc: 0.3904
Epoch 8/20
6460/6680 [============================>.] - ETA: 0s - loss: 7.8733 - acc: 0.4834Epoch 00008: val_loss improved from 8.58505 to 8.57290, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 253us/step - loss: 7.9312 - acc: 0.4799 - val_loss: 8.5729 - val_acc: 0.3892
Epoch 9/20
6660/6680 [============================>.] - ETA: 0s - loss: 7.8558 - acc: 0.4898Epoch 00009: val_loss improved from 8.57290 to 8.37072, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 252us/step - loss: 7.8589 - acc: 0.4897 - val_loss: 8.3707 - val_acc: 0.3952
Epoch 10/20
6640/6680 [============================>.] - ETA: 0s - loss: 7.7289 - acc: 0.5017Epoch 00010: val_loss improved from 8.37072 to 8.36277, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 252us/step - loss: 7.7267 - acc: 0.5016 - val_loss: 8.3628 - val_acc: 0.4084
Epoch 11/20
6540/6680 [============================>.] - ETA: 0s - loss: 7.6539 - acc: 0.5122Epoch 00011: val_loss did not improve
6680/6680 [==============================] - 2s 250us/step - loss: 7.6913 - acc: 0.5097 - val_loss: 8.3630 - val_acc: 0.4048
Epoch 12/20
6480/6680 [============================>.] - ETA: 0s - loss: 7.6779 - acc: 0.5133Epoch 00012: val_loss improved from 8.36277 to 8.26086, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 259us/step - loss: 7.6780 - acc: 0.5133 - val_loss: 8.2609 - val_acc: 0.4240
Epoch 13/20
6660/6680 [============================>.] - ETA: 0s - loss: 7.6599 - acc: 0.5171Epoch 00013: val_loss did not improve
6680/6680 [==============================] - 2s 255us/step - loss: 7.6636 - acc: 0.5169 - val_loss: 8.3361 - val_acc: 0.4204
Epoch 14/20
6520/6680 [============================>.] - ETA: 0s - loss: 7.6194 - acc: 0.5199Epoch 00014: val_loss improved from 8.26086 to 8.22236, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 251us/step - loss: 7.6497 - acc: 0.5181 - val_loss: 8.2224 - val_acc: 0.4180
Epoch 15/20
6540/6680 [============================>.] - ETA: 0s - loss: 7.5786 - acc: 0.5213Epoch 00015: val_loss improved from 8.22236 to 8.12911, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 254us/step - loss: 7.5671 - acc: 0.5222 - val_loss: 8.1291 - val_acc: 0.4263
Epoch 16/20
6500/6680 [============================>.] - ETA: 0s - loss: 7.4843 - acc: 0.5260Epoch 00016: val_loss did not improve
6680/6680 [==============================] - 2s 252us/step - loss: 7.4813 - acc: 0.5256 - val_loss: 8.1297 - val_acc: 0.4251
Epoch 17/20
6580/6680 [============================>.] - ETA: 0s - loss: 7.3097 - acc: 0.5305Epoch 00017: val_loss improved from 8.12911 to 8.00258, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 259us/step - loss: 7.2970 - acc: 0.5314 - val_loss: 8.0026 - val_acc: 0.4311
Epoch 18/20
6480/6680 [============================>.] - ETA: 0s - loss: 7.1743 - acc: 0.5424Epoch 00018: val_loss improved from 8.00258 to 7.90895, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 256us/step - loss: 7.1896 - acc: 0.5416 - val_loss: 7.9089 - val_acc: 0.4251
Epoch 19/20
6540/6680 [============================>.] - ETA: 0s - loss: 7.0790 - acc: 0.5492Epoch 00019: val_loss did not improve
6680/6680 [==============================] - 2s 249us/step - loss: 7.0858 - acc: 0.5488 - val_loss: 7.9667 - val_acc: 0.4335
Epoch 20/20
6460/6680 [============================>.] - ETA: 0s - loss: 7.0128 - acc: 0.5554Epoch 00020: val_loss improved from 7.90895 to 7.80676, saving model to saved_models/weights.best.VGG16.hdf5
6680/6680 [==============================] - 2s 249us/step - loss: 7.0278 - acc: 0.5543 - val_loss: 7.8068 - val_acc: 0.4335
Out[27]:
<keras.callbacks.History at 0x7fa08fc43d68>

In [33]:
## 加载具有最好验证loss的模型

VGG16_model.load_weights('saved_models/weights.best.VGG16.hdf5')

测试模型

现在,我们可以测试此CNN在狗图像测试数据集中识别品种的效果如何。我们在下方打印出测试准确率。


In [34]:
# 获取测试数据集中每一个图像所预测的狗品种的index
VGG16_predictions = [np.argmax(VGG16_model.predict(np.expand_dims(feature, axis=0))) for feature in test_VGG16]

# 报告测试准确率
test_accuracy = 100*np.sum(np.array(VGG16_predictions)==np.argmax(test_targets, axis=1))/len(VGG16_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)


Test accuracy: 43.4211%

使用模型预测狗的品种


In [35]:
from extract_bottleneck_features import *

def VGG16_predict_breed(img_path):
    # 提取bottleneck特征
    bottleneck_feature = extract_VGG16(path_to_tensor(img_path))
    # 获取预测向量
    predicted_vector = VGG16_model.predict(bottleneck_feature)
    # 返回此模型预测的狗的品种
    return dog_names[np.argmax(predicted_vector)]

步骤 5: 建立一个CNN来分类狗的品种(使用迁移学习)

现在你将使用迁移学习来建立一个CNN,从而可以从图像中识别狗的品种。你的 CNN 在测试集上的准确率必须至少达到60%。

在步骤4中,我们使用了迁移学习来创建一个使用基于 VGG-16 提取的特征向量来搭建一个 CNN。在本部分内容中,你必须使用另一个预训练模型来搭建一个 CNN。为了让这个任务更易实现,我们已经预先对目前 keras 中可用的几种网络进行了预训练:

这些文件被命名为为:

Dog{network}Data.npz

其中 {network} 可以是 VGG19Resnet50InceptionV3Xception 中的一个。选择上方网络架构中的一个,他们已经保存在目录 /data/bottleneck_features/ 中。

【练习】获取模型的特征向量

在下方代码块中,通过运行下方代码提取训练、测试与验证集相对应的bottleneck特征。

bottleneck_features = np.load('/data/bottleneck_features/Dog{network}Data.npz')
train_{network} = bottleneck_features['train']
valid_{network} = bottleneck_features['valid']
test_{network} = bottleneck_features['test']

In [36]:
### TODO: 从另一个预训练的CNN获取bottleneck特征
bottleneck_features = np.load('/data/bottleneck_features/DogXceptionData.npz')
train_Xception = bottleneck_features['train']
valid_Xception = bottleneck_features['valid']
test_Xception = bottleneck_features['test']

【练习】模型架构

建立一个CNN来分类狗品种。在你的代码单元块的最后,通过运行如下代码输出网络的结构:

    <your model's name>.summary()


问题 6:

在下方的代码块中尝试使用 Keras 搭建最终的网络架构,并回答你实现最终 CNN 架构的步骤与每一步的作用,并描述你在迁移学习过程中,使用该网络架构的原因。

回答:

Xception_model = Sequential() 这一步是调用Xception的预训练模型

Xception_model.add(GlobalAveragePooling2D(input_shape=train_Resnet50.shape[1:])) 这一步添加一个全局平均池化层避免过拟合

Xception_model.add(Dropout(0.2)) 这一步是添加Dropout层避免过拟合

Xception_model.add(Dense(133, activation='softmax')) 这一步添加133个节点的全连接层,使用softmax激活函数输出每个狗狗品种的概率

使用该网络架构的原因是由于Xception具有如下优点: 1.相比传统的卷积神经网络如VGG复杂度降低,需要的参数数量下降。 2.可以做到更深,不会出现梯度消失的问题。 3.优化简单,分类准确度加深由于使用更深的网络。 4.Xception在众多图像识别领域中拔得头筹。 因此,选取Xception网络可以比之前的VGG网络取得更好的预测效果。

  • 为什么这一架构会在这一分类任务中成功?

    这四个架构都是经过反复多次实验确定的,非常有效果的架构。以Inception net为例,inception net是多层特征提取器,通过分别多次同时提取特征,然后叠加,就可以学到不同层次的特征,所以效果非常好。

  • 为什么早期(第三步 )的尝试不成功?

    第三步中,第一,使用的网络在架构上,非常浅,学到的特征非常少,其次学习库非常小,上面四个网络是在Imagenet上经过大量训练在不同种类的训练集上得来的,这是这个小库无法比拟的。


In [37]:
### TODO: 定义你的框架
# 调用Xception的预训练模型
Xception_model = Sequential()

#加一个全局平均池化层避免过拟合
Xception_model.add(GlobalAveragePooling2D(input_shape=train_Xception.shape[1:]))

#添加Dropout层避免过拟合
Xception_model.add(Dropout(0.2))

#添加133个节点的全连接层,使用softmax激活函数输出每个狗狗品种的概率
Xception_model.add(Dense(133, activation='softmax'))

Xception_model.summary()


_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
global_average_pooling2d_5 ( (None, 2048)              0         
_________________________________________________________________
dropout_11 (Dropout)         (None, 2048)              0         
_________________________________________________________________
dense_5 (Dense)              (None, 133)               272517    
=================================================================
Total params: 272,517
Trainable params: 272,517
Non-trainable params: 0
_________________________________________________________________

In [38]:
### TODO: 编译模型
Xception_model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy'])

【练习】训练模型

问题 7:

在下方代码单元中训练你的模型。使用模型检查点(model checkpointing)来储存具有最低验证集 loss 的模型。

当然,你也可以对训练集进行 数据增强 以优化模型的表现,不过这不是必须的步骤。


In [39]:
### TODO: 训练模型
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.Xception1.hdf5', 
                               verbose=1, save_best_only=True)

history = Xception_model.fit(train_Xception, train_targets, 
          validation_data=(valid_Xception, valid_targets),
          epochs=20, batch_size=20, callbacks=[checkpointer], verbose=1)


Train on 6680 samples, validate on 835 samples
Epoch 1/20
6600/6680 [============================>.] - ETA: 0s - loss: 1.1255 - acc: 0.7239Epoch 00001: val_loss improved from inf to 0.52573, saving model to saved_models/weights.best.Xception1.hdf5
6680/6680 [==============================] - 3s 475us/step - loss: 1.1172 - acc: 0.7256 - val_loss: 0.5257 - val_acc: 0.8335
Epoch 2/20
6620/6680 [============================>.] - ETA: 0s - loss: 0.4270 - acc: 0.8647Epoch 00002: val_loss improved from 0.52573 to 0.49305, saving model to saved_models/weights.best.Xception1.hdf5
6680/6680 [==============================] - 3s 410us/step - loss: 0.4260 - acc: 0.8647 - val_loss: 0.4931 - val_acc: 0.8347
Epoch 3/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.3540 - acc: 0.8870Epoch 00003: val_loss improved from 0.49305 to 0.48966, saving model to saved_models/weights.best.Xception1.hdf5
6680/6680 [==============================] - 3s 411us/step - loss: 0.3586 - acc: 0.8864 - val_loss: 0.4897 - val_acc: 0.8431
Epoch 4/20
6620/6680 [============================>.] - ETA: 0s - loss: 0.3193 - acc: 0.9047Epoch 00004: val_loss improved from 0.48966 to 0.47889, saving model to saved_models/weights.best.Xception1.hdf5
6680/6680 [==============================] - 3s 410us/step - loss: 0.3186 - acc: 0.9046 - val_loss: 0.4789 - val_acc: 0.8587
Epoch 5/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2913 - acc: 0.9126Epoch 00005: val_loss did not improve
6680/6680 [==============================] - 3s 409us/step - loss: 0.2898 - acc: 0.9129 - val_loss: 0.5032 - val_acc: 0.8503
Epoch 6/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2695 - acc: 0.9162Epoch 00006: val_loss did not improve
6680/6680 [==============================] - 3s 409us/step - loss: 0.2683 - acc: 0.9165 - val_loss: 0.4957 - val_acc: 0.8467
Epoch 7/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2508 - acc: 0.9262Epoch 00007: val_loss did not improve
6680/6680 [==============================] - 3s 412us/step - loss: 0.2489 - acc: 0.9268 - val_loss: 0.5211 - val_acc: 0.8539
Epoch 8/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2384 - acc: 0.9277Epoch 00008: val_loss did not improve
6680/6680 [==============================] - 3s 408us/step - loss: 0.2373 - acc: 0.9278 - val_loss: 0.5091 - val_acc: 0.8599
Epoch 9/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2300 - acc: 0.9330Epoch 00009: val_loss did not improve
6680/6680 [==============================] - 3s 410us/step - loss: 0.2286 - acc: 0.9334 - val_loss: 0.5349 - val_acc: 0.8575
Epoch 10/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2114 - acc: 0.9398Epoch 00010: val_loss did not improve
6680/6680 [==============================] - 3s 412us/step - loss: 0.2109 - acc: 0.9398 - val_loss: 0.5300 - val_acc: 0.8575
Epoch 11/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.2026 - acc: 0.9383Epoch 00011: val_loss did not improve
6680/6680 [==============================] - 3s 412us/step - loss: 0.2020 - acc: 0.9385 - val_loss: 0.5516 - val_acc: 0.8659
Epoch 12/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1893 - acc: 0.9433Epoch 00012: val_loss did not improve
6680/6680 [==============================] - 3s 411us/step - loss: 0.1884 - acc: 0.9434 - val_loss: 0.5614 - val_acc: 0.8587
Epoch 13/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1849 - acc: 0.9450Epoch 00013: val_loss did not improve
6680/6680 [==============================] - 3s 407us/step - loss: 0.1841 - acc: 0.9452 - val_loss: 0.5743 - val_acc: 0.8575
Epoch 14/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1761 - acc: 0.9489Epoch 00014: val_loss did not improve
6680/6680 [==============================] - 3s 410us/step - loss: 0.1770 - acc: 0.9487 - val_loss: 0.5765 - val_acc: 0.8551
Epoch 15/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1657 - acc: 0.9514Epoch 00015: val_loss did not improve
6680/6680 [==============================] - 3s 410us/step - loss: 0.1688 - acc: 0.9509 - val_loss: 0.6255 - val_acc: 0.8515
Epoch 16/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1554 - acc: 0.9539Epoch 00016: val_loss did not improve
6680/6680 [==============================] - 3s 411us/step - loss: 0.1581 - acc: 0.9536 - val_loss: 0.6189 - val_acc: 0.8695
Epoch 17/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1474 - acc: 0.9582Epoch 00017: val_loss did not improve
6680/6680 [==============================] - 3s 410us/step - loss: 0.1476 - acc: 0.9581 - val_loss: 0.6452 - val_acc: 0.8575
Epoch 18/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1457 - acc: 0.9568Epoch 00018: val_loss did not improve
6680/6680 [==============================] - 3s 410us/step - loss: 0.1461 - acc: 0.9569 - val_loss: 0.6144 - val_acc: 0.8599
Epoch 19/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1396 - acc: 0.9606Epoch 00019: val_loss did not improve
6680/6680 [==============================] - 3s 414us/step - loss: 0.1408 - acc: 0.9603 - val_loss: 0.6590 - val_acc: 0.8563
Epoch 20/20
6600/6680 [============================>.] - ETA: 0s - loss: 0.1361 - acc: 0.9605Epoch 00020: val_loss did not improve
6680/6680 [==============================] - 3s 415us/step - loss: 0.1349 - acc: 0.9608 - val_loss: 0.6394 - val_acc: 0.8683

In [40]:
### TODO: 加载具有最佳验证loss的模型权重
Xception_model.load_weights('saved_models/weights.best.Xception1.hdf5')

【练习】测试模型

问题 8:

在狗图像的测试数据集上试用你的模型。确保测试准确率大于60%。


In [41]:
### TODO: 在测试集上计算分类准确率
Xception_predictions = [np.argmax(Xception_model.predict(np.expand_dims(feature, axis=0))) for feature in test_Xception]

# 报告测试准确率
test_accuracy = 100*np.sum(np.array(Xception_predictions)==np.argmax(test_targets, axis=1))/len(Xception_predictions)
print('Test accuracy: %.4f%%' % test_accuracy)


Test accuracy: 84.4498%

【练习】使用模型测试狗的品种

实现一个函数,它的输入为图像路径,功能为预测对应图像的类别,输出为你模型预测出的狗类别(Affenpinscher, Afghan_hound 等)。

与步骤5中的模拟函数类似,你的函数应当包含如下三个步骤:

  1. 根据选定的模型载入图像特征(bottleneck features)
  2. 将图像特征输输入到你的模型中,并返回预测向量。注意,在该向量上使用 argmax 函数可以返回狗种类的序号。
  3. 使用在步骤0中定义的 dog_names 数组来返回对应的狗种类名称。

提取图像特征过程中使用到的函数可以在 extract_bottleneck_features.py 中找到。同时,他们应已在之前的代码块中被导入。根据你选定的 CNN 网络,你可以使用 extract_{network} 函数来获得对应的图像特征,其中 {network} 代表 VGG19, Resnet50, InceptionV3, 或 Xception 中的一个。


问题 9:


In [42]:
### TODO: 写一个函数,该函数将图像的路径作为输入
### 然后返回此模型所预测的狗的品种
def Xception_predict_breed(img_path):
    # extract bottleneck features
    bottleneck_feature = extract_Xception(path_to_tensor(img_path))
    # obtain predicted vector
    predicted_vector = Xception_model.predict(bottleneck_feature)
    # return dog breed that is predicted by the model
    return dog_names[np.argmax(predicted_vector)]

步骤 6: 完成你的算法

实现一个算法,它的输入为图像的路径,它能够区分图像是否包含一个人、狗或两者都不包含,然后:

  • 如果从图像中检测到一只,返回被预测的品种。
  • 如果从图像中检测到,返回最相像的狗品种。
  • 如果两者都不能在图像中检测到,输出错误提示。

我们非常欢迎你来自己编写检测图像中人类与狗的函数,你可以随意地使用上方完成的 face_detectordog_detector 函数。你需要在步骤5使用你的CNN来预测狗品种。

下面提供了算法的示例输出,但你可以自由地设计自己的模型!

问题 10:

在下方代码块中完成你的代码。



In [43]:
### TODO: 设计你的算法
### 自由地使用所需的代码单元数吧
from IPython.core.display import Image, display

def dog_breed_algorithm(img_path):
    if dog_detector(img_path) == 1:
        print("hello, dog!")
        display(Image(img_path,width=200,height=200))
        print("Your predicted breed is ... ")
        return print(Xception_predict_breed(img_path))
    elif face_detector(img_path) == 1:
        print("hello, human!")
        display(Image(img_path,width=200,height=200))
        print("You look like a ... ")
        return print(Xception_predict_breed(img_path))
    else:
        display(Image(img_path,width=200,height=200))
        return print("Could not identify a human or dog in the chosen image. Please try again.")

步骤 7: 测试你的算法

在这个部分中,你将尝试一下你的新算法!算法认为看起来像什么类型的狗?如果你有一只狗,它可以准确地预测你的狗的品种吗?如果你有一只猫,它会将你的猫误判为一只狗吗?

上传方式:点击左上角的Jupyter回到上级菜单,你可以看到Jupyter Notebook的右上方会有Upload按钮。

问题 11:

在下方编写代码,用至少6张现实中的图片来测试你的算法。你可以使用任意照片,不过请至少使用两张人类图片(要征得当事人同意哦)和两张狗的图片。 同时请回答如下问题:

  1. 输出结果比你预想的要好吗 :) ?或者更糟 :( ?
  2. 提出至少三点改进你的模型的想法。

1.结果比我预想的好。该算法可以准确识别出图片中是否含有狗或者人

  1. 1)对训练集进行数据增强以优化模型的表现 2)优化神经网络结构 3)增大数据集数据

In [44]:
## TODO: 在你的电脑上,在步骤6中,至少在6张图片上运行你的算法。
## 自由地使用所需的代码单元数吧
for i in range(1, 7):
    filename = 'images/' + str(i) + '.jpg'
    print('filename = ' + filename)
    dog_breed_algorithm(filename)
    print('\n')


filename = images/1.jpg
hello, dog!
Your predicted breed is ... 
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.4/xception_weights_tf_dim_ordering_tf_kernels_notop.h5
83689472/83683744 [==============================] - 1s 0us/step
in/045.Cardigan_welsh_corgi


filename = images/2.jpg
hello, dog!
Your predicted breed is ... 
in/005.Alaskan_malamute


filename = images/3.jpg
Could not identify a human or dog in the chosen image. Please try again.


filename = images/4.jpg
hello, dog!
Your predicted breed is ... 
in/006.American_eskimo_dog


filename = images/5.jpg
hello, human!
You look like a ... 
in/056.Dachshund


filename = images/6.jpg
hello, human!
You look like a ... 
in/050.Chinese_shar-pei


注意: 当你写完了所有的代码,并且回答了所有的问题。你就可以把你的 iPython Notebook 导出成 HTML 文件。你可以在菜单栏,这样导出File -> Download as -> HTML (.html)把这个 HTML 和这个 iPython notebook 一起做为你的作业提交。