一、前言 机器学习/深度学习在有赞应用的越来越多,例如在营销、推荐、风控等场景下都起着越来越重要的作用。对于深度学习在实际业务场景的落地来说,除了数据和算法,工程和系统上的支持同样必不可少,这样的支持包括模型的快速构建与评估,稳定的线上模型服务环境等等。为此,我们开发了有赞智能平台 Sunfish ,本文详细介绍 Sunfish 的设计和实现。 二、背景在有赞,机器学习/深度学习在各个业务场景下发挥着越来越重要的作用。这里以推荐系统为例,介绍一下深度学习的落地实践。在之前的博客文章 有赞推荐系统关键技术 中介绍过有赞微商城个性化推荐系统。简单来说,当用户打开一个有商品推荐位的页面时,推荐系统会根据用户特征按一定策略从商品池中选择出一些候选的推荐商品,这个过程称为召回。然后,针对这些候选商品,需要分别对它们进行打分,选择其中得分最高的商品,推荐给用户,这个过程称为线上精排。这里对某个商品进行打分的操作,就是在使用深度学习模型进行推理。 为了实现个性化推荐系统中的线上精排服务,我们需要进行三个阶段的工作。1.数据探测与准备;2.模型训练与评估;3.模型服务部署。我们会在 有赞大数据平台 上面进行数据探测与准备。在模型训练与评估阶段,算法同学需要选择合适的特征数据,进行适当的特征变换,建立模型,调整超参,评估模型效果。这是一个不断迭代的过程,直到训练出效果满足期望的模型。之后,要把模型发布为线上服务,接受线上模型推理请求。作为一个线上服务,需要保证稳定较低的响应时间,平滑的版本升级,各服务版本的线上监控与评估等要求。为了提升算法业务的开发效率,并提供稳定的算法服务环境,我们开发了 Sunfish ,从模型训练到模型服务部署的一站式智能平台。下文将详细介绍 Sunfish 的设计与实现。 三、 Sunfish 功能架构Sunfish 功能架构如下图所示,主要分为可视化模型训练平台和小盒子模型在线服务平台两个部分。 可视化训练平台主要提供了三个方面的支持:
小盒子模型在线服务平台提供了低延迟高可用的模型推理环境,其功能可分为以下三个方面:
四、可视化模型训练平台4.1 可视化建模我们以鸢尾花( Iris )分类任务的训练为例,介绍如何在 Sunfish 中进行建模训练。在 TensorFlow 的官方文档预创建的 Estimators 中,介绍了如何使用 TensorFlow 的 Estimator API 解决 Iris 分类问题。概括的说,编写基于 预创建的 Estimator 的 TensorFlow 项目,需要开发这么几个部分:创建一个或多个返回 tf.data.Dataset 对象的输入函数;定义模型的特征列;实例化一个 Estimator ,指定特征列和各种超参数;在 Estimator 对象上调用一个或多个方法,传递合适的输入函数以作为数据源。 在 Sunfish 中,我们可以把这类 TensorFlow 项目的以上几个部分抽象并提取了出来,发布为平台中的组件。因此,我们可以在平台中以拖拽式的方式完成 Iris 分类问题的实验构建和模型训练。如下图所示,我们从组件列表中选择需要的各个组件拖到实验画布,为各个节点建立数据和控制依赖,配置相关参数。之后我们运行实验,即可训练得到 Iris 模型。 4.2 实验与组件用户可以为不同的业务创建不同的项目,然后在项目中创建实验。如上图所示,Iris 实验是一个以组件节点构成的 DAG 。组件节点可由页面左侧的组件窗口直接拖动组件到页面中间的画板生成。点击组件节点可以为其配置参数。算法同学可以发布自己的组件,发布时指定该算法的代码实现在 GitLab 上的地址,以及声明组件的输入输出数量和类型即可。在上图的 Iris 实验中,组件节点有两种类型,蓝色的节点为 Python 类型,这类组件可以直接单独运行。如图中 Training_TFRecord 节点,在配置中指定 Hive 表或 SQL 语句,运行该节点会生成 TFRecord 文件。黄色节点为 Module 类型,需要组合到 python 类型的组件中,作为其依赖在运行时调用。如图中 Adam_optimizer 节点,表示 TensorFlow 中的一个 Optimizer ,不能单独运行一个 Optimizer ,而是在训练时作为一个模块被使用。 4.3 系统架构训练平台的系统架构设计如下图所示: 训练平台的集群节点有两类角色, Master 和 Worker ,都是无状态节点,可水平扩容。Master 接受前端请求,负责实验、组件、模型等内容的管理,以及实验状态管控。Worker 负责从 GitLab 获取组件代码实现,运行组件任务,并提供日志查询服务。Master 依赖 Zookeeper 进行容错恢复, Worker 依赖 Zookeeper 进行服务注册。AlgorithmBox 即小盒子模型服务平台, Master 与之交互,进行模型的发布管理。 4.4 实验运行下面以上文提到的 Iris 实验的运行过程为例,介绍其中各模块的交互过程。运行 Iris 实验, Master 首先会将实验传到 Compiler , Compiler 负责对实验进行编译,生成 Plan , Plan 是一个由 Task 构成的 DAG 。Compiler 会协同 PlanManager 根据实验的 Plan 是否已生成,某个实验节点配置及其上游节点配置是否已更新等信息,判断哪些节点需要重新编译生成 Task 。编译过程中,会根据实验节点的组件类型生成对应的 Task ,为节点间的数据传递生成临时载体(如 TFRecord 文件的 HDFS 目录),根据节点的配置项和输入输出项生成配置文件。对于 Module 类型的节点,会将这些节点进行组合,生成一个可运行的 Task 。编译完成之后,会生成 Plan , Plan 中包含一个由 Task 组成的 DAG 。 PlanScheduler 负责实验的调度和运行。一个 Plan 的运行主要通过 Runnable 和 Running 两个 Task 队列进行控制。初始化时将 Plan 中的 Root Tasks 放到 Runnable 队列中,总是根据 Task 最大并行度取 N 个 Task 运行,并把运行中的 Task 放到 Running 队列。当有 Task 运行完成时,从 Running 队列取出,并从其 Child Tasks 中取可运行(其所有 Parent Tasks 已运行成功)的 Tasks,放到Runnable 队列。为了 Master 的宕机容错, PlanScheduler 会将 Runnable 和 Running 等 Plan 运行状态放到 Zookeeper ,并定期更新。当其他 Master 节点发现这个 Plan 的运行状态久未更新,会接管这个 Plan 的运行。在运行过程中, PlanScheduler 还会进行实验停止、 Task 运行超时或失败进而终止实验等处理。TaskScheduler 负责将 Task 发送到 Worker 执行,并在 Worker 宕机时,负责 Task 迁移。 Worker 节点接受 Task 运行请求,从 MySQL 获取 Task 元信息。根据 Task 类型进行不同的处理。对于 Python Task,会从 HDFS 获取运行所需的配置文件,从 GitLab 拉取 Python 代码,然后提交给 TaskExecutor 执行。除此之外, Worker 还接受 Task 停止, Log 查询等服务请求。 运行过程中右键点击某个节点,可以查看实时运行日志。点击实验画板上方的 TensorBoard ,可以选择某个生成 EventLog 的节点,然后打开 TensorBoard 页面,可以查看训练进度,收敛情况等信息。 4.5 模型管理与发布实验运行完成之后,生成的模型会自动保存下来。可以在模型管理页面看到每次运行生成的模型。用户也可以指定模型在 HDFS 上的路径上传已有模型。在模型管理页面,可以选择某个模型发布。模型发布是指将模型发布到小盒子模型服务平台,发布成功之后业务方可以直接通过小盒子的接口进行模型推理服务的调用。小盒子中的模型服务调用会在下节详细介绍。在服务管理页面,用户可以查看所有已发布的模型服务状态,并进行管理。如可以选择服务上下线,可以调整模型服务实例个数,提高吞吐量,也可以直接向模型服务发送请求数据,测试服务功能。 4.6 Notebook我们部署了 JupyterLab 并将页面嵌入 Sunfish,提供 Notebook 建模能力。我们安装了支持 Python2 和 Python3 版本的 Kernel,用户可在 Notebook 中使用 PySpark 直接访问离线数据。我们使用 JupyterLab 的 WorkSpace 特性对用户空间进行隔离。 五、小盒子模型服务平台上文已经介绍过小盒子在模型、服务和业务三个方面的功能。简单来说,使用小盒子进行模型推理可以分为两个步骤,1.模型创建与发布,用户指定模型服务名称、模型服务版本、模型服务类型、模型所在的HDFS路径、集群、业务组、期望的服务实例数量等信息(如果有自定义插件,也需要指定Jar包所在的HDFS路径),在小盒子中发布模型。2.业务方在请求中指定执行计划(详见下文介绍),向小盒子发送算法服务请求。 5.1 系统架构下面来看一下小盒子的设计与实现,小盒子的系统架构如下图: 小盒子的集群节点分为三类角色, Manager 、Master 和 Worker ,都是无状态节点,可水平扩容。Manager 主要负责模型管理和发布,集群和业务组管理,服务路由整理等工作;Master 接受业务方的模型推理请求,根据路由信息,将请求转发到合适的 Worker ;Worker 负责本节点的模型加载、服务健康检查,并提供模型推理服务。对于 TensorFlow 模型, Worker 节点部署了 TensorFlow-Serving 的 Docker 环境,由 TensorFlow-Serving 进行实际的模型推理工作。 5.2 模型发布与路由管理小盒子管理着由很多模型服务节点( Worker )组成的集群,一个模型会根据发布时指定的实例数,加载到 N 个 Worker 节点。当 Master 收到业务请求之后,需要快速的将数据提交到正确的 Worker 进行推理计算。因此,我们需要维护一组模型到 Worker 的路由信息。首先, Manager 会根据模型发布信息,维护一份静态路由( StaticRoute )用于声明模型期望加载到的 Worker 节点集合。另外,模型实际加载情况则由 Worker 各自通过心跳注册到 Zookeeper 。最终实际的路由信息( DynamicRoute )由静态路由和心跳信息聚合得到。 新模型发布时,Manager 收到模型发布请求之后,小盒子会进行以下处理。1.根据请求中的参数,DeploymentManager 从指定集群指定业务组的 Workers 中选择 N 个负载最低(已加载模型数量最少)的节点,作为新发布模型的宿主节点。StaticRouteManager 将此新模型信息及选中 Worker 信息( StaticRoute )更新到 Zookeeper。2.被选中的 Workers 会监听到 Zookeeper 中 StaticRoute 的变更,ModelLoader 会在本节点加载新发布的模型。ServiceMonitor 监听到新模型加载成功之后,会更新本节点的可用模型服务信息。此信息会随着 HeartBeatManager 上报到 Zookeeper。3. Manager 中的 DynamicRouteManager 监听到 Zookeeper 中可用模型服务信息的变化,会结合 StaticRoute 整理得到 DynamicRoute,并更新到 Zookeeper。4. Master 中的 RouteTableManager 监听到 Zookeeper 中 DynamicRoute 变化,更新本地的 RouteTable。 5.3 请求处理过程上文提到过有的算法服务(例如 OCR 场景)可能需要经过多个推理步骤。在小盒子中我们定义了执行计划( Plan )和执行步骤( Stage )的概念,用于描述一个算法服务的请求处理过程。Plan 由一组 Stage 组成,请求需要依次执行各个 Stage 的描述。一个Stage描述了一个模型推理请求,Stage 中指定了模型服务的名称、版本和类型。有的场景下,Stage 中还可以指定多个 SubStage,SubStage 之间可以并发执行,降低整个请求的延迟。我们可以在 Apollo 中为各种业务和场景配置不同的 Plan,对于某个业务下的某个场景,我们可以配置多个 Plan,并指定每个 Plan 的流量比例。这样,业务方的算法服务请求参数中只要声明业务名称和场景名称即可。当然,业务方也可以直接在算法服务请求的参数中指定 Plan 的描述。 小盒子 Master 负责处理算法服务请求。收到一个请求之后,SessionManager 会生成一个 Session 管理请求处理状态及接口超时返回。PlanManager 根据请求中的业务和场景参数从 Apollo 获取 Plan 描述,生成 Plan 实例,并提交到 PlanExecutor。PlanExecutor 依次处理 Plan 中的 Stage。针对每一个 Stage,WorkSender 根据 RouteTable 中的模型服务路由信息,将请求数据发送到对应的 Worker 进行模型推理计算。如果其中有用到用户自定义插件,PluginLoader 负责加载 Jar 包,用于处理各个 Stage 在模型推理的前后进行业务相关的数据处理。Master 中默认的 Plugin 会将请求中的数据进行拆分,并发的向 Worker 请求模型推理服务,再将结果合并,降低服务延迟。 六、展望目前 Sunfish 还处于刚刚成型的阶段,只是满足了算法训练和模型服务的基本需求,我们还有更多的期待和挑战。我们期望 Sunfish 向着产品化和中台化的方向发展。产品化,Sunfish 会进一步优化功能和使用体验,覆盖更多的目标用户。中台化,Sunfish 不仅仅作为提升算法开发效率的工具,更要深入业务场景,沉淀算法能力,优化算法服务构建流程。我们需要在以下这些方面投入更多的精力。
作者:群演 部门:数据中台 |
|