1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 基于 Tensorflow 2.x 从零训练花卉图像识别模型

基于 Tensorflow 2.x 从零训练花卉图像识别模型

时间:2018-08-21 01:34:02

相关推荐

基于 Tensorflow 2.x 从零训练花卉图像识别模型

一、数据集准备

本篇文章使用数千张花卉照片作为数据集,共分为5个分类:雏菊(daisy)、蒲公英(dandelion)、玫瑰(roses)、向日葵(sunflowers)、郁金香(tulips),数据集下载地址:

//example_images/flower_photos.tgz

每个分类的图片放在单独的子目录下,下载完毕后解压可以看到如下所示:

如果想要训练自己的图片,也可以像这样的方式,将每个类别的图片放在相应的子目录下。

下面可以通过pathlib工具对目录进行解析,该工具在安装tensorflow时会自动安装。

例如:使用pathlib查看数据集图片的数量,由于图片都以jpg结尾,因此可以以为来过滤:

import pathlibpath = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)print(len(list(data_dir.glob('*/*.jpg'))))

一共包含3670张图片,对于我们训练模型来说还是有点少,训练起来很容易出现过拟合,后面在训练前会进行图像的增强。

例如再查看向日葵的示例图片:

import pathlibimport matplotlib.pyplot as pltpath = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)# 查看 向日葵 分类的图片sunflowers = list(data_dir.glob('sunflowers/*'))print(str(sunflowers[0]))plt.imshow(plt.imread(str(sunflowers[0])), cmap=plt.cm.gray)plt.show()

二、加载数据集

由于tf2.x推荐使用keras作为上层API工具,在keras中我们可以使用image_dataset_from_directory工具读取图片数据集,并且借助该工具,可以方便的进行数据集的划分、随机打乱、及统一大小操作,避免了自己再对数据集进行繁琐的操作。

使用起来如下所示:

from tensorflow import kerasimport pathlibbatch_size = 32img_height = 180img_width = 180class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)train_ds = keras.utils.image_dataset_from_directory(directory=data_dir,validation_split=0.2,subset="training",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb',)

其中参数表示:

查看image_dataset_from_directory工具读取后的的图像:

import pathlibimport matplotlib.pyplot as pltfrom tensorflow import kerasplt.rcParams['font.sans-serif'] = ['SimHei']path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)batch_size = 32img_height = 180img_width = 180# 使用 80% 的图像进行训练,20% 的图像进行验证。class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']class_names_cn = ['雏菊', '蒲公英', '玫瑰', '向日葵', '郁金香']train_ds = keras.utils.image_dataset_from_directory(directory=data_dir,validation_split=0.2,subset="training",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb',)# 遍历图像# 总批次大小print('训练总批次', len(train_ds))# 获取第一个批次数据plt.figure(figsize=(10, 10))for image_batch, labels_batch in train_ds.take(1):for i in range(9):print('图片批次shape: ', image_batch.shape)print('标签批次shape:', labels_batch.shape)print('单图片shape:', image_batch[i].shape)ax = plt.subplot(3, 3, i + 1)plt.imshow(image_batch[i].numpy().astype("uint8"))plt.title(class_names_cn[labels_batch[i]])plt.axis("off")plt.show()

使用缓冲区优化读取方式

使用上面的方式,会每次都做IO读取操作,从而有可能导致IO阻塞,影响模型训练的进度,因此可以加入缓冲区将图像保留在内存中,确保在训练模型时数据集不会成为瓶颈。在tensorflow中为我们提供了Dataset.cache()进行数据集的缓冲。

使用方式如下:

AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)

例如从缓冲区查看图片:

AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)# 遍历图像# 总批次大小print('训练总批次', len(train_ds))# 获取第一个批次数据plt.figure(figsize=(10, 10))for image_batch, labels_batch in train_ds.take(1):for i in range(9):ax = plt.subplot(3, 3, i + 1)plt.imshow(image_batch[i].numpy().astype("uint8"))plt.title(class_names_cn[labels_batch[i]])plt.axis("off")plt.show()

如果数据集太大无法装入内存,也可以使用此方法创建高性能的磁盘缓存。

三、数据增强

前面在第一点的提到一共包含3670张图片,划分20%的验证集,一共只有2936张图片参与训练,如果细心的小伙伴应该可以发现,5个分类的数据量,其实是不相等的,蒲公英 的数据量明显是比其他分类要多的,有可能在训练的过程中偏向于蒲公英 分类。

因此解决上面问题,对于图像问题可以首先考虑使用数据增强,让每次喂入模型的数据都是有区别的,进而达到扩充数据集,在tensorflow中我们可以通过对图像的随机翻转、随机旋转、随机改变对比度、随机缩放、以及随机裁剪等操作改变图像,并且数据增强的操作我们可以放在Sequential模型中。

当数据增强放在模型中时,在测试时会处于停用状态,只有在调用Model.fit(而非Model.evaluateModel.predict)期间才会对输入图像进行增强。

例如对图像进行随机旋转,这里将图片归一化操作也放在模型中了,这样的好处是在训练模型或预测模型时,可以不用做归一化操作了,同样也可以将Resizing放在模型中:

import pathlibimport matplotlib.pyplot as pltfrom tensorflow import kerasimport tensorflow as tfplt.rcParams['font.sans-serif'] = ['SimHei']path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)# keras 加载数据集batch_size = 32img_height = 180img_width = 180# 使用 80% 的图像进行训练,20% 的图像进行验证。class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']class_names_cn = ['雏菊', '蒲公英', '玫瑰', '向日葵', '郁金香']train_ds = keras.utils.image_dataset_from_directory(directory=data_dir,validation_split=0.2,subset="training",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb',)AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)model = keras.Sequential([# 归一化keras.layers.Rescaling(1. / 255),#随机旋转keras.layers.RandomRotation(0.2)])# 获取第一个批次数据for image_batch, labels_batch in train_ds.take(1):for i in range(len(image_batch)):plt.figure(figsize=(10, 10))for j in range(9):plt.subplot(3, 3, j + 1)augmented_image = model.predict(tf.expand_dims(image_batch[i], 0))print(augmented_image[0].shape)plt.imshow(augmented_image[0])plt.title(class_names_cn[labels_batch[i]])plt.axis('off')plt.show()

如果对使用预测的方式调用model呢,将上面程序中的:

augmented_image = model(tf.expand_dims(image_batch[i], 0))

换成:

augmented_image = model.predict(tf.expand_dims(image_batch[i], 0))

再次运行,可以发现数据增强部分没有了:

除了随机旋转,上面提到还可以加入随机翻转、随机改变对比度、随机缩放等,这里将Resizing也加入到模型中,到后面预测模型时,直接给原图像即可:

import pathlibimport matplotlib.pyplot as pltfrom tensorflow import kerasimport tensorflow as tfplt.rcParams['font.sans-serif'] = ['SimHei']path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)# keras 加载数据集batch_size = 32img_height = 180img_width = 180# 使用 80% 的图像进行训练,20% 的图像进行验证。class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']class_names_cn = ['雏菊', '蒲公英', '玫瑰', '向日葵', '郁金香']train_ds = keras.utils.image_dataset_from_directory(directory=data_dir,validation_split=0.2,subset="training",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb',)AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)# 缩放和归一化IMG_SIZE = 180resize_and_rescale = tf.keras.Sequential([tf.keras.layers.Resizing(IMG_SIZE, IMG_SIZE),tf.keras.layers.Rescaling(1. / 255)])# 图像增强data_augmentation = tf.keras.Sequential([# 翻转tf.keras.layers.RandomFlip("horizontal_and_vertical"),# 旋转tf.keras.layers.RandomRotation(0.2),# 对比度tf.keras.layers.RandomContrast(0.3),# 随机缩放tf.keras.layers.RandomZoom(height_factor=0.3, width_factor=0.3),])model = keras.Sequential([resize_and_rescale,data_augmentation])# 获取第一个批次数据for image_batch, labels_batch in train_ds.take(1):for i in range(len(image_batch)):plt.figure(figsize=(10, 10))for j in range(9):plt.subplot(3, 3, j + 1)augmented_image = model(tf.expand_dims(image_batch[i], 0))print(augmented_image[0].shape)plt.imshow(augmented_image[0])plt.title(class_names_cn[labels_batch[i]])plt.axis('off')plt.show()

四、构建训练模型

本篇文章我们使用自己搭建的模型,训练后在验证集上大概可以达到75%的准确度,下篇文章使用迁移训练优化,正好和本篇的训练结果形成对比,下面是模型的结构:

通过Keras建立模型结构:

import tensorflow as tffrom tensorflow import keras# 定义模型类class mnistModel():# 初始化结构def __init__(self, checkpoint_path, log_path, model_path, num_classes, img_width, img_height):# checkpoint 权重保存地址self.checkpoint_path = checkpoint_path# 训练日志保存地址self.log_path = log_path# 训练模型保存地址:self.model_path = model_path# 数据统一大小并归一处理resize_and_rescale = tf.keras.Sequential([keras.layers.Resizing(img_width, img_height),keras.layers.Rescaling(1. / 255)])# 数据增强data_augmentation = tf.keras.Sequential([# 翻转keras.layers.RandomFlip("horizontal_and_vertical"),# 旋转keras.layers.RandomRotation(0.2),# 对比度keras.layers.RandomContrast(0.3),# 随机裁剪# tf.keras.layers.RandomCrop(IMG_SIZE, IMG_SIZE),# 随机缩放keras.layers.RandomZoom(height_factor=0.3, width_factor=0.3),])# 初始化模型结构self.model = keras.Sequential([resize_and_rescale,data_augmentation,keras.layers.Conv2D(32, (3, 3),kernel_initializer=keras.initializers.truncated_normal(stddev=0.05),kernel_regularizer=keras.regularizers.l2(0.001),padding='same',activation='relu'),keras.layers.MaxPooling2D(2, 2),keras.layers.Conv2D(32, (3, 3),kernel_initializer=keras.initializers.truncated_normal(stddev=0.05),kernel_regularizer=keras.regularizers.l2(0.001),padding='same',activation='relu'),keras.layers.MaxPooling2D(2, 2),keras.layers.Conv2D(32, (3, 3),kernel_initializer=keras.initializers.truncated_normal(stddev=0.05),kernel_regularizer=keras.regularizers.l2(0.001),padding='same',activation='relu'),keras.layers.MaxPooling2D(2, 2),keras.layers.Flatten(),keras.layers.Dense(1024,kernel_initializer=keras.initializers.truncated_normal(stddev=0.05),kernel_regularizer=keras.regularizers.l2(0.001),activation=tf.nn.relu),keras.layers.Dropout(0.2),keras.layers.Dense(256,kernel_initializer=keras.initializers.truncated_normal(stddev=0.05),kernel_regularizer=keras.regularizers.l2(0.001),activation=tf.nn.relu),keras.layers.Dense(num_classes, activation='softmax')])# 编译模型def compile(self):# 输出模型摘要self.model.build(input_shape=(None, 180, 180, 3))self.model.summary()# 定义训练模式pile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])# 训练模型def train(self, train_ds, val_ds):# tensorboard 训练日志收集tensorboard = keras.callbacks.TensorBoard(log_dir=self.log_path)# 训练过程保存 Checkpoint 权重,防止意外停止后可以继续训练model_checkpoint = keras.callbacks.ModelCheckpoint(self.checkpoint_path, # 保存模型的路径# monitor='val_loss', # 被监测的数据。verbose=0, # 详细信息模式,0 或者 1save_best_only=True, # 如果 True, 被监测数据的最佳模型就不会被覆盖save_weights_only=True,# 如果 True,那么只有模型的权重会被保存 (model.save_weights(filepath)),否则的话,整个模型会被保存,(model.save(filepath))mode='auto',# {auto, min, max}的其中之一。 如果 save_best_only=True,那么是否覆盖保存文件的决定就取决于被监测数据的最大或者最小值。 对于 val_acc,模式就会是 max,而对于 val_loss,模式就需要是 min,等等。 在 auto模式中,方向会自动从被监测的数据的名字中判断出来。period=3 # 每3个epoch保存一次权重)# 填充数据,迭代训练self.model.fit(train_ds, # 训练集validation_data=val_ds, # 验证集epochs=200, # 迭代周期verbose=2, # 训练过程的日志信息显示,一个epoch输出一行记录callbacks=[tensorboard, model_checkpoint])# 保存训练模型self.model.save(self.model_path)def evaluate(self, val_ds):# 评估模型test_loss, test_acc = self.model.evaluate(val_ds)return test_loss, test_acc

处理数据集:

import tensorflow as tfimport pathlibfrom tensorflow import kerasdef getData():# 加载数据集path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)# keras 加载数据集batch_size = 32img_height = 180img_width = 180# 使用 80% 的图像进行训练,20% 的图像进行验证。class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']train_ds = keras.utils.image_dataset_from_directory(data_dir,validation_split=0.2,subset="training",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb')val_ds = keras.utils.image_dataset_from_directory(data_dir,validation_split=0.2,subset="validation",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb')AUTOTUNE = tf.data.AUTOTUNEtrain_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)return train_ds, val_ds, len(class_names), img_width, img_height

开始训练模型:

def main():# 加载数据集train_ds, val_ds, num_classes, img_width, img_height = getData()checkpoint_path = './checkout/'log_path = './log'model_path = './model/model.h5'# 构建模型model = mnistModel(checkpoint_path, log_path, model_path, num_classes, img_width, img_height)# 编译模型pile()# 训练模型model.train(train_ds, val_ds)# 评估模型test_loss, test_acc = model.evaluate(val_ds)print(test_loss, test_acc)if __name__ == '__main__':main()

运行后可以看到打印的网络结构:

从训练日志中,可以看到 loss 一直在减小:

训练结束后评估模型的结果,最终在验证集上的准确率为:74.79%

最后看下tensorboard中可视化的损失及准确率:

tensorboard --logdir=log/train

使用浏览器访问:http://localhost:6006/查看结果:

五、模型预测

训练后会在model下生成model.h5模型,下面直接加载该模型进行预测:

import tensorflow as tfimport pathlibfrom tensorflow import kerasimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei']def main():# 加载数据集path = "F:/Tensorflow/datasets/flower/flower_photos"# 解析目录data_dir = pathlib.Path(path)# keras 加载数据集batch_size = 32img_height = 180img_width = 180# 使用 80% 的图像进行训练,20% 的图像进行验证。class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']class_names_cn = ['雏菊', '蒲公英', '玫瑰', '向日葵', '郁金香']val_ds = keras.utils.image_dataset_from_directory(data_dir,validation_split=0.2,subset="validation",image_size=(img_height, img_width),batch_size=batch_size,shuffle=True,seed=123,interpolation='bilinear',crop_to_aspect_ratio=True,labels='inferred',class_names=class_names,color_mode='rgb')model = keras.models.load_model('./model/model.h5')# 获取第一个批次数据for image_batch, labels_batch in val_ds.take(3):plt.figure(figsize=(10, 10))for i in range(9):plt.subplot(3, 3, i + 1)softmax = model.predict(tf.expand_dims(image_batch[i], 0))y_label = tf.argmax(softmax, axis=1).numpy()[0]plt.imshow(image_batch[i].numpy().astype("uint8"))plt.title('预测结果:' + class_names_cn[y_label] + ',概率:' + str("%.2f" % softmax[0][y_label]) + ',真实结果:' +class_names_cn[labels_batch[i]])plt.axis('off')plt.show()if __name__ == '__main__':main()

可以发现还是有些识别失败,毕竟只有大约75%的准确度,下篇文章使用MobileNetV2模型对该数据集进行迁移优化,以提高模型的准确度。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。