成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

無服務器計算的機器學習,出路在哪里?

新聞 機器學習
本文重點關注了基于無服務器計算的機器學習的最新研究進展,并通過四篇研究論文表明了無服務器 ML 框架在執行機器學習任務時性能遠優于經典的基于粗粒度的 VM 集群的ML框架。

 一、機器學習和無服務器學習

1.1、機器學習(ML)在應用場景中遇到了什么問題?

近年來,機器學習(Machine Learning,ML)在圖像識別、文本和語音處理等領域中廣泛應用,改變了人們工作、生活的方式,帶來了巨大的便利性。但同時,ML 用戶也面臨著幾個巨大的挑戰,這些挑戰極大地阻礙了 ML 的生產力和效率。首先,用戶通常需要手動配置許多系統級參數,例如工作服務器 / 參數服務器的數量、內存分配、cpu 數量、物理拓撲等。其次,用戶需要指定大量與 ML 相關的參數,如學習率、學習算法、神經網絡結構等,這些參數與系統級參數之間還存在各種交互作用。第三,ML 工作流通常由多個階段組成,包括預處理、訓練、超參數搜索等等,每個階段都有 ML 用戶必須考慮的不同計算需求。

由于 ML 的這些特點,在實際應用中經常會導致兩個問題:

一是,ML 工作流中不同任務的異構性導致了訓練工作流執行過程中資源的嚴重不平衡。ML 用戶需要單獨考慮每個階段的異構資源需求,常常會導致資源過度配置(Resource Overprovisioning)。當前的 ML 框架通常是基于粗粒度的 VM 集群的,而這些集群并不具備 ML 相關工作負載所需的靈活性。CPU 總利用率低至 20% 的情況并不少見[1]。在實踐中,開發人員在工作流的不同階段反復使用不同的 ML 參數進行實驗會進一步加劇資源過度配置的問題;

二是,ML 用戶需要應對復雜的管理問題,他們面臨著為每個 ML 工作負載提供、配置和管理這些資源的挑戰。利用 VMs 進行機器學習工作負載的系統通常需要用戶重復執行一系列繁重的任務,表 1 中展示了一些任務。這種管理復雜性阻礙了交互和迭代用例,降低了用戶生產力和模型的有效性。

在實踐中,過度資源調配和顯式資源管理負擔這兩個問題是緊密耦合的:ML 用戶在遇到工作流不同階段所需資源精確分配所帶來的難度和人工成本的問題時,通常會采用過度資源調配的方式來應對。

那究竟用什么辦法應對 ML 在實踐中應用的這些問題呢?在這篇文章中我們一起來探討一個目前廣泛應用且獲得了非常好效果的辦法:無服務器計算(Serverless Computing)

表 1. ML 用戶在使用 VM 集群時遇到的任務挑戰。

無服務器計算的機器學習,出路在哪里?

1.2、無服務器計算(Serverless Computing)

無服務器計算是云原生計算模型的一種落地狀態。云計算的發展在經歷了基礎設施即服務(Infrastructure as a Service-IaaS)、平臺即服務(Platform as a Service-PaaS)、軟件即服務(Software as a Service-SaaS)幾個階段后,逐漸進入了無服務器計算的階段。從與之前幾個階段所能提供的服務進行比較的角度分析,無服務器計算可以提供以下一種或兩種服務:

1. 函數即服務 (Functions-as-a-Service-FaaS)。開發人員使用由事件(event) 或 HTTP 請求觸發的函數運行和管理應用程序代碼,開發人員將這些小的代碼單元部署到 FaaS 中,FaaS 按需執行和擴展,開發人員則無需管理服務器或任何其他底層基礎設施。

2. 后端即服務(Backend-as-a-Service-BaaS)。提供第三方的基于 API 的服務用于替換應用程序中的核心功能子集。對于開發人員來說,這些 API 是作為一個自動擴縮容和透明操作的服務提供的,所以對于開發人員來說,這種服務方式也是無服務器的。

從技術實現的角度分析,無服務器計算依靠云基礎設施而不是用戶來自動解決資源調配和管理的挑戰。這種方法依賴于一個更受限制的計算單元,例如 AWS Lambda 的無狀態 Lambda 函數(the Stateless Lambda Function),該計算單元由開發人員提交,并由云基礎設施安排執行。因此,用戶無需手動配置、部署和管理長期計算單元(例如 VM)。無服務器模式的優勢促進了數據中心、云提供商和開放源代碼平臺的快速應用。

無服務器計算所提供的服務包括:一種有時間限制的無狀態函數作為執行程序邏輯的服務 API,以及,一種管理程序狀態的對象存儲系統。通過使用服務 API,用戶可以運行代碼函數 (也稱為操作) 并返回每個函數的結果。無服務器計算還提供 HTTPS 終端,允許開發人員檢索函數結果,開發人員可以通過 HTTPS 終端輸入參數后生成相關函數的觸發事件(或鏈接)。對于能夠清晰地分離程序狀態和邏輯的應用程序設計人員來說,無服務器計算平臺提供了對大型計算能力的即時訪問,使得程序設計人員無需進行復雜的集群部署。

在無服務器計算平臺中,云服務提供商提供了按需執行函數的能力,并對最終用戶隱藏了集群配置和管理開銷。除了可用性方面的好處外,這種模式還提高了效率:云提供商可以以比傳統集群計算更精細的粒度復用資源,并且用戶不需要為空閑資源付費。然而,為了有效地管理資源,云服務提供商對每種資源的使用進行了限制。

計算(computation)。無服務器計算平臺中提供的計算資源通常僅限于一個 CPU 核和一個較短的計算窗口。例如,AWS Lambda 在單個 AVX 內核上提供 900 秒的計算時間,可以訪問高達 3GB 的內存和 512MB 的磁盤存儲。用戶可以執行許多并行函數,并且這些執行的聚合計算性能幾乎呈線性擴展。函數執行中的線性可伸縮性只在單個 worker 之間沒有通信的情況下對并行計算有用。在實際應用中,由于單個 worker 只是瞬時存在的,他們的啟動時間可能是錯開的,因此傳統的類似 MPI 的點對點通信模型無法在這種環境中工作。我們可以考慮利用存儲作為 worker 之間的間接通信通道。

存儲(Storage)。云服務提供商提供了多種存儲選項,從鍵值存儲到關系型數據庫。有些服務不完全是彈性的,因為它們需要預先提供資源。然而,像 Amazon S3 或 Google Cloud Storage 這樣的分布式對象存儲系統提供了無限存儲,用戶只需按存儲的數據量付費。我們可以考慮潛在地將計算期間的中間狀態存儲在分布式對象存儲中,并且仍然可以獲得與從其他節點的 RAM 訪問時相同的帶寬。

控制面(Control Plane)。除了存儲服務,云服務提供商還提供發布 - 訂閱服務,如 Amazon SQS 或 Google Task Queue。這些服務通常不支持高數據訪問帶寬,但提供一致性保證,如至少一次傳遞,并且可以用于 “控制平面” 狀態:所有無服務器函數調用之間共享的任務隊列。云服務提供商還提供一致的鍵值存儲(例如 DynamoDB),可用于跨無服務器函數調用存儲和操作控制平面狀態。

由于無服務器計算存在上述約束條件,在實際應用中,無服務器計算也不是 “完美無缺” 的,應用無服務器計算也面臨很多問題。以 AWS Lambda 為例,利用無服務器計算的主要挑戰是與 Lambda 函數相關聯的非常小的本地資源約束(內存、cpu、存儲、網絡),這是無服務器計算的基礎,正因為這些細粒度的計算單元實現了可伸縮性和靈活性。具體的,無服務器計算面臨著如下問題:

本地內存和存儲空間小(Small local memory and storage)。由于存在計算資源限制,阻止了使用任何未使用這些資源設計的計算框架。例如,我們無法在 AWS Lambda 或具有此類資源受限配置的 VM 上運行 Tensorflow 或 Spark。

低帶寬以及缺乏 P2P 通信(Low bandwidth and lack of P2P communication)。與常規 VM 相比,Lambda 函數的可用帶寬有限。我們發現,最大的 AWS Lambda 只能維持 60MB/s 的帶寬,即使在中型 VM 中,也遠遠低于 1GB/s 的可用帶寬。此外,無服務器計算對通信拓撲施加了進一步的限制。諸如 AWS Lambda 之類的無服務器計算單元不允許對等通信。因此,傳統的用于數據中心 ML 的通用通信策略,例如樹結構或環結構 AllReduce 通信等等,在這樣的環境中都無法有效實現。

短暫且不可預測的加載時間(Short-lived and unpredictable launch times)。Lambda 函數的壽命很短,且啟動時間非常多變。例如,AWS Lambda 在加載后可能需要幾分鐘的時間來啟動。這意味著在訓練過程中,Lambda 會在不可預知的時間開始,并且有可能在訓練中途結束。這就要求 Lambda 的 ML 運行時能夠容忍 worker 的頻繁離開和到達。

缺乏快速共享存儲(Lack of fast shared storage)。因為 Lambda 函數之間不能連接,所以需要使用共享存儲。由于 ML 算法有嚴格的性能要求,這種共享存儲需要低延遲、高吞吐量,并針對 ML 工作負載中的通信類型進行優化。然而,到目前為止,還沒有能夠為云提供所有這些屬性的快速無服務器存儲。

不過,目前已經有不少無服務器計算的落地應用案例。其中,有代表性的公有云無服務器平臺有:

AWS Lambda。亞馬遜的 AWS Lambda,借助 Lambda,幾乎可以為任何類型的應用程序或后端服務運行代碼,而且完全無需管理。只需上傳代碼,Lambda 會處理運行和擴展高可用性代碼所需的一切工作。開發人員可以將代碼設置為自動從其他 AWS 服務觸發,或者直接從任何 Web 或移動應用程序調用。https://aws.amazon.com/cn/lambda/。

Microsoft Azure Functions。微軟的 Azure 是一個事件驅動(Event-drive)的無服務器計算平臺,可以解決復雜的編排問題。本地構建和調試,無需額外設置,在云中大規模部署和操作,并使用觸發器和綁定集成服務。https://azure.microsoft.com/en-us/services/functions/。

Google Cloud Functions。Google 的 Cloud Functions 是一種事件驅動的計算服務。它具有自動擴展、運行代碼以響應事件的能力,僅在代碼運行時付費的能力,并且不需要任何服務器管理。用例包括無服務器應用程序后端,實時數據處理和智能應用程序,如虛擬助手,聊天機器人和情緒分析。https://cloud.google.com/functions/

阿里云函數計算(Function Compute)。阿里的函數計算是一個事件驅動的全托管無服務器計算服務,無需管理服務器等基礎設施,只需編寫代碼并上傳,函數計算會準備好計算資源,并以彈性、可靠的方式運行代碼。所有客戶,函數計算都將提供每月 100 萬次函數調用、400,000 個函數實例資源的免費無服務器算力支持。https://www.aliyun.com/product/fc?spm=5176.10695662.1112509.1.3b6768bc2OOWFL。

有代表性的私有云無服務器框架有:

Fission 。Fission 使用 Kubernetes 構建函數。它允許程序員使用任何編程語言編寫函數,并將其與任何事件觸發器 (如 HTTP 請求) 進行映射。https://fission.io/。

Funktion 。Funktion 是一個開源的容器本地化服務器平臺,使用 Kubernetes 構建函數。它允許程序員用任何編程語言編寫函數,可以在任何地方、任何云上或在本地運行。https://github.com/funktionio/funktion。

Kubeless 。是一個 kubernets 原生的無服務器計算框架。它利用 Kubernetes 資源提供自動縮放、API 路由、監控、故障恢復等功能。https://github.com/kubeless/kubeless。

Apache OpenWhisk 。OpenWhisk 使用 Docker 構建函數,它允許程序員使用 Scala 語言編寫函數,允許在任何規模的事件響應中執行代碼。框架響應類似 HTTP 請求這樣的觸發事件,然后運行 JavaScript 或 Swift 代碼片段。https://openwhisk.apache.org/。

Iron Functions 。Iron 使用 Docker、Swarm、Kubernetes 構建函數,它允許程序員使用 Go 語言編寫函數。https://github.com/iron-io/functions。

OpenLambda。OpenLambda 是一個 Apache 許可的無服務器計算項目,用 Go 編寫,基于 Linux 容器。OpenLambda 的主要目標是探索無服務器計算的新方法。https://github.com/open-lambda/open-lambda。

OpenFaas 。OpenFaaS 是一個使用 Docker 構建無服務器 (Serverless) 功能的框架,它擁有對指標的一級支持。任何流程都可以打包為一個函數,使你能夠使用一系列 web 事件,而無需重復的樣板化編碼。https://www.oschina.net/p/openfaas?hmsr=aladdin1e1。

有代表性的無服務器平臺的包裝框架有:

Zappa(Python,AWS)。Zappa 極大的簡化了在 AWS Lambda + API 網關上發布所有 Python WSGI 應用。相當于是無服務器的部署運行 Python Web 應用。這意味著無限伸縮、零宕機、零維護。https://www.oschina.net/p/python-zappa。

Chalice(Python,AWS)。Chalice 允許開發者快速創建和部署應用,采用 Amazon API 網關和 AWS Lambda 。https://www.oschina.net/p/chalice?hmsr=aladdin1e1。

Claudia.js(Node,AWS)。方便快速部署 Node.js 項目到 AWS Lambda 和 API 網關。它自動化了所有容易出錯的部署和配置任務,并按照 JavaScript 開發人員所期望的開箱即用的方式設置了一切。開發人員可以輕松地開始使用 Lambda 和 API 網關,并專注于解決重要的業務問題,而不是處理 AWS 部署工作流。https://github.com/claudiajs/claudia。

二、引入 ML 的無服務器計算最新研究情況介紹

由上一節的介紹我們知道,目前已經有很多公有云、私有云無服務器計算平臺,也有一些無服務器平臺的包裝框架。可以說,我們想在日常的應用實踐中嘗試無服務器化,已經是比較容易的一件事了。不過,具體到機器學習的問題,這些無服務器計算平臺在 ML 應用場景下都或多或少存在一些問題。

由第一章中的介紹我們可以看到,無服務器計算非常適用于離散化數據中心(Disaggregated Datacenters),但對許多性能關鍵型應用(Performance critical applications)卻不是非常適用,因為無服務器計算方式切斷了傳統的性能優化途徑,例如利用數據局部性進行優化或分層通信等,因此會直接影響性能關鍵型應用的效果。目前無服務器平臺主要用于簡單的事件驅動應用程序,如物聯網自動化、前端 web 服務和日志處理等等。

最近,一些研究人員將無服務器計算應用在更廣泛的場景中,如并行數據分析和分布式視頻編碼。然而,這些工作負載要么只能簡單并行,要么只能跨函數使用簡單的通信模式。復雜的通信模式和工作負載如何有效地適應無服務器計算仍然是一個有待研究的問題。我們這篇文章中重點關注的是用于 ML 的無服務器計算。我們知道,ML 包含大量的參數、復雜的處理流程,是典型的 “性能關鍵型應用”,我們將在這一節中介紹最新的關于“如何將 ML 引入無服務器計算” 這一問題的研究進展。

2.1、A Case for Serverless Machine Learning [2]

無服務器計算的機器學習,出路在哪里?

本文是來自 Berkeley 的研究人員發表在 NIPS2018 中的一篇文章,具體分析了 ML 工作負載環境下的資源管理問題,探討了利用無服務器基礎設施實現 ML 工作流資源管理自動化的研究方向。作者提出了一個無服務器機器學習框架,該框架專門用于無服務器基礎設施和 ML 工作流。

本文所討論的無服務器計算依賴于 Amazon S3 的無狀態 Lambda 函數,這些函數由開發人員提交,并由云基礎設施自動調度。因此,它們避免了開發人員顯式配置、部署和管理長期計算單元(例如 VM)的需要。與一般的無服務器計算平臺不同,無服務器機器學習框架需要滿足三個關鍵目標。首先,它的 API 需要支持廣泛的 ML 任務:數據預處理、訓練和超參數優化。為了簡化從現有 ML 系統的轉換所涉及的工作量,應該用 Python 之類的高級語言開發這樣的 API。第二,為了為無狀態工作者之間的中間數據和消息傳遞提供存儲,它需要提供一個具有豐富接口的低延遲可伸縮數據存儲。第三,要在資源受限的 Lambda 上高效運行,它的 Runtime 必須是輕量級和高性能的

為了滿足這些條件,作者構建了一個專門用于 ML 的無服務器框架。

首先,該框架為 ML 工作流的所有階段提供了一個 API,該 API 實用且易于更廣泛的 ML 社區使用。(1)API 完全包含在 Python 包中,允許 ML 開發人員輕松調用。(2) API 提供了一個抽象底層系統級資源的高級接口。(3) Python 包提供了一個用戶界面,開發人員可以通過該界面可視化工作進度。

然后,該框架包含 Python 前端提供到客戶端后端的接口。這個后端負責管理臨時計算資源和調度任務。在這個后端中,不同的子模塊為 ML 工作流的每個特定階段的邏輯(例如預處理)進行編碼處理。這些子模塊啟動 Lambda 上的 worker,跟蹤計算進度,并在計算完成后將結果返回到 Python 前端。客戶端后端使用內部低級調度程序,該調度程序封裝了與啟動、終止和重新生成在無服務器 Lambda 上運行的任務相關的所有邏輯。這個調度程序還跟蹤所有任務的狀態。

第三,該框架提供一個輕量級 Runtime,它封裝了系統支持的不同計算之間共享的所有函數,從而簡化了新算法的開發。Worker runtime 提供兩個接口。首先,它提供了一個智能迭代器來訓練存儲在 S3 中的數據集。這個迭代器在 Lambda 的本地內存中預取和緩沖 mini-batch,與 worker 的計算并行,以減輕訪問 S3 的高延遲(>10ms)。它為分布式數據存儲提供了一個 API。

最后,該框架為 workers 之間的中間數據和通信提供具有豐富接口的共享存儲。此接口有兩種類型的 API:(1)用于一般消息傳遞、中間數據存儲和數據縮減的鍵值存儲,以及(2)參數服務器接口。為了達到所需的低延遲,將該數據存儲部署在云 VMs 上。為了有效地利用稀缺的網絡資源,對數據存儲接口進行優化處理,例如:數據壓縮、稀疏數據結構、異步通信等。

為了實現簡化機器學習工作流執行的目標,理想的系統應該提供一個簡單但足夠通用的 API。這個 API 需要讓用戶在一個單一的、集成的框架內執行 ML 任務,例如:(1)數據集加載,支持常用的數據格式,(2)數據預處理,(3)模型訓練,(4)大規模的超參數調整。

作者給出了一個例子來展示這個 API 的功能——圖 1 中給出基于 Criteo Kaggle 競爭開發模型的過程,該模型用于預測用戶點擊顯示廣告數據集的廣告的概率。工作流的第一步是加載數據集并將其上載到 Amazon S3。例如,用戶可以調用 load_libsvm 方法來加載以 LIBSVM 格式存儲的數據集,解析數據后自動為其創建分區,然后將其上載到 Amazon S3。第二步,一旦數據加載到 Amazon S3 中,就可以立即進行預處理。系統應該提供一些開發人員常用的預處理方法。例如,用戶可以通過使用 Amazon S3 中數據集的路徑調用 normalize 函數來規范化數據集。一旦加載了數據,用戶就可以通過查看系統的測試損失來訓練不同的模型并查看它們的性能。一旦用戶對某個特定的模型獲得了合理的損失,他們就可以通過超參數搜索對其進行微調。此外,作者設想這樣一個系統允許用戶在每個階段的執行過程中進行多次交互。例如,當超參數搜索任務正在運行時,用戶應該能夠監視每個單獨實驗的測試損失。對于表現不好的實驗(例如,測試損失發散(test loss is diverging)),用戶應該能夠終止它們。這個特性可以通過交互環境(比如 Jupyter)中的用戶界面來實現。

無服務器計算的機器學習,出路在哪里?

圖 1. API 示例。無服務器 ML 的 API 應該支持 ML 開發工作流的不同階段:(a)預處理,(b)訓練,和(c)超參數調優。

為了評估對 ML 無服務器框架的需求,作者引入兩個框架進行性能比對:PyWren[3]和 Bosen[4]。PyWren 是一個專門用于無服務器架構的 Map-reduce 框架。PyWren 提供了可縮放到數千個 workers 的 map 和 reduce 原語。Bosen 是一個分布參數框架,專門用于基于 VM 的 ML 算法。為了進行評估,作者在 PyWren 上實現了一個異步 SGD 訓練算法。在 PyWren 基線實現的基礎上,作者還進行了一組優化。作者使用了來自 Criteo Kaggle 競賽的 Criteo 展示廣告數據集進行實驗。作者在 10 個最大的 AWS Lambda(3GB 內存)上運行 PyWren,在單個 VM(m5.2xlarge1)中的 8 個內核上運行 Bosen。

作者通過記錄隨時間變化的測試損失來測量這兩個系統的性能(圖 2)。對于 PyWren,作者在實現每個優化之后報告這個值。作者累計實現了以下優化:(1)跨迭代重用 Lambda;(2)使用異步 SGD 進行小批量預取;(3)使用低延遲存儲(Redis)代替 Amazon S3;(4)使用具有多 get 操作的稀疏數據傳輸。我們觀察到這些優化顯著改善了 Pyren 在 600 秒后實現的最終測試損失(從 0.61 到 0.57)。盡管有了這些改進,PyWren 仍然比 Bosen 慢得多。進一步的性能分析表明,PyWren 存在一些開銷,例如序列化 / 反序列化數據,以及使用接口不適合 ML 工作負載的遠程存儲(例如 Redis 或 S3)。這一結果表明,在設計無服務器計算框架的早期,需要仔細考慮 ML 工作負載的性能需求。

無服務器計算的機器學習,出路在哪里?

圖 2. PyWren 和 Bosen 在 Criteo-Kaggle 邏輯回歸任務中的表現。PyWren 基線通過重用 Lambda、添加預取、切換到異步計算、用更高性能的 Redis 存儲后端替換 S3 以及支持在單個 RPC 上獲取多個密鑰而得到了增量改進。

此外,作者還構建了本文所提出的框架的原型,包括:(1)具有參數服務器接口的高性能數據存儲,(2)mini-batch 數據的循環緩沖區預取,(3)邏輯回歸 SGD 訓練算法。為了充分驗證這種設計的好處,作者在相同的邏輯回歸任務中對其進行了評估。作者測量了每個 worker 的平均 SGD 迭代時間(見圖 3)。這個時間是 worker 性能的一個指標;較低的迭代時間意味著更頻繁的模型更新和更快的收斂。作者還將這一次的 SGD 算法分解為四個主要步驟:(1)從數據存儲中獲取最新模型,(2)從遠程存儲(例如 S3)中獲取一個 minibatch,(3)計算 SGD 梯度,以及(4)將梯度發送到數據存儲。作者發現,盡管無服務器計算具有固有的開銷,本文所提出的框架原型還是實現了較低的每次迭代時間( 500 μs)--- 與 Bosen 這樣的系統不相上下。這種性能源于兩種機制:(1)遠程 mini-batch 的有效預取和緩沖,以及(2)盡可能與數據存儲通信。首先,minibatch 預取機制通過與計算并行進行,有效地掩蓋了從 S3 獲取 minibatch 所需的時間。實際上,對于中型 / 大型 Lambda,在新的 minibatch 上開始計算所需的時間可以忽略不計,因為大多數情況下,這些數據都是在 worker 需要之前緩存在內存中的。即使從 S3 獲取一個 mini-batch 需要 10ms 也是這樣的。其次,作者發現與數據存儲的通信是有效的(例如,發送梯度的時間可以忽略不計)。由于能夠與數據存儲異步通信,進一步提升了該框架的性能。

無服務器計算的機器學習,出路在哪里?

圖 3. 本文所提出原型每次 SGD 迭代的時間。具體細分為四個主要步驟:(1)將梯度發送到數據存儲,(2)計算梯度,(3)從數據存儲獲取模型,(4)從 S3 獲取 minibatch。

2.2、Cirrus: a Serverless Framework for End-to-end ML Workflows [5]

這篇文章也是節 2.1 中所介紹的 Berkeley 研究小組的研究成果,是對節 2.1 中分析的 NIPS’18 中文章所涉及工作的擴展和延伸。在專門用于無服務器基礎設施和 ML 工作流的無服務器 ML 框架原型的基礎上,將其封裝為一個實現端到端管理的分布式 ML 訓練框架 Cirrus,可以直接調用使用(https://github.com/ucbrise/cirrus),并將相關工作內容發表在發表在 SoCC ’19 中。Cirrus 專門用于無服務器云基礎設施(如 Amazon AWS Lambda)中的 ML 訓練。它提供高級原語來支持 ML 工作流中的一系列任務:數據集預處理、訓練和超參數優化。Cirrus 結合了無服務器接口的簡單性和無服務器基礎設施(具體是指 AWS Lambda 和 S3)的可伸縮性,以最小化用戶的工作。

Cirrus 的設計原則是:

自適應的細粒度資源分配。為了避免由于過度配置而造成的資源浪費,Cirrus 應該靈活地調整為每個工作流階段保留的細粒度資源量。

無狀態服務器端后端。為了確保無服務器計算資源的健壯和高效管理,Cirrus 設計了一個無狀態的服務器端后端。有關當前部署的函數以及 ML 工作流任務和計算單元之間的映射的信息由客戶端后端管理。因此,即使所有云端資源變得不可用,ML 訓練工作流也不會失敗,并且可以在資源再次可用時恢復其操作。

端到端無服務器 API。模型訓練不是 ML 研究人員的唯一任務,數據集預處理、特征工程和參數調整等對于最終生成一個好的模型同樣重要。Cirrus 應該提供一個完整的 API,允許開發人員以最小的工作量端到端的大規模地運行這些任務。

高可擴展性。ML 任務是高度計算密集型的,在沒有有效并行化的情況下需要很長時間才能完成。因此,Cirrus 應該能夠同時運行數千個 workers 和數百個實驗。

與節 2.1 中所介紹的工作類似,Cirrus 利用四個系統模塊來實現上述原則。首先,Cirrus 為 ML 開發人員提供了 Python 前端。這個前端有兩個功能:a)為 ML 訓練的所有階段提供豐富的 API;b)在無服務器的基礎設施中執行和管理大規模計算。其次,Cirrus 提供了一個客戶端后端。第三,為了克服低延遲無服務器存儲的不足,Cirrus 為 worker 共享的所有中間數據提供了低延遲分布式數據存儲。第四,Cirrus 提供了一個在無服務器 Lambda 上運行的 worker 運行時(runtime)。該運行時提供了訪問 S3 中的訓練數據集和分布式數據存儲中的中間數據的有效接口。Cirrus 的完整結構見圖 4。

無服務器計算的機器學習,出路在哪里?

圖 4. Cirrus 系統結構。系統由(有狀態的)客戶端(左)和(無狀態的)服務器端(右)組成。預處理和面向用戶的訓練包含一個前端的 API。客戶端后端管理云功能和向函數分配任務。服務器端由 Lambda Worker 和高性能數據存儲組件組成。Lambda worker 將數據迭代器 API 導出到客戶端后端,并包含許多迭代訓練算法的有效實現。數據存儲用于存儲梯度、模型和中間預處理結果。

Cirrus 的整體結構與節 2.1 中是類似的。Cirrus 的前端和客戶端后端是用 Python 實現的,方便 Cirrus 與現有的機器學習方法相結合。為了提高效率,分布式數據存儲和 worker runtime 用 C++ 實現。表 2 列出了實現的不同組件以及它們的大小和實現語言。Worker runtime 代碼包括迭代器接口和數據存儲客戶端實現。worker runtime 和數據存儲通過 TCP 連接進行通信。作者實現了一個共享組件庫,其中包括線性代數庫、通用實用程序和 ML 算法,這些組件被所有系統組件共享。作者已經公開發布了 Apache 2 開源許可的實現(https://github.com/ucbrise/cirrus)。

無服務器計算的機器學習,出路在哪里?

表 2. Cirrus 組件。

首先,Cirrus 為 ML 工作流的所有階段提供了一個 Python 前端 API。前端是一個高度靈活的 thin Python API,默認情況下,它從開發人員那里抽象出所有的細節,同時提供了通過 API 的參數覆蓋內部配置參數(例如,優化算法)的能力。前端還提供了一個運行在 Plotly 上的用戶界面,供用戶監控工作負載的進度和啟動 / 停止任務。Cirrus Python API 分為三個子模塊。每個子模塊都打包了與工作流的每個階段相關的所有函數和類。(1)預處理。預處理子模塊允許用戶對存儲在 S3 中的訓練數據集進行預處理。此子模塊允許不同類型的數據集轉換:最小 - 最大縮放、標準化和特征散列。(2)訓練。Cirrus 的訓練子模塊支持 ML 模型,這些模型可以通過隨機梯度下降進行訓練。目前 Cirrus 支持稀疏 Logistic 回歸、潛在 Dirichlet 分配、Softmax 和協同過濾。(3)超參數優化。超參數優化子模塊允許用戶在給定的參數集上運行網格搜索。Cirrus 允許用戶改變 ML 訓練參數(例如,學習率、正則化率、小批量大小)以及系統參數(例如,Lambda 函數大小、并發 worker 數量、梯度過濾)。

其次,Cirrus 的 Python 前端提供了一個到 Cirrus 客戶端后端的接口。這個后端的功能和能夠完成的任務與節 2.1 中介紹的框架完全相同。客戶端后端從前端算法中抽象出 Lambda 的管理。客戶端后臺會保存一個當前活動的 Lambda 列表,以及一個 AWS Lambda API 的連接列表(每個連接用于啟動一個 Lambda)。在訓練期間加載的 Lambda 在其生存期結束時自動重新加載(每 15 分鐘一次)。由于 Lambda API 的特殊性,從一臺服務器上快速加載數百個 Lambda 是非常困難的。為了解決這個問題,后端保留一個線程池,可用于響應新 Lambda 任務的請求。

第三,Cirrus 提供了分布式存儲模塊。Cirrus 的數據存儲用于存儲所有 workers 共享的中間數據。由于現有產品中不允許 Lambda 之間進行交互通信,因此 Lambda 需要共享存儲。無服務器 Lambda 的存儲需要滿足三個條件:首先,它需要低延遲(本文實現低至 300μs),以便能夠適應延遲敏感的工作負載,例如用于 ML 訓練的工作負載(迭代 SGD)。其次,它需要擴展到數百個 workers,以利用無服務器基礎架構幾乎線性的可擴展性。第三,它需要一個豐富的接口來支持不同的 ML 用例。例如,數據存儲必須支持 multiget(§6.5)、常規鍵 / 值的 put/get 操作和參數服務器接口。為了實現低延遲,將數據存儲部署在云 VMs 中。它實現了低至 300μs 的延遲,而 AWS S3 的延遲約為 10ms。此延遲對于訓練階段最大化模型的更新至關重要。作者使用稀疏表示來表征梯度和模型以實現高達 100 倍的壓縮比,以便與存儲和批處理請求進行數據交換。為了實現高可伸縮性,Cirrus 包括以下機制:(1)分片存儲,(2)高度多線程,(3)數據壓縮,(4)梯度濾波器和(5)異步通信。Cirrus 的分布式數據存儲提供了一個接口,支持所有在 ML 工作流中存儲中間數據的用例。該接口支持鍵值存儲接口(set/get)和參數服務器接口(send 果然啊 dient/get model)。

最后,Cirrus 提供了一個運行時(Runtime),它封裝了系統支持的不同計算之間共享的所有函數。如圖 5,Cirrus 的 Runtime 為 ML 計算提供了通用抽象(General abstractions)和基本數據類型(Data primitives)用于訪問訓練數據、參數模型和中間結果。這些可用于向 Cirrus 添加新的 ML 模型。為了簡化新算法的開發,Runtime 提供了一組線性代數庫。Cirrus 的初始版本使用外部線性代數庫如 Eigen 進行梯度計算。為了減少 Eigen 處理序列化和反序列化數據的時間,作者最終開發了自己的線性代數庫。對于數據訪問,Runtime 提供了一個由本地循環緩沖區支持的基于 minibatch 的迭代器,允許 worker 以低延遲訪問訓練 minibatch。此外,它還提供了一個高效的 API 來與分布式數據存儲進行通信。

無服務器計算的機器學習,出路在哪里?

圖 5. Cirrus Runtime。minibatch 是異步預取的,并在每個 Lambda 的內存中本地緩存(取決于使用的 Lambda 的大小)。將梯度異步發送至參數服務器,每次迭代模型同步從參數服務器中進行檢索。

作者給出了 Cirrus 在不同階段的詳細工作方式。

(1)數據加載和預處理。Cirrus 假設訓練數據存儲在一個全局存儲中,比如 S3。因此,使用 Cirrus 的第一步就是將數據集上傳到云端。用戶將數據集的路徑傳遞給系統,然后由系統負責解析和上載數據集。在此過程中,Cirrus 將數據集從其原始格式(如 csv)轉換為二進制格式。這種壓縮消除了在訓練和超參數調優階段進行反序列化的需要,這有助于減少 Lambda 工作進程中的計算負載。其次,Cirrus 生成數據集大小相似的分區,并將其上傳到 S3 存儲桶(S3 Bucket)。

Cirrus 還可以應用變換(Transformations)來提高模型的性能。例如,對于 Cirrus 實現的異步 SGD 優化方法,對數據集中的特征進行規范化處理能夠提高訓練的效果。對于這些 transformations,Cirrus 啟動了一個大型 Map Reduce 作業:每個輸入分區一個 worker。在 map 階段,每個 worker 計算其分區的統計信息(例如,平均值和標準差)。在 reduce 階段,這些局部統計信息被聚合以計算全局統計信息。在最后的映射階段,worker 轉換每個分區樣本,給出最終的每列統計信息。對于大型數據集,map 和 reduce 階段會跨大量 worker 和列來聚合每列的統計信息。這會造成每秒生成大量新的寫操作和讀操作,而超出了 S3 支持的事務吞吐量。基于這個原因,作者使用 Cirrus 的低延遲分布式數據存儲來存儲映射的中間結果,并減少了計算量。

(2)模型訓練。Cirrus 使用分布式 SGD 算法進行模型訓練。在訓練期間,worker 運行 Lambda 函數,并迭代計算梯度步長。每個梯度計算需要兩個輸入:一個 minibatch 和最新的模型。minibatch 是 Cirrus 的運行時通過迭代器從 S3 獲取的。因為迭代器在工作內存中緩沖 minibatch,所以檢索 minibatch 的延遲非常低。使用數據存儲 API(get_sparse_model_X)從數據存儲中同步檢索最新的模型。對于每個迭代,每個 worker 都計算一個新的梯度。然后將此梯度異步發送到數據存儲(send_gradient_X)以更新模型。

(3)超參數優化。超參數優化是一種模型參數的搜索方式,該模型參數能夠保證生成最佳準確度。典型的做法是在多維參數空間上執行網格搜索。搜索可以是暴力破解(Brute-force)搜索或自適應搜索。常見的做法是讓網格搜索完整地運行,然后對結果進行后處理,以找到最佳配置。這是一種代價高昂的資源浪費。Cirrus 通過提供超參數搜索儀表板(Hyperparameter search dashboard),來解決這種超時過度配置問題(over-provisioning over time)。Cirrus 超參數儀表板提供了一個統一的界面,用于監控模型隨時間變化的損失收斂情況。它允許用戶選擇單個損失曲線并終止相應的訓練實驗。因此,Cirrus 提供了:啟動超參數搜索的 API 和執行后端;監控模型精度收斂的儀表板;終止單個調優實驗的能力,并節省了過度配置成本。

在文獻 [2] 工作的基礎上,Cirrus 為 ML 用戶提供了一個輕量級的 Python API。作者同樣給出了一個例子來展示這個 API 的功能。如圖 6 所示,這個 API 與圖 1 中給出的文獻 [2] 中的 API 幾乎相同。區別在于本文已經將 Cirrus 封裝為模塊“cirrus”,可直接在 python 中進行 import。

無服務器計算的機器學習,出路在哪里?

圖 6. Cirrus API 示例。Cirrus 支持 ML 開發工作流的不同階段:(a)預處理,(b)訓練,和(c)超參數調優。

作者利用稀疏邏輯回歸任務對比 Cirrus 和兩個專門用于基于 VM 的 ML 訓練框架:TensorFlow[6]和 Bosen[4]。TensorFlow 是一個用于 ML 計算的通用數據流引擎。Bosen 是一個分布式和多線程參數服務器,由 CMU 開發 Petuum 商業化,它針對大規模分布式集群和機器學習算法的陳舊更新進行了優化。邏輯回歸是計算任何給定樣本屬于兩個感興趣的類的概率問題。本文實驗中作者計算網站廣告被點擊的概率,并利用時間函數評估學習收斂性。使用 Criteo 顯示廣告數據集[7]。這個數據集包含 45M 個樣本,大小為 11GB。每個樣本包含 13 個數字特征和 26 個分類特征。在訓練之前,對數據集進行了歸一化處理,將分類特征哈希為一個大小為 2^20 的稀疏向量。為了評估 Bosen,作者使用 1、2 和 4 個 m5.2xlarge 亞馬遜 AWS 實例(每個實例有 8 個 CPU 和 32GB 內存)。對于 Bosen 實驗,作者將數據集分區到所有機器上。為了評估 Cirrus,作者使用 Amazon AWS Lambda 作為 worker,m5.large 實例(2 個 CPU,8GB 內存,10Gbps 網絡)作為參數服務器,AWS S3 存儲用于訓練數據和定期備份型。作者報告了嘗試兩個系統的學習率范圍后得到的最佳結果。對于 Bosen,只改變學習率和工人數量。所有其他配置參數都保留默認值。

圖 7a 顯示了不同數量的服務器(對于 Bosen)和 AWS Lambda(對于 Cirrus)在一段時間內實現的邏輯測試損失。通過對一個包含 50K 樣本的數據集上的訓練模型評估以得到損失值。作者發現,Cirrus 的收斂速度明顯快于 Bosen。Bosen 的性能因為 worker 相互競爭共享本地緩存受到影響,該緩存在將梯度發送到參數服務器之前聚合梯度。這種設計最終導致了 Bosen 收斂速度較慢。在圖 7b 中,作者使用相同的數據集和相同的預處理步驟將 Cirrus 與 TensorFlow 進行了比較。同樣地,Cirrus 性能優于 TensorFlow。

圖 7c 中的實驗對比的是 Cirrus 和 Spark 完成協同過濾任務的性能,該實驗中使用的是 Netflix 數據庫[8]。由圖 7c,Cirrus 比 Spark 收斂得更快,測試損耗更低。此外,作者還觀察到 Spark 的 ALS 實現受到昂貴的 RDD 開銷的影響,因為 Spark 需要將整個數據集加載到內存中。這導致 Spark 花了超過 94% 的時間來做與訓練模型不直接相關的工作。相比之下,Cirrus 從 S3 連續向 worker 流式傳輸數據,這使得他們可以立即開始計算。

無服務器計算的機器學習,出路在哪里?

圖 7. (a) Bosen 和 Cirrus 之間不同設置的時間損失比較。Bosen 達到的最佳損失為 0.485,Cirrus 達到最佳損失的速度至少快了 5 倍(200 秒 vs 1000 秒)。與最先進的 ML 訓練框架相比,Cirrus 可以在一個或兩個 Lambda 的壽命內(300-600 秒)更快地收斂,并且損失更低。(b) Tensorflow Criteo_tft 基準和 Cirrus 的收斂與時間曲線。Tensorflow 是在 32 核節點上執行的,Cirrus 在 10 個 Lambda 中運行。(c) 運行 Netflix 數據集時,Spark (ALS)和 Cirrus 的 RMSE 隨時間變化曲線。Spark 在運行 Netflix 數據集時,前 4 分鐘處理數據,并在 ALS 的 5 次迭代中收斂(RMSE=0.85)后終止。Cirrus 能夠更快收斂到較低的 RMSE(0.833)。

圖 8 中的實驗驗證的是 Cirrus 的可擴展性(Scalability)。通過設計該系統以實現 3 個維度的擴展:用 S3 存儲訓練數據,用 Lambda 計算,以及用分布式參數服務器共享內存,來實現擴展性。

存儲擴展性:Cirrus 通過將 S3 中的訓練數據集分割成中等大小的對象來解決這個問題。作者使用 10MB 的對象,因為作者發現這個大小可以實現良好的網絡利用率,同時對于最小尺寸的 Lambda 來說也足夠小。通過使用大型對象,減少了每秒的請求數量。因此,當每個 worker 從 S3 消耗 30MB/s 的訓練數據時,能夠將 S3 的吞吐量線性擴展到 1000 個 Cirrus workers(圖 8a)。

計算擴展性:由圖 8b,沒有模型和參數的同步得情況下 Cirrus 可以通過并行傳輸輸入訓練數據和計算梯度來實現線性計算可伸縮性。

參數服務器擴展性:在參數服務器層面,主要挑戰來自于每個虛擬機 VM 有限的網絡帶寬,以及更新模型和 worker 請求服務器所需的計算。Cirrus 通過 1)模型分片,2)稀疏梯度 / 模型,3)數據壓縮,4)異步通信來解決這個問題。Cirrus 實現了線性可擴展性,最高可達 600 個 worker(圖 8c)。

無服務器計算的機器學習,出路在哪里?

圖 8. AWS 存儲(GB / 秒)、AWS 無服務器計算(梯度 / 秒)和 Cirrus 數據存儲(樣本 / 秒)的可擴展性。每個 worker 消耗 30MB/s 的訓練數據。

最后,作者對比了專門的 ML 系統 PyWren 與 Cirrus。PyWren 是一個運行在無服務器 Lambda 上的 map-reduce 框架。它提供了可擴展至數千名 worker 的 map 和 reduce 原語。PyWren 的 Runtime 經過優化可以在 AWS Lambda 上運行,AWS Lambda 也是本文用于 Cirrus 實驗的無服務器平臺。作者在實驗中對 PyWren 進行了優化,使其每次模型更新的平均時間提高了 700 倍(從 14 秒到 0.02),但其模型每秒更新次數仍然遠低于 Cirrus(圖 9b),并且收斂速度明顯慢于 Cirrus(圖 9a)。

無服務器計算的機器學習,出路在哪里?

圖 9. PyWren 和 Cirrus 在 10 個 Lambda 上運行時在稀疏邏輯回歸工作負載上的性能。由于結合了預取、在模型訓練迭代中重復使用 Lambda 以及通過 Cirrus 的快速數據存儲進行高效的模型共享,Cirrus 實現了 2 個數量級的模型更新數量增長。訓練數據預取解決了 S3 的高訪問延遲問題,從而使更新速度增加了 10 倍 / 秒。

2.3、Distributed Machine Learning with a Serverless Architecture [9]

本文作者介紹了一個完全基于無服務器架構的分布式機器學習新框架:SIREN。SIREN 由本地客戶端和無服務器云平臺(例如 Amazon Lambda)組成,前者使用深度強化學習(Deep Reinforcement Learning,DRL)agent 進行資源調度決策,后者根據這些調度決策為 ML 訓練作業加載無狀態函數(Stateless Functions)。SIREN 的完整結構框架如圖 10。

無服務器計算的機器學習,出路在哪里?

圖 10.SIREN 結構

首先,將一個代碼包部署到無服務器云平臺中,其中包含用戶定義的 ML 模型及其所依賴的庫。然后,根據初始資源方案(即函數的數量和內存大小)加載無狀態函數群,進行基于 SGD 的第一個 epoch 訓練。在第一個 epoch 結束時,收集作業的函數狀態和統計數據,并以狀態(States)的形式反饋給本地客戶端的 DRL agent,DRL agent 將采取行動為下一個 epoch 做出資源調度決策。SIREN 會隨著訓練作業的 epoch 推進自適應調整資源調度決策:在不同的 epoch 中,可以啟動不同數量、不同內存配置的函數。

SIREN 采用的是 SGD 算法,使用 mini-batches 并在多個 Lambda 函數上運行。每個 Lambda 函數的作用就類似于傳統參數服務器架構中的 worker。SIREN 與參數服務器架構的一個主要區別是,在 SIREN 中不存在參數服務器來處理模型參數更新。相反,數據和模型都存儲在一個共同的數據存儲中(例如 Amazon S3),所有函數都可以訪問。每個函數從公共存儲中讀取當前模型,根據 mini-batches 訓練數據計算梯度,然后直接用新計算的梯度更新公共存儲中的模型。因此,整個架構是無服務器的。在 SIREN 中,作者提出了一種混合同步并行(Hybrid synchronous parallel,HSP)計算模式。如圖 11 所示,在每個 epoch 內,所有的函數都可以異步更新模型,同時在每個 epoch 結束時施加一個同步屏障(Synchronization barrier),以便完成下一個 epoch 的資源調度。

已知 epoch 為 t,第 k 個 mini-batch 為Ξ_t,k,更新模型為:

無服務器計算的機器學習,出路在哪里?

在 epoch t-1 結束時的模型ω與ω_t,0 相同。HSP 在無服務器架構中是高效的,因為加載的函數是同質的,從而導致每個 epoch 的同步代價都很低。在無服務器云平臺中,調用和終止函數也是輕量級的。

無服務器計算的機器學習,出路在哪里?

圖 11. 無服務器云上的混合同步并行(HSP)處理。

作者使用 Python 代碼實現了 SIREN,支持 AWS Lambda 之上的 ML 模型訓練,并全面支持 MXNet APIs。機器學習開發人員可以在 SIREN 上運行他們的傳統 MXNet 項目,而無需重構現有代碼。如圖 10 所示,SIREN 包括三個主要部分:(1)封裝 MXNet 機器學習庫的代碼包;(2)用 AWS SDK boto3 構建本地客戶端,調用并管理 AWS Lambda 中的無狀態函數;(3)用 TensorFlow 實現 DRL agent,進行動態資源配置決策。此外,還對 AWS Lambda 進行了一系列約束,以保證無狀態函數的輕量級和可移植性。

由于 AWS Lambda 的編程 runtime 不支持原生的 ML 訓練算法,作者在代碼包中引入了一部分 MXNet ML 庫。在 AWS Lambda 上,代碼包大小限制為 250 MB,這使得直接將任何現成的 ML 庫(如 MXNet、TensorFlow)加載到 AWS Lambda 上都是不可行的。為了縮小 MXNet 代碼包的大小,作者用不同的編譯選項組合重新編譯了 MXNet 源代碼,并排除了無服務器云中不必要的編譯選項。例如,禁用了 USE_CUDA、USE_CUDNN 和 USE_OPENMP 等選項。

在 AWS Lambda 上,單個函數的計算能力也受到限制:要求每個 Lambda 函數最多在 300 秒內執行完畢,最大內存大小為 3GB。但是,由于 AWS Lambda 支持每個 AWS 賬戶中多達 3000 個函數并發執行,因此 SIREN 通過使用大量 Lambda 函數并行化 ML 訓練工作負載實現了高度的并行性。

作者提出了一種深度強化學習(Deep reinforcement learning,DRL)技術,用于完成 SIREN 中的動態資源部署。強化學習 (RL) 是一種經驗驅動的方法,agent 通過與動態環境的交互以及執行行動獲得獎勵來學習如何在動態環境中表現。DRL 利用深度神經網絡 (Deep neural network,DNN) 來解決 RL 問題。agent 觀察來自動態環境的各種噪聲信號,這些信號被稱為狀態(state),并將這些狀態反饋給 DNN 由其產生動作。agent 在環境中采取動作并獲得獎勵,而獎勵又被用來更新 DNN 中的參數,以做出更好的決策。DRL 在一個閉環中工作以改善決策,其最終目標是使總獎勵最大化。

作者考慮在一個有 M 個樣本的數據集上訓練 ML 工作負載,總獎勵預算為 B。如果達到一定的損失值 L 或者總獎勵預算 B 用完,則訓練終止。在任何一個 epoch t,調度器將對并行調用的函數數量(用 n_t 表示)以及每個函數的內存大小 m_t 做出判斷。令 f_t,i 表示在第 t 個 epoch 加載第 i 個活躍函數,如圖 11 所示。需要注意的是,如果函數 i 已經到了它的運行壽命,則會調用一個新的函數來代替它,且仍然用 f_t,i 來表示,所以在 epoch t 中總會有 n_t 個函數在并發執行。在每一個函數 f_t,i 中,重復計算一個新的 mini-batch 數據的聚合梯度,并根據 HSP 模式下的 SGD 更新模型參數。

在 epoch t 中,假設函數 f_t,i 花費一個完整周期(P^F)_t,i 來獲取 mini-batch 數據,(P^C)_t,i 計算梯度,(P^U)_t,i 更新模型參數。函數 i 在 epoch t 的完整執行時間為:

無服務器計算的機器學習,出路在哪里?

epoch t 在 HSP 的全部持續時間為 P_t=max_i(P_t,i)。在 epoch t 結束時,ML 任務的損失值更新為 l_t。

無服務器云根據函數執行時間和函數內存大小向用戶收費。令 c 表示使用 1GB 內存執行一個函數一秒鐘的單價。一個 epoch t 的總花費為:

無服務器計算的機器學習,出路在哪里?
無服務器計算的機器學習,出路在哪里?

而 ML 任務的總的獎勵成本為:

無服務器計算的機器學習,出路在哪里?

其中,T 表示 epoch 的總數。本文所述任務的目標是最小化作業完成時間,即在一定獎勵預算 B 約束下解決以下優化問題:

無服務器計算的機器學習,出路在哪里?

在每個 epoch t 開始時,DRL agent 決定資源配置計劃 (n_t, m_t),即 DRL 任務中的動作 action,具體如圖 12。衡量動作(n_t, m_t) 有效性的方法是在每個 epoch t 的結束進行數字 reward 量化計算。計算的依據是這個 epoch 持續的時間 P_t 和任務結束時預算是否透支。

無服務器計算的機器學習,出路在哪里?

圖 12.DNN 策略表示的 DRL。

狀態(State):在本文所描述的問題中,epoch t 的狀態表示為:

其中,l_t 表示 epoch t 的損失值,(P^F)_t、(P^C)_t、(P^U)_t 分別表示獲取、平均計算和平均模型參數更新時間,P_t 為 epoch 的執行時間。u_t 和ω_t 分別表示平均內存和 CPU 的利用情況,b_t 為剩余預算。

動作(Action):在本文所描述的問題中,動作表示為 a_t=(n_t, m_t)。n_t 表示激活的函數數量,m_t 表示每個函數的內存大小。DRL agent 根據策略選擇操作,策略定義為給定當前狀態下整個操作空間的概率分布π(a | s)。作者使用策略梯度方法,通過參數θ的函數來近似策略π(a | s)。因此,策略π可以寫成π(a | s, θ),其中θ是要學習的參數。將策略π定義為實值空間的高斯概率密度:

無服務器計算的機器學習,出路在哪里?

基于條件概率π(a_t | s_t-1, θ)確定動作 a_t。然后,在一個大的離散作用空間上學習概率質量函數的問題就轉化為在一個二維連續空間中尋找參數 (μ(s,θ),σ(s,θ)) 的問題。

獎勵(Reward):在本文所描述的問題中,每個 epoch 結束時獎勵定義為:r_t=-β P_t,其中β為正則化系數。epoch t 的時間越長,agent 得到的獎勵就越少。最后一個 epoch T 的獎勵為:

無服務器計算的機器學習,出路在哪里?

換句話說,如果作業成功停止,即在不超出預算 B 的情況下滿足收斂閾值 L,則向 agent 分配正 C 的獎勵。否則,如果作業失敗,即在用完預算 B 之前還沒有收斂,則給獎勵賦值為負 C。在 DRL 中,agent 學習的是累計折扣獎勵:

其中,γ ∈ (0, 1]為未來折扣獎勵因子。在整個 DRL 訓練過程中,上式中的目標函數引導著 agent 找到最優的估計值。

作者模擬了一個無服務器的云環境,運行由 DRL agent 控制的 mini-batch SGD 算法。作者使用 OpenAI Gym 實現模擬環境(https://gym.openai.com (https://gym.openai.com/)),OpenAI Gym 是一個用于評估強化學習算法的開源接口。實驗目的是驗證與傳統的網格搜索(Grid Search)基線方法所找到的最優(靜態)策略相比使用 SIREN 進行調度的優勢。作者比較了在 AWS Lambda 上使用 SIREN 和在 EC2 集群上使用 MXNet 訓練 ML 作業的完成時間和成本。具體實驗中選擇了三種類型的 EC2 實例來構建測試集群:m4.large(2 vCPU,8GB 內存)、m4.xlarge(4 vCPU,16GB 內存)和 m4.2xlarge(8 vCPU,32GB 內存),每小時分別收費 0.1 美元、0.2 美元和 0.4 美元。

圖 13 給出了 SIREN 與網格搜索最佳函數數量的比較實驗。圖 13(a)比較了通過網格搜索和 SIREN 實現的訓練時間。與網格搜索相比,SIREN 在預算為 300 美元的情況下最多可減少 36% 的訓練時間。如圖 13(b)所示,網格搜索列舉了不同預算下不同數量的函數的總獎勵情況。SIREN 能夠根據經驗動態調整函數數量。圖 13(c)給出了分配給每個 epoch 的函數數量。在前幾個 epoch 中,SIREN 啟動了大量的函數以快速降低損失值;在后幾個 epoch,agent 減少了函數數量以節省成本。SIREN 的 DRL agent 通過與模擬的無服務器云的迭代交互進行在線訓練。圖 13(d)中的學習曲線表明,agent 通過探索不同數量的函數來學習最大化總獎勵。agent 的訓練在大約 200 次迭代之后完成。

無服務器計算的機器學習,出路在哪里?

圖 13. SIREN 與網格搜索最佳函數數量比較。

無服務器計算的機器學習,出路在哪里?

圖 14. 通過 SIREN 和 EC2 上的 MXNet 對 MNIST 數據集訓練 LeNet。

圖 14 的實驗對比 SIREN 和 EC2 上的 MXNet。圖 14(a)顯示了使用 12 個 EC2 集群和使用 SIREN 訓練 LeNet 的完成時間和相應的成本。由于 EC2 集群的異質性,EC2 上的成本與訓練完成時間呈非線性關系。例如,m4.xlarge×6 集群和 m4.2xlarge×6 集群幾乎在同一時間完成訓練,但后者產生的成本是前者的兩倍。相比之下,SIREN 通過更多的投資縮短了完成時間。圖 14(b)顯示,SIREN 動態調整每個訓練 epoch 的函數及其內存。當函數數量減少時,每個函數收到的訓練數據分區更大,需要更大的內存來處理數據分區。SIREN 中的 DRL agent 是通過與 AWS Lambda 在線交互進行訓練的。從圖 14(c)中的學習曲線可以看出,經過 150 次左右的迭代,DRL agent 的訓練已經完成。

進一步的,作者在 m4.2xlarge instances 的集群上訓練 LeNet、CNN 模型和線性分類模型并確定相應的成本。然后,在成本相同的情況下在 m4.2xlarge×8 集群上用 SIREN 訓練同樣的模型。圖 15 中的實驗數據顯示,與相同成本的 EC2 集群相比,SIREN 使用這些模型分別減少了 40%、39.4% 和 44.3% 的訓練時間。

無服務器計算的機器學習,出路在哪里?

圖 15. 不同模型相同成本預算下 SIREN 與 EC2 的比較。

2.4、Serverless Linear Algebra [10]

無服務器計算的機器學習,出路在哪里?

本文作者構建了 NumPyWren:一個基于無服務器編程模型的線性代數系統,以及 LAmbdaPACK:一個為高度并行線性代數算法的無服務器執行而設計的領域特定語言。相關工作發表在 SoCC’20 中。

無服務器計算(例如,AWS Lambda、Google Cloud Functions、Azure Functions)是一種編程模型,云提供商在其中管理服務器同時動態管理資源分配。通常,無服務器平臺計算會公開一個有時間限制的、無狀態的 FaaS API,以及一個管理程序狀態的對象存儲系統。對于能夠清晰地分離程序狀態和邏輯的應用程序設計人員來說,無服務器平臺提供了對大型計算能力的即時訪問,而無需應對復雜集群部署的開銷。

本文所研究的內容:密集線性代數(Dense linear algebra)極大地受益于現有的以服務器為中心的數據中心。現有的分布式線性代數框架可以通過利用局部性、網絡拓撲和單個服務器內的資源緊密集成來完成高性能計算。在這樣的背景下作者提出這樣一個問題:這些線性代數算法能否成功地移植到一個分散數據中心中?也就是說,我們能否在無服務器編程模型中實現與基于 MPI 的分布式線性代數框架相當的性能?

本文作者構建了 NumPyWren,一個在無服務器架構上完成線性代數任務的系統。NumPyWren 執行使用 LAmbdaPACK 編寫的程序,LAmbdaPACK 是作者構建的一個高級 DSL,可以簡潔地表示任意基于分片的線性代數算法。NumPyWren 通過無狀態函數執行來執行大規模密集線性代數程序。通過對中間語言 LAmbdaPACK 的分析,作者最終證明了分散式無服務器計算模型(Disaggregated serverless computing model)可以用于具有復雜通信程序的計算密集型程序。

NumPyWren 解決的是類似 Cholesky 分解的線性代數問題。考慮求解線性方程 Ax=b 的問題,其中 a 是對稱正定矩陣。我們可以先把 a 分解成兩個三角形矩陣 a=LL^T,然后解兩個相對簡單的 Ly=b 和 L^T x=y 得到解 x。從這個過程中可以看出,分解是該求解問題中計算代價最高的步驟。Communication-Avoiding Cholesky 是一個很好的計算 Cholesky 分解的算法。該算法將矩陣分成若干塊,并得出一個計算順序使總數據傳輸量最小。具體算法如下:

無服務器計算的機器學習,出路在哪里?

如圖 16,在 outer loop(j)的每一步中,算法首先計算單個塊 Ajj 的 Cholesky 分解(圖 16(a))。這個結果用來更新由 Aij 下面的列塊組成的 "面板(panel)"(圖 16(b))。最后,第 j 列右邊的所有區塊都會根據各自的位置,通過索引更新面板(圖 16(c))。通過向下移動對角線重復這一過程(圖 16(d))。

無服務器計算的機器學習,出路在哪里?

圖 16. 并行 Cholesky 分解的前 4 個時間步驟:0)對角塊 Cholesky 分解,1)并行列更新,2)并行子矩陣更新,3)(后續)對角塊 Cholesky 分解。

作者針對 Algorithm 1 提出了兩點問題。首先,作者認為 Algorithm 1 在執行過程中展現出了動態并行性。外循環(Outer loop)由三個不同的步驟組成,具有不同的并行度,從 O(1)、O(K)到 O(K2),其中 K 是每個步驟的封閉子矩陣大小。其次,該算法在這三個步驟之間存在細粒度的依賴關系,無論是在一個迭代內還是在多個迭代之間。由此,作者提出了本文所考慮的工作,即:實現適應可用的并行化,作者通過將程序分解為可并行運行的細粒度執行單元來實現這一點。為了在無狀態環境中實現這一點,作者建議以分散的方式執行依賴性分析。將描述程序控制流的全局依賴圖分發給每個 worker。然后,每個 worker 根據其在全局任務圖中的當前位置,對其下游依賴關系進行本地推理。

首先,我們介紹本文提出的 LAmbdaPACK:一種用于實現并行線性代數算法的特定語言。LAmbdaPACK 是生成和使用矩陣塊(Tiled matrices)的命令式程序。這些程序可以對標量值執行基本的算術和邏輯運算。它們不能直接讀取或寫入矩陣值;相反,所有實質性的計算都是通過調用矩陣塊上的本機內核來執行的。矩陣塊由索引引用,LAmbdaPACK 程序的主要作用是對內核調用排序,并計算每個調用的分塊索引。LAmbdaPACK 包括簡單的 for 循環和 if 語句,但是沒有遞歸,只有從 LAmbdaPACK 到內核的一級函數調用。每個矩陣塊索引只能寫入一次,這是許多函數式語言的共同設計原則。LAmbdaPACK 中的原語功能強大,包括 Tall Skinny QR(TSQR)、LU、Cholesky 和奇異值分解等等。LAmbdaPACK 的示例性描述如圖 17 所示。

無服務器計算的機器學習,出路在哪里?

圖 17. LAmbdaPACK 語言的示例性描述。

關于 LAmbdaPACK 的算法分析主要包括兩個階段。由于原始未壓縮的 DAG 非常大,其節點數可能會隨著 Cholesky 或 QR 等算法的輸入大小呈立方級增長,因此,第一階段的任務是分析程序并提取任務的壓縮 DAG。DAG 中的每個任務對應一個數組寫入,我們還需提取執行此任務所需的內核計算和數組讀取。由于每個數組讀取都有一個唯一的上游寫入任務,因此此過程是可跟蹤處理的。第二個階段發生在 runtime,在執行任務之后,能夠動態發現下游任務。使用當前循環變量綁定的信息來查詢下游任務的壓縮 DAG。圖 18 和圖 19 分別給出了 LAmbdaPACK 的 DAG 和程序示例。

無服務器計算的機器學習,出路在哪里?

圖 18.LAmbdaPACK DAG 示例。

無服務器計算的機器學習,出路在哪里?

圖 19. LAmbdaPACK 程序示例。

LAmbdaPACK 中沒有并行原語,而是 LAmbdaPACK runtime 通過靜態分析程序來推斷底層依賴關系圖。為了并行執行程序,作者從程序產生的依賴結構構造了一個內核調用的 DAG。作者借用并擴展了循環優化技術(loop optimization),將 LAmbdaPACK 程序轉換為隱式有向無環圖(Implicit DAG)。將程序 DAG 中的每個節點 N 表示為一個元組(line_number, loop_indices)。利用這個信息,可以執行程序迭代空間中的任何語句。

接下來,作者解決推導 DAG 中特定節點的下游依賴關系問題。作者提出在 runtime 處理依賴性分析:每當一個存儲位置被寫入時,確定從同一存儲位置讀取的 N(所有行,所有循環索引)中的表達式。每當一個存儲位置被寫入時,我們確定從同一存儲位置讀取 N(所有行,所有循環索引)中的表達式。作者將約束建模為一個方程組。假設單個線性代數算法中的行數必然很小,而程序迭代空間通常非常大。當數組僅由循環變量的仿射函數索引時,即形式為 ai+b 的函數,其中 i 是循環變量,a 和 b 是編譯時已知的常數,則可以使用循環優化來有效地查找特定節點的依賴關系。

如圖 19 中的程序示例,如果在 runtime 一個 worker 正在執行程序的第 7 行,i=0、j=1 和 k=1,以查找下游依賴項,則分析器將掃描這 7 行中的每一行,并計算是否存在一組有效的循環索引,以便在程序中的該點讀取 S[1,1,1]。如果是這樣,那么元組(line_number, loop_indices)定義了該任務的下游依賴項,并確定為當前任務的子任務。為了便于訪問和開發,作者將 LAmbdaPACK 嵌入 Python 中。由于大多數 LAmbdaPACK 調用優化的 BLAS 和 LAPACK 內核,因此使用高級解釋語言的性能損失很小。LAmbdaPACK 詳細流程見 Algorithm2。

無服務器計算的機器學習,出路在哪里?

然后,我們介紹本文提出的 NumPyWren 框架。NumPyWren 框架包括五個獨立可擴展的主要組件:runtime 狀態存儲、任務隊列、輕量級全局任務調度器、無服務器計算 runtime 和分布式對象存儲。圖 20 展示了 NumPyWren 框架組件。

無服務器計算的機器學習,出路在哪里?

圖 20. NumPyWren 執行框架的體系結構,具體為 6x6cholesky 分解期間的 runtime 狀態。

任務排隊(Task Queue):客戶端進程將需要執行的第一個任務排隊到任務隊列中。任務隊列是一個發布 - 訂閱樣式的隊列,它包含 DAG 中的所有節點,這些節點的輸入依賴關系都已滿足并準備好執行。

執行器配置(Executor Provisioning):任務隊列的長度由配置者(Provisioner)監控,provisioner 管理計算資源以匹配執行期間的動態并行性。在第一個任務排隊后,provisioner 啟動一個執行器(executor),并根據任務隊列大小維護活動 executor 的數量。由于 provisioner 的角色只是輕量級的,所以它也可以作為 “無服務器” 云函數定期執行。

任務執行(Task Execution):執行器管理 NumPyWren 任務的執行和調度。一旦執行器準備就緒,它就輪詢任務隊列以獲取可用的任務,并執行任務中的編碼指令。大多數任務涉及從對象存儲讀取輸入和將輸出寫入對象存儲,以及執行 BLAS/LAPACK 函數等。假定對象存儲是一個分布式持久存儲系統,它支持單個密鑰的先讀后寫一致性。使用一個帶有單一靜態賦值語言的持久對象存儲,有助于設計容錯協議。當執行器接近無服務器系統的 runtime 限制時(AWS Lambda 為 900),執行器自動終止。如果有必要的話,provisioner 將負責雇傭新 worker。容錯協議能夠實現即使工作進程超過 runtime 限制或是在執行過程中被云提供商殺死,程序仍能在有效狀態下運行。

Runtime 狀態更新(Runtime state update):一旦任務執行完成并且輸出被持久化,執行器就會更新 runtime 狀態存儲中的任務狀態。runtime 狀態存儲跟蹤整個執行的控制狀態,并且需要支持每個任務的快速更新。如果已完成的任務具有 “ready” 子任務,則執行器會將該子任務添加到任務隊列中。狀態存儲的原子性保證了每個子任務都能夠被調度。這個使用執行器執行調度的過程實現了高效、分散、細粒度的任務調度。由于計算和存儲的分離,NumPyWren 中的容錯非常容易實現。因為對對象存儲的所有寫入都是持久的,所以在任務完成后都不需要重新計算。

任務租用(Task Lease):在 NumPyWren 中,所有掛起的和可執行的任務都存儲在一個任務隊列中。保持一個不變量,即任務只有在完成后才能從隊列中刪除(例如,runtime 狀態存儲已更新,輸出持久化到對象存儲)。當一個 worker 獲取一條任務,這個 worker 就獲得了該任務的租約(lease)。在租用期間,該任務被標記為不可見,以防止其他 workers 獲取這條任務。

故障檢測和恢復(Failure Detection and Recovery):在正常操作期間,worker 將使用后臺線程續訂任務租約,直到任務完成。如果任務完成,worker 將從隊列中刪除該任務。如果 worker 失敗,它將無法再續訂租約,并且該任務將對任何可用的 worker 可見。因此,故障檢測在租約到期時發生,恢復時間由租約長度決定。

垃圾收集(Garbage collection):由于 NumPyWren 將所有中間狀態存儲到一個持久對象存儲區,因此在不再需要時清除狀態是非常必要的。但是,由于在對象存儲中存儲字節的成本極低,與處理 TB 級中間狀態問題的計算成本相比,在程序結束時進行垃圾收集就足夠了。使用與程序相關聯的唯一 id 標記對象存儲中單個程序執行的所有分配。在程序執行終止后,NumPyWren 通過啟動一組并行的無服務器任務來異步清理對象存儲,以清理與給定程序 id 關聯的所有對象。

自動縮放(Autoscaling):與傳統的無服務器計算模型(每個新任務分配一個新容器)不同,NumPyWren 中的任務調度和 worker 管理是解耦的。這種解耦允許自動擴展計算資源,以實現更好的性價比權衡。在 NumPyWren 中,作者采用了一個簡單的自動縮放啟發式算法,能夠在保持較低作業完成時間的同時獲得很好的利用率。

作者對 4 種線性代數算法:矩陣乘(Matrix Multiply,GEMM)、QR 分解(QR Decomposition,QR)、奇異值分解(SingularValue Decomposition,SVD)、Cholesky 分解(Cholesky Decomposition,Cholesky)進行了實驗評價。對于這四種算法,作者將它們與最先進的 MPI 實現進行比較。其中 Cholesky,GEMM 和 SVDwe 使用 ScaLAPACK 實現,ScaLAPACK 是一個工業級 Fortran 庫,專為高性能、分布式密集線性代數而設計。對于 QR 分解,則使用了 communication-avoiding QR 分解算法的優化實現。NumPyWren 實現大約有 6000 行 Python 代碼,作者將其構建在 Amazon web 服務(AWS)平臺上。對于 runtime 狀態存儲,使用的是 Redis--- 一個由 ElasticCache 提供的鍵值存儲。盡管 ElasticCache 是一種配置的(而不是“無服務器”)服務,但作者發現使用一個實例就足以滿足所有工作負載。此外,作者還發現,可以用托管供應商提供的鍵值存儲(如 DynamoDB)來替換 Redis,但性能略有下降。作者將 Amazon 的簡單隊列服務(Simple queue Service,SQS)用于任務隊列,Lambda 或 EC2,使用 Amazon S3 作為遠程對象存儲。

作者對實驗進行了一些特殊的設備選擇、環境選擇或參數選擇。首先,由于不能很容易地控制并發 Lambda 執行的數量或 AWS 提供的硬件類型,出于實驗控制的原因,作者通過模仿 EC2 上的無服務器 runtime 來進行大部分評估以便與其他系統進行比較。其次,本文的 Lambda 模擬基于 PyWren 框架中的“獨立模式”。PyWren 使用一個單獨的 SQS 隊列來模擬 Lambda 作業隊列,并使用有時間限制的進程來模擬短函數調用。在控制底層硬件(AVX、NIC 等)時,使用 SQS 會導致不確定性。然后,目前 Lambda 的定價是 EC2 現貨價格的 10 倍,這使得本文的大規模實驗無法在 Lambda 上進行。作者通過實驗對比發現,在 EC2 上運行模擬的無服務器環境與在 AWS Lambda 上運行的性能差別最小。最后,模擬環境中的實驗還允許修改某些在真實無服務器環境中用戶無法控制的系統參數,如函數超時等。

表 3 中給出針對四種密集線性代數方法 NumPyWren 與 MPI 的端到端性能比較。作者對比了在完全相同的硬件條件下(8 個 r4.16xlarge 實例中的 256 個物理核),處理大小為 256k(262144)的方陣時 MPI 和 NumPyWren 的性能。我們可以看到無服務器環境施加的限制導致的性能損失在 1.4x 到 1.6x 之間(按 wall-clock time 計算)。

無服務器計算的機器學習,出路在哪里?

表 3. 在具有 512 個虛擬核的集群上,在 N=256K 的方陣上運行時,不同算法的 MPI 與 NumPyWren 執行時間的比較。

在表 4 中,作者比較了 NumPyWren 和 MPI 使用的總核秒數(core-seconds)。對于 MPI,core seconds 是指核的總數乘以 wall-clock runtime。對于 NumPyWren,作者只計算“活動核(Active cores)”,因為空閑核是可以被其他任務利用的。作者通過在無服務器核啟動和冷卻計算過程中每個核的總計算時間中添加一個啟動延時γ來計算總核秒數。具體的,作者選擇γ=20s 以對系統進行保守的評估。對于 QR 和 Cholesky 這些具有可變并行性的算法,雖然 wall-clock time 相當,但作者發現 NumPyWren 使用的核秒數減少了 1.15 倍。對于 SVD,實驗中顯示出超過 3 倍的資源節省效果,不過,產生這種差異一部分是由于使用的 SVD 算法不同。然而對于具有固定數量的并行性的算法(如 GEMM),NumPyWren 中過多的通信會導致更高的資源消耗。

無服務器計算的機器學習,出路在哪里?

表 4. 在一個 256K 大小的方陣上運行算法的 MPI 與 NumPyWren 總 CPU 時間(以核秒為單位)比較。

三、文章小結

本文重點關注了基于無服務器計算的機器學習的最新研究進展。隨著云計算的不斷發展,開發人員對于按需執行或擴展的需求越來越強烈,越來越希望不去應對服務器或其它底層基礎設施,而是集中精力關注于自身應用的開發和調優。無服務器計算的 FaaS 和 BaaS 服務必將迎來更多的關注。但是,正如我們開篇提到的,機器學習的算法或模型中包含大量的參數、復雜的處理流程,是典型的“性能關鍵型應用”。針對機器學習這種要求復雜通信模式和工作負載的應用如何基于無服務器計算去工作仍然是一個有待研究的問題。

本文關注了三個研究小組的四篇研究論文。其中前兩篇文章提出了一種無服務器基礎設施和 ML 工作流的無服務器 ML 框架原型,并將其封裝為一個實現端到端管理的分布式 ML 訓練框架 Cirrus,可以直接調用使用。第三篇文章提出了一個基于無服務器架構的分布式機器學習新框架,以及一種深度強化學習技術,用于實現 SIREN 中的動態資源供應。作者還提出了一種能夠在無服務器架構中高效工作的 HSP 計算模式。最后一篇文章重點關注的是無服務器計算的模密集線性代數程序應用,作者提出了一個在無服務器架構上完成線性代數任務的系統 NumPyWren,通過對中間語言 LAmbdaPACK 的分析,作者最終證明了該分散式無服務器計算模型 NumPyWren 可以用于具有復雜通信程序的計算密集型程序。

在幾篇文章中,作者都通過實驗證明了幾種框架在執行機器學習任務時性能遠優于經典的基于粗粒度的 VM 集群的 ML 框架。盡管無服務器的機器學習具有敏捷、快速、可伸縮性等優點,但是它對機器學習的集成還處于初級階段,它自身也面臨著工具不全面、不成熟或者配置方式不統一等問題。隨著越來越多的研究人員關注,越來越多的應用開發提出成本節約和效率提高的需要,無服務器計算將迎來更快更好的發展。

 

責任編輯:張燕妮 來源: 機器之心Pro
相關推薦

2018-11-26 15:04:49

SDN網絡數據中心

2022-03-18 20:54:24

無服務器計算無服務器服務器

2022-01-05 09:28:31

無服務器計算服務器應用程序

2019-04-01 13:47:57

無服務器計算云服務

2019-04-30 10:27:46

無服務器云計算安全

2018-07-17 15:41:37

服務器虛擬化未來

2019-03-08 10:26:29

無服務器云計算德勤

2017-11-27 10:45:48

無服務器計算容器

2018-03-01 10:26:25

無服務器計算架構

2023-08-27 15:20:58

Serverless架構開發

2020-12-04 15:17:26

物聯網智慧城市技術

2023-07-05 08:00:45

架構

2018-02-28 11:19:41

服務器云計算公共云

2021-11-26 08:00:00

機器學習數據庫AWS

2013-08-01 00:00:00

開發企業級App出路

2017-07-10 15:17:11

無服務器計算云計算IT

2023-10-26 19:15:40

2022-07-01 13:41:32

無服務器容器企業

2020-10-10 07:00:00

無服務器計算容器

2018-02-24 10:15:36

無服務器容器云計算
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费国产网站 | 亚洲一区二区三区视频 | 人人人人爽 | av日韩在线播放 | 国产欧美一区二区三区久久人妖 | 亚洲精品视频免费观看 | 成人免费一区二区 | 免费视频久久 | 久久大| 欧美日韩一区二区视频在线观看 | 欧美一级视频 | 日韩欧美精品在线 | 国产综合av| 欧美xxxx黑人又粗又长 | 亚洲一区高清 | 日韩免费一级 | 91视频在线观看 | 国产精品99久久久久久www | 国产日韩一区二区三区 | 美女在线观看国产 | 中文字幕日韩av | 国产精品久久久久久久久久久久 | 请别相信他免费喜剧电影在线观看 | 亚洲人成在线观看 | 一区二区高清 | 欧美一级免费片 | 国产成人综合亚洲欧美94在线 | 精品欧美乱码久久久久久 | 特级做a爰片毛片免费看108 | 中文字幕福利视频 | www.久久国产精品 | 国产乱码精品1区2区3区 | 国产精品久久久精品 | 欧美一卡二卡在线观看 | 亚洲国产精品一区二区第一页 | 人人玩人人添人人澡欧美 | 网络毛片| 欧美精品久久久久 | 日韩在线精品强乱中文字幕 | 国产精品av久久久久久久久久 | 8x国产精品视频一区二区 |