1. 程式人生 > >深度學習 (四)Keras利用CNN實現圖片識別(Mnist、Cifar10)

深度學習 (四)Keras利用CNN實現圖片識別(Mnist、Cifar10)

視覺集

       視覺資料庫是用來提供給圖片識別領域用素材,目前各個教材常用的主要有手寫數字識別庫、10中小圖片分類庫,詳細介紹如下:

Mnist

       MNIST(Mixed National Institute of Standards and Technology database)是一個計算機視覺資料集,它包含70000張手寫數字的灰度圖片,其中每一張圖片包含 28 X 28 個畫素點。可以用一個二維數字陣列來表示這張圖片,因為它單色只有一個通道計算會方便一些。

Cifar-10

       該資料集主要是由三位作者收集、整理而成,來用於影象識別領域,其中包含60000張圖片,50000是訓練集,10000是測試集,每一張圖片為32323個畫素點,它比手寫數字圖片稍微複雜一點,它有RGB三個顏色通道,方便以後的愛好者專注於提高演算法能力不用單獨去為很多訓練資料耽誤太多時間,現今很多教材或者課程也都是基於這些資料集來講解課程知識。

實戰

下載

網上搜索即可

def load_data(path):
    '''
    載入資料圖片
    :param path: 
    :return:
    '''
    f = np.load(path)
    x_train, y_train = f['x_train'], f['y_train']
    x_test, y_test = f['x_test'], f['y_test']
    f.close()
    return (x_train, y_train), (x_test, y_test)

檢視

def plot_images_labels_prediction(images,labels,prediction,idx,num=10):
    fig = plt.gcf()
    fig.set_size_inches(12,14)
    if num>25:num=25
    for i in range(0,num):
        ax = plt.subplot(5,5,1+i)
        ax.imshow(images[idx],cmap='binary')
        title = 'label='+str(labels[idx])
        if len(prediction)>0:
            title+=",predict="+str(prediction[idx])

        ax.set_title(title,fontsize=10)
        ax.set_xticks([]);ax.set_yticks([])
        idx+=1
    plt.show()

mnist

       下圖為手寫數字識別的影象畫素展示,識別數字都是灰色單通道影象所以展示出來是28*28的矩陣,相比cifar10三通道影象比簡單一些,cifar10每個圖片為下面這樣類似矩陣三個組成。

在這裡插入圖片描述

cifar10

十種訓練集圖片如下:
在這裡插入圖片描述

預處理

       預處理即將圖片處理為對應演算法容易處理的資料格式,對於DNN一般講圖片拉伸為一維向量,CNN並不需要這樣處理,他們都需要的處理是將資料標準化統一量綱,對於畫素一般除以255,數字值小了演算法訓練容易收斂和穩定。

mnist

# print(x_Train.shape[0])
x_Train4D = x_Train.reshape(x_Train.shape[0],28,28,1).astype('float32')
x_Test4D = x_Test.reshape(x_Test.shape[0],28,28,1).astype('float32')

x_Train4D_normalize = x_Train4D/255
x_Test4D_normalize = x_Test4D/255
print(x_Train4D_normalize[0].shape)

y_TrainOneHot = np_utils.to_categorical(y_Train)
y_TestOneHot = np_utils.to_categorical(y_Test)

cifar10

確定網路結構

       這裡我們使用keras來實現卷積網路操作,發現這個框架對於新手來說是很容易上手,定義出來一個外層框架,然後我們依次定義裡面的網路結構即可,每次add進去一層即可,並不需要我們詳細定義每層網路結構,建議初學者也先嚐試這個框架。

mnist

手寫識別網路結構,可以通過summary檢視各層資訊

model = Sequential()
model.add(Conv2D(filters=16,kernel_size=(5,5),padding='same',input_shape=(28,28,1),activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))

model.add(Conv2D(filters=36,kernel_size=(5,5),padding='same',activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Flatten())
# add hidden layer1
model.add(Dense(128,activation='relu'))
model.add(Dropout(0.5))
# add output layer
model.add(Dense(10,activation='softmax'))

# model summary
print(model.summary())

在這裡插入圖片描述

cifar10

       cifar10卷積結構,比mnist多了一層,因為輸入的資料變複雜了,可見模型越深越大表示越複雜,能力也越強,當然越不容易訓練出來結果。

model = Sequential()
# add conv1 layer
model.add(Conv2D(filters=32,kernel_size=(3,3),
                 input_shape=(32,32,3),activation='relu',padding='same'))
# add dropout
model.add(Dropout(rate=0.25))
# add maxpooling layer
model.add(MaxPooling2D(pool_size=(2,2)))
# add conv2 layer
model.add(Conv2D(filters=64,kernel_size=(3,3),activation='relu',padding='same'))
# add dropout
model.add(Dropout(rate=0.25))
# add maxpooling layer
model.add(MaxPooling2D(pool_size=(2,2)))
# add flatten layer
model.add(Flatten())
model.add(Dropout(0.25))
# add hidden layer
model.add(Dense(1024,activation='relu'))
model.add(Dropout(0.25))
# output layer
model.add(Dense(10,activation='softmax'))

print(model.summary())

在這裡插入圖片描述

結構對比

  • 為什麼最後一層神經元個數為分類個數
    從網上搜了下沒找到,個人理解這最後一層之所以神經元個數為分類個數是為了容易出來出來結果,每一類結果都是屬於這一類的概率,非常容易看出來
  • 圖片的單通道和多通道對結構的影響
    通道多了即畫素點多了,引數會成倍增加,比如32323的cifar10圖片,上面第一個卷積層 卷積核為32個 receptive field 為33的大小,那麼引數為32333+32(偏置項) = 869個,將會增加模型的複雜度。
  • 卷積層和池化層通道計算
    卷積層通道的個數是本層定義的,與上一個卷積層沒有直接關係,但是每個通道生成時和上一層的所有通道計算值有關
    池化層不會改變通道個數,可能改變的是每層通道(feature map)的大小

開始訓練

輸入訓練資料訓練

model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])

train_history = model.fit(x_img_train_normalize,y_label_train_OneHot,validation_split=0.2,epochs=10,batch_size=128,verbose=1)

模型儲存

       這一步個人感覺還挺重要,發現網路越深訓練出來一個好的網路越困難,不容易得到結果,尤其是我們個人膝上型電腦,儲存起來下次可以直接使用不在訓練。


    try:
    model.load_weights('SaveModel/cifarCnnModel.h5')
    print('載入模型成功!繼續訓練模型')
except:
    print('載入模型是失敗!開始訓練一個新模型')
model.save_weights('SaveModel/cifarCnnModel.h5')

評估

       下面這個評估圖是以每週期訓練出來的訓練集和驗證集準確率為點進行畫出來的,根據我們經驗訓練集合驗證集準確率越接近表明結果越好,模型效果越好。從圖來看有一些過擬合產生,因為訓練集準確率大於驗證集。

預測

如下圖為手寫識別預測出來的結果

在這裡插入圖片描述

問題點

  • 下載資料
    下載資料時直接用keras下載是很慢的,最快的方式是自己從網上另行下載,下載完修改一些load_data()方法
  • summary
    該方法是model的一個摘要資訊,對於瞭解現在的模型結構非常有幫助,它會告訴你每次結構以及每層多少個訓練引數等資訊
  • cnn通用結構
    input、[ [ conv --> relu ] * N --> pool ?] * M、[ fc-- > relu ]、fc
    說明:第一層為(輸入層)、緊接著為(卷積層 啟用函式) 這一結構可以重複N次,然後接一個池化層,也可以不接池化層,(卷積層 啟用寒素 池化層)這一結構可以重複M次出現,依據問題規則設定,其次是全連線層 啟用函式,這一層可有可無,最後一層為全連線層,一般一定要有。

隨筆

如何把專業的知識講給非專業人聽懂?
       現在可以說各行各業都有行業壁壘,每個一個人事件、生命有限,如何在有限的時間做更多的事情、在短時間內高效率的聽懂別人說什麼或給別人講懂專業知識很重要,事實是專業的內容大部分人不愛聽因為聽著實在沒有意思,很難聽懂,這往往是講者自身沒有完全懂是啥,沒有結合生活中的例子講出來,萬事都是可以拿出來對應到生活中去的,科學來源於生活、實事求是是科學的本質,如何才能讓別人聽懂自己的專業知識呢?
1.合乎邏輯的比喻
       先來一個合乎情理的比喻 ,能量守恆大家都知道如何讓沒學過物理的人知道理解呢,可以比如說像馬兒不給草吃能長肉麼,馬兒跑又會掉肉,能量是不變的,另外舉一個不合理比喻辜鴻銘曾拿一個茶壺多個杯子來說明應該一夫多妻制度,馬上有人反駁為啥茶壺是男人而非女人,可見此比喻並不恰當。
2.簡單化 忽略細節
       在給非自己專業人講東西時,要站在大家能理解的角度將問題,讓別人能聽懂的水平,如電視的解析度常常說1k 高清4k等,其實4k精度是4096畫素,其實對於大部分百姓我們是不需要那麼清楚這個細節,說了返回效果不好。