視覺感知未來,高德數據採集模型部署實踐!

1. 導讀

作為DAU過億的國民出行服務平台,高德地圖每天為用戶提供海量的檢索、定位和導航服務,實現這些服務需要有精準的道路信息,比如電子眼位置、路況信息、交通標識位置信息等。讀者是否會好奇,高德是如何感知到現實世界的道路信息,並提供這些數據給用戶呢?

事實上,我們有很多的方法將現實世界的道路要素採集回收,並更新到高德地圖App上。其中一種非常重要的方法是利用計算機視覺的手段,將視覺算法部署到客戶端,通過對圖片的檢測識別,快速將道路的信息回收。

為了低成本,高實時性地實現道路要素回收,我們藉助MNN引擎(一個輕量級的深度神經網絡推理引擎),將卷積神經網絡模型部署到客戶端,在客戶端進行端側的模型推理,從而完成在計算能力低,內存小的客戶端進行道路要素採集的任務。

傳統的CNN(卷積神經網絡)計算量非常大,且業務場景需要部署多個模型。如何在低性能的設備上部署多個模型,並在不影響實時性的情況下保證應用的”小而優”是一個非常大的挑戰。本文將分享利用MNN引擎在低性能設備上部署深度學習應用的實戰經驗。

2. 部署

2.1 背景介紹

如Figure2.1.1所示,業務背景是將道路要素識別相關的CNN模型部署到客戶端,在端側進行模型推理,並提取道路要素的位置和矢量等信息。

由於該業務場景的需要,目前端上需同時部署10+甚至更多的模型,以滿足更多的不同道路要素的信息提取需要,對於低性能設備來說是非常大的挑戰。

Figure 2.1.1 高德數據採集

為了達到應用的”小而優”,MNN引擎部署模型的過程中遇到了很多問題和挑戰。下面就這些問題和挑戰分享一些經驗和解決辦法。

2.2 MNN部署

2.2.1 內存佔用

應用運行內存對於開發者來說是始終繞不開的話題,而模型推理產生的內存在應用運行內存中佔有很大的比例。因此,為了使模型推理內存儘可能小,在模型部署的過程中,作為開發者必須清楚模型運行產生內存的主要來源。根據我們的部署經驗,部署單模型的過程中,內存主要來源於以下四個方面:

Figure 2.2.1 單模型部署內存佔用

ModelBuffer: 模型反序列化的buffer,主要存儲模型文件中的參數和模型信息,其大小和模型文件大小接近。

**FeatureMaps: **Featuremaps的內存,主要存儲模型推理過程中,每一層的輸入和輸出。

**ModelParams: **模型參數的內存,主要存儲模型推理所需的Weights, Bias, Op等內存。其中Weights佔用了該部分的大部分內存。

**Heap/Stack: **應用運行中產生的堆棧內存。

2.2.2 內存優化

知曉模型運行內存佔用後,就能方便理解模型運行時的內存變化。經過多個模型部署實踐, 為了降低部署模型的內存峰值,我們採取的措施如下:

  • 模型反序列化(createFromFile)並創建內存(createSession)後,將模型Buffer釋放(releaseModel), 避免內存累加。
  • 處理模型輸入,圖像內存和inputTensor可內存復用。
  • 模型後處理,模型輸出Tensor和輸出數據的內存復用。

Figure 2.2.2.1 MNN模型部署內存復用方案

經過內存復用,以部署1個2.4M的視覺模型為例,模型運行時從加載到釋放,中間各階段所佔用內存變化可以用以下曲線表示:

Figure 2.2.2.2 單模型應用內存曲線(Android memoryinfo統計)

  • 模型運行前,模型佔用內存為0M
  • 在模型加載(createFromFile)和創建內存(createSession)後,內存升到5.24M, 來源於模型反序列化和Featuremaps內存創建。
  • 調用releaseModel內存降低至3.09M,原因是釋放了模型反序列化後的buffer。
  • InputTensor和圖像內存復用,應用內存增加到4.25M, 原因是創建了存儲模型輸入的Tensor內存。
  • RunSession(),應用內存增加到5.76M,原因是增加了RunSession過程中的堆棧內存。
  • 在模型釋放後,應用恢復到了模型加載前的內存值。

經過多次模型部署的實踐,下面總結了部署單模型到端的內存峰值預估公式:

MemoryPeak:單模型運行時內存峰值。

StaticMemory:靜態內存,包括模型Weights, Bias, Op所佔內存。

DynamicMemory:動態內存,包括Feature-maps所佔內存。

ModelSize:模型文件大小。模型反序列化所佔內存。

MemoryHS:運行時堆棧內存(經驗取值0.5M-2M之間)。

2.2.3 模型推理原理

本章節分享模型推理原理,以便於開發者遇到相關問題時,快速定位和解決問題。

模型推理前模型的調度: MNN引擎推理保持了高度的靈活度。即可以指定模型不同的運行路徑,也可以對不同的運行路徑指定不同的後端,以提高異構系統的並行性。此過程主要是調度或者任務分發的過程。

對於分支網絡,可以指定當前運行分支,也可以調度分支執行不同後端,提高模型部署的性能。圖Figure2.2.3.1所示為一個多分支模型, 兩個分支分別輸出檢測結果和分割結果。

Figure 2.2.3.1 多分支網絡

部署時可做如下優化 :

  • 指定模型運行的Path。當僅需檢測結果時,只跑檢測分支,無需跑完兩個分支, 減小模型推理時間。
  • 檢測和分割指定用不同的後端。比如檢測指定CPU, 分割指定OpenGL,提高模型並行性。

模型推理前的預處理: 本階段會根據上一步的模型調度信息進行預處理,本質是利用模型信息和用戶輸入配置信息,進行Session(持有模型推理數據)的創建。

Figure 2.2.3.2 根據Schedule創建Session

本階段根據模型反序列化的模型信息和用戶調度配置信息來進行運算調度。用於創建運行的Piplines和對應的計算後端。如Figure2.2.3.3所示。

Figure 2.2.3.3 Session創建

模型的推理: 模型推理本質是根據上一步創建的Session,依次執行算子的過程。運算會根據預處理指定的模型路徑和指定後端進行模型每層的運算。值得一提的是,算子在指定的後端不支持時,會默認恢復到備用後端執行計算。

Figure 2.2.3.4 模型推理計算圖

2.2.4 模型部署時間

本部分統計了單模型部署過程各階段耗時,方便開發者了解各階段的耗時,以便更好的設計代碼架構。(不同設備有性能差異,耗時數據僅供參考)

Figure 2.2.4.1 模型推理計算圖

模型反序列化和Session創建相對耗時較長,進行多張圖的推理時,盡量執行一次。

2.2.5 模型誤差分析

模型部署時,開發者難免會遇到部署端和X86端(Pytorch, Caffe, Tensorflow)訓練模型輸出結果有偏差的情況。下面分享誤差原因, 定位思路以及解決辦法。

模型Inference示意圖如Figure 2.2.5.1所示:

Figure 2.2.5.1 模型Inference示意圖

模型誤差的確定: 查看是否有模型誤差最直觀的方法是,固定部署端模型和X86端模型的輸入值,分別推理,對比部署端模型和X86端模型輸出值,可確認是否有誤差。

模型誤差的定位: 當確定有模型誤差時,先排除因模型輸入誤差導致的模型輸出誤差。因為X86端和部分Arm設備浮點的表示精度不一致,輸入誤差在某些模型中會被累積,最終造成較大的輸出誤差。用什麼方法來排除是輸入誤差導致的問題呢?我們提供一種方法是將模型輸入設置為0.46875(原因是該值在X86設備和部分Arm設備表示一致,本質是1經過移位獲得的浮點數在兩種端上表示均一致)。然後觀察輸出是否一致即可。

模型誤差的定位思路: 在排除模型輸入誤差導致模型輸出誤差(即模型輸入一致時,模型輸出不一致)的情況下,很可能是模型某些算子導致的誤差了。如何定位模型哪個OP導致的誤差呢?通過下述的步驟可以定位模型內部引起誤差的原因:

1)通過runSessionWithCallBack來回調模型每個OP的中間計算結果。目的是定位模型從哪個Op開始出現誤差。

2)定位到該層之後,即可定位到產生誤差的算子。

3)定位到算子後,通過指定的後端信息即可定位到對應的算子執行代碼。

4)定位到對應的執行代碼後,調試定位產生誤差的代碼行,從而定位到產生模型誤差的根本原因。

3. 總結

MNN引擎是一個非常好的端側推理引擎,作為開發者來說,模型的端上部署和性能優化在關注業務邏輯優化的同時,也需關注對引擎計算過程,框架設計和模型加速的思想,反過來可以更好的優化業務代碼,做出真正”小而優”的應用。

4.未來規劃

隨着設備性能的普遍提升,後續的業務會搭載到性能更高的設備,我們會利用更豐富的計算後端做模型的加速,比如OpenCL, OpenGL等, 從而加速模型的推理。

未來設備會搭載更多的模型到客戶端,用於實現更多品類道路要素信息的回收,我們也會利用MNN引擎,探究更高效,更高實時性的代碼部署框架,以更好的服務於地圖採集業務。

我們是高德地圖數據研發團隊,團隊中有大量HC,歡迎對Java後端、平台架構、算法端上工程化(C++)、前端開發感興趣的小夥伴加入,請發送您的簡歷到 [email protected] ,郵件標題格式: 姓名-技術方向-來自高德技術。我們求賢若渴,期待您的加入。