zoukankan      html  css  js  c++  java
  • CNN实战篇-手把手教你利用开源数据进行图像识别(基于keras搭建)

    我一直强调做深度学习,最好是结合实际的数据上手,参照理论,对知识的掌握才会更加全面。先了解原理,然后找一匹数据来验证,这样会不断加深对理论的理解。

     欢迎留言与交流!

    数据来源: cifar10  (其他相关的图片的开源数据集下载见 : https://yq.aliyun.com/articles/576274 文末有全部代码

    PS:神经网络系列多用于图像,文字的生成,解析,识别。因此需要掌握充足的开源数据集来验证所学的算法理论。

    首先下载好数据后解压。数据的样子如下: data_batch1-5是训练集数据,test_batch是验证集, batches.meta是10个标签的含义

    接下来分两个大步骤:

    一是数据处理,使其符合模型的输入接口。

    二是模型搭建,为了训练出准确有效的模型。

    • 数据处理部分:

    在python环境下导入需要的库

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import tensorflow as tf
    from tensorflow import keras
    from keras import backend as K
    K.set_image_dim_ordering('tf') 
    from keras.applications.imagenet_utils import preprocess_input, decode_predictions
    from keras.preprocessing import image

    之后我们先导入并观察数据,处理成keras 搭建的模型可使用的格式。

    导入代码:

    # 定义读取方法
    def unpickle(file):
        import pickle
        with open(file, 'rb') as fo:
            dict = pickle.load(fo, encoding='bytes')
        return dict
    
    # 读取CIFAR10数据
    cifar={}
    # 合并5个训练集
    for i in range(5):    
        cifar1=unpickle('data_batch_'+str(i+1))
        if i==0:
            cifar[b'data']=cifar1[b'data']
            cifar[b'labels']=cifar1[b'labels']
        else:
            cifar[b'data']=np.vstack([cifar1[b'data'],cifar[b'data']])
            cifar[b'labels']=np.hstack([cifar1[b'labels'],cifar[b'labels']])
    # label的含义 写在 batches.meta文件里
    target_name=unpickle('batches.meta')
    cifar[b'label_names']=target_name[b'label_names']    
    
    # 测试集读取
    cifar_test=unpickle('test_batch')
    cifar_test[b'labels']=np.array(cifar_test[b'labels'])

     合并之后,我们观察一下数据:

    发现是rgb三通道数据拉伸成一维的像素了,即原来为32x32x3的rgb图像,变成了3072个行像素。
    如下图:

     

    那么接下来要做的就是还原数据,并看一下打好标签的10类对应的图片长什么样子

    # 定义数据格式 将原三通道的一维数据还原至三通道的二维图片格式
    blank_image= np.zeros((len(cifar[b'data']),32,32,3), np.uint8) #定义一个rpb图的集合
    blank_image2= np.zeros((len(cifar[b'data']),32,32), np.uint8) #定义一个灰度图的集合 稍后写入
    for i in range(len(cifar[b'data'])):
        blank_image[i] = np.zeros((32,32,3), np.uint8) 
        blank_image[i][:,:,0]=cifar[b'data'][i][0:1024].reshape(32,32) #前1024个像素还原为32x32并写入到rgb的第一个red通道.
        blank_image[i][:,:,1]=cifar[b'data'][i][1024:1024*2].reshape(32,32) #中间1024个像素还原为32x32并写入到rgb的第二个green通道.
        blank_image[i][:,:,2]=cifar[b'data'][i][1024*2:1024*3].reshape(32,32) #后1024个像素还原为32x32并写入到rgb的第三个blue通道.
    cifar[b'data']=blank_image
    cifar[b'data2']=blank_image2

    至此测试集还原完毕,可以看到测试集cifar[b'data']变成:

     

    接着同理处理一下验证集,不做详述了:

    # 测试集图片数据还原
    blank_image= np.zeros((len(cifar_test[b'data']),32,32,3), np.uint8)
    blank_image2= np.zeros((len(cifar_test[b'data']),32,32), np.uint8) #定义一个灰度图的集合 稍后写入
    for i in range(len(cifar_test[b'data'])):
        blank_image[i] = np.zeros((32,32,3), np.uint8)
        blank_image[i][:,:,0]=cifar_test[b'data'][i][0:1024].reshape(32,32)
        blank_image[i][:,:,1]=cifar_test[b'data'][i][1024:1024*2].reshape(32,32)
        blank_image[i][:,:,2]=cifar_test[b'data'][i][1024*2:1024*3].reshape(32,32)
    
    # data2处理成黑白 data处理为彩色
    cifar_test[b'data']=blank_image
    cifar_test[b'data2']=blank_image2

    我们选10个种类 各画一张图观察一下:

    # 画图关闭
    target_list=pd.Series(cifar[b'labels']).drop_duplicates() # labels去重
    target_list=target_list.sort_values()  # labels排序
    target_list=list(target_list.index) # 提取后即为10个标签对应的测试集的位置,找出这些位置的图画出来即可
    target_figure=cifar[b'data'][target_list];
    
    for i in range(10):
        plt.subplot(2,5,1+i)
        plt.imshow(target_figure[i]),plt.axis('off') 

    数据集的10个label图:

     

    现在到了数据处理的最后一步,定义测试集和训练集,并归一化:

    # 训练数据集定义,训练集,测试集归一化
    x_train=cifar[b'data'] # 训练集数据
    y_train=cifar[b'labels'] # 训练集标签
    x_test=cifar_test[b'data'] # 验证集标签
    y_test=cifar_test[b'labels'] # 验证集标签
    
    x_train=x_train.reshape(-1,32,32,3) # 训练集格式确保为32x32的rgb三通道格式
    x_test=x_test.reshape(-1,32,32,3)  #  验证集同理
    class_names=cifar[b'label_names'];
    
    x_train = x_train.astype('float32') 
    x_test = x_test.astype('float32')
    x_train=x_train/255 #将像素的0到255 归一化至0-1
    x_test=x_test/255

    至此数据处理部分完毕,等待模型建立后调用即可。

    • 模型建立部分:
    # 由经典卷积神经模型 VGG16 简化而来
    model = tf.keras.models.Sequential([ #此处可简化,但须均维持keras格式或者tf格式
            
      tf.keras.layers.Conv2D(32,
                             kernel_size=(3, 3), 
                             padding='same',
                             activation='relu',
                             input_shape=(32,32,3)
                             ),      #第一层卷积 卷积核为3x3    
                             
      tf.keras.layers.Conv2D(32,
                             kernel_size=(3,3),
                             activation='relu',
                             padding='same',
                             data_format='channels_last',
                             ),     #第二层卷积 卷积核为3x3
    
      tf.keras.layers.MaxPooling2D(pool_size=(2, 2)), # 池化 缩小  
      tf.keras.layers.Dropout(0.25),  # 防止过拟合
                             
      tf.keras.layers.Conv2D(64,
                             kernel_size=(3, 3), 
                             padding='same',
                             activation='relu',
                             input_shape=(32,32,1)
                             ),          
                             
      tf.keras.layers.Conv2D(64,
                             kernel_size=(3,3),
                             activation='relu',
                             padding='same',
                             data_format='channels_last',
                             ),
    
      tf.keras.layers.MaxPooling2D(pool_size=(2, 2)), # 池化缩小  
      tf.keras.layers.Dropout(0.25),   # 防止过拟合
      
      tf.keras.layers.Flatten(),  # 将所有特征展平为一维
      
      tf.keras.layers.Dense(512,activation='relu'), # 与一个512节点全链接,激活条件 relu
      tf.keras.layers.Dropout(0.5),           
    
      tf.keras.layers.Dense(10,activation=tf.nn.softmax) # 分类专用激活函数 softmax
      ])

    CNN框架解析: 按照步骤来--->

      1. 输入rgb图片32x32x3:(一共有50000个,一个一个来)

      2. 经过第一次卷积变成了 32x32x32,

    (第一步卷积说明: 按照我们参数的设置建立了32个神经元,每个神经元由一个3x3x3的卷积核,也就是32个不同的卷积核卷积后形成了32个不同的特征,因此卷积后将32x32x3大小的图特征维度变成了32。其中每一个神经元有3x3x3+1(bias)个权重。 )

      3. 经过第二次卷积变成了 32x32x32,

      4. 再经过2x2的步长为2的池化(最大池化法),缩小一倍。

      5. 经过第三次卷积变成了 32x32x64,

      6. 经过第四次卷积变成了 32x32x64,

      7. 再经过2x2的步长为2的池化,缩小一倍

      8. 随后拉伸为一维和一个512节点进行全连接,

      9. 再与一个10个节点全连接)

     

     

    这一部分对应下图:

     

    模型搭建好以后就要开始使用它,即模型编译与开始训练:

    # 模型编译
    model.compile(optimizer='adam',# keras.optimizers.Adadelta()
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    
    # 模型加载训练集  callbacks=tensorboard 监控
    model.fit(x_train, y_train,batch_size=32, epochs=10,verbose=1, validation_data=(x_test, y_test),
              callbacks=[keras.callbacks.TensorBoard(log_dir='./tmp/keras_log',write_images=1, histogram_freq=1),
                         ]) #verbos 输出日志 keras.callbacks.EarlyStopping(patience=10, monitor='val_acc')
        
    # epochs 数据集所有样本跑过一遍的次数 搭配 batch_size多少个一组进行训练 调整权重
    model.evaluate(x_test, y_test,verbose=0)

      这一部分的核心是需要了解 优化器,loss函数的不同种类和适用范围。即 model.compile中

    optimizer 和 loss 的定义


    待训练完毕后 可以加载tensorboard观察模型和训练过程:
    # tensorboard 加载监控
    import webbrowser
    url='http://a1414l039:6006'
    webbrowser.open(url, new=0, autoraise=True)
    import os
    cmd='cd /d '+os.getcwd()+'\tmp && tensorboard --logdir keras_log'
    os.system(cmd)
    keras.backend.clear_session()#必要-用以解决重复调用时会话应结束

    跑10轮后正确率在验证集和训练集基本维持在78%以上了。

    如果需要提高精确度可以适当将全连接层的节点数放大,比如512换成1000.

    如果图片大于32x32的话,根据情况拓展卷积的深度。

     整体代码附上:

    # -*- coding: utf-8 -*-
    """
    Created on Thu Jan 27 14:29:54 2019
    
    @author: wenzhe.tian
    """
    
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    import tensorflow as tf
    from tensorflow import keras
    from keras import backend as K
    K.set_image_dim_ordering('tf') 
    from keras.applications.imagenet_utils import preprocess_input, decode_predictions
    from keras.preprocessing import image
    
    
    # 定义读取方法
    def unpickle(file):
        import pickle
        with open(file, 'rb') as fo:
            dict = pickle.load(fo, encoding='bytes')
        return dict
    
    # 读取CIFAR10数据
    cifar={}
    # 合并给5个训练集
    for i in range(5):    
        cifar1=unpickle('data_batch_'+str(i+1))
        if i==0:
            cifar[b'data']=cifar1[b'data']
            cifar[b'labels']=cifar1[b'labels']
        else:
            cifar[b'data']=np.vstack([cifar1[b'data'],cifar[b'data']])
            cifar[b'labels']=np.hstack([cifar1[b'labels'],cifar[b'labels']])
    # label的含义 写在 batches.meta文件里
    target_name=unpickle('batches.meta')
    cifar[b'label_names']=target_name[b'label_names']    
    
    # 测试集读取
    cifar_test=unpickle('test_batch')
    cifar_test[b'labels']=np.array(cifar_test[b'labels'])
    
    
    # 定义数据格式 将原三通道的一维数据还原至三通道的二维图片格式
    blank_image= np.zeros((len(cifar[b'data']),32,32,3), np.uint8) #定义一个rpb图的集合
    blank_image2= np.zeros((len(cifar[b'data']),32,32), np.uint8) #定义一个灰度图的集合 稍后写入
    for i in range(len(cifar[b'data'])):
        blank_image[i] = np.zeros((32,32,3), np.uint8) 
        blank_image[i][:,:,0]=cifar[b'data'][i][0:1024].reshape(32,32) #前1024个像素还原为32x32并写入到rgb的第一个red通道.
        blank_image[i][:,:,1]=cifar[b'data'][i][1024:1024*2].reshape(32,32) #中间1024个像素还原为32x32并写入到rgb的第二个green通道.
        blank_image[i][:,:,2]=cifar[b'data'][i][1024*2:1024*3].reshape(32,32) #后1024个像素还原为32x32并写入到rgb的第三个blue通道.
    cifar[b'data']=blank_image
    cifar[b'data2']=blank_image2
    
    # 测试集图片数据还原
    blank_image= np.zeros((len(cifar_test[b'data']),32,32,3), np.uint8)
    blank_image2= np.zeros((len(cifar_test[b'data']),32,32), np.uint8) #定义一个灰度图的集合 稍后写入
    for i in range(len(cifar_test[b'data'])):
        blank_image[i] = np.zeros((32,32,3), np.uint8)
        blank_image[i][:,:,0]=cifar_test[b'data'][i][0:1024].reshape(32,32)
        blank_image[i][:,:,1]=cifar_test[b'data'][i][1024:1024*2].reshape(32,32)
        blank_image[i][:,:,2]=cifar_test[b'data'][i][1024*2:1024*3].reshape(32,32)
    
    # data2处理成黑白 data处理为彩色
    cifar_test[b'data']=blank_image
    cifar_test[b'data2']=blank_image2
    
    # 画图关闭
    #target_list=pd.Series(cifar[b'labels']).drop_duplicates() # labels去重
    #target_list=target_list.sort_values()  # labels排序
    #target_list=list(target_list.index) # 提取后即为10个标签对应的测试集的位置,找出这些位置的图画出来即可
    #target_figure=cifar[b'data'][target_list];
    #
    #for i in range(10):
    #    plt.subplot(2,5,1+i)
    #    plt.imshow(target_figure[i]),plt.axis('off') 
    
    
    # 转化为灰度预测    
    def rgb2gray(rgb):
        r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
        gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
        return gray
    
    for i in range(len(cifar_test[b'data'])):
        temp=rgb2gray(cifar_test[b'data'][i])
        cifar_test[b'data2'][i]=temp
    
    for i in range(len(cifar[b'data'])):
        temp=rgb2gray(cifar[b'data'][i])
        cifar[b'data2'][i]=temp
    
    # 训练数据集定义,训练集,测试集归一化
    x_train=cifar[b'data'] # 训练集数据
    y_train=cifar[b'labels'] # 训练集标签
    x_test=cifar_test[b'data'] # 验证集标签
    y_test=cifar_test[b'labels'] # 验证集标签
    
    x_train=x_train.reshape(-1,32,32,3) # 训练集格式确保为32x32的rgb三通道格式
    x_test=x_test.reshape(-1,32,32,3)  #  验证集同理
    class_names=cifar[b'label_names'];
    
    x_train = x_train.astype('float32') 
    x_test = x_test.astype('float32')
    x_train=x_train/255 #将像素的0到255 归一化至0-1
    x_test=x_test/255
    
    # 由经典卷积神经模型 VGG16 简化而来
    model = tf.keras.models.Sequential([ #此处可简化,但须均维持keras格式或者tf格式
            
      tf.keras.layers.Conv2D(32,
                             kernel_size=(3, 3), 
                             padding='same',
                             activation='relu',
                             input_shape=(32,32,3)
                             ),      #第一层卷积 卷积核为3x3    
                             
      tf.keras.layers.Conv2D(32,
                             kernel_size=(3,3),
                             activation='relu',
                             padding='same',
                             data_format='channels_last',
                             ),     #第二层卷积 卷积核为3x3
    
      tf.keras.layers.MaxPooling2D(pool_size=(2, 2)), # 池化 缩小  
      tf.keras.layers.Dropout(0.25),  # 防止过拟合
                             
      tf.keras.layers.Conv2D(64,
                             kernel_size=(3, 3), 
                             padding='same',
                             activation='relu',
                             input_shape=(32,32,1)
                             ),          
                             
      tf.keras.layers.Conv2D(64,
                             kernel_size=(3,3),
                             activation='relu',
                             padding='same',
                             data_format='channels_last',
                             ),
    
      tf.keras.layers.MaxPooling2D(pool_size=(2, 2)), # 池化缩小  
      tf.keras.layers.Dropout(0.25),   # 防止过拟合
      
      tf.keras.layers.Flatten(),  # 将所有特征展平为一维
      
      tf.keras.layers.Dense(512,activation='relu'), # 与一个512节点全链接,激活条件 relu
      tf.keras.layers.Dropout(0.5),           
    
      tf.keras.layers.Dense(10,activation=tf.nn.softmax) # 分类专用激活函数 softmax
      ])
    
    
    # 模型编译
    model.compile(optimizer='adam',# keras.optimizers.Adadelta()
                  loss='sparse_categorical_crossentropy',#tf.keras.losses.categorical_crossentropy会造成单一种类标签
                  metrics=['accuracy'])
    
    # 模型加载训练集  callbacks=tensorboard 监控
    model.fit(x_train, y_train,batch_size=32, epochs=10,verbose=1, validation_data=(x_test, y_test),
              callbacks=[keras.callbacks.TensorBoard(log_dir='./tmp/keras_log',write_images=1, histogram_freq=1),
                         ]) #verbos 输出日志 keras.callbacks.EarlyStopping(patience=10, monitor='val_acc')
        
    # epochs 数据集所有样本跑过一遍的次数 搭配 batch_size多少个一组进行训练 调整权重
    model.evaluate(x_test, y_test,verbose=0)
    
    
    
    # tensorboard 加载监控
    import webbrowser
    url='http://a1414l039:6006'
    webbrowser.open(url, new=0, autoraise=True)
    import os
    cmd='cd /d '+os.getcwd()+'\tmp && tensorboard --logdir keras_log'
    os.system(cmd)
    
    
    
    # 经过图像处理后的手机图片预测结果
    #plt.figure(figsize=(10,10))
    #for i in range(6):
    #    plt.subplot(3,2,i+1)
    #    plt.xticks([])
    #    plt.yticks([])
    #    plt.grid(False)
    #    plt.imshow(temp_test_ori[i], cmap=plt.cm.binary)
    #    plt.xlabel(answer[i])
    
    '''
    保存模型 加载权重
    '''
    
    # Returns a short sequential model
    keras.backend.clear_session()#必要-用以解决重复调用时会话应结束
    View Code
  • 相关阅读:
    面试题--赵银科技
    面试题--乐视.滴滴
    面试题--CVTE
    面试题--美团
    面试题--百度
    面试题--京东 有用
    mybatis的执行流程 #{}和${} Mysql自增主键返回 resultMap 一对多 多对一配置
    SpringMVC第一天
    LeetCode -- Maximum Product Subarray
    LeetCode -- Product of Array Except Self My Submissions Question
  • 原文地址:https://www.cnblogs.com/techs-wenzhe/p/11102998.html
Copyright © 2011-2022 走看看