深度學習調用TensorFlow、PyTorch等框架

深度學習調用TensorFlow、PyTorch等框架

一.開發目標目標

提供統一接口的庫,它可以從C++和Python中的多個框架中運行深度學習模型。歐米諾使研究人員能夠在自己選擇的框架內輕鬆建立模型,同時也簡化了這些模型的產品離子化。

支持TensorFlow、PyTorch、TorchScript和Keras等深度學習框架。

使用一個API從任何支持的框架運行模型,運行TensorFlow模型看起來就像運行PyTorch模型。

x = np.array([1, 2, 3, 4])

y = np.array([5, 6, 7, 8])

for model_path in [TF_ADDITION_MODEL_PATH, PYTORCH_ADDITION_MODEL_PATH]:

# Load the model

neuropod = load_neuropod(model_path)

# Run inference

results = neuropod.infer({"x": x, "y": y})

# array([6, 8, 10, 12])

print results[“out”]

這樣做的一些好處包括:

所有的推理代碼都是框架不可知的。

如果有必要,可以輕鬆地在深度學習框架之間切換,而無需更改運行時代碼。
避免使用C++ libtorch庫API和C/C++
TFAPI的學習曲線。任何神經網絡模型都可以從C++和Python(甚至是尚未轉化爲TrCHcript腳本的PyTrac模型)運行。

定義問題API

這讓更關注正在解決的問題,而不是用來解決它的框架。

例如,如果爲2d對象檢測定義了問題API,則實現該API的任何模型都可以重用該問題的所有現有推理代碼和基礎結構。INPUT_SPEC = [

# BGR image

{"name": "image", "dtype": "uint8", "shape": (1200, 1920, 3)},

]

OUTPUT_SPEC = [

# shape: (num_detections, 4): (xmin, ymin, xmax, ymax)

# These values are in units of pixels. The origin is the top left corner

# with positive X to the right and positive Y towards the bottom of the

image

{"name": "boxes", "dtype": "float32", "shape": ("num_detections", 4)},

# The list of classes that the network can output

# This must be some subset of ['vehicle', 'person', 'motorcycle',

‘bicycle’]

{"name": "supported_object_classes", "dtype": "string", "shape": ("num_classes",)},

# The probability of each class for each detection

# These should all be floats between 0 and 1

{

“name”: “object_class_probability”, “dtype”: “float32”, “shape”: (“num_detections”, “num_classes”)},

]

爲問題構建單個度量管道。

輕鬆比較解決同一問題的模型(即使它們位於不同的框架中)。

構建優化的推理代碼,可以運行解決特定問題的任何模型。

在運行時無需更改代碼即可解決相同問題的替換模型(即使模型來自不同的框架)
快速實驗。

建立通用工具和管道。

如果有幾個模型接受相似的輸入集,則可以構建和優化一個與框架無關的輸入生成管道,並在模型之間共享它。

其他好處

完全獨立的模型(包括自定義操作)

高效的零拷貝操作

在平臺上測試,包括Mac、Linux、Linux(GPU)

每個受支持框架的四個或五個版本,Python的五個版本

帶有進程外執行的模型隔離

在同一個應用程序中使用多個不同版本的框架

示例:每晚使用Torch的實驗模型和使用Torch1.1.0的模型

用一行代碼從進程內運行切換到進程外運行

二.技術,功能和性能
在這裏插入圖片描述
隨着自動駕駛軟件的發展,一直在尋找新的方法來改進模型。有時候,這意味着要試驗不同的深度學習框架。隨着新的深度學習框架的發佈,以及 TensorFlow 和 PyTorch 等現有框架取得的進步,希望確保工程師和科學家能夠靈活地使用最適合正在研究的問題的工具。

然而不幸的是,要在整個機器學習棧中添加對一個新的深度學習框架的支持,既費資源,又費時間。已經花了大量時間來簡化這個過程。將現有深度學習框架之上的一個抽象層,提供一個統一的接口來運行深度學習模型。讓研究人員可以輕鬆地在自己選擇的框架中構建模型,簡化這些模型的生產化過程。

使用多種深度學習框架

深度學習的發展非常迅速,不同的深度學習框架在不同的任務上有效。因此,在過去的幾年裏,使用過多種深度學習框架。2016 年,Caffe2 是主要的深度學習框架;2017 年集成 TensorFlow。這涉及到與 CUDA 和 cuDNN 的主要集成障礙、Caffe2 和 TensorFlow 的依賴衝突、庫加載問題等。2017 年,在 PyTorch 上開發更多的模型。這些模型本身需要大量的工作,而在與 TensorFlow 同時運行時,還發現內存損壞問題,以及幾個非常難以調試的問題。
在這裏插入圖片描述
圖 1. 利用不同的流行深度學習框架,發展機器學習

自 2018 年以來,有許多深度學習框架已經開源,包括
Ludwig、JAX、Trax、Flax、Haiku 和 RLax,其中許多框架都是最近兩年發佈的。

即便研究人員可以很容易地試驗新框架,但在所有的系統和流程中添加對新深度學習框架的生產支持,也是一項艱鉅的任務。需要對基礎設施和工具的每個部分逐一進行集成和優化。
在這裏插入圖片描述
圖 2. 添加對新框架的支持很困難,因爲運行模型的每個基礎設施都需要支持所有框架。這些基礎設施組件可以是度量管道、模型服務或其生產和測試環境。

在2018 年末,開始構建多種模型,嘗試用不同的方式解決同一問題。例如,利用光學雷達進行 3D 目標檢測,可以採用範圍視圖或鳥瞰視圖。這兩種方法都是有效的,但各有優缺點。不同團隊構建的模型有時也在不同的框架中實現。

爲了使生產化更容易,希望能夠輕鬆地替換解決相同問題的模型,即使是在不同的框架中實現的。

也會遇到一些其的情況,比如新的研究將基於 PyTorch 編寫代碼,但想要快速地與 TensorFlow 中的現有模型進行比較。因爲每個模型都有特定於框架的、模型級的度量管道,所以比較就很難做到了。

爲了在新框架中部署模型,需要重建模型級的度量管道,在所有的系統和流程中對框架進行集成,然後進行額外的優化,以確保能在延遲預算範圍內有效地運行模型。

雖然這些步驟看上去很簡單,但諸如上述問題(如內存損壞、依賴衝突等)導致耗費大量精力來解決,而不能專注於模型開發。

需要一種方法來最大程度地提高研究過程中的靈活性,而不必在過程的其部分重複工作。

軟件方案介紹

針對這一問題的解決方案,能使所有深度學習框架在運行模型時看起來都是一樣的。該框架在所有工具和基礎設施中添加對新框架的支持,就像將其添加到軟件中一樣簡單。
在這裏插入圖片描述
圖 3.一個抽象層,提供了一個統一的接口,可以基於多個框架運行深度學習模型。

軟件框架支持的框架包括:TensorFlow、PyTorch、Keras 和 TorchScript,同時也可以輕鬆地添加新的框架。

自2019 年初在內部發布以來,在快速部署新模型方面發揮了重要作用。在過去的一年裏,已經在AI核心業務部署了數百個模型。這些模型包括需求預測模型、預計到達時間(Estimated time of arrival,ETA)預測、 Eats 的菜單轉錄以及用於自動駕駛汽車的目標檢測模型。

概述

從問題定義的概念開始:模型要解決的“問題”的形式化描述。在這種情況下,問題可能是圖像的語義分割或文本的語言翻譯。通過形式化地定義問題,可以將其視爲一個接口,並抽象出具體的實現。每個模型都實現了一個問題定義。因此,解決相同問題的任何模型都是可互換的,即使使用不同的框架。

工作原理是將現有模型包裝在包中。此包包含原始模型以及元數據、測試數據和自定義操作(如果有的話)。

任何模型都能以任何支持的語言執行。例如,如果用戶想基於 C++ 運行 PyTorch 模型,將在後臺啓動一個 Python 解釋器,並與其通信來運行模型。這麼做是必要的,因爲 PyTorch 模型需要 Python 解釋器才能運行。這一功能允許在將 PyTorch 模型轉換爲 TorchScript 之前進行快速測試,而 TorchScript 可以在 C++ 上直接運行。

目前支持基於 Python 和 C++ 運行模型。但是,爲庫編寫額外的語言綁定也很簡單。例如,機器學習平臺 Michelangelo,使用框架作爲其核心深度學習模型格式,並實現了 Go 綁定來從 Go 運行其生產模型。
在這裏插入圖片描述
圖 4.提供了特定於框架的封裝 API 和與框架無關的推理 API。

推理概述

在深入探討工作原理之前,讓先看看過去用傳統方法是如何將深度學習模型集成到應用程序中的:
在這裏插入圖片描述
圖 5. 應用程序在整個推理過程中直接與深度學習框架交互。

在上圖中,應用程序在推理過程中的所有部分都是直接與 TensorFlow API 進行交互的。

應用程序只與與框架無關的 API 進行交互(下面的所有紫色部分),並且將這些與框架無關的調用轉換爲對底層框架的調用。儘可能使用零拷貝操作來高效地實現這一點。更多細節請參閱下面的“優化”部分。
在這裏插入圖片描述
圖 6.應用程序可以與框架無關的 API 進行交互,而與底層框架進行交互。

有一個可插拔的後端層,每個支持的框架都有自己的實現。這使得向添加新框架變得非常簡單。

進行深度學習

整個深度學習過程,以瞭解是如何幫助簡化實驗、部署和迭代的。

問題定義

要封裝,必須首先創建一個問題定義。如上所述,這是對試圖解決的問題的輸入和輸出的規範描述。這個定義包括所有輸入和輸出張量的名稱、數據類型和形狀。例如,2D 目標檢測的問題定義可能是類似下面這樣的:
在這裏插入圖片描述
上面的定義在形狀定義中使用了“符號”(num_classes 和 num_detections)。符號的每個實例都必須在運行時解析爲相同的值。與僅將形狀元素設置爲
None 相比,這提供了一種更可靠的約束形狀的方式。在上面的例子中,num_detections
跨 boxes 和object_class_probability
必須是相同的。

爲簡單起見,將在本文中使用一個更簡單的問題:加法。
在這裏插入圖片描述
上面的代碼段還定義了測試輸入和輸出數據,將在下面討論。

生成佔位符模型

一旦定義了問題,就可以自動生成一個佔位符模型來實現問題規範。這允許在沒有實際模型的情況下開始集成。

生成的模型接受問題規範中描述的輸入,並返回與輸出規範匹配的隨機數據。
在這裏插入圖片描述
構建模型

在建立了問題定義(並可選地生成佔位符模型)之後,就可以構建模型了。完成了構建和訓練模型的所有正常步驟,但現在在該過程的最後添加了一個導出步驟:
在這裏插入圖片描述
在上面的代碼段中,將模型導出,同時還提供了可選的測試數據。如果提供了測試數據,庫將在導出後立即對模型進行自檢。

Create_TensorFlow_Neuropod和所有其封裝程序的選項都有很好的文檔說明。

構建度量管道

現在有了自己的模型,就可以用 Python 爲這個問題構建度量管道。與沒有框架的情況下做這件事的唯一區別是,使用Python
庫來運行模型,而不是使用特定於框架的 API。
在這裏插入圖片描述
文檔 包含有關 load_neuropod 和 infer 的更多詳細信息。

集成

現在,可以將模型集成到生產 C++ 系統中。下面的示例顯示了C++ API 的使用非常簡單,但該庫也支持更復雜的使用,支持高效的零拷貝操作和包裝現有內存。更多細節,請參考文檔。
在這裏插入圖片描述
與沒有 框架的集成過程不同的是,這個步驟對於每個問題只需執行一次,而不是每個框架執行一次。用戶無需理解 TensorFlow 或者 Torch C++ API 的複雜性,但當研究人員決定要使用哪種框架時,仍然可以提供很大的靈活性。

此外,由於核心 框架庫是用 C++ 編寫的,因此,可以爲其各種編程語言(包括 Go、Java 等)編寫綁定。

優化

對延遲的要求相當嚴格,因此對許多關鍵操作都有零拷貝路徑。在分析和優化方面投入了大量工作,可以成爲實現適用於所有模型的推理優化的核心位置。

作爲這項工作的一部分,每個提交都在持續集成(CI)管道中的以下平臺上進行測試:

· Mac、Linux、Linux(GPU)

· Python 的五個版本

· 每個支持的深度學習框架的五個版本

要了解支持的平臺和框架的最新列表,請查看 文檔。

還提供了一種使用高性能共享內存橋在工作進程中運行模型的方法,這使得可以在不引入顯著延遲損失的情況下,將模型彼此隔離開來。將在本文末尾更詳細地討論這一點。

迭代

一旦構建並集成了模型的第一個版本,就可以迭代並改進解決方案。

作爲這個過程的一部分,如果想嘗試用 TorchScript 模型來代替上面創建的 TensorFlow 模型,那就可以直接替換。
在這裏插入圖片描述
如果沒有集成框架,就將需要重新執行之前的許多步驟。有了集成框架,任何實現相同問題規範的模型都是可互換的。可以重用前面創建的度量管道以及之前做的所有集成工作。

所有運行模型的系統和流程都與框架無關,從而在構建模型時提供了更多的靈活性。讓用戶專注於試圖解決的問題,而不是用來解決問題的技術。

與問題無關的工具

儘管
Neuropod 是從關注一個“問題”開始的,比如給定圖像的 2D 目標檢測,文本的情感分析等等,但可以在 Neuropod 的基礎上構建一個與框架和問題均無關的工具。這讓能夠構建通用的基礎設施,可以用於任何模型。

規範輸入構建管道

Neuropod 的一個有趣的、與問題無關的用例是規範輸入構建管道。在 ATG,有一個已定義的格式 / 規範,用於說明如何以張量表示輸入數據。這涵蓋了傳感器數據,包括光學雷達、雷達、相機圖像以及其信息,如高分辨率地圖等。定義這種標準格式,使得在訓練方面管理超大型數據集變得輕鬆容易。這也使能夠快速構建新模型,因爲許多模型都使用這些輸入數據的子集(例如,只對相機和光學雷達進行操作的模型)。

通過將這種通用輸入格式與 集成框架結合起來,可以構建一個單一的優化輸入構建管道,供所有的模型使用,而不管是在什麼框架中實現的。

只要每個模型都使用同一組特性的子集,輸入構建器就與問題無關。輸入構建器中的任何優化都有助於改進所有的模型。
在這裏插入圖片描述
圖 7.允許構建單一的、優化的輸入構建管道,該管道可以與許多模型一起工作,而不必爲每個模型或每個框架構建一個單獨的管道。

模型服務

另一個與問題無關的基礎設施是模型服務。有一些模型需要大量的輸入,而且模型本身也很大。對於部分模型,在諸如離線模擬之類的任務中在 CPU 上運行是不可行的。從資源效率的角度來看,在所有集羣機器中包含 GPU 是沒有意義的,因此,提供了一種服務,可以讓用戶在遠程 GPU 上運行模型。這與基於 gRPC 的模型服務非常相似。

如果沒有集成框架,那麼模型服務平臺就需要擅長遠程運行 Keras、遠程運行 TensorFlow、遠程運行 PyTorch、遠程運行 TorchScript 等。可能需要實現序列化、反序列化、與 C++ API 的交互,以及對每個框架的優化。所有的應用還需要擅長在本地運行所有這些框架的模型。

因爲在本地和遠程運行有不同的實現,所以整個系統需要關注
2 * # of frameworks 情況。

通過使用集成框架,模型服務可以很好地遠程運行,可以很好地在多個框架中運行模型。

通過將關注點分開,系統必須關注的案例數量以相機啊的方式
(2 + # framworks),而不是以乘積的方式增加。隨着越來越多的框架得到支持,這種差異就變得更加明顯。

不過,可以讓變得更強大。如果添加模型服務作爲後端,任何基礎設施都可以輕鬆地遠程運行模型。
在這裏插入圖片描述
圖 8. 添加模型服務作爲
後端,允許任何使用遠程運行模型的應用程序,而無需進行重大更改。

在後臺,可能看起來像這樣:
在這裏插入圖片描述
圖 9. 應用程序可以將模型執行代理到遠程機器。

這個解決方案不是特定於問題或特定於框架的,而是爲使用應用程序提供了更大的靈活性。

進程外執行
在這裏插入圖片描述
圖 10.支持使用低延遲共享內存橋在獨立的工作進程中運行模型。

輸入通常很大,對延遲非常敏感,所以本地的 gRPC 並不是分離模型的理想方法。相反,可以使用優化的基於進程外執行(Out-of-process
Execution,OPE)的共享內存實現來避免複製數據。這讓可以將模型彼此隔離開來,而不會造成嚴重的延遲損失。

這種隔離很重要,已經看到過在統一進程中運行的框架之間存在衝突,包括微妙的 CUDA 錯誤和內存損壞。這些問題都很難追查。

在運行
Python 模型時,這種隔離也有助於提高性能,因爲能讓用戶避免在所有模型之間共享 GIL。

在單獨的工作進程中運行每個模型還可以啓用“下一步”章節中提到的一些附加功能。

下一步

能夠快速構建和部署新的深度學習模型,但這僅僅是開始。

優化工作包括:

  1. 版本選擇:該功能使用戶能夠在導出模型時,指定框架所需的版本範圍。例如,一個模型可能需要 TensorFlow 1.13.1,將使用正確版本的框架的 OPE 自動運行該模型。這使得用戶能夠在一個應用程序中使用多個框架和每個框架的多個版本。

  2. 封裝操作:這個功能使應用程序能夠指定使用張量“完成”的時間。一旦張量被密封,可以在推理運行之前將數據異步傳輸到正確的目的地(例如本地的 GPU 或輔助進程等)。這有助於用戶並行化數據傳輸和計算。

  3. Dockerized 工作進程:這樣可以在模型之間提供更多的隔離。例如,使用此功能,即使需要不同 CUDA 版本的模型也可以在同一應用程序中運行。

隨着繼續通過增強當前功能並引入新功能來擴展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章