TVM 是什麼?A compiler stack,graph level / operator level optimization,目的是(不一樣框架的)深度學習模型在不一樣硬件平臺上提升 performance (我要更快!)html
TVM, a compiler that takes a high-level specification of a deep learning program from existing frameworks and generates low-level optimized code for a diverse set of hardware back-ends.前端
compiler比較好理解。C編譯器將C代碼轉換爲彙編,再進一步處理成CPU能夠理解的機器碼。TVM的compiler是指將不一樣前端深度學習框架訓練的模型,轉換爲統一的中間語言表示。stack個人理解是,TVM還提供了後續處理方法,對IR進行優化(graph / operator level),並轉換爲目標硬件上的代碼邏輯(可能會進行benchmark,反覆進行上述優化),從而實現了端到端的深度學習模型部署。java
我剛剛接觸TVM,這篇主要介紹瞭如何編譯TVM,以及如何使用TVM加載mxnet模型,進行前向計算。Hello TVM!node
背景介紹
隨着深度學習逐漸從研究所的「伊甸園」迅速在工業界的鋪開,擺在你們面前的問題是如何將深度學習模型部署到目標硬件平臺上,可以多快好省地完成前向計算,從而提供更好的用戶體驗,同時爲老闆省錢,還能減小碳排放來造福子孫。python
和單純作研究相比,在工業界咱們主要遇到了兩個問題:ios
- 深度學習框架實在是太TMTM多了。caffe / mxnet / tensorflow / pytorch訓練出來的模型都彼此有不一樣的分發格式。若是你和我同樣,作過不一樣框架的TensorRT的部署,我想你會懂的。。。
- GPU實在是太TMTM貴了。深度學習春風吹滿地,老黃股票真爭氣。另外一方面,一些嵌入式平臺沒有使用GPU的條件。同時一些人也開始在作FPGA/ASIC的深度學習加速卡。如何將深度學習模型部署適配到多樣的硬件平臺上?
爲了解決第一個問題,TVM內部實現了本身的IR,能夠將上面這些主流深度學習框架的模型轉換爲統一的內部表示,以便後續處理。若想要詳細瞭解,能夠看下NNVM這篇博客:NNVM Compiler: Open Compiler for AI Frameworks。這張圖應該可以說明NNVM在TVM中起到的做用。c++
爲了解決第二個問題,TVM內部有多重機制來作優化。其中一個特色是,使用機器學習(結合專家知識)的方法,經過在目標硬件上跑大量trial,來得到該硬件上相關運算(例如卷積)的最優實現。這使得TVM可以作到快速爲新型硬件或新的op作優化。咱們知道,在GPU上咱們站在Nvidia內部專家的肩膀上,使用CUDA / CUDNN / CUBLAS編程。但相比於Conv / Pooling等Nvidia已經優化的很好了的op,咱們本身寫的op極可能效率不高。或者在新的硬件上,沒有相似CUDA的生態,如何對網絡進行調優?TVM這種基於機器學習的方法給出了一個可行的方案。咱們只需給定參數的搜索空間(少許的人類專家知識),就能夠將剩下的工做交給TVM。若是對此感興趣,能夠閱讀TVM中關於AutoTuner的介紹和tutorial:Auto-tuning a convolutional network for ARM CPU。git
編譯
個人環境爲Debian 8,CUDA 9。github
準備代碼
config文件
編輯config文件,打開CUDA / BLAS / cuBLAS / CUDNN的開關。注意下LLVM的開關。LLVM能夠從這個頁面LLVM Download下載,我以前就已經下載好,版本爲7.0。若是你像我同樣是Debian8,能夠使用for Ubuntu14.04的那個版本。因爲是已經編譯好的二進制包,下載以後解壓便可。算法
找到這一行,改爲
編譯
這裏有個坑,由於咱們使用了LLVM,最好使用LLVM中的clang。不然可能致使tvm生成的代碼沒法二次導入。見這個討論帖:_cc.create_shared error while run tune_simple_template。
python包安裝
demo
使用tvm爲mxnet symbol計算圖生成CUDA代碼,並進行前向計算。
最後的話
我我的的觀點,TVM是一個頗有意思的項目。在深度學習模型的優化和部署上作了不少探索,在官方放出的benchmark上表現仍是不錯的。若是使用非GPU進行模型的部署,TVM值得一試。不過在GPU上,得益於Nvidia的CUDA生態,目前TensorRT仍然用起來更方便,綜合性能更好。若是你和我同樣,主要仍然在GPU上搞事情,能夠密切關注TVM的發展,並嘗試使用在本身的項目中,不過我以爲仍是優先考慮TensorRT。另外一方面,TVM的代碼實在是看不太懂啊。。。
想要更多
- TVM paper:TVM: An Automated End-to-End Optimizing Compiler for Deep Learning
- TVM 項目主頁:TVM
後續TVM的介紹,不知道啥時候有時間再寫。。。隨緣吧。。。
如何評價陳天奇團隊新開源的TVM?
12 個回答
從去年nnvm推出以後,很是感謝在zhihu和
上有一些討論 如何評價陳天奇的模塊化深度學習系統NNVM? ,關於nnvm剩下的瓶頸。這個討論自己加上早期的nnvm編譯嘗試,讓我意識到了能夠支持快速調優底層op的重要性。在接下來的八個多月裏面咱們不斷迭代完成了TVM。
TVM嘗試從更高的抽象層次上總結深度學習op的手工優化經驗,用來使得用戶能夠快速地以自動或者半自動的方法探索高效的op實現空間。
TVM和已有的解決方案不一樣,以XLA做爲例子,TVM走了和目前的XLA比更加激進的技術路線,tvm能夠用來使得實現XLA須要的功能更加容易 :已有的解決方案自己基於高級圖表示的規則變換,能夠產生一些圖級別的組合op優化,如conv-bn fusion,可是依然要依賴於手寫規則來達到從圖的表示到代碼這一步。圖的op表示到代碼自己能夠選擇的東西太多,如何作線程,如何利用shared memory,而大部分沒有在圖語言裏面獲得刻畫,致使難以自動化。 這樣下去深度學習系統的瓶頸必然從op實現的複雜度變成了實現graph compiler中模式生成規則的複雜度。走這個方向須要很是大的工程團隊的支持,而咱們但願採用更少的人力達到一樣甚至更好的效果。
咱們採起了風險更大可是回報也更大的長遠技術路線。簡單地說,TVM經過把圖到op生成規則這一步進一步抽象化,把生成規則自己分紅各個操做原語,在須要的時候加以組合。基於tvm咱們能夠快速地組合出不一樣的schedule方案。
這個想法並不新穎,正如其它回答中提到的Halide,或者polyhedra method都是嘗試去作這件事情。想法雖然美好,可是自動代碼生成這條路線必需要生成代碼效率到達手寫的80%-90%效率以上,纔會有實際使用的價值。一旦到達了80%到90%的效率以上,經過fusion,layout的一些高級聯合優化就能夠彌補這一個gap來獲得比直接組合手寫代碼更好的效果。
可是這也正是這個問題最困難的地方,咱們須要能使得自動或者半自動生成的代碼達到手寫代碼的效果。在TVM以前,已有的解決方案都尚未解決這個問題。我知道的最好的GPU自動生成代碼大概能夠到Cublas的50%的運行效率,而大部分的已有方案只是針對單線程cpu有比較好的效果。
固然已有的解決方案有很多值得參考的地方。好比polyhedra method自己很是精闢地把程序優化的大部分問題總結爲針對整數集的分析。Halide裏面的schedule和declaration分離的思想等。這些思想都很是強地影響了TVM的設計
這自己是一個頗有趣的科研問題,dmlc的的初衷就是去解決這樣新的問題,發佈新的解決方案。TVM在很大程度上解決了這個問題。要解決它,須要作到兩點:設計足夠大的schedule空間,使得它能夠囊括包括cpu和gpu在內能夠作到的手寫優化,設計足夠強大的搜索算法。以前的方法之因此沒有圖片,難點在於須要足夠大的空間。
全部的抽象老是有缺陷的,因此死抱一個固定的抽象確定不能解決全部的問題。可是能夠被寫出來的手工優化基本上也是能夠被抽象的。過去的幾個月咱們就是沿着這樣的思路,不斷地去總結手工優化的經驗加以抽象到TVM中。雖然咱們不敢保證TVM包含全部可能的手工優化,可是我基本上cover了我知識範圍裏面能夠涉及到的東西(使得TVM至少比我知道的多)。隨着TVM的演化,會有更多的這樣的手工優化經驗能夠被加入進來。這也真是須要HPC機器學習和編譯等各方面人才一塊兒協力的結果。
到目前爲止,咱們基本能夠肯定TVM目前提供的schedule空間在cpu上能夠作到90%,類似或者超過一些手寫優化庫效果的方案,在gpu上幾本能夠作到達到或者超過手寫cuda的方案,可是和手寫assembly在一些狀況還有80%的差距(主要來源於gpu的寄存器分配比較困難)。TVM自己也意識到的手寫優化的重要性,會在容許在各個級別混用手寫優化的代碼, 來彌補剩下這一平衡。
這是一個很是激動的前沿課題,基於這個項目自己還會有很多有趣的研究方向,咱們在不少地方已經能夠看到很是好的效果。因此咱們很是但願對於機器學習,hpc,編譯原理,硬件加速 有興趣的同窗一塊兒加入進來,一塊兒來推進這個項目。而由於咱們目前到達的效果自己,TVM已經能夠被使用於實際的應用場景中了。
最後有一些細節上面的東西,TVM自己的設計中是很是注重開發效率和可擴展性。TVM直接提供了很是好用的python和真機調試框架,能夠不依賴於上層框架直接基於python開發調試。這一點使得tvm在調試開發和效率迭代上面比起已有的方案有比較大的優點。將來咱們也會放出一些樣例教程,讓你們均可以開發高效的代碼
將來會有自動圖編譯以及直接在python端定義customop
目前 TVM 放出的資料還較少,週日學習了下代碼,和你們交流分享,有疏漏煩請回復指出。
TVM 的應用場景,是跟 TensorFlow XLA 對標,提供將模型輸出到不一樣設備 native code 的能力。這裏面有幾個能夠對標的組件:
- TOPI (TVM Operator Inventory) 大約對應 XLA HLO, 描述在 DL 領域會用到的高層次 Operator 如 matmul, conv2d 等。這一層次能夠作 CSE、Fusion 等優化。
- Schedule + HalideIR + TVM IR 無對應
- 代碼輸出 TVM 使用 LLVM IR 和 Source Code, 對應 XLA 使用 LLVM IR.
這裏面,TVM 的切入點是在 High Level 到 Low Level 中間,插入了 Schedule 層,大概流程是 NNVM -> TVM/TOPI -> TVM/Schedule -> TVM/HalideIR -> TVM IR -> LLVM IR 或 Source Code。中間 TVM Schedule 的主要思想來自於 Halide. 這裏要簡單介紹一下 Halide 這個爲圖像處理設計的語言。Halide 其特色是計算描述(algorithm)和計算過程(schedule)分離(http://people.csail.mit.edu/jrk/jrkthesis.pdf)。這麼作是由於計算機體系結構的設計(緩存,SIMD 等),直接裸寫算法不能得到最高性能(一個例子是三重循環裸寫矩陣乘會很慢)。所以不一樣的體系結構,對一個算法的計算過程也就不一樣。分離算法定義和計算過程,則方便爲不一樣的體系結構制定不一樣的 schedule, 進一步能夠探索 schedule 的自動生成(Automatically Scheduling Halide Image Processing Pipelines)。更詳細的介紹建議去 Halide 官網 Halide 學習。
當初看到 Halide 的時候,就在想這個想法在 DL 領域必定會有用,現在終於被 DMLC 推進進入了人們的視線。我對這個事情的見解是:
- Halide 能夠比較快的實現一個性能還不錯的 kernel,開發效率很高,換不一樣 schedule 測試方便。對比傳統 kernel 實現通常是手寫 C/C++ 或者彙編代碼,開發效率較低。但任何抽象都不是完美的,有足夠人力的狀況下,傳統寫法必定能夠得到不低於 Halide 的效率。
- Halide 提供了 auto-tune 的可能,但目前也只是在學術界研究,離工業級生產還遠。所以可預見的將來,咱們仍是要爲不一樣的 target 手寫 schedule 的。
根據
介紹,TVM 相對 Halide 作的比較多的工做,主要的是去解決 schedule 空間包含手寫優化的問題。具體內容移步 crowowrk 的回答。
TVM 的另外一個目的是,但願經過 TOPI 這個 Operator 庫,爲全部兼容 dlpack 的深度學習框架提供 kernel 庫,這個目標是十分歡迎的,具體效果還有待觀察。
反過來看 TensorFlow 的 XLA,目前 XLA 還在快速開發中,有幾點能夠注意:
- XLA 並不反對獨立出來給其餘框架用 XLA standalone
- XLA 歡迎嘗試各類 idea,目前有人在 Incorporate Polyhedral Compilation
- XLA 目前不少 kernel 實現是基於 Eigen 的。某種程度上,Eigen 這種數學庫也是 TOPI 的對標。
總的來講,TVM 目標是很好的,很是支持。Soumith (PyTorch 主要做者)也在積極參與 TVM 項目並表示在接下來的幾個月內會有更多關於 TVM/PyTorch 的消息 Twitter 。
edit: 跟做者交流更新了若干技術細節。
最近陣容有點強大。
這週四咱們請了天奇來將門作線上直播,給你們親自講講TVM。
天奇的直播首秀,就在本週四(16號)下午1點,將門創投鬥魚直播間!
歡迎你們呼朋引伴來給天奇打call!!
最近在考慮將深度學習移植到移動端, 面對不少問題:
- ios 11有coreml, ios10用metal, 更低版本須要手寫低版本metal代碼
- neon指令集優化
- 安卓gpu
- caffe,tensorflow,darknet等一堆框架,移植不便,並且沒法一一優化.
瞌睡送枕頭, 感受tvm就是答案.
github上關注了tianqi, 一直納悶最近半年爲啥沒有提交代碼, 難道上課太忙?
直到昨天...
忽然都亮起來了.
吐槽一句, mxnet號稱是輕量級框架, 各類宏,lambda看的懷疑人生,nnvm竟然還能把torch弄過來用Orz. 後來看了caffe, 不少功能都用第三方庫, 主體代碼很清晰簡單啊, 這纔是輕量啊...
利益相關: dmlc腦殘粉, tianqi腦殘粉
你們還記得找不到工做的bhuztez麼
他在去年在一位七歲小朋友的指導下預言,2017年出現的下一代深度學習框架後端會利用Polyhedral model作fusion,減小GPU內存帶寬壓力,提高運行速度。被某人下結論說「真心不會.. 合併帶來的內存節省只是一次elementwise op的代價,比起卷積開銷真是一個毛毛」
https://www.zhihu.com/question/48615510/answer/115592046
而如今的廣告
「如今咱們看到了 TVM 構建了由循環轉換工具好比 loopy、多面體分析啓發的圖優化」
「咱們經過自動融合運算符並使 TVM 生成融合的內核,在圖形節點之間和之中應用優化」
bhuztez不如趕忙改行,事實證實他那點破爛水平,連名校博士頭銜都沒有,真心不適合寫程序 ,我建議他仍是去賣煎餅果子吧 :)
如何評價陳天奇的模塊化深度學習系統NNVM?
7 個回答
北京時間 10 月 13 日 12:00 更新:
加個 Disclaimer: 本文僅表明我的觀點,與僱主無關。
北京時間 10 月 4 日 1:40 更新:
看到做者
我以前的回答,主要是在把 NNVM 跟 TF 內部的 graph 這層比對,並非爲了分出高下,而只是在作技術上的比對,並表達個人一些想法。但願你們不要有門派之爭,理性討論,共同促使技術進步。
=== 10 月 3 日答案原文以下 ===
tl;dr NNVM 的出現不在於技術上有多大突破(該有的 TF 都有),而在於意欲打造一個公共接口(雖然我並不認同)。當下推出的 TinyFlow, 也有一點集合社區力量對抗 Google 的 TensorFlow 的意思。無論怎麼說,DL Framework 社區活躍,終歸是一件好事,做爲從業者很是感謝!
首先建議想作技術分析的同窗,都先看看 TensorFlow 的代碼,雖然量很大,但核心都在core/framework 和 common_runtimecore/distributed_runtime 幾個目錄下面,從 Session 一路分析進去,並不難懂。
nnvm 從 github 上看,是爲了做爲模塊化 DL 系統中的計算圖的一環。NNVM offers one such part, it provides a generic way to do computation graph optimization such as memory reduction, device allocation and more while being agnostic to the operator interface definition and how operators are executed. NNVM is inspired by LLVM, aiming to be a high level intermediate representation library for neural nets and computation graphs generation and optimizations.
這些部分,在 TensorFlow 裏面都有相對成熟的實現。
先說圖表示,在 TensorFlow 裏面有兩種圖,一種是用於接口的,基於 protobuf 表示的圖 tensorflow/graph.proto,稱之爲 GraphDef。另外一種是 C++ 內部運行時用的圖表示 tensorflow/graph.h,稱之爲 Graph. 而 Operator 的定義,TF 是經過一個在 C++ 裏面實現的 DSL 作的 tensorflow/op.h 使用方法例如 tensorflow/math_ops.cc,這個在 NNVM 裏面也採用了相似的形式。
以後作圖的優化,在 C++ 層面有 tensorflow/graph_optimizer.h. 基於這個接口,目前也作了若干實現 tensorflow/graph_optimizer.cc 如常數摺疊,公共表達式消除等。除此以外,在 Python 層面也有 graph_editor 用來作圖的編輯。好比 Sublinear Memory 理論上用 GraphEditor 是能夠作到的。
TF 的 Operator 和 Kernel 也是分開的,相同的 Operator 能夠有 CPU/CUDA 等多種實現,OpenCL 也在進行中。往 TF 裏面加 Operator 並不複雜 https://www.tensorflow.org/versions/r0.11/how_tos/adding_an_op/index.html.
因此先潑一瓢冷水:我粗略的掃了一下 NNVM 的代碼,能夠說目前 NNVM 的目標,在 TF 內部都有實現而且都有比較好的抽象隔離。從新造輪子,政治意義大於技術意義。轉載做者 陳天奇 的微博 關於今天深度學習系統爭論。目前的壁壘並不是... 來自陳天奇怪:
關於今天深度學習系統爭論。目前的壁壘並不是使用哪個,而是系統內部高度耦合,使得改進或者從頭fork打造系統的代價變高。經過模塊化,去中心化來解決這些問題,防止壟斷。 當你們均可以經過組裝組件幾天從頭打造MX, TF或者相似系統的時候。這些爭論就不復存在了。
最近推出的 TinyFlow 號稱是 2k 行的 TensorFlow,但其實看代碼會發現,作到當面這個層面,即便徹底重寫,代碼成本也不算高。TinyFlow 目前(2016.10.3)的本質是一個 Python DSL 到 Lua 代碼的轉換器。而 TensorFlow 自己的結構並不複雜,難點在於無窮多 Operator 的實現,和當初分佈式架構的設計。這兩個實現出來,本質上都是工做量問題,這也是 TF 的 codebase 如此龐大的緣由。
NNVM 目前(2016.10.3)自身也不包括 Operator 的定義,這會致使使用 NNVM 的不一樣框架本質上是沒法互換的。而定義 Operator 這個工做量比較大,甚至不必定能完成(好比不一樣 Framework 對 padding 的定義就不太同樣),不知道 NNVM 是否有意願往這個方向發展。
目前 DL 的領域還在高速發展,新的網絡結構(好比 ResNet, GAN)、計算節點(各類神奇的 Operator)、計算方法(好比 lowbit, sublinear memory)、計算設備(好比 TPU,寒武紀)都在不斷涌現。分佈式架構也在不斷演進。在這個時間點,我認爲 monolithic 的框架重構相對方便,會有更加旺盛的生命力。而 NNVM 的理想,恐怕跟現實仍是有必定差距的。目前更有價值的,我以爲並不在圖表示層,而是各類 Operator 的 kernels. 每一個設備的 kernel 都須要專業人員定製,工做量大,難度高。cudnn 解決了 CUDA 設備上的大部分問題,但仍然有不少 Operator 須要本身實現。lowbit 目前也並無特別可用的實現。若是能有一個統一的庫,定義每一個 Operator 在各類設備上的最優運行代碼,應該對社區更有幫助。而上層的網絡定義,和具體每一個圖的運行調度方式(好比 MXNet 的 Dependency Engine,TensorFlow 的分佈式框架和 rendezvous 設計),這些代碼量不大,但更容易體現出差別化的部分,我想仍是留待每一個框架本身解決吧。
我的愚見,請各位參考。雖然我並不認同 NNVM 的目標,但依然對陳天奇先生對社區的貢獻很是欽佩。各位如想評論,請至少大略瞭解 NNVM,TensorFlow 和 MXNet 的內部實現架構,以節省你們時間。以前討論事後更加意識到了@王健飛 所說的更好地支持更多平臺的op調優的重要性。昨天咱們發佈了dmlc/tvm 來解決這部分問題。
-------
在幾個月以後給了幾個關於NNVM的報告,也思考了它和已有系統的差異。追加一下這一頁slide,是我對於在抽象成面上面各個系統差異的理解。
原回答
-------
我是NNVM的做者。
總結一下,技術上自己的NNVM和現有的東西的差異是最小模塊化和去中心化,下降深度學習系統優化門檻。除了爲了解決現有問題,更可能是爲了將來考慮。
關因而否重複造輪子的問題,圖表示和優化自己在MXNet就已經存在,樓上也提到TF也有對應的抽象,爲何咱們須要從新寫一遍呢,基本上也就是以上兩點緣由。
基本上現有的深度學習系統分紅兩塊,1) 基本的operator的實現, 2)支撐其中的系統調度,優化,解釋或者編譯架構。
在工程難點上,operator須要堆代碼,可是對於工程架構的難度上面而言相對較低(也就是說能夠寫的人比較多一些),可是須要堆比較大量的代碼。而剩下的系統優化部分,內存,執行調度和分佈式優化對於總體系統而言的難度相對高一些。Operator的集合問題雖然是一個問題,看已經有的成熟框架不管是Torch, Theano或者MXNet的operator完整程度基本上能夠知足於大部分應用,也就是說這部分暫時屬於已經解決或者能夠經過堆積工程力量容易解決的問題。樓上說的最小化通用的 Op接口很重要,和NNVM咱們考慮的方向垂直。我以爲相對乾淨的Op應該去推進成爲一個獨立的模塊,而Op實現自己其實沒有必要和框架耦合很深(雖然遺憾的是目前的設計暫時沒有作到這一點)。
NNVM但願解決的是垂直於operator實現的問題。有趣的是其實TF在這一暫時沒有花特別多的力氣,因此會讓人以爲operator是大頭。其實這裏有不少有趣的東西,在執行,調度和編譯優化上面。編程模型和一個圖自己的執行模式和硬件也會有更多的差別。
直接討論一下設計,目前TF採起了單一的動態執行模式,使得自己執行特別依賴於動態內存分配以及threading。而這並不是是大部分場景下的最優方案。大部分場景下基於對於有限的圖進行的靜態分配,能夠更大的緩解這個問題,實際狀況如MX自己的內存損耗能夠作的更好。爲何目前TF不會出現多種執行模式呢,是由於TF自己Op的接口仍是過於通常地針對的動態,而若是要更好的優化須要更細化的Op接口(分開內存分配和計算的部分),這就考慮到一個Op甚至可能有多種接口的可能性。
NNVM自己的圖的設計參考了TF,MX和caffe2的圖部分。樓上的評論基本上提到了幾個很是重要的概念,即系統優化和Op具體的性息相關。可是和PL不一樣,咱們不能直接簡單的抽象出有限個操做來表示整個程序。這個時候彷佛框架和Op會有比較強的關聯性,致使比較大的耦合。可是也並不是如此,由於每個優化自己其實只依賴於Op的部分屬性。好比有同窗提到的常數摺疊,其實須要知道的是一個Op是不是常數,以及如何去展開常數兩個函數。NNVM自己的作法是容許註冊這兩個額外屬性來容許常數摺疊優化。可是當不須要這個優化的時候,能夠直接去掉這一部分。使得深度學習的優化能夠插拔。如今看起來可能有些overkill,可是咱們相信將來深度學習系統在這方面會有很大的發展,容許不一樣的優化來自於不一樣羣體和研究人員是咱們更加喜歡的方式。
基於以上緣由,NNVM容許給每一個op註冊任意的信息。而且能夠使得屬性和註冊和op的實現分開。這相對於TF的op接口而言是一個進步的地方。TF內部的全部op屬性是須要提早數據結構指定的,也就是說,目前TF能夠註冊shape inference, op的輸入參數的個數,可是沒法註冊好比咱們須要的新的細化Op接口,或者有些人關心的代碼生成函數。若是須要加入這些特性,必需要修改Op的接口。這意味着全部的開發須要在一箇中心,而且只能保留你們關心的東西。若是forkA有feature1, forkB有feature2的狀況,forkB想要拿到 feature1就會比較不方便。由於NNVM容許不修改Op接口註冊任意信息,相對解決了這個問題。
固然模塊化和去中心化並不是對於全部人都重要,就是見仁見智把。
將來的深度學習系統會有更多系統的問題,使得優化和執行更加多樣化。咱們不可以期待全部的優化都來自於一個團隊,或者只應用於一個框架。更多的優化看起來會帶來更多的耦合,可是也並不是如此。
發佈TinyFlow緣由很簡單。大部分人並無意識到其實目前深度學習的「系統」部分能夠經過簡單抽象獲得。TinyFlow做爲一個教程性質的項目,能夠用比較短的代碼展現目前有的大部分優化概念,而且把Op的部分代理給Torch(由於Op自己雖然重要,可是並不做爲架構一部分)。若是能夠有更多的同窗來關注深度學習系統優化,基本這個項目的目的就達到了。
值得認可的是,NNVM目前只是走出了第一步,只是包含了MXNet原有的一些優化,暫時內容很少,咱們會繼續帶來更多好玩的東西。 咱們也會繼續堅持模塊化和去中心化的思想,使得咱們新的成果能夠更好的用在各個平臺裏面
NNVM Compiler: Open Compiler for AI Frameworks
盜一張文中圖:
給我一種感受,nnvm牽起了pytorch,cntk,caffe2,caffe,keras(?)的小手,開始幹xla。。(keras怎麼哪都有你。。)
例如一個高速網絡架構下,一個關鍵內存未對齊的操做可能損失可觀的總體性能,一個忽視的鎖的設計也可能損失可觀的性能,一個tcp超時參數的不合理可能損失樂觀的性能,甚至一個硬件插槽的不合理也可能損失可觀總體的性能,一個不合理的通訊算法架構等等。
一般,系統領域fine-grained的優化實現相當重要,這須要對平臺的長期耕耘,也須要決策層面長期的支持,絕逼幾個單純的架構能解決的問題。
不多見到從distributed level將系統設計,大都仍是仍是based single node層面講靈活性,期待有更多關於總體system層面討論架構。
最近有點勤奮。
在沐哥醉心與寫書和帶娃的日子裏,爲了讓你們不忘了咱們。
這週四咱們請了天奇來將門作線上直播,給你們親自講講TVM。
天奇的直播首秀,就在本週四(16號)下午1點,將門創投鬥魚直播間!
歡迎你們呼朋引伴來給天奇打call!!
NNVM Compiler是由mxnet的做者們提出的一個神經網絡的一箇中間表示。它包括兩部分:NNVM和TVM。NNVM主要作的是graph層面的表示,它經過表示節點和節點之間的鏈接關係,把Operator鏈接成一個computation graph,再經過TVM來優化每個節點,來生成最終可執行的代碼。
對於計算圖級別優化,其實NNVM作了一些,可是他並無作到極致,還有不少咱們認爲可優化的地方,它其實裏面並無提供。固然NNVM可能只是爲了創建一個這樣的平臺,讓你們去貢獻代碼,作這樣一個開源社區的概念。固然就是說若是要追求極致性能的話,有時候仍是須要關注這樣的一個優化的手段。
原文有一些有效的優化手段介紹:https://zhuanlan.zhihu.com/p/33693725?group_id=944960178852945920
Momenta:Paper Reading | 讓深度學習更高效運行的兩個視角
我不清楚做者們是否想考慮過相反的情形,即對DL來講,有沒有可能Operator是核心,框架是輔助?
從做者的回覆來看,是有些看不上Op開發的,都打算代理給Torch了
如何學習TVM的代碼?
2 個回答
或許和不少人不一樣,以個人經驗來看,以爲理解TVM,或者推理框架必定要從前端開始。即你從一個Tensorflow模型 / MXNet模型等,是如何轉爲NNVM的,而後再應該是後續的圖優化,以及後續的TVM Tensor,LLVM代碼生成等東西。
爲何我會這麼強調從前端開始呢?由於不理解前端模型,就很難理解後續TVM爲何是這樣,並且出了錯之後很難知道究竟是什麼緣由,好比不少時候找了半天,其實只是你忘記了模型輸入圖片的預處理,卻誤認爲是後續卷積的調度優化作的有問題,因此我強烈建議先從一個模型前端開始,在tvm/nnvm/frontend裏面選取一個前端。而選取前端開始不該該僅僅是看,Bug / 需求驅動永遠是最好學習源代碼的方式,建議從一個固化好的模型開始,而後補足NNVM算子,好比Mobilenet / Resnet50等,這裏也是讓你熟悉工具,熟悉NNVM的開始,可能會遇到不少問題,可是一個一個克服會收穫不少,這裏面推薦一個看模型的好工具: https://github.com/lutzroeder/Netron 我也是看蘋果公司一我的用了之後發現的,確實是好東西。
接下來你應該首先理解TOPI,這是架設在NNVM與TVM之間的東西(首先忽略圖優化,你後面再去看),由於你須要理解NNVM Symbol (其它模型在轉爲NNVM前端表示時會以Symbol形式的Api表示) 如何與TVM之間是如何鏈接起來的,在這裏面你會有點迷糊,由於TVM是C++和Python混合的工程,這裏面你須要在這二者跳來跳去,可是你這一步你最重要的是抓住兩個核心: FTVMCompute (@reg.register_compute) / @reg.register_schedule,這一個你須要分別在nnvm/top裏面的C++ / Python去找,top裏面會告訴你是如何從NNVM進入topi的。
這一步完成之後,你則須要進入topi裏面的任意一個後端Target去看,我暫時推薦x86後端,由於這一個後端尚未被AutoTVM改造。對於你來講,理解起來更容易。在這裏你會遇到topi/nn裏面的@tvm.target.generic_func到相似具體@generic.schedule_conv2d_nchw.register(["cpu"])的改變,這是TVM的核心所在,對於卷積這樣的數據負載處理,爲了優化而沿用Halide的思想: 計算與調度分離。爲了理解這個,你最好參考一下這個文檔: https://docs.tvm.ai/tutorials/optimize/opt_gemm.html#sphx-glr-tutorials-optimize-opt-gemm-py
到這一步理解好之後,後續的TVM底層API大部分狀況下你都不須要去動,包括後續的LLVM自動生成,優化等你也大部分不須要去動,由於相似CNN這樣的網絡,大部分你要作的工做就是在調度上,如何減小Cache Miss ,如何更好的讓數據Locality是更關鍵的地方。
到這一步之後,你能夠再回過頭去理解圖優化的部分,如Operator Fusion / FoldScaleAxis等,以及包括TVM目前最核心最不同凡響的地方: AutoTVM(https://docs.tvm.ai/tutorials/autotvm/tune_nnvm_arm.html#sphx-glr-tutorials-autotvm-tune-nnvm-arm-py),這是TVM去擊敗NCNN等用手寫彙編的推理框架的關鍵一環,用機器學習去解決機器學習的問題,讓你從調度參數的設置中解放出來,而專心寫調度算法。這裏面目前ARM CPU的調度算法並不是是最優的,可是從測試來看,至少在測試中使用硬件和環境來看,已經超過能找到的推理框架。後續我將撰寫一篇文章到TVM社區,將我在ARM CPU的工做寫出來,這將改善目前ARM CPU的官方調度版本,這將在Mobilenet等模型中有很好的提高,敬請關注!
TVM是很好的一個項目,這種基於編譯優化思想的深度學習推理框架正是我贊同的,雖然還有不少工做須要作,可是我認爲它已經走在一個很好的方向上了。
一步一步解讀神經網絡編譯器TVM(一)——一個簡單的例子
前言
這是一個TVM教程系列,計劃從TVM的使用說明,再到TVM的內部源碼,爲你們大體解析一下TVM的基本工做原理。由於TVM的中文資料比較少,也但願貢獻一下本身的力量,若有描述方面的錯誤,請及時指出。
那啥是TVM?
簡單來講,TVM能夠稱爲許多工具集的集合,其中這些工具能夠組合起來使用,來實現咱們的一些神經網絡的加速和部署功能。這也是爲何叫作TVM Stack了。TVM的使用途徑很廣,幾乎能夠支持市面上大部分的神經網絡權重框架(ONNX、TF、Caffe2等),也幾乎能夠部署在任何的平臺,例如Windows、Linux、Mac、ARM等等。
如下面一張圖來形容一下,這張圖來源於(https://tvm.ai/about):
乍看這麼多感受很是地複雜,但咱們只須要知道TVM的核心功能就能夠:TVM能夠優化的訓練好的模型,並將你的模型打包好,而後你能夠將這個優化好的模型放在任何平臺去運行,能夠說是與落地應用息息相關。
TVM包含的東西和知識概念都有不少,不只有神經網絡優化量化op融合等一系列步驟,還有其餘更多細節技術的支持(Halide、LLVM),從而使TVM擁有很強大的功能…好了廢話不說了,再說就憋不出來了,若是想多瞭解TVM的能夠在知乎上直接搜索TVM關鍵字,那些大佬有不少關於TVM的介紹文章,你們能夠去看看。
其實作模型優化這一步驟的庫已經出現不少了,不管是Nvidia自家的TensorRT仍是Pytorch自家的torch.jit
模塊,都在作一些模型優化的工做,這裏就很少說了,感興趣的能夠看看如下文章:
利用Pytorch的C++前端(libtorch)讀取預訓練權重並進行預測
利用TensorRT實現神經網絡提速(讀取ONNX模型並運行)
利用TensorRT對深度學習進行加速
開始使用
說到這裏了,感受有必要說下:咱們爲何要使用TVM?
若是你想將你的訓練模型移植到Window端、ARM端(樹莓派、其餘一系列使用該內核的板卡)或者其餘的一些平臺,利用其中的CPU或者GPU來運行,而且但願能夠經過優化模型來使模型在該平臺運算的速度更快(這裏與模型自己的算法設計無關),實現落地應用研究,那麼TVM就是你的不二之選。另外TVM源碼是由C++和Pythoh共同搭建,閱讀相關源碼也有利於咱們程序編寫方面的提高。
安裝
安裝其實沒什麼多說的,官方的例子說明的很詳細。你們移步到那裏按照官方的步驟一步一步來便可。
不過有兩點須要注意下:
- 建議安裝LLVM,雖然LLVM對於TVM是可選項,可是若是咱們想要部署到CPU端,那麼llvm幾乎是必須的
- 由於TVM是python和C++一塊兒的工程,python能夠說是C++的前端,安裝官方教程編譯好C++端後,這裏建議選擇官方中的Method 1來進行python端的設置,這樣咱們就能夠隨意修改源代碼,再從新編譯,而Python端就不須要進行任何修改就能夠直接使用了。
(官方建議使用Method 1)
利用Pytorch導出Onnx模型
說了這麼多,演示一個例子才能更好地理解TVM究竟是作什麼的,因此咱們這裏以一個簡單的例子來演示一下TVM是怎麼使用的。
首先咱們要作的是,獲得一個已經訓練好的模型,這裏我選擇這個github倉庫中的mobilenet-v2,model代碼和在ImageNet上訓練好的權重都已經提供。好,咱們將github中的模型代碼移植到本地,而後調用並加載已經訓練好的權重:
import torch
import time from models.MobileNetv2 import mobilenetv2 model = mobilenetv2(pretrained=True) example = torch.rand(1, 3, 224, 224) # 假想輸入 with torch.no_grad(): model.eval() since = time.time() for i in range(10000): model(example) time_elapsed = time.time() - since print('Time elapsed is {:.0f}m {:.0f}s'. format(time_elapsed // 60, time_elapsed % 60)) # 打印出來時間
這裏咱們加載訓練好的模型權重,並設定了輸入,在python端連續運行了10000次,這裏咱們所花的時間爲:6m2s。
而後咱們將Pytorch模型導出爲ONNX模型:
import torch
from models.MobileNetv2 import mobilenetv2 model = mobilenetv2(pretrained=True) example = torch.rand(1, 3, 224, 224) # 假想輸入 torch_out = torch.onnx.export(model, example, "mobilenetv2.onnx", verbose=True, export_params=True # 帶參數輸出 )
這樣咱們就獲得了mobilenetv2.onnx
這個onnx格式的模型權重。注意這裏咱們要帶參數輸出,由於咱們以後要直接讀取ONNX模型進行預測。
導出來以後,建議使用Netron來查看咱們模型的結構,能夠看到這個模型由Pytorch-1.0.1導出,共有152個op,以及輸入id和輸入格式等等信息,咱們能夠拖動鼠標查看到更詳細的信息:
好了,至此咱們的mobilenet-v2模型已經順利導出了。
利用TVM讀取並預測ONNX模型
在咱們成功編譯而且能夠在Python端正常引用TVM後,咱們首先導入咱們的onnx格式的模型。這裏咱們準備了一張飛機的圖像:
這個圖像在ImageNet分類中屬於404: 'airliner'
,也就是航空客機。
下面咱們將利用TVM部署onnx模型並對這張圖像進行預測。
import onnx
import time import tvm import numpy as np import tvm.relay as relay from PIL import Image onnx_model = onnx.load('mobilenetv2.onnx') # 導入模型 mean = [123., 117., 104.] # 在ImageNet上訓練數據集的mean和std std = [58.395, 57.12, 57.375] def transform_image(image): # 定義轉化函數,將PIL格式的圖像轉化爲格式維度的numpy格式數組 image = image - np.array(mean) image /= np.array(std) image = np.array(image).transpose((2, 0, 1)) image = image[np.newaxis, :].astype('float32') return image img = Image.open('../datasets/images/plane.jpg').resize((224, 224)) # 這裏咱們將圖像resize爲特定大小 x = transform_image(img)
這樣咱們獲得的x
爲[1,3,224,224]
維度的ndarray
。這個符合NCHW格式標準,也是咱們通用的張量格式。
接下來咱們設置目標端口llvm
,也就是部署到CPU端,而這裏咱們使用的是TVM中的Relay IR,這個IR簡單來講就是能夠讀取咱們的模型並按照模型的順序搭建出一個能夠執行的計算圖出來,固然,咱們能夠對這個計算圖進行一系列優化。(如今TVM主推Relay而不是NNVM,Relay能夠稱爲二代NNVM)。
target = 'llvm' input_name = '0' # 注意這裏爲以前導出onnx模型中的模型的輸入id,這裏爲0 shape_dict = {input_name: x.shape} # 利用Relay中的onnx前端讀取咱們導出的onnx模型 sym, params = relay.frontend.from_onnx(onnx_model, shape_dict)
上述代碼中導出的sym
和params
是咱們接下來要使用的核心的東西,其中params就是導出模型中的權重信息,在python中用dic表示:
而sym
就是表示計算圖結構的功能函數,這個函數中包含了計算圖的流動過程,以及一些計算中須要的各類參數信息,Relay IR以後對網絡進行優化就是主要對這個sym
進行優化的過程:
fn (%v0: Tensor[(1, 3, 224, 224), float32], %v1: Tensor[(32, 3, 3, 3), float32], %v2: Tensor[(32,), float32], %v3: Tensor[(32,), float32], %v4: Tensor[(32,), float32], %v5: Tensor[(32,), float32], ... %v307: Tensor[(1280, 320, 1, 1), float32], %v308: Tensor[(1280,), float32], %v309: Tensor[(1280,), float32], %v310: Tensor[(1280,), float32], %v311: Tensor[(1280,), float32], %v313: Tensor[(1000, 1280), float32], %v314: Tensor[(1000,), float32]) { %0 = nn.conv2d(%v0, %v1, strides=[2, 2], padding=[1, 1], kernel_size=[3, 3]) %1 = nn.batch_norm(%0, %v2, %v3, %v4, %v5, epsilon=1e-05) %2 = %1.0 %3 = clip(%2, a_min=0, a_max=6) %4 = nn.conv2d(%3, %v7, padding=[1, 1], groups=32, kernel_size=[3
15 條評論
一個在科研界混的人以爲NNVM更像一個科研項目,設計追求各類靈活,這在很大程度上確實可以促進社區的發展。但重口難調啊,設計無比靈活的東西針對特定需求,性能卻不必定牛逼,在工程應用上各家有各家的獨特需求和硬件條件,我的以爲更須要針對需求的定製。
我是作系統多媒體框架的,對DL的東西不瞭解,看了陳天奇的回答,以爲蠻有感觸的。
Android上的多媒體框架就是一個渣。stagefright的player連google本身的應用都不使用;java層從新封裝的exoplayer也並無爲廣大第三方應用所接受。由於他的思路就是面向特定的應用來構建實現,沒有留給第三方開發者真正的定製化開發空間。好比你想在視頻播放過程當中加一個額外的後處理實現圖像加強,這不可以經過添加一個新的組件來簡單的解決。從陳天奇的回覆來看,TF也是相似的解決方案 -- 你想添加一個額外的處理環節,很難。而這是在作框架設計的時候須要慎重考慮的東西。
而參考其餘的多媒體框架,像Linux的GStreamer,Windows的DShow;都是能夠方便地作到上面的事情。也就是他們是真的面向pipeline的graph設計:每一個模塊(算子,plugin)具備統一的直觀的接口(而不是具象的每一個特定屬性);不一樣的模塊(算子)能夠自動地完成鏈接和交互數據。內存的管理和數據的流動在框架的約束下交由模塊自動完成。這樣在搭建的新的場景的時候,就是壘積木,足夠簡單;在pipeline中添加和去除處理環節也能夠經過幾行代碼來完成。
至於「相對乾淨的Op應該去推進成爲一個獨立的模塊」,這個能夠參考多媒體領域的ffmpeg。他就是focuse在具體的視頻編解碼和處理功能,而獨立於不一樣的多媒體框架(能夠充分被Gstreamer,VLC,甚至stagefright所使用)
這種框架設計的思路,隨着場景和工程的演進,能夠表現出更多的優點。