持久化数据
数据分析
特征预处理
模型训练
模型验证以及调优(调优是调速度和精度)
-
精度调优:查找预测错误的样本,观察预测错误的样本与预测正确的样本,分析模型为什么对这类样本出错,调整策略
-
速度调优:速度关注的推理速度,可以使用轻量型模型,可以通过模型压缩(剪枝,量化,OONX)
模型保存和上传
因为模型训练在此环境下发生,因此它是所需算力最大的环境,需要有GPU进行算力支持
(所有)训练好的模型是需要保存的,
一般使用训练结束时的时间戳作为模型版本的标识,方便回溯。
用于测试模型加载服务的关键指标,保证模型的正常预测功能以及相关工程化措施
需要提供测试用例给测试工程师来测试评估模型是否满足需求,模型稳定性是否满足上线条件
需要与生产环境配置保持一致,算力要求不高,不需要GPU进行算力支持
-
申请时说明测试环境用来做什么,需要什么样的配置,算力适当即可,一般不配备GPU
-
申请完之后需要将模型部署在测试服务器上,并应当满足上下游服务需求的接口
测试的服务满足上线指标要求之后,即可将代码和模型部署在生产环境中。供线上请求调用。
与测试环境相同,生产环境决定测试环境,需要使用多借点分布,保证高可用性。
由服务器自动创建和维护的文件(需要在代码中嵌入日志相关的功能)
日志一般负责信息打印,异常查询,数据指标的统计
打印的内容一般会按照功能分为不同的级别(可以自定义级别)
- DEBUG:测试过程中,寻找Bug是打印的信息,不会在生产环境中出现
- INFO:正常的输出信息,作为某些功能正常启用的提示
- WARNING:警告信息,作为模型功能可能出现异常的警告
- ERROR:错误信息,说明某些代码运行出现了错误,需要及时修正
按天将日志进行分割
日志会越来越多占用磁盘空间,服务器上一般只保留3天的日志信息,定期将多余的日志存档转义到专门的硬盘存储
Logging工具的使用:
import os
# 从logging的处理器中导入时间切片处理器
from logging.handlers import TimedRotatingFileHandler
# 定义日志的写入路径
log_root = "./"
log_path = log_root + "flask.log"
if not os.path.exists(log_root):
os.mkdir(log_root)
# 日志处理器主要配置日志处理方式的相关参数
# filename: 日志存储路径+日志名称
# when: 按什么时间分割,D: 天,H:小时,M:分钟,S:秒
# interval: 时间的数量,比如3天或15分钟
# backupCount: 后台日志的数量,保存多少份时停止,防止磁盘溢出
# encoding: 日志内容编码
# delay: 是否立即生效
# utc: 是否按照原子钟世界标准时间,否则使用本地时间
handler = TimedRotatingFileHandler(
filename=log_path,
when="D",
interval=1,
backupCount=15,
encoding="UTF-8",
delay=False,
utc=True,
)
# 过往日志后缀
# eg: flask.log.2020-12-08
handler.suffix = "%Y-%m-%d"
规范日志样式实现:
import logging
# 定义日志输出格式
LOG_FORMAT = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
# 设置处理器格式
handler.setFormatter(LOG_FORMAT)
# 实例化一个日志对象,日志对象具有添加某个处理器的功能
logger = logging.getLogger(__name__)
# 添加处理器
logger.addHandler(handler)
# 设置日志打印级别,默认不打印低于INFO(包括)的日志
logger.setLevel(logging.INFO)
# 打印具体信息
# 实际生产中,将打印模型的输入输出等关键信息
logger.info('info message')
输出效果:
2020-12-15 17:56:34,868 - INFO - info message
样例:
logger.setLevel(logging.INFO)
logger.info('开始读取文件')
img = cv2.imread(...)
if img is None:
logger.setLevel(logging.ERROR)
logger.info('错误 图片读取失败')
else:
logger.setLevel(logging.INFO)
logger.info('图片读取完成')
- 制作多个版本,同一时间让组成成分相同的房客群组随机访问这些版本,收集个群组的用户的体验数据和业务数据,最后分析评估出最好的版本,正式采用
- 目的是为了验证新方法/新模型的上线效果
- 具体来讲,最简单A/B testing一般包括两种对比逻辑:
- 从服务端分流的小流量整体对比实验:90%流量按照原有逻辑,10%的流量分给新的模型预测。
- 从模型服务进行分流对比:因为小流量对比实验受到一些极端情况的影响,比如线上分类不均衡。模型在小流量上给出了接近统一的结果,为了监控和避免这种情况,需要在服务中再将10%的中模型预测成1的数据进行分流,成为实验组和对照组,这样我们就是真正在比较模型预测成1的价值。90%的流量称为旧算法组。
在线服务指标
- RT(requests time):
- 整个模型服务响应时间,服务中模型预测的总体时间。
- 算法的速度对RT有直接影响。
- RT要求:
- 一般情况下,对于算法服务的RT要求小于100ms。
- Flask框架下常规模型的响应时间:
- sklearn实现的LR(线性回归):<10ms
- sklearn实现的GBDT(梯度提升树):<20ms
- Pytorch实现的MLP(全连接感知机):<70ms
- Tensorflow实现的MLP(全连接感知机):<90ms
- Pytorch实现的目标检测:<100ms
- Tensorflow实现的目标检测:<100ms
响应时间自测方法:
import time
import requests
url = "http://0.0.0.0:5005/v1/test/"
data = {
"userId": 102079311,
"birthday": "1995-04-29T00:00:00.000+0000",
"appId": 20000,
"channel": 1,
"countryId": 182,
"createTime": "2020-11-02T02:51:19.000+0000",
"languageId": "20",
"type": 6,
"stoneVersion": "other",
"platformType": 2,
"screenSize": "720*1440",
"deviceName": "TECNO-BB4k",
}
start = time.time()
res = requests.post(url, json=data)
end = time.time()
print(end - start)
print(res.text)
- QPS:
- 每秒处理请求次数,与APP日活和峰值时刻有关
- QPS一般对应量级:
- 日活30万左右,服务QPS>20
- 日活100万左右,服务QPS>80
- 日活1000万左右,服务QPS>500
- 日活3亿左右,服务QPS>5000
企业内部使用的项目管理和代码托管平台
是一个面向开源以及私有软件项目的托管平台
代码合并之前需要code review,让你的同事检测代码确无误
测试,需要提供给QA人员测试用例以及预计响应的输出
正式环境的节点申请¶
- 考虑业务端服务器的位置,因为算法服务是内嵌服务,所以需要在同一个region。
- 为了保证RT和QPS的重要指标,机器情况一般与测试机相同(4C,8G)。
- 线上环境部署使用“干净”的基础镜像,并使用git clone代码。
Docker部署¶
- Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
- 将运行环境打包成虚拟容器,使用DockerFile进行服务启动命名的编写。