高國(guó)樑,陳雷放,劉一鳴
1(中國(guó)石油大學(xué)(華東) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,青島 266580)
2(青島農(nóng)業(yè)大學(xué) 理學(xué)與信息科學(xué)學(xué)院,青島 266109)
3(華北電力大學(xué)(保定) 電氣與電子工程學(xué)院,保定 071003)
近年來(lái),深度學(xué)習(xí)技術(shù)廣泛應(yīng)用在圖像處理、語(yǔ)音識(shí)別等領(lǐng)域,極大地促進(jìn)了人工智能的進(jìn)步,對(duì)人類生活的改善有著重要意義.然而,深度學(xué)習(xí)實(shí)踐并非一件易事,往往需要花費(fèi)大量的時(shí)間和精力,因此,使深度學(xué)習(xí)變得盡可能“簡(jiǎn)單”顯得尤為重要.深度學(xué)習(xí)托管平臺(tái)可為深度學(xué)習(xí)實(shí)驗(yàn)任務(wù)提供全流程的管理服務(wù),給深度學(xué)習(xí)實(shí)踐帶來(lái)了積極影響.結(jié)合深度學(xué)習(xí)平臺(tái)項(xiàng)目的實(shí)際需求,本文設(shè)計(jì)和實(shí)現(xiàn)了分布式任務(wù)執(zhí)行系統(tǒng)DTES (Distributed Task Execution System).DTES參照任務(wù)執(zhí)行集群的實(shí)時(shí)監(jiān)控?cái)?shù)據(jù)選出當(dāng)前壓力最小的節(jié)點(diǎn)并向其發(fā)送執(zhí)行請(qǐng)求,節(jié)點(diǎn)根據(jù)請(qǐng)求信息去獲取任務(wù)所需數(shù)據(jù),隨后創(chuàng)建Docker 容器來(lái)執(zhí)行該任務(wù),同時(shí)回收任務(wù)執(zhí)行期間的日志信息.此外,DTES 基于Spring Boot 框架實(shí)現(xiàn),具有良好的穩(wěn)定性和可擴(kuò)展性.
深度學(xué)習(xí)全流程托管平臺(tái)是一個(gè)管理深度學(xué)習(xí)實(shí)驗(yàn)任務(wù)的網(wǎng)頁(yè)應(yīng)用系統(tǒng).該平臺(tái)提供了數(shù)據(jù)集管理、數(shù)據(jù)集標(biāo)注、模型訓(xùn)練以及模型部署等服務(wù),它將深度學(xué)習(xí)實(shí)驗(yàn)的所有流程全部集中到網(wǎng)頁(yè)端進(jìn)行,有效提升了研究效率.研究者首先將數(shù)據(jù)集上傳到平臺(tái),接著對(duì)數(shù)據(jù)集進(jìn)行標(biāo)注,之后在平臺(tái)中編寫程序訓(xùn)練模型,最后將符合要求的模型導(dǎo)出部署.
本文結(jié)合深度學(xué)習(xí)平臺(tái)的具體需要和深度學(xué)習(xí)實(shí)驗(yàn)的特性[1–3],提出了一種面向深度學(xué)習(xí)模型訓(xùn)練的分布式任務(wù)執(zhí)行系統(tǒng).
需求分析是系統(tǒng)開(kāi)發(fā)的基礎(chǔ),關(guān)系到開(kāi)發(fā)工程的成敗和軟件產(chǎn)品的質(zhì)量.本文提出的分布式任務(wù)執(zhí)行系統(tǒng)主要應(yīng)用于網(wǎng)頁(yè)端深度學(xué)習(xí)模型訓(xùn)練,系統(tǒng)的主要功能有監(jiān)控、任務(wù)調(diào)度、任務(wù)執(zhí)行、日志管理等,具體如下.
(1)監(jiān)控:包括對(duì)執(zhí)行器節(jié)點(diǎn)的監(jiān)控以及對(duì)任務(wù)的監(jiān)控,其中節(jié)點(diǎn)監(jiān)控主要是監(jiān)控各個(gè)節(jié)點(diǎn)執(zhí)行器的CPU、GPU、內(nèi)存、磁盤、網(wǎng)絡(luò)等情況,任務(wù)監(jiān)控主要是監(jiān)控任務(wù)的當(dāng)前狀態(tài);
(2)任務(wù)調(diào)度:調(diào)度器首先獲取各個(gè)節(jié)點(diǎn)執(zhí)行器的內(nèi)存、CPU 等資源的占用情況,以確定可以繼續(xù)執(zhí)行任務(wù)的執(zhí)行器,然后向執(zhí)行器發(fā)送任務(wù)執(zhí)行請(qǐng)求;
(3)任務(wù)執(zhí)行:調(diào)度器發(fā)出的執(zhí)行請(qǐng)求進(jìn)入執(zhí)行器的任務(wù)隊(duì)列,執(zhí)行器按照優(yōu)先級(jí)順序執(zhí)行任務(wù),并返回執(zhí)行結(jié)果;
(4)日志管理:記錄系統(tǒng)在任務(wù)執(zhí)行過(guò)程中產(chǎn)生的日志.
DTES 作為一個(gè)完善的系統(tǒng),除了滿足上述功能性需求外,還應(yīng)該滿足一定的非功能性需求[4],如安全性、兼容性、健壯性、可擴(kuò)展性等.
系統(tǒng)流程圖如圖1所示.
圖1 DTES 流程圖
3.1.1 系統(tǒng)架構(gòu)設(shè)計(jì)
深度學(xué)習(xí)全流程托管平臺(tái)采用微服務(wù)架構(gòu) [5,6] 實(shí)現(xiàn),分布式任務(wù)執(zhí)行系統(tǒng)DTES 最終會(huì)作為一個(gè)微服務(wù)集成到平臺(tái)中.DTES 選用Spring Boot 作為框架,通過(guò)Spring Cloud 提供的注冊(cè)中心組件Eureka 將DTES作為一個(gè)微服務(wù)注冊(cè)到深度學(xué)習(xí)平臺(tái)的Eureka 服務(wù)端,實(shí)現(xiàn)分布式任務(wù)執(zhí)行系統(tǒng)和其他微服務(wù)之間的相互訪問(wèn),通過(guò)Ribbon 組件實(shí)現(xiàn)HTTP 負(fù)載均衡,通過(guò)Hystrix 實(shí)現(xiàn)容錯(cuò)處理.
DTES 采用模塊化設(shè)計(jì),根據(jù)系統(tǒng)的需求分析,將系統(tǒng)劃分為監(jiān)控、任務(wù)調(diào)度、任務(wù)執(zhí)行、日志管理4 大功能模塊,其中每個(gè)模塊又包含相應(yīng)的子模塊.系統(tǒng)的功能模塊分解圖如圖2所示.
圖2 DTES 功能模塊圖
DTES 采用分層模式,系統(tǒng)整體分為數(shù)據(jù)層、業(yè)務(wù)層和接口層3個(gè)層級(jí).層級(jí)之間相互配合,共同實(shí)現(xiàn)任務(wù)執(zhí)行功能.系統(tǒng)架構(gòu)圖如圖3所示.
圖3 DTES 架構(gòu)圖
(1)數(shù)據(jù)層:數(shù)據(jù)層包括MySQL 數(shù)據(jù)庫(kù)、Redis 數(shù)據(jù)庫(kù)和RabbitMQ 消息中間件.其中MySQL 主要用于存儲(chǔ)任務(wù)的基本信息,如任務(wù)的狀態(tài)、工作目錄、環(huán)境變量等;Redis 用于全局的數(shù)據(jù)緩存;RabbitMQ 用于消息同步和存放任務(wù)執(zhí)行結(jié)果,供深度學(xué)習(xí)平臺(tái)的其他微服務(wù)進(jìn)行消費(fèi).
(2)業(yè)務(wù)層:業(yè)務(wù)層包括系統(tǒng)的業(yè)務(wù)邏輯實(shí)現(xiàn).其中監(jiān)控模塊負(fù)責(zé)監(jiān)控執(zhí)行器節(jié)點(diǎn)的資源使用情況和任務(wù)的運(yùn)行狀態(tài);任務(wù)調(diào)度模塊接收提交的任務(wù)并添加到任務(wù)資源庫(kù),根據(jù)系統(tǒng)中的調(diào)度策略從資源庫(kù)中讀取任務(wù)的基本信息并依此確定執(zhí)行器節(jié)點(diǎn),最后將任務(wù)發(fā)送給執(zhí)行器;任務(wù)執(zhí)行模塊接收?qǐng)?zhí)行請(qǐng)求并執(zhí)行具體的任務(wù),將執(zhí)行結(jié)果返回給任務(wù)調(diào)度模塊;日志管理模塊則主要負(fù)責(zé)收集任務(wù)執(zhí)行過(guò)程中產(chǎn)生的日志信息,以便于后續(xù)對(duì)任務(wù)進(jìn)行分析.
(3)接口層:系統(tǒng)使用REST (REpresentational State Transfer,表述性狀態(tài)轉(zhuǎn)移)協(xié)議對(duì)外提供接口服務(wù),使用JSON 進(jìn)行數(shù)據(jù)交互.系統(tǒng)對(duì)外開(kāi)放的接口主要有任務(wù)提交接口和日志查詢接口.
3.1.2 數(shù)據(jù)庫(kù)設(shè)計(jì)
數(shù)據(jù)庫(kù)設(shè)計(jì)是設(shè)計(jì)階段的重要一環(huán),分布式任務(wù)執(zhí)行系統(tǒng)選用MySQL 數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)的持久化.根據(jù)系統(tǒng)的業(yè)務(wù)功能設(shè)計(jì),系統(tǒng)共涉及任務(wù)、日志、執(zhí)行器節(jié)點(diǎn)等多個(gè)實(shí)體,現(xiàn)將系統(tǒng)中重要的數(shù)據(jù)表描述如表1和表2所示.
表1 任務(wù)數(shù)據(jù)表
表2 日志數(shù)據(jù)表
3.2.1 任務(wù)調(diào)度模塊設(shè)計(jì)與實(shí)現(xiàn)
任務(wù)調(diào)度是DTES的重要功能之一,整個(gè)系統(tǒng)的有序穩(wěn)定運(yùn)行得益于系統(tǒng)合理有效的調(diào)度策略[7–11].除了常見(jiàn)的任務(wù)隊(duì)列和隨機(jī)分配方法,系統(tǒng)還實(shí)現(xiàn)了依據(jù)執(zhí)行器資源利用率進(jìn)行調(diào)度的策略.DTES為監(jiān)控模塊[12–14]的核心類GlobalHardwareMonitor的update 方法添加Spring的心跳機(jī)制@Scheduled (fixedDelay=5000)實(shí)現(xiàn)對(duì)各個(gè)節(jié)點(diǎn)執(zhí)行器信息的實(shí)時(shí)更新.@Scheduled的參數(shù)定義了update 方法執(zhí)行的時(shí)間規(guī)則,如fixedDelay=5000 表示該方法從上一次執(zhí)行完開(kāi)始計(jì)時(shí),經(jīng)過(guò)固定的延遲時(shí)間5000 ms 后執(zhí)行下一次.在update 方法中訂閱執(zhí)行器資源利用信息,并依據(jù)利用率進(jìn)行任務(wù)調(diào)度.
任務(wù)調(diào)度模塊的核心類圖如圖4所示.在Schedule-ThreadSupport 類中調(diào)用TaskManager 接口的schedule方法進(jìn)行任務(wù)調(diào)度,schedule 方法通過(guò)上行鏈路服務(wù)UpstreamService 發(fā)送HTTP GET 請(qǐng)求至下行鏈路控制器ClientController 加載任務(wù)信息.此外,UpstreamService中注入了硬件監(jiān)控服務(wù)GlobalHardwareMonitor,依據(jù)任務(wù)信息和硬件資源利用率確定執(zhí)行節(jié)點(diǎn),并將結(jié)果返回給TaskManager.
圖4 DTES 任務(wù)調(diào)度核心類圖
3.2.2 任務(wù)執(zhí)行模塊設(shè)計(jì)與實(shí)現(xiàn)
顧名思義,任務(wù)執(zhí)行模塊[15–17]負(fù)責(zé)任務(wù)的具體執(zhí)行.考慮到深度學(xué)習(xí)平臺(tái)的實(shí)際需求以及深度學(xué)習(xí)實(shí)驗(yàn)的特性,DTES 現(xiàn)階段的任務(wù)執(zhí)行主要基于Docker容器[18]的方式.用戶根據(jù)具體的任務(wù)自定義Docker 鏡像并將鏡像上傳至深度學(xué)習(xí)平臺(tái),深度學(xué)習(xí)平臺(tái)的其他微服務(wù)可以接收鏡像并保存,由于不是本文的重點(diǎn),具體過(guò)程不再詳細(xì)陳述.
任務(wù)執(zhí)行模塊的核心類圖如圖5所示.接口Task-Manager中聲明了任務(wù)執(zhí)行的必需方法,它有兩個(gè)實(shí)現(xiàn)類,分別是ThreadPoolTaskManager和AbstractDocker-TaskManager,后者是對(duì)Docker 容器任務(wù)管理器的抽象,它提供了用于獲取任務(wù)鏡像的方法pullImage.若后續(xù)需要支持其他類型的任務(wù),只要實(shí)現(xiàn)該接口進(jìn)行擴(kuò)展即可.DefaultTaskManagerImpl是容器任務(wù)管理的具體實(shí)現(xiàn)類,UpstreamService 使用Spring的@Autowired注入進(jìn)來(lái),實(shí)現(xiàn)了啟動(dòng)方法start和終止方法kill 等任務(wù)處理方法.
圖5 任務(wù)執(zhí)行核心類圖
任務(wù)執(zhí)行的具體流程如下:
(1)TaskManager 調(diào)用UpstreamService的loadTask方法加載任務(wù),loadTask 使用Spring的RestTemplate方式發(fā)送HTTP GET 請(qǐng)求至下行鏈路控制器Client-Controller,ClientController 調(diào)用TaskService 查詢數(shù)據(jù)庫(kù)將結(jié)果返回;
(2)TaskManager 調(diào)用UpstreamService的prepare方法更新任務(wù)的當(dāng)前狀態(tài),prepare 使用Spring的RestTemplate 方式發(fā)送HTTP POST 請(qǐng)求至任務(wù)控制器TaskController,TaskController 將任務(wù)的狀態(tài)由ACCEPTED(被執(zhí)行器接受)改為PREPARING(任務(wù)數(shù)據(jù)準(zhǔn)備中);
(3)檢查任務(wù)所需鏡像是否存在,若不存在則進(jìn)行拉取;
(4)設(shè)置數(shù)據(jù)卷等信息;
(5)創(chuàng)建Docker 容器;
(6)開(kāi)啟日志收集;
(7)啟動(dòng)Docker 容器,執(zhí)行任務(wù).
3.2.3 日志管理模塊設(shè)計(jì)與實(shí)現(xiàn)
作為系統(tǒng)的支撐模塊,日志管理模塊在分布式任務(wù)執(zhí)行系統(tǒng)中也起著重要的作用[19–21].日志管理模塊收集了任務(wù)執(zhí)行期間產(chǎn)生的日志信息,給用戶對(duì)任務(wù)結(jié)果的分析總結(jié)帶來(lái)極大的便利.
日志管理模塊的核心類圖如圖6所示.TaskLog-Receiver 類用于收集任務(wù)日志,當(dāng)有當(dāng)異步事件結(jié)果產(chǎn)生時(shí)就會(huì)調(diào)用該類的onNext 方法,onNext 方法通過(guò)Builder 模式實(shí)例化一個(gè)LogRecord 對(duì)象,記錄下日志產(chǎn)生的時(shí)間、日志級(jí)別、日志具體內(nèi)容等信息.DTES的日志級(jí)別分為3 種,分別是標(biāo)準(zhǔn)輸出STDOUT、標(biāo)準(zhǔn)錯(cuò)誤STDERR和純?nèi)罩据敵鯮AW.實(shí)例化的LogRecord對(duì)象通過(guò)日志服務(wù)LogService 傳到上行鏈路服務(wù)UpstreamService,之后用RestTemplate 方式通過(guò)HTTP POST 請(qǐng)求轉(zhuǎn)發(fā)給LogController,LogController 調(diào)用日志存儲(chǔ)服務(wù)LogStorageService 將任務(wù)日志保存到MySQL 數(shù)據(jù)庫(kù)中.LogController 對(duì)外開(kāi)放了日志查詢API,深度學(xué)習(xí)全流程托管平臺(tái)的其他相關(guān)微服務(wù)可以調(diào)用該API 將日志信息展示給用戶.
圖6 DTES 日志管理核心類圖
代碼1.任務(wù)執(zhí)行核心代碼public void start(DockerTask task){DockerTask task=upstreamService.loadTask();upstreamService.prepare(task.getTaskId());TaskContext.Builder contextBuilder=TaskContext.Builder.aTaskContext();contextBuilder.withDockerTask(task);prepareImage(task.getImage());List
contextBuilder.withContainerId(containerResponse.getId());TaskLogReceiver logReceiver=applicationContext.getBean(TaskLogReceiver.class,task.getTaskId());dockerClient.attachContainerCmd(containerResponse.getId()).withStdOut(true).withStdErr(true).withFollowStream(true).exec(logReceiver);contextBuilder.withLogAdapter(logReceiver);this.taskContextHolder.put(task.getTaskId(),contextBuilder.build());dockerClient.startContainerCmd(containerResponse.getId()).exec();}
接口測(cè)試用來(lái)測(cè)試系統(tǒng)與外界之間以及系統(tǒng)內(nèi)部各個(gè)模塊之間的交互,主要測(cè)試系統(tǒng)的依賴關(guān)系以及數(shù)據(jù)傳遞等.使用Postman 對(duì)DTES 任務(wù)提交接口的測(cè)試如圖7所示.
圖7 接口測(cè)試
DTES 作為子系統(tǒng)測(cè)試通過(guò)后,集成到整個(gè)深度學(xué)習(xí)平臺(tái)中.深度學(xué)習(xí)平臺(tái)部署在由3 臺(tái)物理機(jī)構(gòu)成的集群中,每臺(tái)機(jī)器的硬件配置為CPU Intel Core i7@2.40 GHz,內(nèi)存8 GB,硬盤100 GB,操作系統(tǒng)Ubuntu 16.04 LTS.本節(jié)以深度學(xué)習(xí)模型訓(xùn)練任務(wù)為例,對(duì)DTES的功能進(jìn)行測(cè)試.
如圖8所示,用戶進(jìn)入工作臺(tái)頁(yè)面,在左側(cè)的目錄樹(shù)中將任務(wù)需要使用的圖片數(shù)據(jù)和標(biāo)簽數(shù)據(jù)掛載進(jìn)來(lái),接著創(chuàng)建程序文件并在右側(cè)區(qū)域進(jìn)行代碼編碼.編碼完成后,鼠標(biāo)右鍵點(diǎn)擊左側(cè)項(xiàng)目的根目錄選擇“創(chuàng)建訓(xùn)練任務(wù)”,創(chuàng)建成功后,DTES 在后臺(tái)進(jìn)行調(diào)度執(zhí)行.切換到任務(wù)詳情頁(yè)面,可以看到任務(wù)的實(shí)時(shí)日志信息,如圖9所示.
圖8 創(chuàng)建任務(wù)
圖9 日志查詢
為測(cè)試DTES的性能,本節(jié)使用TensorFlow 框架在已集成DTES的深度學(xué)習(xí)平臺(tái)與未集成DTES的第一代平臺(tái)中多次訓(xùn)練手寫數(shù)字MNIST 數(shù)據(jù)集,并對(duì)完成同一訓(xùn)練任務(wù)的平均用時(shí)進(jìn)行比較.實(shí)驗(yàn)數(shù)據(jù)如表3所示.
表3 不同平臺(tái)完成同一訓(xùn)練任務(wù)的平均耗時(shí)(單位:s)
通過(guò)對(duì)比可以看出,對(duì)于同樣的深度學(xué)習(xí)訓(xùn)練任務(wù),DTES 可以節(jié)約大約23.8%的時(shí)間,具有較高的應(yīng)用價(jià)值.
使用深度學(xué)習(xí)平臺(tái)在網(wǎng)頁(yè)端進(jìn)行深度學(xué)習(xí)模型訓(xùn)練可以給實(shí)驗(yàn)研究人員帶來(lái)極大的便利,本文結(jié)合平臺(tái)的實(shí)際需要設(shè)計(jì)并實(shí)現(xiàn)了分布式任務(wù)執(zhí)行系統(tǒng)DTES.DTES 基于Spring Boot 框架實(shí)現(xiàn)任務(wù)調(diào)度、任務(wù)執(zhí)行、日志管理等功能,可以作為一個(gè)微服務(wù)快速集成到深度學(xué)習(xí)平臺(tái)中.DTES 將接收的任務(wù)按照既定的調(diào)度策略進(jìn)行調(diào)度并創(chuàng)建Docker 容器進(jìn)行執(zhí)行,同時(shí)將任務(wù)執(zhí)行期間產(chǎn)生的日志信息反饋給實(shí)驗(yàn)研究人員.經(jīng)過(guò)系統(tǒng)測(cè)試,DTES 已經(jīng)達(dá)到預(yù)期目標(biāo),但仍然存在待完善之處,接下來(lái)要繼續(xù)對(duì)該系統(tǒng)進(jìn)行擴(kuò)展,使其可以支持更多類型的任務(wù).