2019年5月6日 星期一

使用Keras卷積神經網路辨別肺部醫學影像(四)

上一章「使用Keras卷積神經網路辨別肺部醫學影像(三)」

在上一章,我們得到了一個.npy檔,裡面裝著4500張男性影像和4500張女性影像。
在這一章,我們將建立訓練模型,並輸出成.h5的檔案 


在開始之前,需要理解下列名詞:
  • 卷積層
  • 池化層
  • Onehot encoding
  • 平坦層
  • 隱藏層
  • 輸出層
  • 活化函數
  • 損失函數

CNN卷積神經網路可分為兩大部分:
  • 影像的特徵提取:卷積層、池化層
  • 完全連結神經網路:平坦層、隱藏層、輸出層


關於卷積運算: 
卷積層的意義是將一個影像進行卷積運算,產生多個影像。 我們在進行影像操作時會使用到濾鏡,不同的濾鏡套用到影像中會產生不同的影像,可幫助程式提取不同的特徵。 

關於縮減取樣:
 池化層中的Max-Pool運算可將影像進行縮減取樣,例如一張14x14的影像,進行Max-Pool運算後,縮小為7x7的影像。  

關於Onehot encoding: 經過Onehot encoding轉換,可幫助程式進行分類。 
例如數字3,可轉換為[0,0,0,1,0,0,0,0,0,0] 
例如數字8,可轉換為[0,0,0,0,0,0,0,0,1,0] 

關於神經網路: 
有個公式是:輸出=活化函數(輸入X權重X偏差) 
例如現有250個輸入神經元,每個神經元乘上一個權重並加上偏差之後,可以傳遞到第二層(隱藏層)。 
在隱藏層又乘上權重並加上偏差之後,可將結果傳達到第三層(輸出層或第二層隱藏層)。

關於活化函數:
當神經元接收刺激時(輸入X權重+輸出)經過活化函數的運算,大於臨界值會傳遞到下一個神經元。

關於損失函數:

損失函數用以計算誤差,從錯誤中學習。


建立一個train_4500x4500的python檔案,程式碼如下:


import array import sys 
import numpy as np 
import cv2 
import glob 
import random from keras.utils 
import np_utils from keras.models 
import Sequential from keras.layers 
import Dense,Dropout,Activation,Flatten from keras.layers 
import Conv2D,MaxPooling2D,ZeroPadding2D 

#設定亂數種子,讓每次訓練產生的結果相同 np.random.seed(0) 

#產生性別標籤清單 
labellist=[] 

#設定影像寬度、高度 
imgwidth=128 imgheight=128 
#將上次產生的.npy檔載入至名為x_img_train的Array中 x_img_train=np.load("size128_F4500_M4500.npy") 
#設定前4500個影像標籤為女性(用數字1表示) 
for i in range(0,4500): 
(四個空格)labellist.append(1) 
#設定後4500個影像標籤為男性(用數字0表示) 
for i in range(4500,9000):
(四個空格)labellist.append(0) 
#將性別標籤轉換成Array 
y_label_train=np.array(labellist, dtype=np.int32) 
#將x_img_train轉換成(9000x128x128x1)的四維陣列 x_img_train=np.reshape(x_img_train,9000,imgwidth,imgheight,1))
#將性別標籤進行Onehot encoding轉換 y_label_train_OneHot=np_utils.to_categorical(y_label_train) 
#建立模型 
model=Sequential() 
#建立卷積層1 
model.add(Conv2D(filters=32,kernel_size=(5,5),input_shape=(imgwidth,imgheight,1),activation='relu',padding='same')) 
#隨機釋放神經元 
model.add(Dropout(rate=0.3)) 
#建立池化層1 
model.add(MaxPooling2D(pool_size=(2,2)))
#建立卷積層2 
model.add(Conv2D(filters=64,kernel_size=(5,5),activation='relu',padding='same')) 
#隨機釋放神經元 
model.add(Dropout(0.3)) 
#建立池化層2 
model.add(MaxPooling2D(pool_size=(2,2))) 
#建立卷積層3 
model.add(Conv2D(filters=128,kernel_size=(5,5),activation='relu',padding='same')) 
#隨機釋放神經元 
model.add(Dropout(0.5)) 
#建立池化層3 
model.add(MaxPooling2D(pool_size=(2,2))) 
#建立平坦層 
model.add(Flatten()) 
#隨機釋放神經元 
model.add(Dropout(rate=0.25)) 
#建立隱藏層 
model.add(Dense(512,activation='relu')) 
#隨機釋放神經元 
model.add(Dropout(rate=0.4)) 
#建立輸出層 
model.add(Dense(2,activation='softmax')) 
#定義訓練方式 model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy']) 
#設定訓練用的函數 
def training(times):
(四個空格)train_history=model.fit(x_img_train,y_label_train_OneHot,shuffle=True,epochs=times,batch_size=128,verbose=2) 
#如未完成訓練,可載入上次產生的訓練檔接續訓練
from keras.models import load_model #model.load_weights("Fiborsis_sex.h5")
#訓練1回,每回訓練20次 
for k in range(1): 
(四個空格)print('訓練第:',k,'次') 
training(20)
#輸出權重 
model.save_weights("Fiborsis_sex.h5")
#輸出模型檔 
model.save("Fiborsis_sex_result.h5")


程式碼解說: 我們一開始建立影像清單(labellist),並按照上次產生的size128_F4500_M4500.npy檔案中的性別順序填入標籤(女性為1男性為0)。  
for i in range(0,4500):
    labellist.append(1) 
for i in range(4500,9000):
    labellist.append(0) 

接著對資料進行前置處理。
y_label_train=np.array(labellist, dtype=np.int32) x_img_train=np.reshape(x_img_train,9000,imgwidth,imgheight,1)) y_label_train_OneHot=np_utils.to_categorical(y_label_train) 

建立卷積層,讓程式隨機產生32個濾鏡,每個濾鏡為5x5的大小,輸入的形狀為(寬度,長度,1),活化函數為relu 。
model.add(Conv2D(filters=32,kernel_size=(5,5),input_shape=(imgwidth,imgheight,1),activation='relu',padding='same')) 

為了防止過度擬合的現象發生,隨機放棄30%的神經元。
 model.add(Dropout(rate=0.3)) 

將圖片縮小,進行縮減取樣。 
model.add(MaxPooling2D(pool_size=(2,2)))  

再次建立卷積層,共產生64個濾鏡,每個濾鏡為5x5的大小,輸入的形狀為(寬度,長度,1),活化函數為relu 。
model.add(Conv2D(filters=64,kernel_size=(5,5),activation='relu',padding='same')) 

隨機放棄30%的神經元
 model.add(Dropout(rate=0.3)) 

進行縮減取樣(然後依此類推)。 
model.add(MaxPooling2D(pool_size=(2,2)))  

輸出結果,共有兩種結果 。
model.add(Dense(2,activation='softmax')) 

執行訓練,輸入x_img_train,引入y_label_train_OneHot做為參考解答。
啟動shuffle將資料順序打亂。 epochs代表進行幾次訓練周期,batch_size為每一批次共有幾筆資料,verbose=2代表顯示訓練過程。
train_history=model.fit(x_img_train,y_label_train_OneHot,shuffle=True,epochs=times,batch_size=128,verbose=2)

將訓練到一半的model儲存起來,下次可接續訓練(Fiborsis_sex.h5為檔名)
model.save_weights("Fiborsis_sex.h5")

載入上次儲存的模行檔(Fiborsis_sex.h5為檔名)
model.load_weights("Fiborsis_sex.h5")

輸出模型檔 (Fiborsis_sex_result.h5為檔名)
model.save("Fiborsis_sex_result.h5")


在下一個章節,我們將執行訓練檔,並驗證結果。

下一章「使用Keras卷積神經網路辨別肺部醫學影像(五)」