一、写在前面的话
1.1 本文介绍
在我们的日常工作中,很多算法同学可能会遇到一个问题,那就是console in,console out,这个时候会发现这种模式在做实验,复现paper的时候还可以,但是在工作过程中,这种模式和工程化相差甚远。(在实际工作中,算法的工作需要给算法模型提供接口,供工程部门或者客户调用)。所以这里就提到一个重要概念:
本文将会从0开始搭建一个算法模型,然后基于该模型开放
So,读了本文,算法工程化不是梦,迎娶白富美指日可待!!!
1.2 本文内容开发环境搭建
构建算法模型(本文案例为基于bert fine-tune的文本分类模型)
基于django的接口开发
tensorflow server 安装部署
接口测试
1.3 本文相关
1、本文中的代码和数据均开源在github上,可以完整运行。github地址为:https://github.com/charlesXu86/HelloWorldgithub.com
2、本文中的数据和代码不涉及业务,可以放心引用,放心git clone。
3、不建议star本项目,如果觉得这篇文章对你有用,star我别的项目吧。嘿嘿嘿~~吼吼
二、Django Server开发
2.1 Django开发环境
本文需要的开发环境要求为:
python 3.6 + Django + Tensorflow + pycharm(可选)+ docker
a. 首先安装django
pip install django
b. 新建一个django的项目
新建django项目有两种方式:一种是通过shell终端,另外一种是通过pycharm搭建,两种方式殊途同归,大家可以通过自己的习惯选择。
shell 模式:
django-admin startproject HelloWorld
pycharm懒人模式:
File ->New project 选中Django
图一 pycharm新建Django工程
稍等片刻,此时在你的目录下会有一个名叫HelloWorld的Django项目:
$ cd HelloWorld/
$ tree
.
|-- HelloWorld
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| `-- wsgi.py
`-- manage.py
目录说明:HelloWorld: 项目的容器。
manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
HelloWorld/__init__.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
HelloWorld/settings.py: 该 Django 项目的设置/配置。
HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。
HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。
注:上述标红的两个文件在后续接口开发的过程中会有改动,后续一一说明
(知乎居然没有标红功能)settings.py urls.pyyypppy..ssllrr u . . u ur . . .
c. 启动项目
项目新建好了就可以测试启动项目了,此时也可以使用两种模式:
shell模式:
python manage.py runserver 0.0.0.0:8000 后面两个参数为 ip:port
后台启动:
nohup python manage.py runserver 172.16.19.74:8009 > /home/logs/bot_log 2>&1 &
pycharm模式:
图二 启动项目
启动成功的界面:
图三 项目启动成功
同时,你也可以在pycharm里面设置参数,指定ip,端口,gpu,远程解释器等等(万能的pycharm)
图四 运行配置
在图四中:
Host:指定项目运行的ip,这里可以指定你的服务器地址
Port:指定运行端口
Additional options:指定项目运行的额外参数,例如指定显卡
Python interpreter:指定python解释器。现在很多人的工作模式是在本地写代码,在远程服务器上run,pycharm提供了很好的功能,可以连接远程服务器,AutoUpload,无缝run。这里就不做说明了,如果有不回配置的,请@我。
至此,Django项目搭建完成。
注:1、shell模式中的后两个参数为ip:port,在这里可以指定你的运行ip和端口
2、图二中pycharm启动项目的图中,1为运行模式启动,2为debug模式启动,为了方便调试代码,一般选用2
4、Django是一个非常优秀的Web端框架,有很强的扩展性,包括权限、数据库,视图等等,因为我们关注的是接口开发,所以其他功能忽略。
5、很多人会拿Django和Flask做比较,觉得Flask更轻量级,开发也很快捷,个人觉得开发接口的话,代码都很小,不做比较。
2.2 构建深度学习模型
项目搭建好了,我们现在开始构建算法模型,在这里我们选用的案例为基于bert fine-tune的语义相似度匹配模型,数据为LCMQC的数据。下面来说明怎么构建模型。
a. 新建文件夹
按照我的开发习惯,我会按照模块来新建文件夹或者python package,这样看起来清新舒适,后期维护起来也比较方便。在这里我们新建一个python package,名字叫HelloWorld_model,这个package转门存放我们的算法代码,因为我们一个服务不会只提供一个接口,每一个算法模型(代码)用一个文件夹管理,方便管理。新建好的文件目录为:
图五 model模块
这个红色框框里存放我们的算法代码。
b. 构建算法模型
在这里我用原生Tensorflow (版本为1.14)构建的基于bert fine-tune的语义相似度模型,代码参考的google 的bert官方代码。代码实现细节我就不细说了,但是我会说明我的一些改动。
数据格式:
图六 bert similar数据
数据的格式为: sentence1\tsentence2\tlabel
代码:
图七 代码结构
config_bert.py: 模型的超参数配置等,包括加载预训练模型文件
run_similarity_bert.py: 模型实现。代码细节请移步github。
在这里我要对run_similarity_bert.py代码做一些说明:
1、代码的开头有
from bert4tf import modeling
from bert4tf import tokenization
from bert4tf import optimization
这个bert4tf是我自己封装的一个pypi的包,目前V1.0版本只支持加载bert,(超级简单的封装)。您可以
pip install bert4tf
如果不想安装,也可以将bert的三个源码文件放到项目文件夹来引用他。
2、代码里加入了python的queen(队列)包,在整个predict的预测流程为:
请求 -> 入队 -> 模型推断 -> 出队 -> 返回
3、模型predict的方法为:
def predict(self, sentence1, sentence2):
if self.mode is None:
raise ValueError("Please set the 'mode' parameter")
self.input_queue.put([(sentence1, sentence2)])
prediction = self.output_queue.get()
return prediction
这个方法在BertSim 类下,这样写的好处是工程部署以后,每次只需要在服务启动时初始化一次模型(初始化会非常慢)。
注:1、bert4tf是我自己开发的一个包,目前V1.0版本只支持加载bert,现在正在积极开发V2.0,此版本支持tf2.0,支持加载bert、albert、roberta权重,支持layer操作等等功能
2、也有其他大佬做了相关工作,我开发这个的目的一方面是自己学习,另一方面是为了自己定制化调用
2.3 开发RESTful API
模型构建好了我们就可以开发接口了,在这里我们用django的一个新命令来新建一个接口模块:
python manage.py startapp HelloWorld_rest
然后在该模块下新建一个名为Api的文件夹,将HelloWorld模块里的url.py文件复制到HelloWorld_rest目录下。
图八 加入rest模块结构
至此,整个项目的全部结构已基本完成,接下来我来讲述api路由,接受post请求,传入模型预测,接收预测结果,接口返回的整个流程。
a. 在Api文件下新建一个文件夹
图九 Api目录
Similar_server.py: 接受post请求的入口,这个方法的作用是解析请求参数,把解析好的参数传入模型,接收到模型预测结果后,再封装成JsonReponse的格式返回。
Get_similar.py:这是一个中间过渡方法。这个方法存在的意义是将模型和接口解耦。因为我们很多时候在接收到模型的返回之后还有一些逻辑操作,这些操作既不方便写在模型文件里,也不方便写在接口文件里。
写到这里,你以为接口开发就完成了吗?No!因为还有一个重要的工作没做,那就是配置接口路由
b. 配置接口路由
图十 路由配置文件
首先配置图十中1,1中urls.py的完整代码为:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include("HelloWorld_rest.urls")),
]
在这里加入了一行
path('api/', include("HelloWorld_rest.urls")),
这个urls.py文件起到一个全局路由的作用,加入的这一行代码的意思是路由到我们的rest模块下的urls.py文件中,第一个参数是路径。
然后修改图十中2的urls.py文件,这个文件的作用是路由到我们的接口中,代码为:
from django.urls import path
from HelloWorld_rest.Api.similar.Similar_server import sim_server
urlpatterns = [
path('similar', sim_server)
]
第一个参数是我们的接口url,和前面的文件拼接起来就是api/similar.
c. 修改全局配置文件settings.py
到目前为止,接口开发工作已经完成,但是在请求的时候可能还存在一些问题,比如无法接受接口的返回值,安全验证错误等,这是我们要修改一下全局配置文件settings.py,修改内容如下:
1、ALLOWED_HOSTS = ['*'] *的意思是接受任何ip请求,同时你也可以指定ip,以“,”分隔
2、注释掉MIDDLEWARE中“django.middleware.csrf.CsrfViewMiddleware”这一行
3、将语言和时区修改为
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
4、当你请求远程服务器时,请确保你关闭了防火墙,或者添加了ip白名单
d. 启动服务
图十一 启动服务
点击七星瓢虫按钮,以debug模式启动服务,正常启动后你将看到:
图十二 服务启动成功
e. 接口测试
我是在mac下用的ApiPost工具来测试的,同时你也可以使用postman,接口的url为:
ip:port/api/你定义的接口名称
测试结果如下所示:
图十三 接口测试结果
通过上图我们可以看到,测试的这两句话的语义相似度为0.939,效果还可以,但是我在2080的gpu服务器上匹配一次需要花费80ms左右,在某些qps要求较高的场景下,这个速度太慢了,所以接下来的一个工作就是不丧失精度的条件下提高接口的响应。比如:
1、使用albert(会丧失精度,空间换时间)
2、使用DistilBert
3、提高数据传输速率等等
2.4 小结
接口的开发工作完成了,现在做一些总结,纵观上述的开发流程,代码量是不大的,主要的代码还是构建一个好的模型,但是在开发的过程中也需要注意一些问题:
1、predict函数一定要写在一个class下,在服务启动的时候初始化这个类(只初始化一次),这样才能保证后续的每次请求快速得到模型的返回结果。
2、在请求量大的时候需要注意并发,比如我在构建模型的时候用到了队列
3、在一个服务有多个模型的时候,这个时候在django的作用下,有时模型的session会发生错乱,从而报错。这类情况已有解决方案,后续说明
4、django还有一个非常好的功能,就是热启动,何为热启动呢,就是当你的代码发生任何改动时,django会检测到,然后自动重启项目。
三、Tensorflow server 开发搭建
明天更新~~