摘要:我我的開發的深度學習腳手架 ModelZoo 發佈了!python
好多天沒有更新原創文章了,國慶前的一段時間確實比較忙,整個九月在參加各類面試,另外還有公司的項目,還有本身的畢設,另外還須要準備參加一些活動和講座,時間排的很緊,不過還在這些事情基本在國慶來臨之際都暫告一段落了,因此國慶我也沒打算再幹太多事情,就準備在家休養生息。git
在家一段時間,我嘗試對以前作過的一些項目進行一些梳理,另外還對一些比較新的技術進行了一些探索,這其中就包括深度學習相關的一些框架,如 TensorFlow、Keras 等等。github
想必你們都或多或少聽過 TensorFlow 的大名,這是 Google 開源的一個深度學習框架,裏面的模型和 API 能夠說基本是包羅萬象,但 TensorFlow 其實有不少讓人吐槽的地方,好比 TensorFlow 早期是隻支持靜態圖的,你要調試和查看變量的值的話就得一個個變量運行查看它的結果,這是極其不友好的,而 PyTorch、Chainer 等框架就天生支持動態圖,能夠直接進行調試輸出,很是方便。另外 TensorFlow 的 API 各個版本之間常常會出現不兼容的狀況,好比 1.4 升到 1.7,裏面有至關一部分 API 都被改了,裏面有的是 API 名,有的直接改參數名,有的還給你改參數的順序,若是要作版本兼容升級,很是痛苦。還有就是用 TensorFlow 寫個模型,其實相對仍是比較繁瑣的,須要定義模型圖,配置 Loss Function,配置 Optimizer,配置模型保存位置,配置 Tensor Summary 等等,其實並無那麼簡潔。面試
然而爲啥這麼多人用 TensorFlow?由於它是 Google 家的,社區龐大,還有一個緣由就是 API 你別看比較雜,可是確實比較全,contrib 模塊裏面你幾乎能找到你想要的全部實現,並且更新確實快,一些前沿論文如今基本都已經在新版本里面實現了,因此,它的確是有它本身的優點。架構
而後再說說 Keras,這應該是除了 TensorFlow 以外,用的第二普遍的框架了,若是你用過 TensorFlow,再用上 Keras,你會發現用 Keras 搭模型實在是太方便了,並且若是你仔細研究下它的 API 設計,你會發現真的封裝的很是科學,我感受若是要搭建一個簡易版的模型,Keras 起碼得節省一半時間吧。併發
一個好消息是 TensorFlow 如今已經把 Keras 包進來了,也就是說若是你裝了 TensorFlow,那就能同時擁有 TensorFlow 和 Keras 兩個框架,哈哈,因此你最後仍是裝個 TensorFlow 就夠了。app
還有另外一個好消息,剛纔我不是吐槽了 TensorFlow 的靜態圖嘛?這的確是個麻煩的東西,不過如今的 TensorFlow 不同了,它支持了 Eager 模式,也就是支持了動態圖,有了它,咱們能夠就像寫 Numpy 操做同樣來搭建模型了,要看某個變量的值,很簡單,直接 print 就 OK 了,不須要再去調用各類 run 方法了,能夠直接拋棄 Session 這些繁瑣的東西,因此基本上和 PyTorch 是一個套路的了,並且這個 Eager 模式在後續的 TensorFlow 2.0 版本將成爲主打模式。簡而言之,TensorFlow 比以前好用多了!框架
好,以上說了這麼多,我今天的要說的正題是什麼呢?嗯,就是我基於 TensorFlow Eager 模式和 Keras 寫了一個深度學習的框架。說框架也不能說框架,更準確地說應該叫腳手架,項目名字叫作 ModelZoo,中文名字能夠理解成模型動物園。ide
有了這個腳手架,咱們能夠更加方便地實現一個深度學習模型,進一步提高模型開發的效率。函數
另外,既然是 ModelZoo,模型必不可少,我也打算之後把一些經常使用的模型來基於這個腳手架的架構實現出來,開源供你們使用。
有人說,你這不是閒的蛋疼嗎?人家 Keras 已經封裝得很好了,你還寫個啥子哦?嗯,沒錯,它的確是封裝得很好了,可是我以爲某些地方是能夠寫得更精煉的。好比說,Keras 裏面在模型訓練的時候能夠自定義 Callback,好比能夠實現 Tensor Summary 的記錄,能夠保存 Checkpoint,能夠配置 Early Stop 等等,但基本上,你寫一個模型就要配一次吧,即便沒幾行代碼,但這些不少狀況都是須要配置的,因此何須每一個項目都要再去寫一次呢?因此,這時候就能夠把一些公共的部分抽離出來,作成默認的配置,省去沒必要要的麻煩。
另外,我在使用過程當中發現 Keras 的某些類並無提供我想要的某些功能,因此不少狀況下我須要重寫某個功能,而後本身作封裝,這其實也是一個可抽離出來的組件。
另外還有一個比較重要的一點就是,Keras 裏面默認也不支持 Eager 模式,而 TensorFlow 新的版本偏偏又有了這一點,因此兩者的兼併必然是一個絕佳的組合。
因此我寫這個框架的目的是什麼呢?
第一,模型存在不少默認配置且可複用的地方,能夠將默認的一些配置在框架中進行定義,這樣咱們只須要關注模型自己就行了。
第二,TensorFlow 的 Eager 模式便於 TensorFlow 的調試,Keras 的高層封裝 API 便於快速搭建模型,取兩者之精華。
第三,如今你能夠看到要搜一個模型,會有各類花式的實現,有的用的這個框架,有的用的那個框架,並且參數、數據輸入輸出方式五花八門,實在是讓人頭大,定義這個腳手架能夠稍微提供一些規範化的編寫模式。
第四,框架名稱叫作 ModelZoo,但個人理想也並不只僅於實現一個簡單的腳手架,個人願景是把當前業界流行的模型都用這個框架實現出來,格式規範,API 統一,開源以後分享給全部人用,給他人提供便利。
因此,ModelZoo 誕生了!
開發的時候,我本身首先先實現了一些基本的模型,使用的是 TensorFlow Eager 和 Keras,而後試着抽離出來一些公共部分,將其封裝成基礎類,同時把模型獨有的實現放開,供子類複寫。而後在使用過程當中本身還封裝和改寫過一些工具類,這部分也集成進來。另外就是把一些配置都規範化,將一些經常使用參數配置成默認參數,同時放開重寫開關,在外部能夠重定義。
秉承着上面的思想,我大約是在 10 月 6 日 那天完成了框架的搭建,而後在後續的幾天基於這個框架實現了幾個基礎模型,最終打磨成了如今的樣子。
GitHub 地址:https://github.com/ModelZoo/ModelZoo
框架我已經發布到 PyPi,直接使用 pip 安裝便可,目前支持 Python3,Python 2 還沒有作測試,安裝方式:
pip3 install model-zoo
其實我是很震驚,這個名字竟然沒有被註冊!GitHub 和 PyPi 都沒有!不過如今已經被我註冊了。
OK,接下來讓咱們看看用了它能怎樣快速搭建一個模型吧!
咱們就以基本的線性迴歸模型爲例來講明吧,這裏有一組數據,是波士頓房價預測數據,輸入是影響房價的各個因素,輸出是房價自己,具體的數據集能夠搜 Boston housing price regression dataset 瞭解一下。
總之,咱們只須要知道這是一個迴歸模型就行了,輸入 x 是一堆 Feature,輸出 y 是一個數值,房價。好,那麼咱們就開始定義模型吧,模型的定義咱們繼承 ModelZoo 裏面的 BaseModel 就行了,實現 model.py 以下:
from model_zoo.model import BaseModel
import tensorflow as tf
class BostonHousingModel(BaseModel):
def __init__(self, config):
super(BostonHousingModel, self).__init__(config)
self.dense = tf.keras.layers.Dense(1)
def call(self, inputs, training=None, mask=None):
o = self.dense(inputs)
return o
好了,這就定義完了!有人會說,你的 Loss Function 呢?你的 Optimizer 呢?你的 Checkpoint 保存呢?你的 Tensor Summary 呢?不須要!由於我已經把這些配置封裝到 BaseModel 了,有默認的 Loss Function、Optimizer、Checkpoint、Early Stop、Tensor Summary,這裏只須要關注模型自己便可。
有人說,要是想自定義 Loss Function 咋辦呢?自定義 Optimizer 咋辦呢?很簡單,只須要複寫一些基本的配置或複寫某個方法就行了。
如改寫 Optimizer,只須要重寫 optimizer 方法便可:
def optimizer(self):
return tf.train.AdamOptimizer(0.001)
好,定義了模型以後怎麼辦?那固然是拿數據訓練了,又要寫數據加載,數據標準化,數據切分等等操做了吧,寫到什麼方法裏?定義成什麼樣比較科學?如今,咱們只須要實現一個 Trainer 就行了,而後複寫 prepare_data 方法就行了,實現 train.py 以下:
import tensorflow as tf
from model_zoo.trainer import BaseTrainer
from model_zoo.preprocess import standardize
tf.flags.DEFINE_integer('epochs', 100, 'Max epochs')
tf.flags.DEFINE_string('model_class', 'BostonHousingModel', 'Model class name')
class Trainer(BaseTrainer):
def prepare_data(self):
from tensorflow.python.keras.datasets import boston_housing
(x_train, y_train), (x_eval, y_eval) = boston_housing.load_data()
x_train, x_eval = standardize(x_train, x_eval)
train_data, eval_data = (x_train, y_train), (x_eval, y_eval)
return train_data, eval_data
if __name__ == '__main__':
Trainer().run()
好了,完事了,模型如今已經所有搭建完成!在這裏只須要實現 prepare_data 方法,返回訓練集和驗證集便可,其餘的什麼都不須要!
數據標準化在哪作的?這裏我也封裝好了方法。
運行在哪運行的?這裏我也作好了封裝。
模型保存在哪裏作的?一樣作好了封裝。
Batch 切分怎麼作的?這裏也作好了封裝。
咱們只須要按照格式,返回這兩組數據就行了,其餘的什麼都不用管!
那一樣的,模型保存位置,模型名稱,Batch Size 多大,怎麼設置?仍是簡單改下配置就行了。
如要修改模型保存位置,只須要複寫一個 Flag 就行了:
tf.flags.DEFINE_string('checkpoint_dir', 'checkpoints', help='Data source dir')
好了,如今模型能夠訓練了!直接運行上面的代碼就行了:
python3 train.py
結果是這樣子的:
Epoch 1/100
1/13 [=>............................] - ETA: 0s - loss: 816.1798
13/13 [==============================] - 0s 4ms/step - loss: 457.9925 - val_loss: 343.2489
Epoch 2/100
1/13 [=>............................] - ETA: 0s - loss: 361.5632
13/13 [==============================] - 0s 3ms/step - loss: 274.7090 - val_loss: 206.7015
Epoch 00002: saving model to checkpoints/model.ckpt
Epoch 3/100
1/13 [=>............................] - ETA: 0s - loss: 163.5308
13/13 [==============================] - 0s 3ms/step - loss: 172.4033 - val_loss: 128.0830
Epoch 4/100
1/13 [=>............................] - ETA: 0s - loss: 115.4743
13/13 [==============================] - 0s 3ms/step - loss: 112.6434 - val_loss: 85.0848
Epoch 00004: saving model to checkpoints/model.ckpt
Epoch 5/100
1/13 [=>............................] - ETA: 0s - loss: 149.8252
13/13 [==============================] - 0s 3ms/step - loss: 77.0281 - val_loss: 57.9716
....
Epoch 42/100
7/13 [===============>..............] - ETA: 0s - loss: 20.5911
13/13 [==============================] - 0s 8ms/step - loss: 22.4666 - val_loss: 23.7161
Epoch 00042: saving model to checkpoints/model.ckpt
能夠看到模型每次運行都會實時輸出訓練集和驗證集的 Loss 的變化,另外還會自動保存模型,自動進行 Early Stop,自動保存 Tensor Summary。
能夠看到這裏運行了 42 個 Epoch 就完了,爲何?由於 Early Stop 的存在,當驗證集通過了必定的 Epoch 一直不見降低,就直接停了,繼續訓練下去也沒什麼意義了。Early Stop 哪裏配置的?框架也封裝好了。
而後咱們還能夠看到當前目錄下還生成了 events 和 checkpoints 文件夾,這一個是 TensorFlow Summary,供 TensorBoard 看的,另外一個是保存的模型文件。
如今能夠打開 TensorBoard 看看有什麼狀況,運行命令:
cd events
tensorboard --logdir=.
能夠看到訓練和驗證的 Loss 都被記錄下來,並化成了圖表展現。而這些東西咱們配置過嗎?沒有,由於框架封裝好了。
好,如今模型有了,咱們要拿來作預測咋作呢?又得構建一邊圖,又得從新加載模型,又得準備數據,又得切分數據等等,仍是麻煩,並無,這裏只須要這麼定義就行了,定義 infer.py 以下:
from model_zoo.inferer import BaseInferer
from model_zoo.preprocess import standardize
import tensorflow as tf
tf.flags.DEFINE_string('checkpoint_name', 'model.ckpt-20', help='Model name')
class Inferer(BaseInferer):
def prepare_data(self):
from tensorflow.python.keras.datasets import boston_housing
(x_train, y_train), (x_test, y_test) = boston_housing.load_data()
_, x_test = standardize(x_train, x_test)
return x_test
if __name__ == '__main__':
result = Inferer().run()
print(result)
這裏只須要繼承 BaseInferer,實現 prepare_data 方法就行了,返回的就是 test 數據集的 x 部分,其餘的仍是什麼都不用幹!
另外這裏額外定義了一個 Flag,就是 checkpoint_name,這個是必不可少的,畢竟要用哪一個 Checkpoint 須要指定一下。
這裏咱們仍是那數據集中的數據當測試數據,來看下它的輸出結果:
[[ 9.637125 ]
[21.368305 ]
[20.898445 ]
[33.832504 ]
[25.756516 ]
[21.264557 ]
[29.069794 ]
[24.968184 ]
...
[36.027283 ]
[39.06852 ]
[25.728745 ]
[41.62165 ]
[34.340042 ]
[24.821484 ]]
就這樣,預測房價結果就計算出來了,這個和輸入的 x 內容都是一一對應的。
那有人又說了,我若是想拿到模型中的某個變量結果怎麼辦?仍是很簡單,由於有了 Eager 模式,直接輸出就好。我要自定義預測函數怎麼辦?也很簡單,複寫 infer 方法就行了。
好,到如今爲止,咱們經過幾十行代碼就完成了這些內容:
數據加載和預處理
模型圖的搭建
Optimizer 的配置
運行結果的保存
Early Stop 的配置
Checkpoint 的保存
Summary 的生成
預測流程的實現
總而言之,用了這個框架能夠省去不少沒必要要的麻煩,同時相對來講比較規範,另外靈活可擴展。
以上就是 ModelZoo 的一些簡單介紹。
如今這個框架剛開發出來幾天,確定存在不少不成熟的地方,另外文檔也尚未來得及寫,不過我確定是準備長期優化和維護下去的。另外既然取名叫作 ModelZoo,我後面也會把一些經常使用的深度學習模型基於該框架實現出來併發布,包括 NLP、CV 等各大領域,同時在實現過程當中,也會發現框架自己的一些問題,並不斷迭代優化。
好比基於該框架實現的人臉情緒識別的項目:https://github.com/ModelZoo/EmotionRecognition
其識別準確率仍是能夠的,好比輸入這些圖片:
模型即可以輸出對應的情緒類型和情緒分佈:
Image Path: test1.png
Predict Result: Happy
Emotion Distribution: {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 1.0, 'Sad': 0.0, 'Surprise': 0.0, 'Neutral': 0.0}
====================
Image Path: test2.png
Predict Result: Happy
Emotion Distribution: {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.998, 'Sad': 0.0, 'Surprise': 0.0, 'Neutral': 0.002}
====================
Image Path: test3.png
Predict Result: Surprise
Emotion Distribution: {'Angry': 0.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Sad': 0.0, 'Surprise': 1.0, 'Neutral': 0.0}
====================
Image Path: test4.png
Predict Result: Angry
Emotion Distribution: {'Angry': 1.0, 'Disgust': 0.0, 'Fear': 0.0, 'Happy': 0.0, 'Sad': 0.0, 'Surprise': 0.0, 'Neutral': 0.0}
====================
Image Path: test5.png
Predict Result: Fear
Emotion Distribution: {'Angry': 0.04, 'Disgust': 0.002, 'Fear': 0.544, 'Happy': 0.03, 'Sad': 0.036, 'Surprise': 0.31, 'Neutral': 0.039}
====================
Image Path: test6.png
Predict Result: Sad
Emotion Distribution: {'Angry': 0.005, 'Disgust': 0.0, 'Fear': 0.027, 'Happy': 0.002, 'Sad': 0.956, 'Surprise': 0.0, 'Neutral': 0.009}
若是你們對這個框架感興趣,或者也想加入實現一些有趣的模型的話,能夠在框架主頁提 Issue 留言,我很是歡迎你的加入!另外若是你們感受框架有不足的地方,也很是歡迎提 Issue 或發 PR,很是很是感謝!
最後,若是你喜歡的話,還望能贈予它一個 Star,這樣我也更有動力去維護下去。
項目的 GitHub 地址:https://github.com/ModelZoo/ModelZoo。
謝謝!