您好,登录后才能下订单哦!
导语:如果说算法和数据是跑车的发动机和汽油,那么系统则是变速箱,稳定而灵活的变速箱,是图像识别服务向前推进的基础。算法、数据、系统三位一体,随着算法的快速发展和数据的日益积累,系统也在高效而稳定地升级。
1.高性能与高可靠
任何在线系统都面临性能和可靠性的挑战。在我们系统中,图像识别服务作为在线服务,需要能够迅速响应并返回,并且不管在框架层还是算法层,都要保证高可用性。
2.系统解耦&高可扩展性
作为一个分布式的算法系统,算法模块与框架的解耦,可以使算法与后台人员更高效的同步开发,分别对算法与框架进行更新迭代。而高可扩展性,既要求框架在集群上支持资源可扩展,又要求做到单机算法的迅速接入与替换。
3.复杂业务与算法的支撑
随着业务的持续接入和算法的复杂化,需要在框架层灵活的支撑算法,高效的算法模块重用以及迅速适配新算法并接入新业务。
4.不同运行环境的支持
算法系统,尤其是图像识别算法系统,运行环境包括CPU与GPU,框架除了要支持不同环境的高效运行,也要支持不同环节运行在不同硬件环境,以保证资源合理高效的使用。
算法研究&模型训练:
算法人员进行算法研究、模型训练,以及业务对接。算法无需关心框架以及系统的调度,只需输出单个模块的算法SO和训练好的模型文件。
框架开发&算法集成:
后台开发人员进行服务框架的开发及算法SO的集成运行,即把算法研究人员研究好的算法及模型文件集成到服务框架中,提供稳定的在线图像识别服务。
接下来将重点介绍系统的设计和实现。
框架层使用Java编码,算法层采用插件化设计,可以加载jar包、so或者其他脚本,这是一个多语言的混编系统。图像识别算法一般都是计算密集型,并且一部分需要运行在GPU上,所以在算法层,我们使用CUDA C++编写so,使用JNI挂载so进行算法的执行和调度。如图1,架构上主要分为三层:接入层、框架层、算法层,再加上评测系统、存储系统、监控告警系统、日志系统等周边系统构成一套完整的图像识别服务系统。
图1 图像识别服务框架系统构架图
接入层:包括协议转换、参数输配和结果适配等
框架层:图像识别服务运行的系统框架,加载运行算法SO,提供稳定的识别服务,包括
Master:接收接入层的请求,进行请求拆分、请求调度、结果合并等
Worker:实际执行算法的进程载体,主要包含算法SO/模型的加载、更新,进行算法的执行
Zookeeper:存储worker心跳信息、算法映射关系、算法执行计划、算法静态/动态快照信息等
ConfigServer:监听worker心跳并实时更新动态动态路由表,触发master更新路由规则及连接池
算法层:算法人员提供各种算法模型及算法so
周边系统
评测系统:提供版本评测功能
存储系统:非敏感图片及badcase存储
监控告警:监控服务的运行状态,在异常时进行告警
日志系统:请求日志的存储,为问题的跟踪排查提供依据框架运行时
本节将结合实际的OCR预测请求剖析框架的运行态。
1) 一个OCR识别实例
如图2,我们以STR(Scene Text Recognition,场景文字识别)为例,一个典型的使用场景为广告图片素材理解。在任务中,我们将识别图片中文字,并给出具体坐标。
图2 一个OCR识别实例
2) 系统运行态
如图3,我们详细剖析上述实例在框架中的运行过程
图3 系统运行态
1. 业务侧的请求携带图片内容(或图片URL)、bid(标识不同的业务)及tid(标识不同算法大类)。
2. 在master节点找到对应的算法,然后找到相应执行计划,执行计划中定义了算法的执行步骤;根据各个步骤找到相应的路由节点,master将请求拆分/打包/路由到相应的worker
3. master将原始图片路由分发到检测子系统。检测过程运行在GPU上,算法so检测出模块中各个图片框(如图2中的“京东”、“内外真皮”等),切分好之后将结果返回给master
4. master将检测结果拆分,并行分发到识别子系统。识别过程运行在GPU上,算法so将识别出单个图片框的文字,分别返回给master
5. master将识别结果汇总,一起发送到重排序子系统。重排序子系统运行在CPU上,算法so将结果返回给master
6. master将最终结果封装并返回
每个模块在整个过程中的作用为:
算法映射:由bid+tid映射到一个具体的子算法。相同的tid与不同的bid组合,可以支持不同业务对同类算法的定制
执行计划:定义算法的执行步骤,比如图中STR图片文字识别,包含三个步骤:检测、识别和重排序
动态快照:即动态路由表,定义了算法每个阶段映射到的具体节点。worker上报心跳,由ConfigServer整理生成动态路由表,由Master节点监听路由表的变更
热更新能力是一个系统提供可靠和稳定服务的基础功能,即可以保证系统的无损升级,又可以保障系统的容灾能力。如图4,系统主要借助zookeeper和worker的心跳机制实现集群热更新。
图4 集群热更新
Worker:在启动的时候与zookeeper建立临时节点维持心跳信息
Configserver:监听worker在zookeeper的心跳信息,如果worker断连或重连,configserver立刻感知到并修改动态快照
Master:监听zookeeper上的动态快照信息,动态快照变更立刻触发路由规则及路由连接池的更新
通过这几个角色的配合,在worker节点出现异常的情况下,master迅速就完成了切换,保证了系统的稳定。这种机制也支持了集群的热更新,在需要对某个worker进行更新时,先对worker进行下线,master感知后不向此worker发请求,完成更新启动后,master再跟其重新建立连接并发送请求。
单点热更新,指的是在不重启服务的前提下,对进程内单个或多个模块进行替换升级。与其他业务系统不一样的,在算法平台下,考虑以下两种场景:
1) 一个进程内加载了多个算法SO,需要对其中一个算法模块进行更新;
2) 算法链上串行多个模块,需要对其中一个模块进行实验或更新。
这两种场景下,使用集群热更新或者对进程进行重启,就有点重了,所以我们实现了一套进程内单个so的动态更新方案。通过Java代码是无法直接实现SO的动态加载的,如图4,我们引入了代理so,通过在代理so进行(dlopen,dlsym和dlclose)操作,从而达到动态加载SO的目的。同时在代理so中,我们封装了所有JNI转换和算法需要使用的接口,很好的进行了框架和算法的解耦。除了so的动态加载,我们还实现了模型的动态加载。
图5 单点热更新
通常在分布式框架中,是不需要静态快照的。但在算法系统中,我们通常需要频繁上下线一批so,而这些so会分布在不同机器不同节点上。虽然简单的通过上传/删除服务器上的本地so文件,可以触发相关进程的动态加载/卸载,但这种操作繁复,在算法复杂,一个进程加载了很多so的时候,操作容易出错。所以,这里我们需要在运维上对系统进行优化,提高系统可运维性。
如图5,我们在动态快照的同时还引入了静态快照,静态快照由运维通过脚本或配置文件写入静态路由表,将一个集群的预期初始状态配置到zookeeper上。ConfigServer整理静态快照和worker上报的心跳信息,生成最终的动态快照。
图6 静态快照
这种动静态快照结合的集群快照机制相比只有动态快照在运维上会稍微复杂,但可以通过运维工具降低复杂度。动静态集群快照机制优势也很明显,第一、在复杂算法下,不容易出错;第二、可以在动态快照之余快速上下线一些算法或者更改算法流程。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。