1. 程式人生 > >用keras作CNN卷積網絡書本分類(書本、非書本)

用keras作CNN卷積網絡書本分類(書本、非書本)

div 問題: 標簽 turn 生成 ring module 數據質量 讀取

本文介紹如何使用keras作圖片分類(2分類與多分類,其實就一個參數的區別。。。呵呵)

先來看看解決的問題:從一堆圖片中分出是不是書本,也就是最終給圖片標簽上:“書本“、“非書本”,簡單吧。

先來看看網絡模型,用到了卷積和全連接層,最後套上SOFTMAX算出各自概率,輸出ONE-HOT碼,主要部件就是這些,下面的nb_classes就是用來控制分類數的,本文是2分類:

from keras.models import Sequential  
from keras.layers.core import Dense, Dropout, Activation, Flatten  
from keras.layers.convolutional import Convolution2D, MaxPooling2D  
from keras.optimizers import SGD  


def Net_model(nb_classes, lr=0.001,decay=1e-6,momentum=0.9):  
    model = Sequential()  
    model.add(Convolution2D(filters=10, kernel_size=(5,5),
                            padding=‘valid‘,  
                            input_shape=(200, 200, 3)
)) model.add(Activation(‘tanh‘)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(filters=20, kernel_size=(10,10))) model.add(Activation(‘tanh‘)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(1000)) model.add(Activation(‘tanh‘)) model.add(Dropout(0.5)) model.add(Dense(nb_classes)) model.add(Activation(‘softmax‘)) sgd = SGD(lr=lr, decay=decay, momentum=momentum, nesterov=True) model.compile(loss=‘categorical_crossentropy‘, optimizer=sgd) return model

上面的input_shape=(200, 200, 3)代表圖片像素大小為寬高為200,200,並且包含RGB 3通道的圖片,不是灰度圖片(只要1個通道)

也就是說送入此網絡的圖片寬高必須200*200*3;如果不是這個shape就需要resize到這個shape

下面來看看訓練程序,首先肯定是要收集些照片,書本、非書本的照片,我是分別放在了0文件夾和1文件夾下了,再帶個驗證用途的文件夾validate:

技術分享圖片  

訓練程序涉及到幾個地方:照片文件的讀取、模型加載訓練與保存、可視化訓練過程中的損失函數value

照片文件的讀取

import cv2
import os
import numpy as np
import keras

def loadImages():
    imageList=[]
    labelList=[]

    rootdir="d:\\books\\0"
    list =os.listdir(rootdir)
    for item in list:
        path=os.path.join(rootdir,item)
        if(os.path.isfile(path)):
            f=cv2.imread(path)
            f=cv2.resize(f, (200, 200))#resize到網絡input的shape
imageList.append(f) labelList.append(0)#類別0 rootdir="d:\\books\\1" list =os.listdir(rootdir) for item in list: path=os.path.join(rootdir,item) if(os.path.isfile(path)): f=cv2.imread(path) f=cv2.resize(f, (200, 200))#resize到網絡input的shape imageList.append(f) labelList.append(1)#類別1 return np.asarray(imageList), keras.utils.to_categorical(labelList, 2)

關於(200,200)這個shape怎麽得來的,只是幾月前開始玩opencv時隨便寫了個數值,後來想利用那些圖片,就適應到這個shape了

keras.utils.to_categorical函數類似numpy.onehot、tf.one_hot這些,只是one hot的keras封裝

模型加載訓練與保存

nb_classes = 2  
nb_epoch = 30
nb_step = 6
batch_size = 3

x,y=loadImages()

from keras.preprocessing.image import ImageDataGenerator
dataGenerator=ImageDataGenerator()
dataGenerator.fit(x)
data_generator=dataGenerator.flow(x, y, batch_size, True)#generator函數,用來生成批處理數據(從loadImages中)

model=NetModule.Net_model(nb_classes=nb_classes, lr=0.0001) #加載網絡模型

history=model.fit_generator(data_generator, epochs=nb_epoch, steps_per_epoch=nb_step, shuffle=True)#訓練網絡,並且返回每次epoch的損失value

model.save_weights(‘D:\\Documents\\Visual Studio 2017\\Projects\\ConsoleApp9\\PythonApplication1\\書本識別\\trained_model_weights.h5‘)#保存權重
print("DONE, model saved in path-->D:\\Documents\\Visual Studio 2017\\Projects\\ConsoleApp9\\PythonApplication1\\書本識別\\trained_model_weights.h5")

ImageDataGenerator構造函數有很多參數,主要用來提升數據質量,比如要不要標準化數字

lr=0.001這個參數要看經驗,大了會導致不收斂,訓練的時候經常由於這個參數的問題導致重復訓練,這在沒有GPU的情況下很是痛苦。。痛苦。。。痛苦。。。

model.save_weights是保存權重,但是不保存網絡模型 ,對應的是model.load_weights方法

model.save是保存網絡+權重,只是。。。。此例中用save_weights保存的h5文件是125M,但用save方法保存後,h5文件就增大為280M了。。。

上面2個save方法都能finetune,只是靈活度不一樣。

可視化訓練過程中的損失函數value

import matplotlib.pyplot as plt

plt.plot(history.history[‘loss‘])
plt.show()

技術分享圖片  

貌似沒啥好補充的。。。

AND。。。。看看預測部分吧,這部分加載圖片、加載模型,似乎都和訓練部分雷同:

def loadImages():
    imageList=[]

    rootdir="d:\\books\\validate"
    list =os.listdir(rootdir)
    for item in list:
        path=os.path.join(rootdir,item)
        if(os.path.isfile(path)):
            f=cv2.imread(path)
            f=cv2.resize(f, (200, 200))
            imageList.append(f)

    return np.asarray(imageList)

x=loadImages()


x=np.asarray(x)

model=NetModule.Net_model(nb_classes=2, lr=0.0001)
model.load_weights(‘D:\\Documents\\Visual Studio 2017\\Projects\\ConsoleApp9\\PythonApplication1\\書本識別\\trained_model_weights.h5‘)

print(model.predict(x))
print(model.predict_classes(x))
y=convert2label(model.predict_classes(x))
print(y)

predict的返回其實是softmax層返回的概率數值,是<=1的float

predict_classes返回的是經過one-hot處理後的數值,此時只有0、1兩種數值(最大的value會被返回稱為1,其他都為0)  

convert2label:

def convert2label(vector):
    string_array=[]
    for v in vector:
        if v==1:
            string_array.append(‘BOOK‘)
        else:
            string_array.append(‘NOT BOOK‘)
    return string_array

這個函數是用來把0、1轉換成文本的,小插曲:

本來這裏是中文的“書本”、“非書本”,後來和女兒一起調試時發現都顯示成了問號,應該是中文字符問題,就改成了英文顯示,和女兒一起寫代碼是種樂趣啊!

本來只是顯示文本,感覺太無聊了,因此加上了opencv顯示圖片+分類文本的代碼段:

for i in range(len(x)):
    cv2.putText(x[i], y[i], (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2)
    cv2.imshow(‘image‘+str(i), x[i])

cv2.waitKey(-1)

技術分享圖片  

OK, 2018年繼續學習,繼續科學信仰。

用keras作CNN卷積網絡書本分類(書本、非書本)