文章目录

前言

利用TensorFlow实现对花朵数据集的图片分类

提示:以下是本篇文章正文内容,下面案例可供参考

一、数据集

数据集是五个分别存放着对应类别花朵图片的五个文件夹,包括daisy(雏菊)633张;dandelion(蒲公英)898张,rose(玫瑰)641张,sunflower(向日葵)699张,tulips(郁金香)799张。

二、代码

1.下载数据集

import tensorflow as tf
AUTOTUNE = tf.data.experimental.AUTOTUNE
import pathlib
data_root_orig = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz', fname='flower_photos', untar=True)
data_root = pathlib.Path(data_root_orig)
print(data_root)
for item in data_root.iterdir():  
    print(item)

打印下载后的文件路径和文件成员:
output:C:\Users\Administrator.keras\datasets\flower_photos
C:\Users\Administrator.keras\datasets\flower_photos\daisy
C:\Users\Administrator.keras\datasets\flower_photos\dandelion
C:\Users\Administrator.keras\datasets\flower_photos\LICENSE.txt
C:\Users\Administrator.keras\datasets\flower_photos\roses
C:\Users\Administrator.keras\datasets\flower_photos\sunflowers
C:\Users\Administrator.keras\datasets\flower_photos\tulips

2.统计并观察数据

#获取五个文件夹的名字
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
label_names

output:[‘daisy’, ‘dandelion’, ‘roses’, ‘sunflowers’, ‘tulips’]

#将文件夹的名字(即花的分类)标上序号
label_to_index = dict((name, index) for index, name in enumerate(label_names))
label_to_index

{‘daisy’: 0, ‘dandelion’: 1, ‘roses’: 2, ‘sunflowers’: 3, ‘tulips’: 4}

#获取所以图片的标签(0,1,2,3,4)
import random
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)
image_count = len(all_image_paths)
image_count

all_image_labels = [label_to_index[pathlib.Path(path).parent.name]for path in all_image_paths]
print("First 10 labels indices: ", all_image_labels[:10])

3670
First 10 labels indices: [0, 2, 3, 4, 2, 1, 4, 1, 4, 0]

下面我们先来观察一张图片

#观察第一张图片
img_path = all_image_paths[0]
img_path

‘C:\Users\Administrator\.keras\datasets\flower_photos\daisy\11124324295_503f3a0804.jpg’

#读取原图
img_raw = tf.io.read_file(img_path)
#转换成TensorFlow可以使用的tensor类型
img_tensor = tf.image.decode_image(img_raw)
print(img_tensor.shape)
print(img_tensor.dtype)

(309, 500, 3)
<dtype: ‘uint8’>

#对图片按要求进行转换,这里将size规定到【192,192】;值域映射到【0,1】
img_final = tf.image.resize(img_tensor, [192, 192])
img_final = img_final/255.0
print(img_final.shape)
print(img_final.numpy().min())
print(img_final.numpy().max())

(192, 192, 3)
0.0
0.99984366

定义预处理和加载函数

def preprocess_image(image):  
    image = tf.image.decode_jpeg(image, channels=3)  
    image = tf.image.resize(image, [192, 192])  
    image /= 255.0  # normalize to [0,1] range  return image
    return image
def load_and_preprocess_image(path):  
    image = tf.io.read_file(path)  
    return preprocess_image(image)

导入matpoltlib进行画图(导入失败的解决方案见我的另一篇博文)

import matplotlib.pyplot as plt
image_path = all_image_paths[0]
label = all_image_labels[0]
print (load_and_preprocess_image(img_path))
plt.imshow(load_and_preprocess_image(img_path))
plt.grid(False)

在这里插入图片描述

3,构建数据集

使用tf.data.Dataset来构建规范的数据集

#“from_tensor_slices ”方法使用张量的切片元素构建图片路径的数据集
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
#同理,构建标签数据集,并用tf.cast转换成int64数据类型
label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))
#根据路径获取图片,并经过加载和预处理得到图片数据集
image_ds = path_ds.map(load_and_preprocess_image )
image_ds

<MapDataset shapes: (192, 192, 3), types: tf.float32>

将image_ds和label_ds打包成新的数据集

image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))
print(image_label_ds)

<ZipDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int64)>

画图查看我们构造完成的新数据

plt.figure(figsize=(8,8))
for n,image_label in enumerate(image_label_ds.take(4)):
    plt.subplot(2,2,n+1)
    plt.imshow(image_label[0])
    plt.grid(False)

在这里插入图片描述

4,迁移学习进行分类

接下来用创建的数据集训练一个分类模型,简单起见,直接用tf.keras.applications包中训练好的模型,并将其迁移到我们的图片分类问题上来。这里使用的模型是MobileNetV2模型

#迁移MobileNetV2模型,并且不加载顶层
base_model=tf.keras.applications.mobilenet_v2.MobileNetV2(include_top=False,weights='imagenet',input_shape=(192,192,3))
inputs=tf.keras.layers.Input(shape=(192,192,3))

#模型可视化1,使用model.summary()方法
base_model.summary()
 

接下来,我们打乱一下数据集,并定义好训练过程中每个批次(Batch)数据的大小

#使用shuffle方法打乱数据集
image_count = len(all_image_paths)
ds = image_label_ds.shuffle(buffer_size = image_count)
#让数据集重复多次
ds = ds.repeat()
#设置每个批次的大小
BATCH_SIZE = 32
ds = ds.batch(BATCH_SIZE)
#通过prefetch方法让模型的训练和每个批次数据的加载并行
ds = ds.prefetch(buffer_size = AUTOTUNE)

然后,针对MobileNetV2改变一样数据集的取值范围,因为MobileNetV2接受输入的数据值域是【-1,1】,而我们之前的预处理函数将图片的像素值映射到【0,1】

def change_range(image,label):
    return 2*image-1,label
keras_ds = ds.map(change_range)

接下来定义模型,由于预训练好的MobileNetV2返回的数据维度是(32,6,6,128),其中32是一个批次Batch的大小,“6,6”是输出的特征的大小为6*6,1280代表该层使用的1280个卷积核。为了使用花朵分类问题,需要做一下调整

model = tf.keras.Sequential([
    base_model,
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(len(label_names),activation="softmax")
    
])

如上代码,我们用Sequentail建立我们的网络结构,base_model是迁移过来的模型,我们添加了全局评价池化层GlobalAveragePooling,经过此操作6*6的特征被降维,变为(32,1280)。最后,由于该分类问题有五个结果,我们增加一个全连接层(Dense)将维度变为(32,5)。

最后,编译一下模型,同时制定使用的优化器,损失函数和评价标准
model.compile(optimizer = tf.keras.optimizers.Adam(),
             loss='sparse_categorical_crossentropy',
             metrics=['accuracy'])
model.summary()

在这里插入图片描述

使用model.fit训练模型,epochs是训练的回合数,step_per_epoch代表每个回合要去多少个批次数据。通常等于我们数据集大小除以批次大小后取证(3670/32≈10)

model.fit(ds,epochs=10,steps_per_epoch=100)

在这里插入图片描述

虽然没有跑完整个代码,但是已经能看出来准确度达到一个很高的程度,并在逐步上升。