flask部署深度学习模型

作为著名Python web框架之一的Flask,具有简单轻量、灵活、扩展丰富且上手难度低的特点,因此成为了机器学习和深度学习模型上线跑定时任务,提供API的首选框架。

众所周知,Flask默认不支持非阻塞IO的,当请求A还未完成时候,请求B需要等待请求A完成后才能被处理,所以效率非常低。但是线上任务通常需要异步、高并发等需求,本文总结一些在日常使用过程中所常用的技巧。

一、前沿

异步和多线程有什么区别?其实,异步是目的,而多线程是实现这个目的的方法。异步是说,A发起一个操作后(一般都是比较耗时的操作,如果不耗时的操作就没有必要异步了),可以继续自顾自的处理它自己的事儿,不用干等着这个耗时操作返回。

实现异步可以采用多线程技术或则交给另外的进程来处理,详解常见这里

二、实现方法

  • Flask启动自带方法
  • 采用gunicorn部署

    1、Flask中自带方法实现

    run.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018-12-01 16:37
# @Author  : mokundong
from flask import Flask
import socket
from time import sleep  

myhost = socket.gethostbyname(socket.gethostname())
app = Flask(__name__)  

@app.route('/job1')
def some_long_task1():
    print("Task #1 started!")
    sleep(10)
    print("Task #1 is done!")  

@app.route('/job2')
def some_long_task2(arg1, arg2):
    print("Task #2 started with args: %s %s!" % (arg1, arg2))
    sleep(5)
    print("Task #2 is done!")  

if __name__ == '__main__':
    app.run(host=myhost,port=5000,threaded=True)

app.run(host=xxx,port=xx,threaded=True)
中threaded开启后则不需要等队列。

2、gunicorn部署

Gunicorn 是一个高效的Python WSGI Server,通常用它来运行 wsgi application 或者 wsgi framework(如Django,Paster,Flask),地位相当于Java中的Tomcat。gunicorn 会启动一组 worker进程,所有worker进程公用一组listener,在每个worker中为每个listener建立一个wsgi server。每当有HTTP链接到来时,wsgi server创建一个协程来处理该链接,协程处理该链接的时候,先初始化WSGI环境,然后调用用户提供的app对象去处理HTTP请求。
关于gunicorn的详细说明,可以参考这里

使用命令行启动gunicorn有两种方式获取配置项,一种是在命令行配置,一种是在配置文件中获取。

run.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018-12-01 17:00
# @Author  : mokundong
from flask import Flask
from time import sleep  

app = Flask(__name__)  

@app.route('/job1')
def some_long_task1():
    print("Task #1 started!")
    sleep(10)
    print("Task #1 is done!")  

@app.route('/job2')
def some_long_task2(arg1, arg2):
    print("Task #2 started with args: %s %s!" % (arg1, arg2))
    sleep(5)
    print("Task #2 is done!")  

if __name__ == '__main__':
    app.run()

命令行配置

gunicorn --workers=4 --bind=127.0.0.1:8000 run:app

更多配置见官网

配置文件获取配置

gunicorn_config.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018-12-01 17:10
# @Author  : mokundong
import os
import socket
import multiprocessing
import gevent.monkey

gevent.monkey.patch_all()
myhost = socket.gethostbyname(socket.gethostname())  

debug = False
loglevel = 'info'
hosts = get_host_ip()
bind = hosts+":5000"
timeout = 30      #超时

pidfile = "log/gunicorn.pid"
accesslog = "log/access.log"
errorlog = "log/debug.log"

daemon = True #意味着开启后台运行,默认为False
workers = 4 # 启动的进程数
threads = 2 #指定每个进程开启的线程数
worker_class = 'gevent' #默认为sync模式,也可使用gevent模式。
x_forwarded_for_header = 'X-FORWARDED-FOR'

启动命令如下

gunicorn -c gunicorn_config.py run:app

三、补充

1、关于线程的补充

在工作中我还遇到一种情况,当一个请求过来后,我需要两种回应,一个是及时返回app运行结果,第二个响应是保存数据到日志或者数据库。往往我们在写数据的过程中会花销一定的时间,导致结果返回会有所延迟,因此我们需要用两个线程处理这两个任务,那么我们如下处理。

run.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018-12-01 17:20
# @Author  : mokundong
from flask import Flask,request
from time import sleep
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(2)
app = Flask(__name__)

@app.route('/job')
def run_jobs():
    executor.submit(some_long_task1)
    executor.submit(some_long_task2, 'hello', 123)
    return 'Two jobs was launched in background!'
def some_long_task1():
    print("Task #1 started!")
    sleep(10)
    print("Task #1 is done!")

def some_long_task2(arg1, arg2):
    print("Task #2 started with args: %s %s!" % (arg1, arg2))
    sleep(5)
    print("Task #2 is done!")

if __name__ == '__main__':
    app.run()

2、关于获取IP的补充

上述代码中通过获取hostname,然后再通过hostname反查处机器的IP。这个方法是不推荐的。因为很多的机器没有规范这个hostname的设置。
另外就是有些服务器会在 /etc/hosts 中添加本机的hostname的地址,这个做法也不是不可以,但是如果设置成了 127.0.0.1,那么获取出来的IP就都是这个地址了。
这里给出一种优雅的方式获取IP,利用 UDP 协议来实现的,生成一个UDP包,把自己的 IP 放如到 UDP 协议头中,然后从UDP包中获取本机的IP。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018-12-01 17:30
# @Author  : mokundong
# 可以封装成函数,方便 Python 的程序调用
import socket

def get_host_ip():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()

    return ip

总结

当然推荐使用gunicorn部署多线程,Flask自带的,emmmm,测试玩儿玩儿吧。
在写作过程中才发现自己知识漏洞不是一般多,共勉!

flask部署深度学习模型的更多相关文章

  1. PyTorch如何构建深度学习模型?

    简介 每过一段时间,就会有一个深度学习库被开发,这些深度学习库往往可以改变深度学习领域的景观.Pytorch就是这样一个库. 在过去的一段时间里,我研究了Pytorch,我惊叹于它的操作简易.Pyto ...

  2. Apple的Core ML3简介——为iPhone构建深度学习模型(附代码)

    概述 Apple的Core ML 3是一个为开发人员和程序员设计的工具,帮助程序员进入人工智能生态 你可以使用Core ML 3为iPhone构建机器学习和深度学习模型 在本文中,我们将为iPhone ...

  3. 用 Java 训练深度学习模型,原来可以这么简单!

    本文适合有 Java 基础的人群 作者:DJL-Keerthan&Lanking HelloGitHub 推出的<讲解开源项目> 系列.这一期是由亚马逊工程师:Keerthan V ...

  4. CUDA上深度学习模型量化的自动化优化

    CUDA上深度学习模型量化的自动化优化 深度学习已成功应用于各种任务.在诸如自动驾驶汽车推理之类的实时场景中,模型的推理速度至关重要.网络量化是加速深度学习模型的有效方法.在量化模型中,数据和模型参数 ...

  5. 用TVM在硬件平台上部署深度学习工作负载的端到端 IR 堆栈

    用TVM在硬件平台上部署深度学习工作负载的端到端 IR 堆栈 深度学习已变得无处不在,不可或缺.这场革命的一部分是由可扩展的深度学习系统推动的,如滕索弗洛.MXNet.咖啡和皮托奇.大多数现有系统针对 ...

  6. CUDA上的量化深度学习模型的自动化优化

    CUDA上的量化深度学习模型的自动化优化 深度学习已成功应用于各种任务.在诸如自动驾驶汽车推理之类的实时场景中,模型的推理速度至关重要.网络量化是加速深度学习模型的有效方法.在量化模型中,数据和模型参 ...

  7. TVM将深度学习模型编译为WebGL

    使用TVM将深度学习模型编译为WebGL TVM带有全新的OpenGL / WebGL后端! OpenGL / WebGL后端 TVM已经瞄准了涵盖各种平台的大量后端:CPU,GPU,移动设备等.这次 ...

  8. AI佳作解读系列(一)——深度学习模型训练痛点及解决方法

    1 模型训练基本步骤 进入了AI领域,学习了手写字识别等几个demo后,就会发现深度学习模型训练是十分关键和有挑战性的.选定了网络结构后,深度学习训练过程基本大同小异,一般分为如下几个步骤 定义算法公 ...

  9. 『高性能模型』Roofline Model与深度学习模型的性能分析

    转载自知乎:Roofline Model与深度学习模型的性能分析 在真实世界中,任何模型(例如 VGG / MobileNet 等)都必须依赖于具体的计算平台(例如CPU / GPU / ASIC 等 ...

随机推荐

  1. java Class中得到构造方法Constructor、方法Method、字段Field

    常用方法: Constructor类用于描述类中的构造方法: Constructor<T> getConstructor(Class<?>... parameterTypes) ...

  2. CF1163F Indecisive Taxi Fee

    NOIP之前留的坑 CF1163F Indecisive Taxi Fee 经典问题:删边最短路 在Ta的博客查看 任意找一条最短路E,给E上的点和边新加入一个1~len的编号 最短路上的边变大麻烦 ...

  3. springboot上传文件时500错误,提示临时目录无效

    org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nes ...

  4. sci,ei,istp三大科技文献检索系统

    印刷版(SCI) 双月刊 ,500种 联机版(SciSearch) 周更新 ,600种 光盘版(带文摘)(SCICDE) 月更新 ,500种(同印刷版) 网络版(SCIExpanded) 周更新 ,6 ...

  5. linux 快速和慢速处理

    老版本的 Linux 内核尽了很大努力来区分"快速"和"慢速"中断. 快速中断是那些能够很 快处理的, 而处理慢速中断要特别地长一些. 慢速中断可能十分苛求处理 ...

  6. Java 趣事之 a=a++ 和 a=++a(转)

    转自:https://blog.csdn.net/LovePluto/article/details/81062176 如果问 a++ 和 ++a 的区别,估计很多都能回答上来.a++ 是先取 a 的 ...

  7. Visual Studio插件【一】:前端

    JQuery Code Snippets https://github.com/kspearrin/Visual-Studio-jQuery-Code-Snippets 简单用法 jq   +tab ...

  8. 025.MFC_窗口操作

    窗口操作 一.建立名为dialogOp 的mfc 工程 ,添加9个button 和1个check box组件,并按如图修改caption属性. 最大化窗口 双击最大化button,进入dialogOp ...

  9. Linux 批量安装依赖

    1.依赖检测失败,xxx被xxxx需要. 当我安装rpm 的时候,出现依赖检测失败. 我们可以到http://rpmfind.net/linux/rpm2html/search.php 这个网站上去搜 ...

  10. MySQL的读写分离与主从同步数据一致性

    有没有做MySQL读写分离?如何实现mysql的读写分离?MySQL主从复制原理的是啥?如何解决mysql主从同步的延时问题? 高并发这个阶段,那肯定是需要做读写分离的,啥意思?因为实际上大部分的互联 ...