CNN實戰篇-手把手教你利用開源數據進行圖像識別(基於keras搭建)

我一直強調作深度學習,最好是結合實際的數據上手,參照理論,對知識的掌握纔會更加全面。先了解原理,而後找一匹數據來驗證,這樣會不斷加深對理論的理解。html

 歡迎留言與交流!python

 

數據來源: cifar10  (其餘相關的圖片的開源數據集下載見 : https://yq.aliyun.com/articles/576274 文末有所有代碼web

PS:神經網絡系列多用於圖像,文字的生成,解析,識別。所以須要掌握充足的開源數據集來驗證所學的算法理論。算法

 

首先下載好數據後解壓。數據的樣子以下: data_batch1-5是訓練集數據,test_batch是驗證集, batches.meta是10個標籤的含義網絡

 

接下來分兩個大步驟:session

一是數據處理,使其符合模型的輸入接口。app

二是模型搭建,爲了訓練出準確有效的模型。框架

  • 數據處理部分:

在python環境下導入須要的庫ide

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
相關文章
相關標籤/搜索