前言:cube是开源的云原生机器学习平台,目前包含特征平台,支持在/离线特征;数据源管理,支持结构数据和媒体标注数据管理;在线开发,在线的vscode/jupyter代码开发;在线镜像调试,支持免dockerfile,增量构建;任务流编排,在线拖拉拽;开放的模板框架,支持tf/pytorch/spark/ray/horovod/kaldi等分布式训练任务;task的单节点debug,分布式任务的批量优先级调度,聚合日志;任务运行资源监控,报警;定时调度,支持补录,忽略,重试,依赖,并发限制,定时任务算力的智能修正;nni,katib,ray的超参搜索;多集群多资源组,算力统筹,联邦调度;tf/pytorch/onnx模型的推理服务,serverless流量管控,tensorrt gpu推理加速,依据gpu利用率/qps等指标的 hpa能力,虚拟化gpu,虚拟显存等服务化能力。 目前开源到github:https://github.com/tencentmusic/cube-studio
一个从申请机器,配置环境,拉取数据,处理数据,算法训练,调试,模型测试,服务化上线全流程的算法工程师,在下面的每个环境都浪费了很多时间,而不是主要集中在算法模型的构建上
cube一站式的机器学习平台,从平台架构上解决下面几个问题时做了一些更贴近用户实用化考虑。
均衡算力
- 现在大部分算力散落在算法工程个人用户手中,这些算力在个人机器上,机器的主要使用者是个人或少数几人,无法在时间维度上做到时刻高利用率。
- 任务是有cpu和gpu需求区分的,而gpu机器上的cpu资源一般是比较少的,在gpu机器上跑cpu任务,gpu浪费严重。
- 在个人gpu机器上运行个人任务,由于本身分布式成本比较高,多数任务是单机运行,运行时间长。而且分布式训练的性能与机器数量一定程度线性相关,所以能用到更多的资源才能更大程度上加速任务
- 个人代码和环境都依赖了固定的机器,机器的回收,损坏,裁撤,磁盘不足等事件频繁,花费很多时间工程运维。
- 在gpu训练中,也有很多多机多卡的gpu利用率提升方案。用户对于在固定机器成本上提升利用率的动力不足。因为他们会优先考虑通过扩容提升效率。
基于这些问题,所以cube机器学习平台,统筹底下的算力,最大程度上提高剩余资源的利用率。
另外对于个人原有算力和新增算力,可以建设新集群,加入公有云集群或者建设私有集群。不影响原有机器的文件系统,同时又能使用起平台的公共算力。
同时对于多算力组支持了动态的均衡。
通过项目组下算力的均衡,基本能把保持任务挂起的等待时长不超过5分钟。
除了集群间的算力均衡,平台会智能修正任务的启动资源配置,在分布式训练加速中有介绍,也能提高资源的利用率。
适配多类型存储挂载
另外为了减少数据导入导出链路,避免用户在开发/调试/训练/校验/上线时,数据文件在不同系统中间导来导去。所以平台在开发notebook,数据文件处理训练pipeline,以及模型服务化中挂载相同的分布式存储,同时不同用户会挂载不同的子目录。同一个用户在平台的各个环境部分/mnt/下面都是个人的工作目录。
另外平台加入了多类分布式存储使用的能力,只需要在项目组中指定所需要的分布式目录或者固定的挂载目录,就可以使用适合自己的分布式存储。并且可以多人共用分布式存储,满足团队工作目录的需求。
存储的扩缩容是弹性的,用户也就去除了存储大小的问题。平台有存储的监控功能,会提醒用户及时删除不再使用的大文件目录。
另外在推荐领域和音视频领域他们关注的重点稍有不同。在推荐中的处理文件多为在tdw处理后的csv TB级别的大文件,而在音视频中使用的多为几K到几M级别的小文件,小文件数目达千万个。所以音视频对分布式存储文件检索的能力要求更高。目前平台为小文件场景适配网络跳转更加少,时延更小的同网段的ssd ceph。
另外同时支持内存挂载存储的形式,用于任务中临时的存储目录,减少数据处理中的io操作。
多数据源对接
数据在进入平台后是不需要相互导入导出转移的。但是部分数据是在外部的,现在平台通过下面介绍插件形式加入了平台与个人机器数据/hdfs/cos/tdw/cdmq里面的数据的互通,以及平台与外部平台lhotse/tesla等任务平台的链接。
在线镜像调试构建
在云原生里面,所有的调度都是基于容器的。但是这一部分的门槛就比较高
1、算法工程师并不熟悉docker技术,另外部分环境在转化为dockerfile中浪费了很多时间,并且可能在编写dockerfile时比较难,比如在一个镜像中安装多个cuda的场景。
2、基础镜像封装太多,用户也不知道该选择什么镜像。
针对用户不熟悉docker技术的情况,cube加入了在线构建镜像的功能,启动调试直接进入bash命令行,像在tlinux上随意运行命令安装环境,之后点击保存后就会自动生成用户的工作镜像,并push到仓库。期间cube会自动寻找用户进行镜像调试的pod,在同一台机器上将用户的容器 commit成镜像,并将构建成的镜像进行推送到仓库。
对于有很多基础镜像的问题,cube采用的方案是仅封装cuba/python这类基础环境,对于再上层的环境,在任务运行时现场安装。也就是在基本的模板中都保留了能够让用户自定义初始化环境的命令的功能。毕竟对于长期运行的任务,安装环境的时间成本并不高。
notebook
在线开发是一个很重要的功能,对于大部分用户来说,在线的IDE比命令vim是要方便很多。cube主要提供了vscode和jupyter两种在线ide。并没有将代码和数据进行分割存储。所以在notebok中打开的就是完全用户自己的代码和数据,可以方便的进行调试。
vscode
在vscode里面(本质为theia),用户跟本地vscode基本一致,上传下载文件/文件夹,命令行终端。并且在其中封装了用户的基本环境需求,l5/北极星/python等。
需要说明的是,由于theia的功能中有些进程,例如rg进程,会不停的搜索扫描目录下的所有文件。由于个人目录下包含了用户的代码和用户的数据,文件可能非常多(千万到亿级别的文件数量)。这些进程扫描会严重拖慢分布式存储的性能。所以cube做了定时检杀的功能。
jupyter
数据挖掘的用户使用jupyter会更多一些。在jupyter里面跟vscode里面相同,也安装了一些实用的插件,比如tensorboard,用户可以在jupyter里面打开pipeline中tf的log目录,然后使用tensorboard按钮启动tensorboard查看该训练的情况。
notebook的资源占用问题
用户在使用notebook时的一个主要问题是,notebook是一个开发代码的组件,并不能像pipeline一样,运行时占用资源,运行结束自动释放资源。notebook是一个申请资源启动后,用户可以自由的开发代码,使用命令行调试的组件。但是用户的代码开发并不会占用太多资源,所以在代码开发期间,申请的资源是浪费的。有类似方案将代码开发和命令的实例运行区分开,命令启动时现场启动kernel pod。如下
但是这样同样有一个问题,就是用户在启动运行实例的前面还会安装独特的环境,新pod的关闭启动会不停的重置环境,太频繁的启动都会重新安装环境。但是对于资源浪费就只能靠监控通知清理来尽快释放资源。cube采用了两种方案,一种是独占资源同时定时清理,一种是只设置notebook的pod的limit,而不设置request,这种方案就对不资源进行独占,这样也就不用清理notebook,只是允许了notebook中的资源干扰。
notebook的环境重置问题
如果notebook因为oom,定时清理,机器故障,主动reset等可能原因而重启,notebook中的环境就会丢失。所以cube添加了notebook启动后自动执行/mnt/$username/init.sh脚本,用户只需要在init.sh中定义notebook特定的环境,就可以在启动notebook后拥有自己独特的环境了。
多实例的virtualservice代理
每个用户可以配置多个notebook,可以自己配置资源,镜像,类型。每个notebook是通过istio virtualservice来代理的。所以只要notebook支持url prefix,就可以被加进来做为notebook。每个notebook实例,都有独立的以名称为prefix的url地址,在virtualservice配置绑定istio ingressgateway,这样来实现多实例。
拖拉拽的pipeline编排
cube使用argo作为任务流workflow,为了能降低用户的学习成本,开发了一套拖拉拽的任务流拖拉拽的编排能力。并且在其中加入了很多分布式的任务模板。基本上的分布式框架都可以得到解决。各类的分布式框架在分布式任务加速中也有介绍。
单task调试
通过notebook我们可以进行代码的编辑,通过镜像调试,我们可以构建自己的镜像。然后就可以编排pipeline了。pipeline运行前需要独立运行调试单个task,所以cube添加单task独立调试的功能。
每个task都可以独立的debug和独立的运行。这样就不用每次都运行整体pipelie才能进行调试。并且对于分布式任务的task,cube在模板中借用stern实现了多个容器log聚合查看。这样就不用每个worker的日志都单独去看了。
并且每个任务启动后都可以看到相应的资源情况,这样可以边调试边配置资源的申请量,在gpu上也可以及时知道是否还有优化的空间,目前任务的主要瓶颈在哪里。这样能更加直观的确定优化的方向。
断点重训
对于某些部分一个任务流运行后,可能在某些任务开始出现偶发性错误,cube提供了,在结束pipeline后,在失败点继续训练的功能,这样就能不用重复训练某些耗时的任务。同时cube也为每个任务提供了重试次数和超时时长的配置。并且提供了任务的实时和离线日志查询两种功能。
定时调度
任务编排调试好以后,还要进行定时调度。cube支持了定时调度任务的,补录,忽略,重试,依赖,并发限制,过期淘汰等功能,能应对任务堆积,平台故障等可能问题。
并且对同一个pipeline,区分对待手动运行和定时调度两种实例。
开放式插件
平台自带的任务模板并不一定能完全满足所有需求,除了用户可以使用自定义镜像,自定义命令的方式外。平台将插件的注册功能开放给用户。
这样用户就可以自己定义模板,然后开放给全平台的用户使用。用户只需要自己构建好功能镜像,然后将其他用户使用此镜像需要填写的参数注册进来了就可以了。其中也包含的模板特定功能,例如固定的挂载,固定的环境变量,k8s账号等。
NNI超参搜索
除了包含katib超参搜索,也加入了nni的超参搜索,支持更多的算法。同时在pipeline中也支持了边训练边超参搜索的模板。只需要用户的代码能接受超参数作为输入,同时上报作为超参算法的目标值
# 上报当前迭代目标值
nni.report_intermediate_result(test_acc)
# 上报最终目标值
nni.report_final_result(test_acc)
# 接收超参数为输入参数
parser.add_argument('--batch_size', type=int)
这样就能及时的在页面上查看超参搜索的效果了。
每个用户可以启动多个超参搜索的实例,通过url prefix作为前端的路由。