本文整理自字节跳动根底架构研发工程师单既喜在 ArchSummit 全球架构师峰会上的讲演,首要介绍字节跳动离线练习开展的三个阶段和关键节点,以及云原生离线练习中十分重要的两个部分——核算调度和数据编列,终究将结合前两部分共享字节跳动在实践中沉积的 4 个事例。
1.事务背景
云原生离线练习结构支撑了字节跳动内部“引荐”“广告”“查找”等场景,如头条引荐、抖音视频引荐、穿山甲广告、千川图文广告、抖音查找等事务的超大规划深度学习练习——以上场景的机器学习练习均是根据Primus练习结构完结。
整个机器学习生态从上到下分为“渠道层”“结构层”“资源层” 3 个部分。字节跳动算法工程师运用 Reckon 练习渠道完结了模型编写、练习、上线的悉数进程。Reckon 练习渠道中包括根据 TF 深度优化定制的 4 大深度学习结构——Lagrange 结构、Lagrange-Lite、蒲公英、美洲豹,这 4 个结构均经过Primus结构进行托管。
在托管调查中,Primus 作为分布式机器学习调度与数据融合结构,完结了云原生练习结构布置、分布式练习数据读取的悉数进程,Primus 结构以云原生的办法运转在 YARN 和 Kubernetes 调度系统中,并经过 HDFS、FeatureStore 等办法获取练习数据交给 TF Worker 进行练习。
2. 字节跳动在离线练习方向的开展历程
云原生核算是软件开发中的一种办法,它运用云核算“在现代动态环境(例如公共云、私有云和混合云)中构建和运转可扩展的运用程序”。经过声明性代码布置的容器、微服务、无服务器功用和不可变根底设施等技术是这种架构风格的常见元素。
字节跳动在云原生离线练习方向的开展大约分为三个阶段:单人物云原生练习 1.0,多人物云原生练习 2.0,云原生练习 3.0 三个阶段。
2.1单人物云原生练习 1.0
离线练习结构 1.0 系统诞生于 2015 年 10 月(内部代号 Zion)。
离线练习 Zion 结构是根据 Hadoop Streaming 架构在深度学习场景下的深度定制,每个练习作业对应一个 Hadoop YARN 上的 Zion 任务,具有(PS-Worker)架构分布式练习器、多数据格式多数据源混合练习、HDFS 样本读取、练习练习进展 Checkpoint 功用。
(PS-Worker)架构分布式练习器根据 Google 的 Tensorflow 结构深度定制,首要选用 Worker-PS 架构进行练习。此架构分为 PS 端与 Worker 端两个部分——其间 PS(ParameterServer) 是参数服务器,首要功用是存储并更新参数;Worker 是模型练习器,按练习数据分片,首要功用是读数据,对变量求梯度。
离线练习结构 1.0 对每个模型创立一套 Worker 实例,每个实例 Worker 和预布置在 Mesos 上的服务化 PS 完结通讯、读取样本、核算梯度、模型 Dump 的全进程。
离线练习结构 1.0 于 2019 年进行了系统级重构,新一代离线练习结构 2.0 增加了“多人物弹性调度”“多人物 Failover 才干”“练习进展增量 Checkpoint ”等功用,供给“灵敏”“高效”“易用”的模型练习才干。
2.2 多人物云原生练习 2.0
在 “云原生练习 1.0” 施行进程中,咱们发现了许多影响系统稳定性、易用性、保护性的问题。
问题1:练习作业调度集中化问题
字节跳动所有的离线练习作业办理都是根据集中式的练习调度服务(对应开源系统的 TF-Extend)。这个调度服务经过轮训的办法,完结每个练习作业的 PS 资源和 YARN 资源请求,如 PS 模型加载、YARN 练习任务创立、PS 模型保存等整个练习声明周期的各项工作,因此跟着练习作业的增加,集中式调度出现了功用瓶颈,且调度服务的晋级与不稳定等影响了较多的练习作业运转。
问题2:PS 资源与 Worker 资源匹配问题
离线练习 1.0 阶段,公司所有的 PS 均经过服务化的办法请求运用。选用服务化的办法是为了处理 PS 分片修正、服务扩容、分片 Reshard 等需求杂乱运维操作的问题。一起,经过服务化办法也能够完结多个练习作业 PS 资源共享,进步物理机资源运用率。
可是,跟着事务量的增加,服务化 PS 逐渐暴露出了与练习 Worker 难匹配的问题:
- 资源不匹配 : 新增的练习物理资源需求别离充值到 PS 服务端并上线,一起充值到 YARN 服务中才干进行练习;
- 网络不匹配 : 需求处理服务化 PS 与 YARN 练习资源之间的跨机房、跨网段导致的通讯开支。
离线练习 2.0 增加了独占式的 API Server ,用于供给云原生分布式调度才干:
- 伴生式练习办理NorbertDriver : 将每个中心调度中枢的作业都装备对应的调度大脑,经过声明式的 API Server 操控每个调度的拓扑——Worker 人物和 PS 人物。
- 声明式APIServer : 在每个离线练习 Job 中,都内建了一个独占式的 API Server。Norbert 练习办理 Driver 大脑经过声明式 API Service,发布操控练习拓扑、动态增加数据源、动态创立人物等练习需求;Primus 结构 Watch 并响应声明,完结从头请求容器、从头规划人物、从头构建 Task 等具体工作。
- 伴生式 Parameter Server : 声明式 API Server 创立的伴生 PS 人物,完结每套练习作业专属自己的 Prameter Server,能够支撑 PS Shard Failover、主动 PS 分片 ReHash、PS 资源弹性扩缩容等才干,完全处理了服务化 PS 和练习 Worker 的资源匹配难题。
根据云原生练习的 2.0 架构,字节跳动离线练习的作业规划从 2020 年至 2022 年,完结了从 150 万核到 400 万核的打破,而且与Flink 、 Spark一同成为公司离线YARN集群的 TOP 核算结构。
2.3云原生离线练习 3.0
云原生练习 2.0 资源布置在字节跳动深度定制的离线调度 YARN 集群中。为进一步完结离线在线资源并池、离线与在线练习一致,云原生离线练习 3.0 根据 Operator 架构增加了对 Kubernetes 运转环境的支撑,完结了 YARN+Kubernetes 的云原生多 Runtime 练习。
当内部大量资源从 YARN 迁移到 Kubernetes 后,系统不再为每个作业都发生一个 API Server 而是复用 Kubernetes 集群的大局 API Server,由 Norbert Driver 向大局 API Server 发布练习需求声明。
3.0 阶段整个离线练习的结构拓扑能够到达每天 10000 Job 的量级,单最大作业数 4000 个,每天 400 万 vCore 的总量 。 结构一起支撑YARNRuntime 与K8sRuntime 等多种 Runtime,现在现已有约 160 万核的离线练习作业布置在 Kubernetes 集群上(占总练习量的 40%)。
3. 云原生离线练习-弹性调度
字节跳动云原生离线练习包括了两个重要的组成部分——弹性调度和数据编列。
3.1 弹性核算调度简述
云原生的核算调度系统是经过字节自研的 Primus Operator 打造完结的,具有以下四个特点:
- 容器化 : 在 Kubernetes 和 YARN 上大规划践行容器化带来的阻隔和环境预备方面的优势;
- 弹性 API-Server : 经过自研的 API Server 在 Kubernetes 上复用 API Server 的形式完结弹性作业调度的才干;
- 多人物+异构 : 不只能调动 CPU 还能调动有状况的 GPU,完结多人物异构架构的才干;
- 微服务 : 完结调度 Operator 及神经中枢 Norbert 微服务之间的通讯互联。
Primus Operator 总体根据开源 Cookie Builder 架构,拥有四个流通状况:首要调查整个 Job 的状况,然后将状况 Update 到 Job CRD 的 Status 内,再去查看用户/作业需求方的作业拓扑希望,核算需求请求的 POD 资源,终究在 Reconcile 时完结第二步 Update 成果和第三步 Compute 希望值之间的和谐,然后完结整个状况的流通。
3.2弹性核算调度架构
每个 Job CRD 都有 Spec 和 Status 两个部分,为了完结多人物调度,咱们进一步打造了 CRD 宗族。除了上文说到的 Job CRD,每个 Job 会关联若干个 PrimusRole CRD。一起针对数据部分,咱们笼统了 PrimusData CRD。在 PrimusRole CRD 中,每一个人物都对应一个 Role 的 CRD。所有 Primus Job 的拓扑终究被和谐出来的成果,便是在 Kubernetes 或者 YARN 中的一个作业结构(如上图下方)。
咱们能够看到,TensorFlow 和 PS Worker 等相关的作业都被创立出来,一起每一个 Job 都有自己的总控中枢,即咱们根据 Java 写的 Primus AM Pod。这个中枢首要担任和谐整个进程、记录练习进展、供给 UI 展现、记录前史进程。根据这样一个系统,咱们完结了 Primus Job 的创立。
Primus Job 创立成功后,当某一个副本失利时,咱们就能够经过调度大脑获取到当时副本的信息,每个人物对应的若干副本,多个人物就组成了整个弹性调度的拓扑。
下面来看弹性调度战略到底有多弹性?咱们为了弹性调度都处理了哪些问题?
针对原生的 TensorFlow,咱们将其分为自研的 Dynamic 战略和针对原生 TensorFlow 的刚性战略:
- 原生的 Dynamic 战略指人物能够动态地彼此服务,能够在任何时刻重启人物,不要求所有人物重启之后才干开端练习;
- 刚性战略指对于原生的 TensorFlow 需求支撑 Work 和 PS 服务的彼此发现,所以根据这种战略,在所有人物都请求到资源后一致发送发动指令,完结 IP 加端口的彼此传递。
后面咱们引进了Order 战略,以弹性的办法请求 Worker 人物,大大减少等候的周期,避免了在等候进程中造成的资源浪费。
3.3弹性核算调度运用
运用1:练习 SlowStart 优化模型练习
针对 Worker 无状况的 Sailor 人物,咱们选用弹性声明,经过不断修正 API Server 操控人物内的副本数进行练习。
在开端阶段,咱们以两个副本的办法进行慢速练习,使模型快速找到局部最优状况。
模型趋于稳定后,咱们再不断地扩展模型 Worker 的数量,完结大吞吐的模型练习,然后进步形式练习的速度。
运用2:Gang 性多人物调度支撑
针对有状况的 Parameter Server 的人物,咱们引进了刚性的请求战略:
- 在 YARN 集群上,经过修正 YARN 调度器完结了 GangScheduler,支撑对 PS 拓扑的资源 Gang 性请求与释放;
- 在 Kubernetes 集群上,经过自研的 Pod Group 完结了 PS 人物的精细化资源办理,一起支撑了调度打散、最小 Gang 性数量、调度亲和与反亲和战略等杂乱场景的 PS 调度需求。
运用3:超大规划的在离线混部练习
- 混部Smart Resource: 弹性调度不只操控人物数量的多少,而且能够操控人物的规范,然后进步集群运用率,比起经过声明式的 API 动态修正人物的规范,Smart Resource 将混部资源的运用率从 20%进步到了 70%;
- 潮汐/反潮汐战略: 资源运用存在顶峰和低谷,针对这一景象,咱们运用了面向 API Server 的弹性调度机制——在在线事务低峰时,咱们有资源用于练习,于是咱们就拉起更多人物,进步练习功率;在在线事务的晚顶峰时,咱们又会把练习资源缩容到 0,把离线练习的机器学习暂时挂起,出让资源去援助在线事务,如抖音、头条的引荐,但此时 Job 仍是正在运转的。借此,咱们到达了更好地节约资源和开支、进步资源集群运用率的意图。
4. 云原生离线练习-数据编列
在离线模型练习中,练习样本数据办理、读取、加工等对模型练习起到了至关重要的作用。
样本数据在字节跳动内部不同场景下存放在不同的系统中——有存储在 HDFS 中的文件类样本资源,也有存在 Kafka 里的流式练习样本资源,还有团队自研的 Feature Store 样本资源。
云原生离线练习结构(Primus)一起掩盖多种数据源的编列,支撑不同数据源在天、小时、分钟级的编列战略;能够完结上面说到的三个练习资源中的穿插组合、过滤、打散、对齐等丰富数据编列才干。
一起,在元数据编列进程中,练习结构有新数据的感知和增量编列才干。Primus经过持续扫描 HDFS 和 Feature Store 的新增数据进行模型更新,保证练习作用能够匹配用户最新行为。
4.1 多数据源练习元数据编列
在广告等 CVR 转化模型中,天然地需求对同一用户不同 APP 上的行为数据进行建模并猜测。
字节跳动的算法工程师依托云原生离线练习的数据编列才干,对抖音、头条和西瓜事务的三个数据源进行了建模练习,每个数据源别离按 00 小时、01 小时、02 小时进行存储,一起在头条和西瓜中每个小时都进行一次聚合,终究到达在 00 小时别离消费了头条、西瓜和抖音的一个数据,而在第 01 小时经过多个数据源的从头摆放,避免了模型编列的趋向性,在第 02 小时进一步进行数据源打散。
这个例子充分展现了咱们在 Partition 内 Shuffle,按小时 Group By,以及持续追新的才干。
4.2 练习样本元数据办理
在样本元数据分发阶段,咱们将多个元数据组成了 DataStreamA,在流式阶段叫 DataStreamB,这是一个多阶段练习的进程。这两个 DataStream 都组成了同一个 PrimusData CRD。
- DataStream 里的若干个 DataSource 被按天、按小时聚合之后,会经过 Primus AM 完结文件的切分,切分的力度是按天、按小时聚合之后的原始 HDFS 途径或者 Feature Store 目录。切分的成果是若干个 Split 经过心跳的办法下发 Task 到 Executor。
- 跟着心跳,咱们还会每时每刻回发当时练习中 Task 的消费进展,以便利 FellOver 的时分,咱们能够从断点傍边继续消费来进行练习。一起,练习的进展被 Primus AM 记录到 HDFS 中,而且持久化,以便利整个 Application 挂掉之后,咱们能够从 HDFS 的练习状况傍边得到康复。
4.3 跨进程数据传输实践
-
根据匿名管道的数据传输: Executor 里有两个进程,一个是 TensorFlow Worker,用于从管道里读取咱们经过 HDFS 解析之后的样本数据;另一个是 Executor JVM 数据进程,进行 HDFS Client 读取后,将序列化的样本经过 Linux 匿名管道传输给 TensorFlow Worker 进程。
在实践进程中,咱们发现匿名管道天然存在两个问题:跨进程通讯和多个 Producer 竞赛抢锁,由此也就增加了从用户态到内核态复制的开支和资源竞赛的问题。
-
高档数据传输办法: 如 Domain Socket,咱们选用 Producer 和 Worker 经过两个 TCP Socket 传输的办法,避免了多个 Producer 的管道竞赛,但这样仍然会存在内核带的复制以及序列化和反序列化的开支。随后,咱们引进了跨进程之间 Share Memory 机制,做到了多进程办理。终究咱们选用 JNI 一致进程机制兼并两个进程,完结了样本读取、加工、传输全流程 Lib 化,完全处理了跨进程之间的 IPC 开支。
5. 事例与最新实践
上文论述了咱们在数据编列和核算调度方面的积累与沉积,下面介绍咱们将这两部分组合起来,一起结合事务的需求,在实践中进行运用的重要事例。
5.1从服务化 PS 到云原生全伴生 PS
在 1.0 阶段,咱们没有将 PS 归入到云原生中,而是选用了服务化的 PS,这种办法存在如下坏处:
- 需求同机房撮合;
- 资源运用率低;
- 运维与布置难度大;
- 阻隔性差(网络、内存带宽、CPU)。
于是咱们就引进了云原生化的 PS onYARN / K8sSavePoint,即伴生 Parameter Server 练习机制,这一演进一起也伴跟着咱们的作业规划从 150 万核到 400 万核的增加。咱们在这一阶段完结了如下功用:
- PS 拓扑刚性调度 : 在 YARN 和 Kubernetes 上都完结了 PS 刚性请求和刚性调度;
- 服务发现 ( consul-> 自研)、健康查看 : 经过自研的声明式 API Server 完结了服务发现,一起完结了 Parameter Server 的健康查看、守时查看以及毛病康复机制;
- 单作业 + 容器化布置 : 根据容器化的办法阻隔不同的 Parameter Server,避免它们 CPU 运用之间的彼此干扰;
- PS 进程Numa阻隔 : Numa Bind:避免 CPU 跨 Numa 访问内存带来的功用退化;
- PS SavePoint 机制 : 守时记录 Parameter Server 现在整个拓扑中最新的模型状况;一起咱们为数据也设置了 SavePoint 机制,将两个查看点进行对齐和一起康复,然后完结伴生 Primus Server 练习的反常康复;
- 全链路 Incremental Checkpoi nt: 不止 Parameter Server 能够增量 Checkpoint,练习 Worker 也能够增量 Checkpoint,这就意味着在任何时刻,当一台 Primus Server 发生毛病之后,它只需用增量的办法去康复这一个单点即可;
- PS Smart Resource 机制 : 不断地压缩 Parameter Server 声明规范和它的运用规范之间的 Gap,进步集群的运用率。
5.2PrimusFlow 练习数据实时预处理
在模型的调研中一般会面对的问题是:一个等候和两个浪费——即 Spark 预处理的等候、模型练习进程中 Spark 核算的开支和存储的开支。
为此,咱们引进了伴生数据预处理的模型练习机制——PrimusFlow。一方面,它能够支撑丰富的数据源,任何一个被 Spark 预处理的数据源,都能够被 PrimusFlow 机制处理,咱们经过 Spark 读取,Load 中间状况进行练习。别的,PrimusFlow 支撑更丰富的调研形式,支撑行级 Shuffle,咱们能够进行数据预处理,按行或按某个用户 ID 进行加工处理,以此来进步模型练习作用。
此外,咱们选用多数据流编列,先对 Spark 进行预处理,鄙人一个阶段用上一个 Spark 预处理的成果进行模型练习,一起在这一阶段并行进行第二个单元的 Spark 预处理,由此真正完结了无需等候的单元调度形式。
经过 PrimusFlow 机制,加上行级 Shuffle 的才干,咱们在十分多的场景中都取得了模型作用进步 10% 以上的收益,而且在国外许多场景进行了落地。经过上述办法,咱们处理了一个等候和两个浪费的问题。
5.3练习批流一体架构
在实践进程中,咱们发现批式处理结构也有流式消费的才干。现在的模型建模,一方面是烧脑建模,另一方面是烧卡建模。离线练习在不断地烧卡,一起流式练习进程中也需求烧卡,这首要是由于现有的 Flink 流式练习消费现已无法满意晚顶峰时抖音引荐里杂乱模型需求的练习才干,因此就需求增加算力,引进 GPU 资源。但 Flink 并不支撑异构人物的 Task Manager,而 Primus 天然便是一个异构多人物的练习结构。
根据上述需求,咱们在 Primus 中加入了流式练习的才干,打造了多人物异构的流式练习结构,一起支撑 GPU 调度、伴生的 API Server 及毛病康复,一起咱们完结了 All to All 的 Shuffle 才干,即引进 Rebalance 机制进步流式练习的才干。
此外,由于 Primus 结构本身便是批流协同的结构,既能够支撑多人物 GPU 的批练习,也能够支撑多人物异构的流练习,在离线练习完毕之后,能够直接切换到流式练习阶段去复用同一个 Partition Server,咱们以此完结了流批协同和流批一体的意图。
5.4Primus Native 样本数据传输 Library
Primus Native 系统是针对字节跳动深度学习的数据子系统增强,别离在 Data Master 部分和 Data Executor 部分进行了云原生改造,开展为愈加灵敏、愈加高效的深度学习数据引擎。
元数据MetaManager 编列部分: 咱们不只引进了声明式 API,也引进了 Python for Java 的 Gateway 架构,这个架构支撑起了 Primus Native 的数据声明系统。相比于声明式 API 的数据界说办法,Python Gateway 架构在灵敏性+扩展性方面更有优势:
- 用户能够愈加灵敏的运用 Primus Native Python UDF 灵敏操控样本按文件时间摆放、按特定字段摆放等高度自在的样本文件编列战略;
- 完结了 Python 数据和 Java 数据的灵敏转化,练习器能够更好地获取当时任务编列和任务消费样本的详细信息,灵敏地进行练习作用评价、抽样等操作。
练习 Worker 读取部分: 咱们引进了 SO 化的数据传输机制,兼并两个进程到一个练习进程内部,完全免除了序列化和反序列化的开支、用户态到内核态的数据复制,也节约了云原生环境下单容器内多进程的保护难度。
经过上述优化,针对一个规范引荐练习任务:
- 总吞吐量从 3.3GB/s 进步至 13.5GB/s,进步了4 倍;
- 单节点吞吐率从 411MB/s 进步到 1.2GB/s,进步了4 倍;
- CPU 运用率从 2.25 中心进步到 5.25 中心,进步了2.3 倍。
6. 总结
综上所述,咱们在本文中论述了字节跳动离线练习开展的三个阶段,以及云原生离线练习中十分重要的两个部分——核算调度和数据编列,终究结合前两部分共享了字节跳动在实践中沉积的重要实践事例。