1. 摘要
圖像分類,也可以稱作圖像識别,顧名思義,就是辨識圖像中的物體屬于什麼類别。核心是從給定的分類集合中給圖像配置設定一個标簽的任務。實際上,這意味着我們的任務是分析一個輸入圖像并傳回一個将圖像分類的标簽。在這裡,我們将分别自己搭建卷積神經網路、遷移學習分别對圖像資料集進行分類。本篇使用的資料集下載下傳位址為:
連結:https://pan.baidu.com/s/1soJXb2_UJKeHYT0mpec2Fw
提取碼:rm3c
datasets檔案夾底下包括兩個檔案夾存放各自的圖檔資料集。
2.搭建卷積神經網絡實作圖像分類
卷積神經網絡與普通的神經網絡的差別在于,卷積神經網絡包含了一個卷積層convolutional layer和池化層pooling layer構成的特征提取器。卷積神經網路中每層卷積層由若幹卷積單元組成,每個卷積單元的參數都是通過反向傳播算法優化得到的。卷積運算的目的是提取輸入的不同特征,第一層卷積層可能隻能提取一些低級的特征如邊緣、線條和角等層級,更多層的網絡能從低級特征中疊代提取更複雜的特征。池化層(Pooling layer),通常在卷積層之後會得到次元很大的特征,将特征切成幾個區域,取其最大值或平均值,得到新的、次元較小的特征。
如上圖左,全連接配接神經網絡是一個“平面”,包括輸入層—激活函數—全連接配接層,右圖的卷積神經網絡是一個“立體”,包括輸入層—卷積層—激活函數—池化層—全連接配接層。卷積神經網絡提取的資料量更大,是以常用在圖像處理上。
接下來,我們自己搭建神經網絡對上面同樣的資料集進行分類
- 首先,導入相應的包
import numpy as np
from glob import glob
from keras.preprocessing import image
from sklearn.model_selection import train_test_split
from keras.utils import np_utils
from keras.layers import Conv2D,MaxPooling2D,Dense,Flatten,Dropout,GlobalAveragePooling2D
from keras.models import Sequential
from keras.callbacks import ModelCheckpoint
- 定義兩類圖檔的标簽值
y_baseji = np.zeros([209,],dtype=int)
y_pug = np.ones([200,],dtype=int)
y_data = list(y_baseji) + list(y_pug)
y_data = np.array(y_data)
y_data = np_utils.to_categorical([y_data],2).reshape(409,2)
y_data.shape
- 合并資料集X-data,友善後續處理
X_data_basenji =glob("./datasets/basenji/*")
X_data_pug = glob("./datasets/pug/*")
X_data = np.array(X_data_basenji + X_data_pug)
- 劃分訓練集和測試集
X_train,X_test,y_train,y_test = train_test_split(X_data,y_data,test_size=0.25,random_state=0)
count = int(len(X_test)/2)
X_val = X_test[count:]
y_val = y_test[count:]
X_test = X_test[:count]
y_test = y_test[:count]
- 将圖檔路徑轉換為數組并且歸一化,友善後須放入卷積神經網絡裡面
# 将圖檔路徑轉換為神經網路所需要的輸入shape
def path_to_tensor(path):
img = image.load_img(path,target_size=(224,224,3))# 此處的img為PIL格式
picture = image.img_to_array(img) # picture轉換成數組形式
return np.expand_dims(picture,axis=0)
def paths_to_tensor(paths):
list_of_tensors = [path_to_tensor(path) for path in paths]
return np.vstack(list_of_tensors)
Xtrain = paths_to_tensor(X_train).astype(np.float32)/255
Xtest = paths_to_tensor(X_test).astype(np.float32)/255
Xval = paths_to_tensor(X_val).astype(np.float32)/255
- 搭建卷積神經網絡
model_recognition = Sequential()
model_recognition.add(Conv2D(filters=16,kernel_size=(2,2),strides=(1,1),padding="same",activation="relu",input_shape=(224,224,3)))
model_recognition.add(MaxPooling2D(pool_size=(2,2)))
model_recognition.add(Dropout(0.2))
model_recognition.add(Conv2D(filters=32,kernel_size=(2,2),strides=(1,1),padding="same",activation="relu"))
model_recognition.add(MaxPooling2D(pool_size=(2,2)))
model_recognition.add(Dropout(0.2))
model_recognition.add(Conv2D(filters=64, kernel_size=(2, 2), strides=(1, 1), padding='same', activation='relu'))
model_recognition.add(MaxPooling2D(pool_size=(2, 2)))
model_recognition.add(Dropout(0.2))
model_recognition.add(GlobalAveragePooling2D())
model_recognition.add(Dropout(0.5))
model_recognition.add(Dense(2,activation="softmax"))
model_recognition.summary()
- 編譯并訓練網絡
modelcheckpoint = ModelCheckpoint(filepath="mp.h5",verbose=1,save_best_only=True)
model_recognition.fit(Xtrain,y_train,epochs=30,validation_data=(Xval,y_val),batch_size=32,callbacks=[modelcheckpoint],verbose=1)
- 加載模型,測試訓練集上的精确度
model_recognition.load_weights("mp.h5")
score = model_recognition.evaluate(Xtest, y_test, verbose=1)
print("Test {}: {:.2f}. Test {}: {:.2f}.".format(model_recognition.metrics_names[0],
score[0]*100,
model_recognition.metrics_names[1],
score[1]*100))
9. 離線情況下,進行圖檔分類,如下:
from keras.models import load_model
# 加載模型
model = load_model('mp.h5')
# 加載一張病理圖像來測試模型精确度
test_img_path = X_test[22]
# 将圖像轉換成4維的NumPy數值數組
image_tensor = path_to_tensor(test_img_path)
# 歸一化 轉換成0到1之間的數值
image_tensor = image_tensor.astype(np.float32) / 255
# 模型預測機率
predicted_result = model.predict(image_tensor)
# 列印輸出機率
print(predicted_result)
3.遷移學習實作圖像分類
問題來了?什麼是遷移學習?遷移學習(Transfer learning) 顧名思義就是把已訓練好的模型(預訓練模型)參數遷移到新的模型來幫助新模型訓練。考慮到大部分資料或任務都是存在相關性的,是以通過遷移學習我們可以将已經學到的模型參數(也可了解為模型學到的知識)通過某種方式來分享給新模型進而加快并優化模型的學習效率不用像大多數網絡那樣從零學習。其中,實作遷移學習有以下三種手段:
1.Transfer Learning:當機預訓練模型的全部卷積層,隻訓練自己定制的全連接配接層。
2.Extract Feature Vector:先計算出預訓練模型的卷積層對所有訓練和測試資料的特征向量,然後抛開預訓練模型,隻訓練自己定制的簡配版全連接配接網絡。
3.Fine-tuning:當機預訓練模型的部分卷積層(通常是靠近輸入的多數卷積層,因為這些層保留了大量底層資訊)甚至不當機任何網絡層,訓練剩下的卷積層(通常是靠近輸出的部分卷積層)和全連接配接層。
預訓練模型有很多,本文選用InceptionV3預訓練模型,它是由谷歌團隊從ImageNet的1000個類别的超大資料集訓練而來的,表現優異,經常用來做計算機視覺方面的遷移學習研究和應用。
- 首先導入需要的包
from keras.applications.inception_v3 import InceptionV3,preprocess_input,decode_predictions
from keras.models import Model
from keras.layers import Dense,GlobalAveragePooling2D,Dropout
from keras.preprocessing import image
from keras.optimizers import SGD
from keras.callbacks import ModelCheckpoint
#導入圖檔資料增強器
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
- 定義一個類,用來訓練微調後的InceptionV3模型,包括資料增強、自己定義全連接配接層、當機預訓練模型進行訓練以及微調模型進行訓練。
class InceptionV3Retrained:
#添加全連接配接層
def add_new_last_layers(self,base_model,num_classes):
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024,activation="relu")(x)
predictions = Dense(num_classes,activation="softmax")(x)
model = Model(input=base_model.input,output=predictions)
return model
# 當機預訓練模型之前的層
def freeze_previous_layers(self,model,base_model):
for layer in base_model.layers:
layer.trainable = False
model.compile(optimizer='adam',loss="categorical_crossentropy",metrics=['accuracy'])
#微調模型
def fine_tune_model(self,model):
for layer in model.layers[:172]:
layer.trainable = False
for layer in model.layers[172:]:
layer.trainable = True
model.compile(optimizer="adam",loss="categorical_crossentropy",metrics=["accuracy"])
#繪制損失值與精确值
def plot_training(self,history):
acc = history.history["acc"]
val_acc = history.history["val_acc"]
loss = history.history["loss"]
val_loss = history.history["val_loss"]
epochs = range(len(acc))
plt.plot(epochs, acc, 'r.')
plt.plot(epochs, val_acc, 'r')
plt.title('Training and validation accuracy')
# 繪制訓練損失和驗證損失
plt.figure()
plt.plot(epochs, loss, 'r.')
plt.plot(epochs, val_loss, 'r-')
plt.title('Training and validation loss')
plt.show()
# 訓練模型
def train(self,num_classes,batch_size,epochs):
# 資料增強
train_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,rotation_range=20,width_shift_range=0.2,
height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True)
valid_datagen = ImageDataGenerator(preprocessing_function=preprocess_input,rotation_range=20,width_shift_range=0.2,
height_shift_range=0.2,shear_range=0.2,zoom_range=0.2,horizontal_flip=True)
train_generator = train_datagen.flow(Xtrain,y_train,batch_size=batch_size)
valid_generator = valid_datagen.flow(Xval,y_val,batch_size=batch_size)
# 初始化inceptionV3模型,include_top=False表示初始化模型時不包含InceptionV3網絡結構層中的最後的全連接配接層
base_model = InceptionV3(weights="imagenet",include_top=False)
model = self.add_new_last_layers(base_model,num_classes)
self.freeze_previous_layers(model,base_model)
checkpointer = ModelCheckpoint(filepath="inception_new.h5",verbose=1,save_best_only=True)
print("開始訓練")
history_tl = model.fit_generator(train_generator,steps_per_epoch=Xtrain.shape[0]/batch_size,
validation_steps=Xval.shape[0] / batch_size,
epochs=epochs,
verbose=1,
callbacks=[checkpointer],
validation_data=valid_generator)
# 微調模型
self.fine_tune_model(model)
print("微調模型後,再次訓練模型")
history_ft = model.fit_generator(train_generator,
steps_per_epoch=Xtrain.shape[0] / batch_size,
validation_steps=Xval.shape[0] / batch_size,
epochs=epochs,
verbose=1,
callbacks=[checkpointer],
validation_data=valid_generator)
self.plot_training(history_ft)
- 對微調後的模型進行訓練50次。
batch_size = 32
epochs = 50
num_classes = 2
incepV3_model = InceptionV3Retrained()
incepV3_model.train(num_classes=num_classes,batch_size=batch_size,epochs=epochs)
- 在測試集上,測試訓練結果的精确度。
# 測試模型的精确度
# 建立一個不帶全連接配接層的InceptionV3模型
test_model = InceptionV3(weights='imagenet', include_top=False, input_shape=Xtest.shape[1:])
# 添加全連接配接層輸出層
incepV3_model = InceptionV3Retrained()
trained_model = incepV3_model.add_new_last_layers(test_model, num_classes)
# 加載剛才訓練的權重到模型中
trained_model.load_weights("inception_new.h5")
# 編譯模型
trained_model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
# 評估模型
score = trained_model.evaluate(Xtest, y_test, verbose=1)
print("Test {}: {:.2f}. Test {}: {:.2f}.".format(trained_model.metrics_names[0],
score[0]*100,
trained_model.metrics_names[1],
score[1]*100))
由以上結果可以看出,微調後的模型對于測試集上的精确度達到了96.08%,比你我們自己搭建的神經網絡模型(78.43%)準确了很多,表現十分優秀。