用keras作CNN卷積網絡書本分類(書本、非書本)
本文介紹如何使用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的shapeimageList.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卷積網絡書本分類(書本、非書本)