网易严选机器学习平台建设实践
网易严选产品技术团队
随着严选的发展,用户数的增加,深度学习算法在业务中承担的角色越来越重要。在推荐、搜索和广告等业务承担的增长指标越来越多,如何提高算法人员的开发效率,成了严选算法工程亟需解决的问题。为了解决以上问题,严选机器学习平台应运而生。本文主要介绍严选机器学习平台的建设思路,以供大家参考。
1. 业务背景
随着严选的发展,用户数的增加,深度学习算法在业务中承担的角色越来越重要。在推荐、搜索和广告等业务承担的增长指标越来越多,如何提高算法人员的开发效率,成了严选算法工程亟需解决的问题。为了解决以上问题,严选机器学习平台应运而生。本文主要介绍严选机器学习平台的建设思路,以供大家参考。
严选算法在19年之前还处在刀耕火种阶段,所有的训练和预测基本都是在物理机或者虚拟机上执行的,调度也是依赖crontab命令,不仅效率低下,也非常的难于管理。
以下为19年之前算法开发以及调度上线的流程:
算法人员需要申请训练物理机,找SA安装算法框架以及rsync命令等。整个操作流程非常复杂,不同的算法用户还要规划好用显卡的时间,调度起来很容易出问题。
因此,在19年底严选开始了算法平台的立项开发,平台主要针对严选算法业务的使用习惯进行产品设计开发,以提升严选算法开发同学的效率。
2. 算法开发流程
要提供平台提升算法同学的开发效率,首先要了解严选算法的开发流程。算法开发一般分为以下几个阶段:数据预处理、特征工程、模型训练、模型校验和模型上线。
这几个阶段通常还需要定时的调度,以便让模型能从最新的样本数据中学习到更优的参数。整个流程大致如下图:
- 数据收集: 数据预处理阶段通常在猛犸大数据平台通过mapreduce或者spark完成,猛犸也有相应的调度引擎Azkaban,该步骤算法平台不过多涉及。
- 数据处理: 该部分特指特征工程,特征工程阶段的边界比较模糊,既可以在猛犸中进行特征工程,也可以在模型中进行特征工程(tensorflow内嵌很多特征处理方法),这里说的特征工程特指模型外部进行的特征工程。该步骤通常也通过pyspark进行处理。
- 模型训练: 模型训练是算法平台的核心功能,主要提供各种深度学习框架的能力以及训练日志输出,资源使用的监控等功能。
- 模型评估: 为了保证训练出来的模型效果不会太差,通常会进行模型的校验,该阶段通常和模型训练的代码组织在一起,目前由算法同学管理。
- 模型发布: 模型训练的结果通常是一个包含了各种参数的文件,模型上线通常需要使用模型容器加载该参数文件,并提供相应的http服务或者grpc服务供线上业务使用,该部分也是算法平台提供的核心能力。
- 流程调度: 模型上线完成之后,模型通常需要定时的迭代,以不断地从新的样本学习到最近的参数,所以整个流程的调度也是机器学习平台必要的功能;调度框架业界已经很成熟了,从Azkaban到Airflow,再到K8S上面的Argo,由于严选大数据平台其他内部业务已经有了airflow调度引擎的使用经验,平台决定使用airflow作为模型训练调度的引擎。
3. 开源调研
彼时,算法平台已经百花齐放,业内也有很多的实现方案,包括阿里的PAI、腾讯的TAI机器学习平台。而开源世界比较火的就是kubeflow,由于kubeflow是云原生的,严选业务这两年也在向云服务转型,经过几轮的调研,严选机器学习平台决定基于kubeflow进行改造开发。
由于kubeflow是云原生的机器学习平台,平台的各个功能模块都是以组件化的形式提供能力的。各个组建通过提供的SDK松散耦合。这为算法平台的定制化开发提供了很大的便利。
严选算法平台决定使用kubeflow提供的各种训练组件(主要是训练Operator)、Fairing SDK以及Kfserving,在此基础上进行构建。
4. 整体架构
严选算法平台的整体架构图如下:
图中虚线标注部分是K8S集群云部署的,Smart-jobs、Smart-infer以及Smart-backend三个后台应用云外部署。其中:
- Smart-jobs主要用来统一管理训练任务,所有的训练任务创建以及查询都要经过该服务。
- Smart-Infer统一管理推理服务,推理服务的创建查询都走这个服务。
- Smart-backend为前台界面统一提供后端接口,接口请求的数据统一由smart-infer和smart-jobs承接,该应用相当于一个代理。前端只跟该服务交互。
Smart-infer和Smart-jobs主要是通过K8S提供的API Server来跟K8S组件交互,来生成分布式训练任务、在线推理服务等。
整个核心的训练组件都是部署在K8S集群的,平台还通过对训练和推理集群分别部署实现了离线和在线的隔离,两个集群共享模型文件主要通过Ceph文件系统。
5. 分模块介绍
5.1 训练
训练一般需要包含两个概念,一个是训练的开发环境,另外一个是训练的运行时环境。
- 开发环境
顾名思义,就是算法用户开发代码,调试代码的地方。 - 训练环境
真正执行训练任务的地方,通常需要使用GPU进行训练,样本大的情况下还需要分布式训练。
用户在开发环境完成代码开发之后,需要将代码部署到训练环境训练,而这个过程是通过fairing SDK实现的,结构图如下:
5.1.1 开发环境(Notebook)
由于算法开发基本上都使用Python进行开发,所有的算法开发人员基本上都有使用Jupyter开发的经验,简单易上手。因此平台决定使用Jupyter Notebook作为算法开发的环境,使用Jupyter Hub进行用户隔离。
开发环境限制:目前平台会为每个用户单独创建一个Jupyter Notebook开发环境的Pod,Pod资源目前是默认的(不分配GPU),后续可能会对该部分进行优化。由于个人Notebook资源分配有限,因此仅限于小数据量调通流程,大数据量训练需要转移到训练环境。
平台通过为每个用户的Notebook暴露端口,用户可以使用VScode进行远程的大型工程开发。
开发环境的代码通过编写并运行submit.py,即可将开发环境的代码提交到远程的训练环境。(submit.py主要将本地代码打包成docker镜像,然后通过请求java服务生成相应的训练任务。)
submit.py代码的简单示例如下:
代码提交使用如下:
...
worker_config = { 'resource': { 'cpu': 1, 'gpu': 1, # 根据需要填写
'memory': 2, # 默认单位2GB
'gpu_type':'2080-ti' # 选填
}
}
job = TrainJob(entry_point='train.py',
worker_config=worker_config,
job_name='test-train-task-job',
project_path='/home/jovyan/test_project', # 可选参数,默认执行路径
frame_version='tensorflow==1.14.0', #可选参数,默认tensorflow==1.14.0
code_params={'env_1':'value1'},#环境变量,训练代码可以获取该变量
stream_log=True #可选参数,在当前terminal输出训练日志,默认False
)
job.submit()
...
5.1.2 训练环境
训练环境通过Kubeflow提供的各种训练Operator组件生成和维护,该组件介绍详见 Kubeflow Training Operator介绍[1]。
5.2 模型管理
目前严选训练任务大多数是定时训练调度的,平台对于每次调度产出的模型提供了管理功能。用户使用Python SDK即可将模型文件保存到平台。
from kubeflow.fairing import utils
version = utils.model_upload(model_name="tf-model",model_path='/model/saved_model_half_plus_three/00000123',frame="tensorflow",frame_version="1.14.0")
print(version)
模型管理方便了模型文件的回溯,如果新训练出来的模型文件效果不理想可以及时退回到任意历史版本。提升了平台的稳定性。
5.3推理
用户在Notebook训练完成之后需要将模型发布上线,平台提供了一整套的流程简化模型的发布流程。
上线完成之后模型主要提供预测接口给线上业务使用,对接口响应时间比较敏感,因此推理服务的设计方案经过了一系列的优化。从原生kfserving到istio微服务,再到域名转发优化,一步一步优化到业务方能接受的水平(网络消耗3ms以内)。
5.3.1 推理模型发布架构
推理服务发布的大概步骤如图所示:
- 用户使用SDK保存模型到ceph,之后通过SDK调用发布接口
- 推理管理服务调用K8S API 发布推理的deploy和svc资
- 推理管理服务调用轻舟API发布istio的vs和route资源
- 推理服务里服务调用轻舟API注册路由到轻舟Gateway,完成发布
5.3.2 推理网络流量模型
kfserving方案
Kfserving原生推理服务的部署方式是使用knative进行函数式的部署,用户不需要手动调整资源配置,knative会自动扩缩容。彼时,knative底层以来istio进行网络流量管理。
经过队Knative进行了简单的性能压测,发现当时性能太差,完全达不到业务方的需求,即便通过设置minScale为1去除掉0启动的功能,性能也完全达不到要求,因此在刚开始的时候就排除了原生Kfserving的方案。
性能压测对比:
5.3.3 微服务
排除了原生kfserving方案之后,平台又调研了其他两个方案:
- deployment + service + nodeport
- deployment + istio
第一种方案使用的是最原始的k8s部署服务的方式,没有加入任务其他组件,直接使用原声deployment控制pod的部署,service + nodeport暴露服务,性能理论上也是最优的。
第二种方案加入了istio微服务中间件。
大致对两种方案进行了新能测试:
性能测试结果发现两种方案性能差异不大,但是使用第一种方案,对于将来服务增多情况下的流量管理会变得不方便,另外纯原生的k8s方案也没有提供各种指标监控模块等,也不好观测,因此最终选择了istio方案。
由于杭研在开源istio的方案下进行了很多的优化,因此采用优化后的istio组件,并且后续去处了istio-sidecar组件,进一步对性能进行了优化。
服务调用方目前通常是部署在云外,需要通过域名访问云内服务,因此平台对于nginx域名转发方案也进行了重新选择,减少nginx跳转。
最终流量模型如下:
注:gateway-proxy是杭研优化过后的istio网关,负责流量重定向。
5.4 流程编排调度(Pipeline)
Kubeflow目前使用argo作为调度引擎,Argo 是 Applatix 推出的一个开源项目,为 Kubernetes 提供 container-native(工作流中的每个步骤是通过容器实现)工作流程。Argo 可以让用户用一个类似于传统的 YAML 文件定义的 DSL 来运行多个步骤的 Pipeline,但是人员有限,本着不引入过多复杂性的原则,我们直接使用了严选大数据平台比较熟悉的Airflow作为Pipeline调度引擎。
上图大概展示了Airflow调度一个模型的流程,平台通过扩展Airflow的Operator来提供各种各样的能力,各个Operator通过DAG配置文件组织起来,串联起模型训练到上线的各个流程。
DAG直接使用Python代码编写,DAG文件类似于:
...
# 调度任务名字,简介,调度周期(每日)dag = DAG( 'my-pipeline', # DAG-ID,注意不要重复,目前用户间没有隔离,最好加上姓名前缀
default_args=default_args,
description='A simple minst DAG',
schedule_interval='30 03 * * *', #调度周期:crontab语句)
...
# 起始任务,依赖猛犸调度start = HttpMammutSensor(task_id='run_this_first',
flow_name='猛犸调度的名字',
node_name='调度节点名字,可以不填默认最后一个节点',
flow_period='1d', # 目前只支持依赖天调度
submitter=['xxxx@corp.netease.com'], # 猛犸相关调度开发者邮箱(注意区别于自己)
timeout=60*60*24, # 依赖检测超时时间,如果上游猛犸任务检测timeout还未完成,则失败
execution_timeout=timedelta(minutes=60), # 该节点运行60分钟超时
dag=dag)
...
# 训练任务,填入前面得到的镜像名字# 也可使用其他类型的训练operator,参考[训练节点改造]train = SmartworkOperator(task_id="train_task",
image="your_image",
execution_timeout=timedelta(seconds=60), # 该节点运行60s超时
arguments=["--key=xxx"], # 支持传入参数
dag=dag)
...
infer = SmartworkInferOperator(task_id="infer_task",dag=dag,
model_name="dminst-my", #名字,字母数字-,无下划线
model_type="tensorflow", #模型类型(tensorflow,sklearn,xgboost,pytorch)
model_path="/model/dminst",#模型存放位置(必须在/model目录下)
version="1.11.0", #模型训练框架版本号
infer_config=infer_config, #单实例资源使用情况
replicas=2 # 副本数
)
start >> train >> infer
其中start、train、infer都是调度的节点,而且Airflow支持扩展各种节点,平台目前扩展除了训练节点、推理节点等功能。方便了算法用户的使用。
彼时,严选算法业务主要开发深度学习模型,对于拖拽式构建Pipeline组件功能需求不高,后续可能会考虑可视化生成DAG方式。
5.5 其他
5.5.1 特征
特征不同于其他功能模块,特征是融合在模型训练到上线的各个阶段的,提供支撑,以全局的视角来看,特征贯穿整个算法开发的流程:
算法平台集成开发了特征平台模块,提供特征复用、特征在线推理获取、训练和预测一致保证等功能。特征平台的能力一定程度上决定了算法平台的能力。
特征能做到实时化,算法模型才能做到实时化。
由于特征平台概念以及逻辑比较复杂,就不在这里展开描述,后续会另写文章补全。
5.5.2 监控&&可视化
日志的查看: 通过开源方案filebeat-> elasticsearch完成日志的收集,通过前端开发页面展示日志;
资源的可视化: 使用开源k8s指标收集方案,通过promethues收集指标。并通过grafana展示;
Tensorboard展示: tensorboard是算法人员经常使用的工具,对于设置了tensorboard日志路径的任务,算法平台自动创建相应的tensorboard服务,并创建路由转发,将页面暴露给用户。
5.5.3 文件系统打通
算法平台是和大数据平台紧密结合的,样本和特征的数据源基本上都是在猛犸大数据平台,因此获取HDFS以及Hive表数据是算法平台训练环境和开发环境都必须打通的。
目前通过通过镜像安装的方式支持文件系统的打通。
6. 业务接入
平台上线完成之后,主要提供给严选算法业务使用,目前严选算法组已经全部接入到算法平台。承接了包括搜索、广告、推荐、NLP和图像处理等业务,推动了严选业务智能化的发展。对比之前的算法开发方式,算法平台优化开发人效上百人/日,助力严选降本增效。
7. 下一步规划
算法平台基本满足了算法业务开发人员的需求,但是目前特征平台跟算法平台结合的还是不够好,后续将会着重在特征平台和算法平台的融合。通过特征平台特征和样本的实时化推动算法平台训练模型的实时化;除此之外通过SDK的设计开发收口训练样本的生成和预测请求的构造,实现特征的统一。进一步提升算法开发的效率以及对线上服务稳定性的保障。
8. 附录
[1] Kubeflow Training Operator介绍:https://www.kubeflow.org/docs/components/training/