跟繁琐的模型说拜拜!深度学习脚手架 ModelZoo 来袭!

摘要:我我的开发的深度学习脚手架 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。

谢谢!

相关文章
相关标签/搜索