Gradio快速搭建ML/DL Web端服务
前言
当我们训练好了某个模型并且效果还不错时,最先想到的应该是部署.部署又可以分为线上Web服务和边缘模块上;为了汇报的时候往往还是选择线上部署,毕竟盒子部署好了还得配置相应的硬件输入也不方便展示.在这个专栏之前尝试用fastapi
搭建了Web服务,并且将一些算法模型部署到api接口中,但是由于要自己设计一些预处理工作,路由,附加功能以及页面UI感觉很麻烦.所以今天就介绍一下这个基于fastapi
构建的一个非常方便就可以部署模型且功能强大的Web框架—Gradio
官网的链接在这里,大家可以先去了解一下Gradio 虽然目前没有中文文档,但是我感觉整个文档写的还是比较好读懂的,所以一步步模仿文档操作也不难.
介绍了这么多,下面就开始写demo.计划是这样的
- 撸一个简单的层数较少的ResNet,在
cifar10
上训练一下,得到一个效果好点的模型(这里不用迁移主要是之前为了实验重参数化在ResNet结构上效果不好然后自己写了个简单的顺便拿来用了) - 使用
Gradio
编写一个Web服务,其实只要我们实现推理的函数就行,其他的只用实例化接口,非常快捷.
开始
模型训练
最基本的残差模块堆一堆,接个全连接层分类然后就可以训练了.这里面训练特意尝试了一下最近几年提出的Ranger优化器代替之前的sgd,adam之类的,效果确实会有一定的提升.
来看看官网给出的效果对比,经过我的训练结果来看也确实如此.
使用的最简单方法只需要把ranger21.py下载下来,然后训练的时候引用就可以了或者按照官网用pip安装pip install git+https://github.com/lessw2020/Ranger21.git
只有几层的模型最终效果也还不错
这样我们就得到了一个效果还可以的模型,下一步就使用Gradio
部署
部署
从官网的入门教程可以看到,我们需要自己写推理部分的函数,并且在gr.Interface()
中注册自己的函数以及输入输出一些附加功能.不得不说这个框架封装之后真的非常便于开发,很多功能已经实现了,比如:结果缓存,模型解释甚至考虑到访问量特别大的时候利用队列而不是并行进行结果响应等等
先加载模型然后写推理函数
if os.path.exists("best.pt"): ResNet.load_state_dict(torch.load('best.pt')) ResNet.eval() labels=['airplane','automobile','bird','cat','deer','dog','frog','horse','ship','truck'] def classify_image(img): img = transforms.ToTensor()(img).unsqueeze(0).cuda() with torch.no_grad(): predict=torch.nn.functional.softmax(ResNet(img)[0],dim=0).cpu() confidence={labels[i]:float(predict[i]) for i in range(10)} return confidence 复制代码
然后注册到接口实例化并启动
gr.Interface( fn=classify_image, inputs=gr.Image(type="pil",shape=(320,320)), outputs=gr.Label(num_top_classes=10), examples=['./data/airplane.jpeg'], interpretation="default",cache_examples=True,title="Shelgi的分类Demo" ).launch(enable_queue=True) 复制代码
上面的解释基于默认的``SHAP`,也可以设置自己的解释函数.再来看看其他的结果
到这里基本的部署部分就实现了,但是和以往惯例一样,还是要带着看看源码的.
源码部分
开头我说这是基于fastapi
封装的框架,那就来好好看看它的一些实现.最基本的既然是Web框架,那就先看看它的路由部分.
很明显,就是利用fastapi来搭建路由的.App继承了FastAPI,实现了自己的相关路由(login,config,file等等)
然后在blocks中有更多功能的实现,包括前处理,运行注册函数和后处理,这也是调用预测的重要代码
再来看看基础设置,类似于url,端口号这些都在networking.py中,设置如下图
当然,我们也可以对页面进行一些自己的diy设计
可以看出来静态页面是从templates文件夹下读取的,所以我们可以对页面进行更改.或者我们想要简单的添加一些内容,可以试试模仿标题和描述那样,调用Markdown()进行添加
当然如果想要创造更复杂的页面,就需要使用Blocks
一点点的组建页面,更详细的内容有兴趣的可以去官网文档中好好查看.