機器學習模型python在線服務部署的兩種實例

背景

衆所周知python在機器學習實踐中的應用普遍深刻,而在咱們業務中的應用集中在提供線上實時風控輸出服務,好比國內業務的模型在線服務架構和海外業務的後臺決策引擎架構。這兩種應用的結合就要求咱們考慮如何高效安全便捷地來實現模型的在線部署,爲上游提供服務。
   在咱們的考慮中,不管是代碼複雜程度和業務場景,仍是語言自己的特色,模型部署都有趨於向微服務架構轉型的趨勢和須要。一方面,須要進行代碼分離來明確責任分工提升開發效率和容錯性。另一個方面,python在CPU密集型的應用中表現是沒法使人滿意的。爲了使用協程來提升異步性從而處理更多的併發請求,最直接地就是將CPU密集轉化爲IO密集,由於Python天生就適合IO密集型的網絡應用。
   所以,咱們生產中將模型計算抽取爲model_lib代碼庫,而且經過微服務online_model進行交互。這裏咱們調研過兩種模型部署的方式,最終選擇了第一種。python

1、基於flask框架進行模型部署

Flask是一個輕量級的可定製框架,具備靈活、輕便且高效的特色,而且是標準的wsgi接口框架,易於擴展和維護。nginx

1. 爲何選用nginx+uwsgi+flask這種技術架構

1)    Uwsgi搭配nginx性能快,內存佔用低,高度可定製,自帶詳盡日誌功能,支持平滑重啓。git

2)    Flask徹底兼容了wsgi標準; 微框架,擴展性強; 徹底基於unicode,不需處理編碼問題;自帶服務可獨立作單元測試及開發。
image.pnggithub

3)    咱們客戶端採用了tornado協程,已經實現了將cpu計算轉爲io操做,服務端徹底是CPU密集的模型計算,不會釋放進程,異步框架保持大量文件描述符狀態耗費內存,所以不適用異步IO框架。web

2. 業務流程框架

image.png

3. 部署方式:

部署方式採用nginx+uwsgi+flask的方式,uwsgi可直接接受socket而不是http請求提升性能,再將服務轉發給flask框架,這裏注意flask此類wsgi標準接口的服務框架好比djangoweb.py在生產中通常不使用自帶服務,而是在上層部署uwsgi或者gunicorn做爲服務器來進行服務轉發,上層再用nginx來作負載均衡,這樣能夠提升服務穩定性和性能。
(這裏的inrouter層是咱們本身封裝的路由層,用來作集羣中的路由轉發,nginx只在本地作了端口轉發。)
image.pngdjango

4. 代碼示例:

uwsgi服務配置:json

[uwsgi]
# 監聽端口
socket=127.0.0.1:8200
# 進程數
processes=20

;async=4
;threads=2
;enable-threads = true
# 運行的目錄
chdir = /home/rong/www/online_model
# wsgi文件
wsgi-file = model_main.py
callable=app
# 是否要有主進程
master = true
# 後臺運行及其打印的日誌
daemonize = /home/rong/www/log/uwsgi.log
# 主進程pid文件
pidfile = /home/rong/www/log/online_model/pid/uwsgi.pid
# 日誌切割大小
log-maxsize = 5000000
# 不記錄請求信息的日誌。只記錄錯誤以及uWSGI內部消息到日誌中。
disable-logging = false
# 超時時間
http-timeout= 3
# 傳輸數據大小限制
buffer-size  = 1048576
# 每一個進程單獨加載
lazy-apps = true

flask服務關鍵代碼:flask

import importlib
import json
import cProfile
import pstats
import StringIO
import time
import traceback
from flask import Flask, request
from common import rong_logger
from common.global_variable import StaticCacheClass
import autopath  # 不能去掉

app = Flask(__name__)
# 這裏是模型代碼庫的統一入口,模型代碼庫中是經過抽象類實現的規範化的模型代碼實例,經過此服務提供調用,也經過離線調度進行跑批任務。保證線上線下模型調用一致性
online_model_main = importlib.import_module('online_model_main')

MUST_PARAMS = ['resource_id', 'feature_dict']
SUCCESS_STATUS = 0
ERROR_STATUS = 1


# 路由函數只容許post請求
@app.route("/", methods=['POST'])
def model_main():
    uniq_id = '[%s][%s]' % (request.form.get('resource_id', ''), request.url)
    try:
        status, msg, params = _check_params(request)
        if status != SUCCESS_STATUS:
            rong_logger.error(uniq_id + 'params error, detail: %s' % msg)
            status, msg, result = status, msg, None
        else:
            resource_id, feature_dict = params['resource_id'], json.loads(params['feature_dict'])
            status, msg, result = online_model_main.main(resource_id, feature_dict, request, rong_logger)
            rong_logger.info(uniq_id + '[%s][%s][%s]' % (status, msg, result))

    except Exception as e:
        rong_logger.error(uniq_id + 'error: %s, detail: %s' % (str(e), traceback.format_exc()))
        status, msg, result = 5, 'online_model_error:' + str(e), None
    return _get_response(status, msg, result)

模型代碼庫模型實例:後端

其中 XgboostExecutor類是基於xgb模型抽象類實現的類,經過它來實例化一個模型對象,給flask應用提供調用。具體咱們再也不深究,有興趣能夠再寫一個專題來介紹模型代碼庫的部署。安全

# -*- coding:utf-8 -*-
# !/usr/bin/python

import logging
import os

from executor.src.load_helper import read_cur_path
from executor.xgboost_model_executor import XgboostExecutor

logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(message)s')




[model_path, features_path,feature_importance_path] = map(
    read_cur_path, ["xgb_model", "feature_list","feature_importance"]
)
model = XgboostExecutor(model_path, features_path,
                        feature_check_white_list=["n21_score"],
                        white_or_weight=False,
                        feature_check_weight_limit= 1,
                        feature_importance_path=feature_importance_path,
                        manager="qutianhang@xx.com",
                        developer="qutianhang@xx.com",
                        correlation="negative")

5. 性能比對

微服務改造後20併發請求模型:
image.png
微服務改造前20併發請求模型:
image.png

本機測試併發性能就提升了20%,但注意這是在高併發的狀況下,就單條請求來看,微服務並不能顯著提升性能。

2、 基於grpc進行在線模型部署

在 gRPC 裏客戶端應用能夠像調用本地對象同樣直接調用另外一臺不一樣的機器上服務端應用的方法,可以更容易地建立分佈式應用和微服務。與許多 RPC 系統相似,gRPC 也是基於如下理念:定義一個服務,指定其可以被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,並運行一個 gRPC 服務器來處理客戶端調用。在客戶端擁有一個存根可以執行在服務端實現的同樣的方法(這個方法就相似於接口)
image.png

1. 爲何選用grpc進行模型部署

  • 1)grpc使用ProtoBuf來定義服務、請求返回的數據格式,壓縮和傳輸效率高,語法簡單,表達力強。(以下爲ProtoBuf的序列化和反序列話性能表現

image.pngimage.png

  • 2)grpc可支持tensorflow serving的接口調用,tensorflow完成模型訓練和部署後,可提供服務接口,給grpc進行調用。實現方便,高效,自帶版本管理、模型熱更新等,很適合大規模線上業務,是咱們下一步模型部署的技術方向。
  • 3)gRPC支持多種語言,並可以基於語言自動生成客戶端和服務端功能庫。

2. 部署方式(業務流程與以前相同)

部署方式採用nginx+grpc,要求nginx支持http2.0。在客戶端將json特徵字典轉爲protobuf。(https://github.com/NextTuesday/py-pb-converters/blob/master/pbjson.py 這裏附上json和protobuf互相轉化的腳本。)

image.png

3. 服務發現與負載均衡

image.png

4. 開發流程

image.png

客戶端:
image.png

服務端:
image.png

3、 兩種方式線上模型部署對比

1)    grpc使用protbuf更加複雜,須要在客戶端服務端均保留protbuf文件並作校驗,而flask只須要作好統一的接口標準規範便可。

2)    grpc使用http2.0更適用移動端的在線模型預測,或者基於tensorflowd的大規模線上模型部署和預測,flask更適用後端面向服務的手動模型部署和預測。

3) grpc節省數據空間,但與python交互須要作json和protbuf數據轉換,flask兼容wsgi標準,適用於RESTful類服務,但數據傳輸佔用空間較大。

相關文章
相關標籤/搜索