• 【tensorflow2.6】图片数据建模流程:猫狗分类,83.6%识别率


    目标:识别猫和狗

    一、猫狗数据集

    数据集下载:

    到底部关注公众号,回复:猫狗数据集
    
    • 1

    训练数据集(每一张图片都有dog和cat标签):
    在这里插入图片描述
    测试集(图片没有标签):
    在这里插入图片描述

    二、训练环境

    • kaggle
    • tenslrflow2.6

    三、数据处理

    import numpy as np # linear algebra
    import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
    
    # Input data files are available in the read-only "../input/" directory
    # For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory
    
    import os
    for dirname, _, filenames in os.walk('/kaggle/input'):
        for filename in filenames:
            print(os.path.join(dirname, filename))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如下:
    在这里插入图片描述
    导入相关模块:

    import os
    import zipfile # 解压zip
    import pandas as pd 
    from tqdm import tqdm # 进度条显示
    import tensorflow as tf
    import matplotlib.pyplot as plt
    import matplotlib.image as mpimg
    from tensorflow.keras.optimizers import RMSprop
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    创建一个主文件夹:

    work_path = './cats_and_dogs_filtered'
    if not os.path.exists(work_path):
        os.mkdir(work_path)
    
    • 1
    • 2
    • 3

    把训练集和测试集的图片解压到主文件夹下面:

    local_zip = '../input/dogs-vs-cats/test1.zip'
    zip_ref = zipfile.ZipFile(local_zip,'r')
    zip_ref.extractall(work_path)
    
    local_zip = '../input/dogs-vs-cats/train.zip'
    zip_ref = zipfile.ZipFile(local_zip,'r')
    zip_ref.extractall(work_path)
    
    zip_ref.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    把训练集的数据读出来:

    train_path = os.path.join(work_path, 'train') # 拼接训练集文件名
    test_path = os.path.join(work_path, 'test1')  # 拼接测试集文件名
    
    train_df = pd.DataFrame({'image_name':os.listdir(train_path)}) # 读取图片名。os.listdir是列出文件下所有。
    train_df['label'] =train_df['image_name'].apply(lambda x: x.split('.')[0]) # 分割添加标签列
    train_df
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    同理把测试集的数据读取出来:

    test_df = pd.DataFrame({'image_name':os.listdir(test_path)})
    test_df['label'] =test_df['image_name'].apply(lambda x: x.split('.')[0])
    test_df
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    把所有狗的图片单独放在一个文件夹:

    dog_path_train = os.path.join(train_path, 'dog') # 拼接dog路径
    os.mkdir(dog_path_train) # 创建文件夹
    dog_df_train = train_df[train_df.label=='dog'] # 选出标签为狗的图片
    for n in tqdm(dog_df_train.image_name):
        os.rename((os.path.join(train_path, n)), (os.path.join(dog_path_train, n))) # 重命名
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    同理把猫的数据放在一个文件夹:

    cat_path_train = os.path.join(train_path, 'cat')
    os.mkdir(cat_path_train)
    cat_df_train = train_df[train_df.label=='cat']
    for n in tqdm(cat_df_train.image_name):
        os.rename((os.path.join(train_path, n)), (os.path.join(cat_path_train, n)))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    现在简单的检测一下目录的基本结构,当然这不是必须的部分:

    base_dir = './cats_and_dogs_filtered'
    
    print(' 基本主目录')
    print(os.listdir(base_dir))
    
    print('\n 训练目录')
    train_path = f'{base_dir}/train'
    print(os.listdir(train_path))
    
    print('\n 测试目录')
    print(os.listdir(test_path)[:5])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如图:
    在这里插入图片描述
    再继续检查目录:

    train_dir = os.path.join(base_dir,'train') # 拼接训练路径
    validation_dir = os.path.join(base_dir,'test1') # 拼接测试集路径
    
    train_cats_dir = os.path.join(train_dir,'cat') # 在训练集下拼接猫路径
    train_dogs_dir = os.path.join(train_dir,'dog') # 狗路径
    
    train_cats_names = os.listdir(train_cats_dir) # 列出所有猫的文件名
    train_dogs_names = os.listdir(train_dogs_dir) # 列出所有狗的文件名
    
    print(train_cats_names[:5])# 查看前五个
    print(train_dogs_names[:5])  # 查看前五个
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出:

    ['cat.5965.jpg', 'cat.10318.jpg', 'cat.11796.jpg', 'cat.10908.jpg', 'cat.7301.jpg']
    ['dog.3554.jpg', 'dog.5088.jpg', 'dog.7240.jpg', 'dog.2206.jpg', 'dog.7740.jpg']
    
    • 1
    • 2

    查看训练集测试集等数量:

    print(f'训练集猫数量 = {len(train_cats_names)}')
    print(f'训练集狗数量 = {len(train_dogs_names)}')
    print(f'测试集猫和狗数量= {len(os.listdir(validation_dir))}')
    
    • 1
    • 2
    • 3

    输出:

    训练集猫数量 = 12500
    训练集狗数量 = 12500
    测试集猫和狗数量= 12500
    
    • 1
    • 2
    • 3

    四、建立模型

    首先普及一点基本,卷积层语法如下:

    tf.keras.layers.Conv2D(
        filters,
        kernel_size,
        strides=(1, 1),
        padding='valid',
        data_format=None,
        dilation_rate=(1, 1),
        groups=1,
        activation=None,
        use_bias=True,
        kernel_initializer='glorot_uniform',
        bias_initializer='zeros',
        kernel_regularizer=None,
        bias_regularizer=None,
        activity_regularizer=None,
        kernel_constraint=None,
        bias_constraint=None,
        **kwargs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    相关参数如下:
    在这里插入图片描述
    在这里插入图片描述
    池化层语法如下:

    tf.keras.layers.MaxPool2D(
        pool_size=(2, 2),
        strides=None,
        padding='valid',
        data_format=None,
        **kwargs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    参数说明:
    在这里插入图片描述
    Flatten语法:

    tf.keras.layers.Flatten(
        data_format=None, **kwargs
    )
    
    • 1
    • 2
    • 3

    参数说明:一般默认即可
    在这里插入图片描述
    连接层语法如下:

    tf.keras.layers.Dense(
        units,
        activation=None,
        use_bias=True,
        kernel_initializer='glorot_uniform',
        bias_initializer='zeros',
        kernel_regularizer=None,
        bias_regularizer=None,
        activity_regularizer=None,
        kernel_constraint=None,
        bias_constraint=None,
        **kwargs
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编译(配置模型进行训练)语法:

    compile(
        optimizer='rmsprop',  			# 字符串(优化器的名称)或优化器实例
        loss=None,						# 损失函数,如果模型有多个输出,您可以通过传递字典或损失列表来对每个输出
        								# 使用不同的损失,由模型最小化的损失值将是所有独立损失的总和。
        metrics=None,					# 在训练和测试期间,模型要评估的度量标准列表,通常将使用metrics=['accuracy']。
        								# 要为一个多输出模型的不同输出指定不同的度量,可以传递一个字典,例如metrics={'output_a': 'accuracy', 'output_b': ['accuracy', 'mse']}。
        								# 还可以传递矩阵列表(len = len(输出)),比如metrics=[[accuracy'], ['accuracy', 'mse'],或者metrics=['accuracy', ['accuracy', 'mse']]。
        loss_weights=None,				# 指定标量系数(Python浮点数)的可选列表或字典,以对不同模型输出的损失贡献进行加权。
        sample_weight_mode=None,		# 如果需要按时间步长进行样本加权(2D加权),请将其设置为“时间”。没有默认的采样权值(1D)。
        weighted_metrics=None,			# 在训练和测试期间,将通过sample_weight或class_weight评估和加权的度量列表。
        target_tensors=None,			# 默认情况下,Keras将为模型的目标创建占位符,这些占位符将在训练期间与目标数据一起提供。
        								# 相反,如果您想使用自己的目标张量(反过来,Keras在训练时不会期望这些目标的外部Numpy数据),可以通过target_tensors参数指定它们。
        distribute=None,				# 在TF 2.0中不支持
        **kwargs						# 任何额外的参数
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    参数说明:

    编写代码模型函数如下:

    def create_model():
    # 使用models.Sequential()来搭建神经网络
    # Sequential()方法是一个容器,在容器中添加对应参数
    #在Sequential()的输入参数中描述从输入层到输出层的网络结构
      model = tf.keras.models.Sequential([ 
        # 本案例中Conv2D卷积层,第一个参数:filter 卷积核个数, 第二个参数strides 卷积步长,此三个参数activation:激活函数
        # 第四个参数input_shape表示150*150图片
        tf.keras.layers.Conv2D(16,(3,3), activation = 'relu', input_shape=(150,150,3)),
        # 在 2x2 池化窗口中取最大值
        tf.keras.layers.MaxPooling2D(2,2),
        
        tf.keras.layers.Conv2D(32,(3,3), activation = 'relu'),
        tf.keras.layers.MaxPooling2D(2,2),
    
        tf.keras.layers.Conv2D(64,(3,3), activation = 'relu'),
        tf.keras.layers.MaxPooling2D(2,2),
    
        tf.keras.layers.Flatten(),
         # 输出空间的维度512.激活函数relu
        tf.keras.layers.Dense(512, activation = 'relu'),
        # 激活函数sigmogid
        tf.keras.layers.Dense(1, activation='sigmoid')
      ])
    
      # learning_rate学习率为0.01,loss损失函数为二元交叉熵损失
     # metrics评估列表,这里只采用了accuracy准确度
      model.compile(optimizer=RMSprop(learning_rate=0.001),
                    loss='binary_crossentropy',
                    metrics=['accuracy']) 
        
      return model
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    执行模型:

    model = create_model()
    model.summary() # 打印模型摘要
    
    • 1
    • 2

    如图:
    在这里插入图片描述

    五、图像处理

    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    
    train_datagen = ImageDataGenerator(rescale=1./255,
          rotation_range=40,
          width_shift_range=0.2,
          height_shift_range=0.2,
          shear_range=0.2,
          zoom_range=0.2,
          horizontal_flip=True,
          fill_mode='nearest',
          validation_split=0.2
                                      )
    
    train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150,150),
        batch_size=50,
        class_mode='binary',
        subset='training'
    ) 
    
    
    validation_generator = train_datagen.flow_from_directory(
        train_dir, # 与训练集类似
        target_size=(150, 150),
        batch_size=50,
        class_mode='binary',
        subset='validation')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    构建回调:

    #构建新回调的抽象基类
    class mycallback(tf.keras.callbacks.Callback):
        def on_epoch_end(self,epoch,logs={}):
            if(logs.get('val_accuracy')>=0.90): # 大于90%停止
                self.model.stop_training = True
                
    callback = mycallback()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    六、训练模型

    参数说明:

    fit(
        x=None,
        y=None,
        batch_size=None, 				# 每次梯度更新的样本数
        epochs=1,						# 训练模型的迭代数
        verbose=1,
        callbacks=None,
        validation_split=0.0,			# 将训练数据的一部分用作验证数据
        validation_data=None,			# 用于评估损失的数据和每个epoch结束时的任何模型度量。模型不会根据这些数据进行训练。validation_data将覆盖validation_split。
        shuffle=True,					# 在每个epoch之前对训练数据进行洗牌
        class_weight=None,			
        sample_weight=None,
        initial_epoch=0,				# 开始训练的时间(对于恢复之前的训练很有用)。
        steps_per_epoch=None,
        validation_steps=None,
        validation_freq=1,
        max_queue_size=10,
        workers=1,
        use_multiprocessing=False,
        **kwargs
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    因此代码如下:

    history = model.fit(
        train_generator,
        steps_per_epoch = train_generator.samples//50,
        epochs = 30, # 迭代次数
        verbose=1, # 1表示显示进度条
        validation_data = validation_generator, # 在每个 epoch 结束时评估损失和任何模型指标的数据。模型不会根据这些数据进行训练
        validation_steps = validation_generator.samples//50,#b验证数据
        callbacks=[callback] #回调列表
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    训练两个小时候,得到如下:
    在这里插入图片描述

    七、模型评估

    打印准确度:

    print("最大准确度: {}%".format(round(100*max(history.history['val_accuracy']), 2)))
    
    • 1

    输出:

    最大准确度: 83.86%
    
    • 1

    八、可视化

    打印loss变化:

    import matplotlib.pyplot as plt
    def plot_metric(history, metric):
        train_metrics = history.history[metric]
        val_metrics = history.history['val_'+metric]
        epochs = range(1, len(train_metrics) + 1)
        plt.plot(epochs, train_metrics, 'bo--')
        plt.plot(epochs, val_metrics, 'ro-')
        plt.title('训练集和验证集 '+ metric)
        plt.xlabel("Epochs")
        plt.ylabel(metric)
        plt.legend(["train_"+metric, 'val_'+metric])
        plt.show()
        
    plot_metric(history,"loss")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    如图:
    在这里插入图片描述
    打印准确度变化:

    plot_metric(history,"accuracy")
    
    • 1

    如图:
    在这里插入图片描述

    九、保存模型

    model.save('./tf_model_savedmodel', save_format="tf")
    print('保存的模型成功..')
    
    • 1
    • 2

    输出:

    保存的模型成功..
    
    • 1

    十、参考

    tensorflow官方API文档:

    https://www.tensorflow.org/api_docs/python/tf_overview

    十一、感悟

    这是我第一次尝试搭建神经网络,猫狗分类是一个非常经典的案例了,在这整个学习中花了很长时间,比如:模型的搭建流程,模型的参数设置。开始我在本机训练模型,发现训练很久,自己电脑受不住,因此不得不转向kaggle上训练,经过了长达两个小时多的训练,最终识别率为83.86%。虽然不是很好,但也是经过一次很大的尝试。希望在后续中继续探索图片的分类,实际上我认为其它的图片分类与猫狗分类是类似的,因此有了迁移学习的概念,当然具体我还不了解,还在学习中。

    tensorflow的模型搭建流程可以总结为:
    在这里插入图片描述

    我的安排是:先学习一些经典案例,然后再深入学习这些基本的原理知识,这样学习对我来说更加高效。当然我希望您读这篇文章已经掌握机器学习大部分内容,为此我花了半个月的时间研读和实践了机器学习。

    欢迎关注我的个人公众号:
    在这里插入图片描述

  • 相关阅读:
    JDBC反序列化分析
    【马蹄集】第二十四周——高精度计算专题
    【计网 DNS】计算机网络 DNS协议详解:中科大郑烇老师笔记 (六)
    如何保证云docker容器重启后数据不丢失
    如何免费给PDF文件添加标注?
    【C++】假设给类分配的是栈的空间,那么计算机是如何访问栈中不同位置的对象的数据的呢?
    print输出
    RocketMQ-流程图-概念
    CrossOver 23.6 让Mac可以运行Windows程序的工具
    Netty 学习(三):通信协议和编解码
  • 原文地址:https://blog.csdn.net/weixin_46211269/article/details/125835345