基于Tensorflow和Keras实现卷积神经网络CNN

基于Tensorflow和Keras实现卷积神经网络CNN——猫狗分类

  • 一、环境的配置
  • 二、神经网络CNN的介绍
  • 三、数据集的准备
  • 四、猫狗分类的实例——基准模型
  • 五、基准模型的调整
  • 六、使用VGG19实现猫狗分类
  • 七、参考资料

一、环境的配置

  1. 安装Anaconda
    具体安装过程,请自行百度
  2. 配置TensorFlow、Keras
    ①创建虚拟环境
    输入下面命令
    conda create -n tf1 python=3.6
    #tf1是自己为创建虚拟环境取的名字,后面python的版本可以根据自己需求进行选择
    
    ②安装tensorflow和keras
    pip install 包名
    #直接这样安装可以由于网络的原因,安装失败或者安装很慢
    #解决方式:
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名
    #此次安装命令如下:
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow==1.14.0
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple keras==2.2.5
    

二、神经网络CNN的介绍

  1. 神经网络说明
    具体内容请参考:
    https://blog.csdn.net/love__live1/article/details/79480845
  2. 卷积神经网络CNN的结构
    ①输入层
    用于数据的输入
    ②卷积层
    使用卷积核进行特征提取和特征映射
    ③激励层
    由于卷积也是一种线性运算,因此需要增加非线性映射
    ④池化层
    进行下采样,对特征图稀疏处理,减少数据运算量。
    ⑤全连接层
    通常在CNN的尾部进行重新拟合,减少特征信息的损失
  3. 整个结构图
  4. 重要层的说明
    ①卷积层
    基于Tensorflow和Keras实现卷积神经网络CNN_第1张图片
    上面图中是33的卷积核(卷积核一般采用33和2*2 )与上一层的结果(输入层)进行卷积的过程
    ②池化层
    基于Tensorflow和Keras实现卷积神经网络CNN_第2张图片
    最大池化,它只是输出在区域中观察到的最大输入值
    均值池化,它只是输出在区域中观察到的平均输入值
    两者最大区别在于卷积核的不同(池化是一种特殊的卷积过程)
    ③全连接层

    全连接过程,跟神经网络一样,就是每个神经元与上一层的所有神经元相连
  5. 卷积神经网络CNN的特点
    ①局部连接
    每个神经元不再和上一层的所有神经元相连,而只和一小部分神经元相连。这样就减少了很多参数。
    ②权值共享
    一组连接可以共享同一个权重,而不是每个连接有一个不同的权重,这样又减少了很多参数。
    ③下采样
    可以使用Pooling来减少每层的样本数,进一步减少参数数量,同时还可以提升模型的鲁棒性。

三、数据集的准备

  1. 数据集的下载
    kaggle网站的数据集下载地址:
    https://www.kaggle.com/lizhensheng/-2000
    百度网盘下载
    链接:https://pan.baidu.com/s/13hw4LK8ihR6-6-8mpjLKDA
    密码:dmp4
  2. 数据集的分类
    将下载的数据集进行解压操作
    基于Tensorflow和Keras实现卷积神经网络CNN_第3张图片
    按照命名进行分类
    分类前
    基于Tensorflow和Keras实现卷积神经网络CNN_第4张图片
    分类后
    基于Tensorflow和Keras实现卷积神经网络CNN_第5张图片
    分类代码如下
    import tensorflow as tf
    import keras
    import os, shutil 
    # 原始目录所在的路径
    original_dataset_dir = 'G:\\Cat_And_Dog\\kaggle\\train\\'
    
    # 数据集分类后的目录
    base_dir = 'G:\\Cat_And_Dog\\kaggle\\cats_and_dogs_small'
    os.mkdir(base_dir)
    
    # # 训练、验证、测试数据集的目录
    train_dir = os.path.join(base_dir, 'train')
    os.mkdir(train_dir)
    validation_dir = os.path.join(base_dir, 'validation')
    os.mkdir(validation_dir)
    test_dir = os.path.join(base_dir, 'test')
    os.mkdir(test_dir)
    
    # 猫训练图片所在目录
    train_cats_dir = os.path.join(train_dir, 'cats')
    os.mkdir(train_cats_dir)
    
    # 狗训练图片所在目录
    train_dogs_dir = os.path.join(train_dir, 'dogs')
    os.mkdir(train_dogs_dir)
    
    # 猫验证图片所在目录
    validation_cats_dir = os.path.join(validation_dir, 'cats')
    os.mkdir(validation_cats_dir)
    
    # 狗验证数据集所在目录
    validation_dogs_dir = os.path.join(validation_dir, 'dogs')
    os.mkdir(validation_dogs_dir)
    
    # 猫测试数据集所在目录
    test_cats_dir = os.path.join(test_dir, 'cats')
    os.mkdir(test_cats_dir)
    
    # 狗测试数据集所在目录
    test_dogs_dir = os.path.join(test_dir, 'dogs')
    os.mkdir(test_dogs_dir)
    
    # 将前1000张猫图像复制到train_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(train_cats_dir, fname)
        shutil.copyfile(src, dst)
    
    # 将下500张猫图像复制到validation_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(validation_cats_dir, fname)
        shutil.copyfile(src, dst)
        
    # 将下500张猫图像复制到test_cats_dir
    fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(test_cats_dir, fname)
        shutil.copyfile(src, dst)
        
    # 将前1000张狗图像复制到train_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(train_dogs_dir, fname)
        shutil.copyfile(src, dst)
        
    # 将下500张狗图像复制到validation_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(validation_dogs_dir, fname)
        shutil.copyfile(src, dst)
        
    # 将下500张狗图像复制到test_dogs_dir
    fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
    for fname in fnames:
        src = os.path.join(original_dataset_dir, fname)
        dst = os.path.join(test_dogs_dir, fname)
        shutil.copyfile(src, dst)
    
    查看分类后,对应目录下图片数量
    #输出数据集对应目录下图片数量
    print('total training cat images:', len(os.listdir(train_cats_dir)))
    print('total training dog images:', len(os.listdir(train_dogs_dir)))
    print('total validation cat images:', len(os.listdir(validation_cats_dir)))
    print('total validation dog images:', len(os.listdir(validation_dogs_dir)))
    print('total test cat images:', len(os.listdir(test_cats_dir)))
    print('total test dog images:', len(os.listdir(test_dogs_dir)))
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第6张图片

四、猫狗分类的实例——基准模型

  1. 构建网络模型
    #网络模型构建
    from keras import layers
    from keras import models
    #keras的序贯模型
    model = models.Sequential()
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(32, (3, 3), activation='relu',
                            input_shape=(150, 150, 3)))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核2*2,激活函数relu
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #flatten层,用于将多维的输入一维化,用于卷积层和全连接层的过渡
    model.add(layers.Flatten())
    #全连接,激活函数relu
    model.add(layers.Dense(512, activation='relu'))
    #全连接,激活函数sigmoid
    model.add(layers.Dense(1, activation='sigmoid'))
    
    查看模型各层的参数状况
    #输出模型各层的参数状况
    model.summary()
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第7张图片
  2. 配置训练方法
    model.compile(optimizer = 优化器,
                  loss = 损失函数,
                  metrics = ["准确率”])
    
    其中,优化器和损失函数可以是字符串形式的名字,也可以是函数形式。
    from keras import optimizers
    
    model.compile(loss='binary_crossentropy',
                  optimizer=optimizers.RMSprop(lr=1e-4),
                  metrics=['acc'])
    
  3. 文件中图像转换成所需格式
    将训练和验证的图片,调整为150*150
    from keras.preprocessing.image import ImageDataGenerator
    
    # 所有图像将按1/255重新缩放
    train_datagen = ImageDataGenerator(rescale=1./255)
    test_datagen = ImageDataGenerator(rescale=1./255)
    
    train_generator = train_datagen.flow_from_directory(
            # 这是目标目录
            train_dir,
            # 所有图像将调整为150x150
            target_size=(150, 150),
            batch_size=20,
            # 因为我们使用二元交叉熵损失,我们需要二元标签
            class_mode='binary')
    
    validation_generator = test_datagen.flow_from_directory(
            validation_dir,
            target_size=(150, 150),
            batch_size=20,
            class_mode='binary')
    
    在这里插入图片描述
    查看处理结果
    #查看上面对于图片预处理的处理结果
    for data_batch, labels_batch in train_generator:
        print('data batch shape:', data_batch.shape)
        print('labels batch shape:', labels_batch.shape)
        break
    
    在这里插入图片描述
  4. 模型训练并保存生成的模型
    #模型训练过程
    history = model.fit_generator(
          train_generator,
          steps_per_epoch=100,
          epochs=30,
          validation_data=validation_generator,
          validation_steps=50)
    #保存训练得到的的模型
    model.save('G:\\Cat_And_Dog\\kaggle\\cats_and_dogs_small_1.h5')
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第8张图片
  5. 结果可视化
    #对于模型进行评估,查看预测的准确性
    import matplotlib.pyplot as plt
    
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    
    epochs = range(len(acc))
    
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    
    plt.figure()
    
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    
    plt.show()
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第9张图片
    基于Tensorflow和Keras实现卷积神经网络CNN_第10张图片
    由可视化结果,可以发现训练的loss是成上升趋势。所以,训练获得的模型存在一些问题,导致模型过拟合。过拟合是为了得到一致假设而使假设变得过度严格,实际训练得到的模型的分类效果不佳。

五、基准模型的调整

  1. 图像增强
    利用图像生成器定义一些常见的图像变换,图像增强就是通过对于图像进行变换,从而,增强图像中的有用信息。
    #该部分代码及以后的代码,用于替代基准模型中分类后面的代码(执行代码前,需要先将之前分类的目录删掉,重写生成分类,否则,会发生错误)
    from keras.preprocessing.image import ImageDataGenerator
    datagen = ImageDataGenerator(
          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')
    
    ①rotation_range
    一个角度值(0-180),在这个范围内可以随机旋转图片
    ②width_shift和height_shift
    范围(作为总宽度或高度的一部分),在其中可以随机地垂直或水平地转换图片
    ③shear_range
    用于随机应用剪切转换
    ④zoom_range
    用于在图片内部随机缩放
    ⑤horizontal_flip
    用于水平随机翻转一半的图像——当没有假设水平不对称时(例如真实世界的图片)
    ⑥fill_mode
    用于填充新创建像素的策略,它可以在旋转或宽度/高度移动之后出现
  2. 查看增强后的图像
    import matplotlib.pyplot as plt
    # This is module with image preprocessing utilities
    from keras.preprocessing import image
    fnames = [os.path.join(train_cats_dir, fname) for fname in os.listdir(train_cats_dir)]
    # We pick one image to "augment"
    img_path = fnames[3]
    # Read the image and resize it
    img = image.load_img(img_path, target_size=(150, 150))
    # Convert it to a Numpy array with shape (150, 150, 3)
    x = image.img_to_array(img)
    # Reshape it to (1, 150, 150, 3)
    x = x.reshape((1,) + x.shape)
    # The .flow() command below generates batches of randomly transformed images.
    # It will loop indefinitely, so we need to `break` the loop at some point!
    i = 0
    for batch in datagen.flow(x, batch_size=1):
        plt.figure(i)
        imgplot = plt.imshow(image.array_to_img(batch[0]))
        i += 1
        if i % 4 == 0:
            break
    plt.show()
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第11张图片
  3. 网络模型增加一层dropout
    #网络模型构建
    from keras import layers
    from keras import models
    #keras的序贯模型
    model = models.Sequential()
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(32, (3, 3), activation='relu',
                            input_shape=(150, 150, 3)))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核2*2,激活函数relu
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #卷积层,卷积核是3*3,激活函数relu
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    #最大池化层
    model.add(layers.MaxPooling2D((2, 2)))
    #flatten层,用于将多维的输入一维化,用于卷积层和全连接层的过渡
    model.add(layers.Flatten())
    #退出层
    model.add(layers.Dropout(0.5))
    #全连接,激活函数relu
    model.add(layers.Dense(512, activation='relu'))
    #全连接,激活函数sigmoid
    model.add(layers.Dense(1, activation='sigmoid'))
    #输出模型各层的参数状况
    model.summary()
    from keras import optimizers
    
    model.compile(loss='binary_crossentropy',
                  optimizer=optimizers.RMSprop(lr=1e-4),
                  metrics=['acc'])
    
    不添加dropout的网络结构
    基于Tensorflow和Keras实现卷积神经网络CNN_第12张图片
    添加dropout后的网络结构
    基于Tensorflow和Keras实现卷积神经网络CNN_第13张图片
  4. 训练模型
    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,)
    # Note that the validation data should not be augmented!
    test_datagen = ImageDataGenerator(rescale=1./255)
    train_generator = train_datagen.flow_from_directory(
            # This is the target directory
            train_dir,
            # All images will be resized to 150x150
            target_size=(150, 150),
            batch_size=32,
            # Since we use binary_crossentropy loss, we need binary labels
            class_mode='binary')
    validation_generator = test_datagen.flow_from_directory(
            validation_dir,
            target_size=(150, 150),
            batch_size=32,
            class_mode='binary')
    history = model.fit_generator(
          train_generator,
          steps_per_epoch=100,
          epochs=100,
          validation_data=validation_generator,
          validation_steps=50)
    model.save('G:\\Cat_And_Dog\\kaggle\\cats_and_dogs_small_2.h5')
    
    只进行数据增强的训练结果
    基于Tensorflow和Keras实现卷积神经网络CNN_第14张图片
    数据增强和dropout层增加的训练结果基于Tensorflow和Keras实现卷积神经网络CNN_第15张图片
  5. 结果可视化
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(len(acc))
    plt.plot(epochs, acc, 'bo', label='Training acc')
    plt.plot(epochs, val_acc, 'b', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='Training loss')
    plt.plot(epochs, val_loss, 'b', label='Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    
    只进行数据增强的可视化结果
    基于Tensorflow和Keras实现卷积神经网络CNN_第16张图片
    基于Tensorflow和Keras实现卷积神经网络CNN_第17张图片
    数据增强和dropout层增加的可视化结果基于Tensorflow和Keras实现卷积神经网络CNN_第18张图片
    基于Tensorflow和Keras实现卷积神经网络CNN_第19张图片
    对比基准模型来看,可以很清楚的发现loss的整体趋势是变小的。对比,只进行图像增强获得的模型和进行图像增强与添加dropout层获得的模型,可以发现前者在训练过程中波动会更大,后者在准确上小于前者。两者虽然在准确率有所变小,但是都避免了过拟合。

六、使用VGG19实现猫狗分类

  1. 初始化一个VGG19网络实例
    from keras.applications import VGG19
    conv_base = VGG19(weights = 'imagenet',include_top = False,input_shape=(150, 150, 3))
    conv_base.summary()
    
    首次运行时候,会自动从对应网站下载h5格式文件
    在这里插入图片描述
    上面下载很慢,而且还有可能在中途挂掉,因此建议将网址复制到浏览器上,直接下载。然后,将下载的文件,放到对应的目录下
    我下载存放的位置
    在这里插入图片描述
    其模型网络结构
    基于Tensorflow和Keras实现卷积神经网络CNN_第20张图片
    基于Tensorflow和Keras实现卷积神经网络CNN_第21张图片
  2. 将猫狗数据集传递给神经网络
    将分类后的猫狗数据集传递给神经网络,让它把图片的隐含信息给抽取出来
    import os 
    import numpy as np
    from keras.preprocessing.image import ImageDataGenerator
    # 数据集分类后的目录
    base_dir = 'E:\\Cat_And_Dog\\kaggle\\cats_and_dogs_small'
    train_dir = os.path.join(base_dir, 'train')
    validation_dir = os.path.join(base_dir, 'validation')
    test_dir = os.path.join(base_dir, 'test')
    datagen = ImageDataGenerator(rescale = 1. / 255)
    batch_size = 20
    def extract_features(directory, sample_count):
        features = np.zeros(shape = (sample_count, 4, 4, 512))
        labels = np.zeros(shape = (sample_count))
        generator = datagen.flow_from_directory(directory, target_size = (150, 150), 
                                                batch_size = batch_size,
                                                class_mode = 'binary')
        i = 0
        for inputs_batch, labels_batch in generator:
            #把图片输入VGG16卷积层,让它把图片信息抽取出来
            features_batch = conv_base.predict(inputs_batch)
            #feature_batch 是 4*4*512结构
            features[i * batch_size : (i + 1)*batch_size] = features_batch
            labels[i * batch_size : (i+1)*batch_size] = labels_batch
            i += 1
            if i * batch_size >= sample_count :
                #for in 在generator上的循环是无止境的,因此我们必须主动break掉
                break
            return features , labels
    #extract_features 返回数据格式为(samples, 4, 4, 512)
    train_features, train_labels = extract_features(train_dir, 2000)
    validation_features, validation_labels = extract_features(validation_dir, 1000)
    test_features, test_labels = extract_features(test_dir, 1000)	
    
    在这里插入图片描述
  3. 将抽取的特征输入到我们自己的神经层中进行分类训练
    from keras import models
    from keras import layers
    from keras import optimizers
    #构造我们自己的网络层对输出数据进行分类
    model = models.Sequential()
    model.add(layers.Dense(256, activation='relu', input_dim = 4 * 4 * 512))
    model.add(layers.Dropout(0.5))
    model.add(layers.Dense(1, activation = 'sigmoid'))
    model.compile(optimizer=optimizers.RMSprop(lr = 2e-5), loss = 'binary_crossentropy', metrics = ['acc'])
    history = model.fit(train_features, train_labels, epochs = 30, batch_size = 20, 
                        validation_data = (validation_features, validation_labels))
    
    训练过程(时间很快)
    基于Tensorflow和Keras实现卷积神经网络CNN_第22张图片
  4. 训练结果和校验结果的可视化
    import matplotlib.pyplot as plt
    acc = history.history['acc']
    val_acc = history.history['val_acc']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label = 'Train_acc')
    plt.plot(epochs, val_acc, 'b', label = 'Validation acc')
    plt.title('Trainning and validation accuracy')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label = 'Training loss')
    plt.plot(epochs, val_loss, 'b', label = 'Validation loss')
    plt.title('Training and validation loss')
    plt.legend()
    plt.show()
    
    基于Tensorflow和Keras实现卷积神经网络CNN_第23张图片
    基于Tensorflow和Keras实现卷积神经网络CNN_第24张图片
    从训练结果可以看出,其模型得到结果比前面自己构建的都有好。

七、参考资料

  1. 【TensorFlow&Keras】入门猫狗数据集实验–理解卷积神经网络CNN
  2. 基于jupyter notebook的python编程-----猫狗数据集的阶段分类得到模型精度并进行数据集优化

你可能感兴趣的:(tensorflow,机器学习,keras,猫狗分类,CNN)