前言:
上一节介绍了如何使用迁移学习进行图像识别,但是只是在单模型上进行图像识别的任务。本节介绍的是如何使用融合模型进行图像识别任务。说起来有点抽象,但是原理并不复杂。具体过程如下:
首先: 我们将图像放到InceptionV3、InceptionResNetV2模型之中,并且得到图像的隐层特征,PS(其实只要你要愿意可以多加几个模型的)
然后: 我们把得到图像隐层特征进行拼接操作, 并将拼接之后的特征经过全连接操作之后用于最后的分类。这里我谈一下我认为这样做的好处吧,如果我们参加一个图像识别的比赛话,大家用到的单模型可能都是一样的,对于图像的预处理操作这样不是什么难事,调参的方式也就那几样,这些几乎没有什么区别。而比赛当中的区分点就是你的模型要比对手的模型更好提取图像的特征。使用多个模型对不同的数据预处理操作,得到不同的特征,这绝对是个利器。PS(别过拟合就好)
最后最最最关键的是这个代码是个残次品,我会把其中出现的问题指出来,看看有没有人会帮助我一下吧。
# 首先我们看一下使用方法一的时候出现的问题也即是 method =old_idea
TypeError: Error when checking model input: data should be a Numpy array, or list/dict of Numpy arrays. Found: <keras.preprocessing.image.DirectoryIterator object at 0x00000180F3B8B278>...
错误原因是因为模型的输入是个numpy,但是我的模型输入却是keras.preprocessing.image.DirectoryIterator这个类型,其实想不明白了,但是我在单模型的情况下输入DirectoryIterator这个类就没有问题,但是为什么在多模型不行了,所以我就猜测是不是fit_generator不支持使用多个ImageDataGenerator类输入。okay于是我Google一下发现了keras的官方文档中是支持多ImageDataGenerator类的输入的,于是就有了方法2。
# 然后我们看一下使用方法二的时候出现的问题也即是 method == 'keras_method':
首先奉献上keras官方文档中图像预处理的方法。我对官方中fit_generator()同时使用多个ImageDataGenerator对象做个简单解读,也即是我同时定义两个ImageDataGenerator两个对象,然后同时flow_from_directory()加载数据,对于加载的数据使用zip进行合并操作得到唯一元素,作为fit_generator()的输入。理论上应该没有啥问题了。
BUT还是报错AttributeError: 'tuple' object has no attribute 'shape',还报错的明明白白告诉我们最后我们使用zip拼接的数据类型是元组,但是fit_generator()中需要的参数要能获得图像的形状。盲猜是keras的版本更新,这种加载数据的方法被丢弃了。我一直是使用tensorflow的,keras刚接触没几天,没必要花太多心思在这个方面。
方法三: Google一下看看有没有在做类似的事情,发现知乎上有人已经试过了融合模型了。想要复现的可以直接参考他的代码即可。
在我的代码中数据预处理的方式可以直接看method == 'keras_method'。
# -*- coding: utf-8 -*-
# @Time : 2019/7/6 16:06
# @Author : YYLin
# @Email : 854280599@qq.com
# @File : Fused_Model_All_Method.py
# 首先试一下两个模型的融合方法
from keras.applications.inception_v3 import InceptionV3
from keras.applications.inception_resnet_v2 import InceptionResNetV2
import keras
from keras.layers import Concatenate
import sys
from keras.preprocessing.image import ImageDataGenerator
batch_size = 64
img_size = 299
# method 是用来控制选择什么样的方式进行数据加载
method = 'old_idea'
if method == 'old_idea':
# 之前加载数据的方式 定义加载数据集的方法
train_data_generator = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
valid_data_generator = ImageDataGenerator(rescale=1. / 255)
train_generator = train_data_generator.flow_from_directory('../Dataset/Cat_Vs_Dog/train',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary')
valid_generator = valid_data_generator.flow_from_directory('../Dataset/Cat_Vs_Dog/valid',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary')
print('用于训练模型的数据类型是:', type(train_generator), train_generator)
elif method == 'keras_method':
# 这种方式参考的链接 keras官方文档中数据 https://keras.io/zh/preprocessing/image/
train_data_generator_main = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True)
train_data_generator_aux = ImageDataGenerator(rescale=1. / 255, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True)
valid_data_generator_main = ImageDataGenerator(rescale=1. / 255)
valid_data_generator_aux = ImageDataGenerator(rescale=1. / 255)
# 定义用于训练的数据 train_generator
seed = 1
train_generator_main = train_data_generator_main.flow_from_directory('../Dataset/Cat_Vs_Dog/train',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary',
seed=seed)
train_generator_aux = train_data_generator_aux.flow_from_directory('../Dataset/Cat_Vs_Dog/train',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary',
seed=seed)
train_generator = zip(train_generator_main, train_generator_aux)
# 定义用于验证的数据 valid_generator
seed = 2
valid_generator_main = valid_data_generator_main.flow_from_directory('../Dataset/Cat_Vs_Dog/valid',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary',
seed=seed)
valid_generator_aux = valid_data_generator_aux.flow_from_directory('../Dataset/Cat_Vs_Dog/valid',
target_size=(img_size, img_size),
batch_size=batch_size, class_mode='binary',
seed=seed)
valid_generator = zip(valid_generator_main, valid_generator_aux)
print('用于训练模型的数据类型是:', type(train_generator), train_generator)
elif method == 'to_do_method':
print('抱歉!!!!!暂时还没有想到好的方法解决这个问题, 以后补上')
sys.exit()
# 开始进行模型融合 得到两个模型的隐层特征 进行拼接操作 最后定义损失函数以及优化器
# 定义第一个模型 InceptionV3
base_model_InceptionV3 = InceptionV3(weights='imagenet', include_top=False)
X_InceptionV3 = base_model_InceptionV3.output
print('使用InceptionV3时模型的输出是:', type(X_InceptionV3), X_InceptionV3)
X_InceptionV3 = keras.layers.GlobalAveragePooling2D()(X_InceptionV3)
X_InceptionV3_hide = keras.layers.Dense(1024, activation='relu')(X_InceptionV3)
# 定义第二个模型 InceptionResNetV2
base_model_InceptionResNetV2 = InceptionResNetV2(weights='imagenet', include_top=False)
X_InceptionResNetV2 = base_model_InceptionResNetV2.output
X_InceptionResNetV2 = keras.layers.GlobalAveragePooling2D()(X_InceptionResNetV2)
X_InceptionResNetV2_hide = keras.layers.Dense(1024, activation='relu')(X_InceptionResNetV2)
# 模型拼接 并且模型的输出
x = Concatenate()([X_InceptionV3_hide, X_InceptionResNetV2_hide])
predictions = keras.layers.Dense(1, activation='sigmoid')(x)
model = keras.Model(inputs=[base_model_InceptionV3.input, base_model_InceptionResNetV2.input], outputs=predictions)
# 设置这些层的weight为不能训练
for layer in base_model_InceptionV3.layers:
layer.trainable = False
for layer in base_model_InceptionResNetV2.layers:
layer.trainable = False
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
model.fit_generator(
train_generator,
steps_per_epoch=500,
epochs=20,
workers=1,
validation_data=valid_generator,
validation_steps=100
)