在下方的代码单元(cell)中,我们导入了一个狗图像的数据集。我们使用scikit-learn库中的load_files函数来获取一些变量:
importrandomrandom.seed(8675309)#加载打乱后的人脸数据集的文件名human_files=np.array(glob("/data/lfw/*/*"))random.shuffle(human_files)#打印数据集的数据量print('Thereare%dtotalhumanimages.'%len(human_files))Thereare13233totalhumanimages.
在如下代码单元中,我们将演示如何使用这个检测模型在样本图像中找到人脸。
在上方的代码中,faces以numpy数组的形式,保存了识别到的面部信息。它其中每一行表示一个被检测到的脸,该数据包括如下四个信息:前两个元素x、y代表识别框左上角的x和y坐标(参照上图,注意y坐标的方向和我们默认的方向不同);后两个元素代表识别框在x和y轴两个方向延伸的长度w和d。
我们可以将这个程序封装为一个函数。该函数的输入为人脸图像的路径,当图像中包含人脸时,该函数返回True,反之返回False。该函数定义如下所示。
#如果img_path路径表示的图像检测到了脸,返回"True"defface_detector(img_path):img=cv2.imread(img_path)gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)faces=face_cascade.detectMultiScale(gray)returnlen(faces)>0【练习】评估人脸检测模型问题1:在下方的代码块中,使用face_detector函数,计算:
理想情况下,人图像中检测到人脸的概率应当为100%,而狗图像中检测到人脸的概率应该为0%。你会发现我们的算法并非完美,但结果仍然是可以接受的。我们从每个数据集中提取前100个图像的文件路径,并将它们存储在human_files_short和dog_files_short中。
human_files_short=human_files[:100]dog_files_short=train_files[:100]##请不要修改上方代码##TODO:基于human_files_short和dog_files_short##中的图像测试face_detector的表现print(np.mean([face_detector(human)forhumaninhuman_files_short]))print(np.mean([face_detector(dog)fordogindog_files_short]))1.00.11问题2:就算法而言,该算法成功与否的关键在于,用户能否提供含有清晰面部特征的人脸图像。那么你认为,这样的要求在实际使用中对用户合理吗?如果你觉得不合理,你能否想到一个方法,即使图像中并没有清晰的面部特征,也能够检测到人脸?
回答:不合理;使用CNN;
Tips:
补充阅读材料:
我们建议在你的算法中使用opencv的人脸检测模型去检测人类图像,不过你可以自由地探索其他的方法,尤其是尝试使用深度学习来解决它:)。请用下方的代码单元来设计和测试你的面部监测算法。如果你决定完成这个选做任务,你需要报告算法在每一个数据集上的表现。
##(选做)TODO:报告另一个面部检测算法在LFW数据集上的表现###你可以随意使用所需的代码单元数
在实现了图像处理的部分之后,我们就可以使用模型来进行预测。这一步通过predict方法来实现,它返回一个向量,向量的第i个元素表示该图像属于第i个ImageNet类别的概率。这通过如下的ResNet50_predict_labels函数实现。
fromkeras.applications.resnet50importpreprocess_input,decode_predictionsdefResNet50_predict_labels(img_path):#返回img_path路径的图像的预测向量img=preprocess_input(path_to_tensor(img_path))returnnp.argmax(ResNet50_model.predict(img))完成狗检测模型
我们通过这些想法来完成下方的dog_detector函数,如果从图像中检测到狗就返回True,否则返回False。
defdog_detector(img_path):prediction=ResNet50_predict_labels(img_path)return((prediction<=268)&(prediction>=151))【作业】评估狗狗检测模型
问题3:
在下方的代码块中,使用dog_detector函数,计算:
###TODO:测试dog_detector函数在human_files_short和dog_files_short的表现print(np.mean([dog_detector(human)forhumaninhuman_files_short]))print(np.mean([dog_detector(dog)fordogindog_files_short]))0.01.0步骤3:从头开始创建一个CNN来分类狗品种
现在我们已经实现了一个函数,能够在图像中识别人类及狗狗。但我们需要更进一步的方法,来对狗的类别进行识别。在这一步中,你需要实现一个卷积神经网络来对狗的品种进行分类。你需要从头实现你的卷积神经网络(在这一阶段,你还不能使用迁移学习),并且你需要达到超过1%的测试集准确率。在本项目的步骤五种,你还有机会使用迁移学习来实现一个准确率大大提高的模型。
值得注意的是,对狗的图像进行分类是一项极具挑战性的任务。因为即便是一个正常人,也很难区分布列塔尼犬和威尔士史宾格犬。
不难发现其他的狗品种会有很小的类间差别(比如金毛寻回犬和美国水猎犬)。
同样,拉布拉多犬(labradors)有黄色、棕色和黑色这三种。那么你设计的基于视觉的算法将不得不克服这种较高的类间差别,以达到能够将这些不同颜色的同类狗分到同一个品种中。
黄色拉布拉多犬(YellowLabrador)|棕色拉布拉多犬(ChocolateLabrador)|黑色拉布拉多犬(BlackLabrador)
我们也提到了随机分类将得到一个非常低的结果:不考虑品种略有失衡的影响,随机猜测到正确品种的概率是1/133,相对应的准确率是低于1%的。
请记住,在深度学习领域,实践远远高于理论。大量尝试不同的框架吧,相信你的直觉!当然,玩得开心!
数据预处理
通过对每张图像的像素值除以255,我们对图像实现了归一化处理。
fromPILimportImageFileImageFile.LOAD_TRUNCATED_IMAGES=True#Keras中的数据预处理过程train_tensors=paths_to_tensor(train_files).astype('float32')/255valid_tensors=paths_to_tensor(valid_files).astype('float32')/255test_tensors=paths_to_tensor(test_files).astype('float32')/255100%|██████████|6680/6680[01:14<00:00,89.11it/s]100%|██████████|835/835[00:08<00:00,100.40it/s]100%|██████████|836/836[00:08<00:00,101.04it/s]【练习】模型架构
创建一个卷积神经网络来对狗品种进行分类。在你代码块的最后,执行model.summary()来输出你模型的总结信息。
我们已经帮你导入了一些所需的Python库,如有需要你可以自行导入。如果你在过程中遇到了困难,如下是给你的一点小提示——该模型能够在5个epoch内取得超过1%的测试准确率,并且能在CPU上很快地训练。
问题4:
回答:我使用上图提示的步骤搭建卷积网络,该架构能取得很好表现的原因有:1)卷积层对图片中的特征进行局部感知,以便后续从更高层次对局部进行综合操作,从而得到全局信息;2)池化层用于特征降维,压缩数据和参数的数量,减小过拟合,同时提高模型的容错性;MaxPooling能够保留最强的特征,抛弃其他弱的此类特征。GlobalAveragePooling将最后一层的特征进行均值池化,形成一个特征点,将这些特征点组成最后的特征向量以进行softmax计算;3)密集层里是高度提纯的特征,它将进行最后的分类;sigmoid的梯度在饱和区域非常平缓,接近于0,很容易造成梯度消失的问题。而Relu的梯度大多数情况下是常数,有助于解决深层网络的收敛问题。
更进一步,可以尝试不同的模型结构,如更多的卷积层和全连接层、更多的节点数、使用不同类型的正则化层(Dropout、BatchNormalization等)、使用不同的权值初始化方案(truncated_normal、xavier等)、使用不同的激活函数(LeakyReLU、eLU等)、抉择使用Flatten还是GlobalAveragePooling2D等。在实际应用中,你需要根据场景的不同来设计不同的模型架构、使用不同的超参数。对比各类结构和超参数给模型带来的影响,有助于你更好的理解模型的结构。
##编译模型model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])Tips:
提高准确率有很多小技巧~
通常模型的大小取决于数据的量和复杂度,但是如果你使用max-pooling,你需要增加向上的每一层的神经元(比如你可以double一下)。通常在denselayer之前有2-5层,kernelsize3-5就差不多。你也可以用gridsearch找一组比较满意的参数~
常用的正则化方法:
更多的阅读资料:
【练习】训练模型
问题5:
在下方代码单元训练模型。使用模型检查点(modelcheckpointing)来储存具有最低验证集loss的模型。
keras.callbacks.ModelCheckpoint(filepath,monitor='val_loss',verbose=0,save_best_only=False,save_weights_only=False,mode='auto',period=1)或者你也可以把epoch&modelaccuracy和epoch&modelloss的关系图打印出来,然后找一个比较满意的epoch,参考代码如下:
#Fitthemodelhistory=model.fit(X,Y,validation_split=0.33,epochs=150,batch_size=10,verbose=0)#listalldatainhistoryprint(history.history.keys())#summarizehistoryforaccuracyplt.plot(history.history['acc'])plt.plot(history.history['val_acc'])plt.title('modelaccuracy')plt.ylabel('accuracy')plt.xlabel('epoch')plt.legend(['train','test'],loc='upperleft')plt.show()#summarizehistoryforlossplt.plot(history.history['loss'])plt.plot(history.history['val_loss'])plt.title('modelloss')plt.ylabel('loss')plt.xlabel('epoch')plt.legend(['train','test'],loc='upperleft')plt.show()Tips:
更多阅读材料:
测试模型
在狗图像的测试数据集上试用你的模型。确保测试准确率大于1%。
#获取测试数据集中每一个图像所预测的狗品种的indexdog_breed_predictions=[np.argmax(model.predict(np.expand_dims(tensor,axis=0)))fortensorintest_tensors]#报告测试准确率test_accuracy=100*np.sum(np.array(dog_breed_predictions)==np.argmax(test_targets,axis=1))/len(dog_breed_predictions)print('Testaccuracy:%.4f%%'%test_accuracy)Testaccuracy:2.2727%步骤4:使用一个CNN来区分狗的品种
得到从图像中提取的特征向量(BottleneckFeatures)
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激活函数,对每一个狗的种类都包含一个节点。
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)OutputShapeParam#=================================================================global_average_pooling2d_2((None,512)0_________________________________________________________________dense_2(Dense)(None,133)68229=================================================================Totalparams:68,229Trainableparams:68,229Non-trainableparams:0_________________________________________________________________Tips:迁移学习的思路就是将一个预训练的模型,通过新的训练集进行二次训练。分为三种形式:
现在,我们可以测试此CNN在狗图像测试数据集中识别品种的效果如何。我们在下方打印出测试准确率。
#获取测试数据集中每一个图像所预测的狗品种的indexVGG16_predictions=[np.argmax(VGG16_model.predict(np.expand_dims(feature,axis=0)))forfeatureintest_VGG16]#报告测试准确率test_accuracy=100*np.sum(np.array(VGG16_predictions)==np.argmax(test_targets,axis=1))/len(VGG16_predictions)print('Testaccuracy:%.4f%%'%test_accuracy)Testaccuracy:41.5072%使用模型预测狗的品种
fromextract_bottleneck_featuresimport*defVGG16_predict_breed(img_path):#提取bottleneck特征bottleneck_feature=extract_VGG16(path_to_tensor(img_path))#获取预测向量predicted_vector=VGG16_model.predict(bottleneck_feature)#返回此模型预测的狗的品种returndog_names[np.argmax(predicted_vector)]
步骤5:建立一个CNN来分类狗的品种(使用迁移学习)
现在你将使用迁移学习来建立一个CNN,从而可以从图像中识别狗的品种。你的CNN在测试集上的准确率必须至少达到60%。
在步骤4中,我们使用了迁移学习来创建一个使用基于VGG-16提取的特征向量来搭建一个CNN。在本部分内容中,你必须使用另一个预训练模型来搭建一个CNN。为了让这个任务更易实现,我们已经预先对目前keras中可用的几种网络进行了预训练:
这些文件被命名为为:
Dog{network}Data.npz其中{network}可以是VGG19、Resnet50、InceptionV3或Xception中的一个。选择上方网络架构中的一个,他们已经保存在目录/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']###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']Tips:当下比较主流的架构非ResNet-50和Xception莫属~轻易就能达到80%以上的准确率~
【练习】模型架构
建立一个CNN来分类狗品种。在你的代码单元块的最后,通过运行如下代码输出网络的结构:
问题6:
在下方的代码块中尝试使用Keras搭建最终的网络架构,并回答你实现最终CNN架构的步骤与每一步的作用,并描述你在迁移学习过程中,使用该网络架构的原因。
回答:1)将Xception最后一层的输出作为全局平均池化层的输入;2)增加一个全连接层,使用softmax激活函数,节点数设置为狗的种类数133。选用Xception是因为该模型在狗狗分类中准确率较高;这一架构会在这一分类任务中成功的原因:1)利用迁移学习获取了优良的模型和参数;2)训练次数提高到了20次;早期(第三步)尝试不成功的原因:1)模型相对来说没有迁移学习中的好;2)训练次数只有5次,太少了。
问题7:
在下方代码单元中训练你的模型。使用模型检查点(modelcheckpointing)来储存具有最低验证集loss的模型。
训练过程中,注意到第2次epoch之后验证误差就几乎没有提升了,同时因为你保存了最优模型,意味着你后面的训练都是在浪费计算资源;同时也观察到,第20次epoch时,验证误差远大于训练误差,这说明模型出现了过拟合。思考并尝试尽量减轻这种过拟合现象吧~
提示:
【练习】测试模型
问题8:
在狗图像的测试数据集上试用你的模型。确保测试准确率大于60%。
###TODO:在测试集上计算分类准确率Xception_predictions=[np.argmax(Xception_model.predict(np.expand_dims(feature,axis=0)))forfeatureintest_Xception]test_accuracy=100*np.sum(np.array(Xception_predictions)==np.argmax(test_targets,axis=1))/len(Xception_predictions)print('Testaccuracy:%.4f%%'%test_accuracy)Testaccuracy:83.9713%【练习】使用模型测试狗的品种
实现一个函数,它的输入为图像路径,功能为预测对应图像的类别,输出为你模型预测出的狗类别(Affenpinscher,Afghan_hound等)。
与步骤5中的模拟函数类似,你的函数应当包含如下三个步骤:
提取图像特征过程中使用到的函数可以在extract_bottleneck_features.py中找到。同时,他们应已在之前的代码块中被导入。根据你选定的CNN网络,你可以使用extract_{network}函数来获得对应的图像特征,其中{network}代表VGG19,Resnet50,InceptionV3,或Xception中的一个。
问题9:
###TODO:写一个函数,该函数将图像的路径作为输入###然后返回此模型所预测的狗的品种defXception_predict_breed(img_path):bottleneck_feature=extract_Xception(path_to_tensor(img_path))predicted_vector=Xception_model.predict(bottleneck_feature)returndog_names[np.argmax(predicted_vector)]
步骤6:完成你的算法
实现一个算法,它的输入为图像的路径,它能够区分图像是否包含一个人、狗或两者都不包含,然后:
我们非常欢迎你来自己编写检测图像中人类与狗的函数,你可以随意地使用上方完成的face_detector和dog_detector函数。你需要在步骤5使用你的CNN来预测狗品种。
下面提供了算法的示例输出,但你可以自由地设计自己的模型!
问题10:
在下方代码块中完成你的代码。
###TODO:设计你的算法###自由地使用所需的代码单元数吧defdog_check(img_path):ifdog_detector(img_path):print('It''sadog.Itlookslike')print(Xception_predict_breed(img_path))elifface_detector(img_path):print('It''shuman.Itlookslike')print(Xception_predict_breed(img_path))else:print('Error!')