3D Unet小簡介 — 實作(2)
這一篇要來看的是神經網路訓練周邊的檔案。根據上一篇的檔案分類介紹,主要會用到的檔案包括:
1. data
2. generator
3. training
這三項。這三個檔案又會視需要各自引用其他檔案的物件來使用,如果有必要我們再跟著追蹤到其他檔案去,把物件找出來。
訓練的流程
如果去看keras的源碼,可以知道訓練的關鍵流程如下:
1. 神經網路呼叫data generator,請它提供資料。
2. data generator根據batch size提供一個batch的資料,神經網路接受後開始訓練,並根據訓練結果和標籤差異計算損失,以反向傳播的方式更新梯度。梯度的更新方法則視設定而決定。
3. 如此將一輪的資料訓練完成後進到下一輪,重新開始訓練。
4.訓練完成後會回饋紀錄檔。若有設定儲存路徑的話還可以把訓練好的模型儲存到指定路徑,以待下次訓練使用,或開始實用。
關於epoch、batch size、steps per epoch、反向傳播、梯度下降等概念,之後如果有時間我再另寫文章介紹。但其實這類的觀念介紹網路上已經有很多文章,且描述很完整;若讀者尚不清楚,其實也不建議繼續看這篇文章,而建議先把這些概念釐清較好。
訓練的關鍵流程放在training檔中。核心指令是model.fit_generator。這個函式也可以用fit取代。我們看一下這段程式碼好了:
model.fit_generator(generator=training_generator,
steps_per_epoch=steps_per_epoch,
epochs=n_epochs,
validation_data=validation_generator,
validation_steps=validation_steps,
callbacks=get_callbacks(model_file,
initial_learning_rate=initial_learning_rate,
learning_rate_drop=learning_rate_drop,
learning_rate_epochs=learning_rate_epochs,
learning_rate_patience=learning_rate_patience,
early_stopping_patience=early_stopping_patience))
其實源碼中有提到各個參數的意義,照著設定就可以了,而且命名其實很直觀。根據提到的參數,可以了解到執行訓練需要設定的元素有
1. 訓練/驗證的資料
2. 訓練/驗證的epoch、steps(也就是需要batch size和sample數)
3. callback
最後這個callback其實是前面寫的函數:
def get_callbacks(model_file, initial_learning_rate=0.0001,
learning_rate_drop=0.5, learning_rate_epochs=None,
learning_rate_patience=50,
logging_file="training.log", verbosity=1,
early_stopping_patience=None):
先不看這個函數執行的內容,但可以從設定的參數大約猜測到這邊在做什麼。沒錯,就是設定超參數,以及提供儲存訓練好的模型的路徑。
callback有人翻作回調函數,就是把一個函數預先設定在另一個函數裡面,這樣執行函數時就會連帶被執行。所以keras提供了這個模式,讓我們可以打包其他keras的API在裡面,需要的時候就會執行並將結果運用在callback裡面。這樣的好處是可以保持callback的彈性。函數最後會返回一個list,這個list裡面包含其他的keras API,等到callback需要時就可以執行些API得到結果,並運用這些結果。
callbacks = list()
...
return callbacks
最後講一下第一行的程式。(第11行)
K.set_image_dim_ordering('th')
K是來自於import backend as K。backend有點像keras的後台,其實是連動到一個類似log的json檔案(keras.json),裡面紀錄一些keras的基本設定,這些設定在該環境或是該模型訓練的狀況下是通用的。
set_image_dim_ordering就是其中一項設定,對應的是要設定你的資料陣列維度排序。’th’表示theano,’tf’表示tensorflow。排序怎麼會跟這兩家函式庫有關?當然有關,因為兩家的表示習慣不一樣。在theano,陣列維度表示是(channel, height, width),而tensorflow則是(height,width, channel)。所以其實可以表示成"channel first"或是"channel last"的意思。這樣的表示方法,對應到程式的神經網路架構輸入端、每一層的運算,以及輸出,所以必須先做好設定以免引起混亂。同時一旦設定好,後續的設置也必須符合此順序,否則會跳錯誤。
資料產生器
資料產生器是keras提供的一種方便。在資料量還不大的時候,我們可以讓記憶體讀取全部資料並存到暫存區,之後再送入神經網路訓練。但在資料量大的時候這個方法將會使記體體超過負荷。data generator可以即時提供神經網路資料,而且還可以應付多線作業(例如用分別的GPU訓練或是同時進行多線神經網路作業),所以可以很大程度的減輕記憶體的負擔,又不會拖累訓練效率。
第104行以後的是測試碼。和原始的程式碼相比對,我主要是移除了create patch的內容,因為我想先試試看把整個影像丟進去訓練。經過初始裁切之後,輸入的圖片大小為32*288*288,我覺得不算大。
從這個檔可以大概窺知資料產生器的過程:
1. 取得所有要訓練的sample清單(list)。
2. 洗牌,並按照設定的比例分出訓練組(training)和驗證組(validation)的清 單。
3. 根據總資料量和設定的batch size決定steps_per_epoch。
4. 根據清單在已經寫成資料集的hdf5檔尋找並讀取,該資料應為陣列。
5. 隨著神經網路呼叫,按batch給予資料訓練和驗證,直到一個epoch結束之後重置。
主程式是get_training_and_validation_generators,其核心為get_and_add_data以及convert_data。與split相關的函數主要負責將validation和training list分開。
資料
從資料集中提出一部份作訓練用。額外儲存成一個檔,要訓練時直接從中讀取到data generator。根據程式原碼,儲存的檔案格式為hdf5,但就我從各方面詢問了解,還蠻少人會把資料存成這個格式的…(可能是我詢問的樣本太小也不一定?)總之,這個是有彈性的。可以存成json,或其他格式。為了最小限度的改動原碼,我們還是採用hdf5儲存吧。
測試碼的部分我就先移除了。這個檔案的改動比較大,主要是因為我先把patch、flip以及其他augmentation相關的要素先拿掉。因為沒有要做augmentation,所以就把原碼中affine的儲存空位取消;這是從data generator的原碼裡affine是用在data augmentation相關的函式中推測而來的,我並沒有從原作者詢問到相關資訊。
此外,原碼中第27行有個get_data函式,這個函式我無法從任何其他檔案中追蹤到來源;它也顯然不是python預建的函式。所以我推測是原碼中取得圖片轉成numpy array的方法。它的上一行還有reslice_image_set,這個應該也算是資料的前處理步驟,在改動的時候移除。
儲存檔案的基本精神是一定要照順序,而且一對對存;data、truth一組,存到hdf5事先規劃好的欄位上。由於hdf5檔要求預先把欄位中的陣列大小也設定好,因此若陣列與欄位中的設定有出入,就無法存入,會跳錯誤。這點請務必注意。
此外,原碼使用tables打開檔案。建議用with open()…的方式,這樣即使第一次執行跳了錯誤,也不會陷在讀取的記憶體中,等到要執行修正的程式時會顯示「已經把檔案打開了,無法讀取」的錯誤。若用tables打開檔案,最後務必要再下一行file.close()讓檔案關閉,記憶體斷開。
以上是訓練周邊的相關檔案物件。下一篇就進入訓練的主軸 — 模型架構檔以及訓練的執行檔案。