TVM自定义数据类型
TVM自定義數(shù)據(jù)類型
 本文將介紹“自定義數(shù)據(jù)類型”框架,該框架可在TVM中使用自定義數(shù)據(jù)類型。
 介紹
 在設計加速器時,關鍵是如何近似地表示硬件中的實數(shù)。這個問題具有長期的行業(yè)標準解決方案:IEEE 754浮點標準。然而,當試圖通過構(gòu)建高度專業(yè)化的設計來最大限度地利用硬件時,使用通用IEEE 754浮點數(shù)是否有意義?知道工作負載的數(shù)字要求,是否可以構(gòu)建更小,更快或更省電的數(shù)據(jù)類型?答案是肯定的!研究人員已經(jīng)開始在學術和工業(yè)加速器設計中嘗試新的數(shù)據(jù)類型。例如,Google的Tensor處理單元(TPU)使用bfloat類型:單精度IEEE浮點數(shù),已被截斷為16位。許多深度學習工作負載的數(shù)值要求不嚴格,這種截斷通常不會影響模型的準確性,同時會立即將存儲成本降低一半。
 在研究人員開始為其數(shù)據(jù)類型構(gòu)建硬件之前,需要確定其數(shù)據(jù)類型在關心的工作負載中如何以數(shù)字方式表現(xiàn)。這通常涉及建立其數(shù)據(jù)類型的軟件仿真版本(例如Berkeley SoftFloat或libposit),將數(shù)據(jù)類型直接入侵工作負載中,以查看工作負載如何使用該數(shù)據(jù)類型執(zhí)行工作。更好的是將數(shù)據(jù)類型直接集成到編譯器本身中,以便可以編譯許多不同的工作負載以使用該數(shù)據(jù)類型。兩種路由都可能很乏味,考慮到現(xiàn)代編譯器的大小和復雜性,后一種路由通常變得難以管理。取自GitHub的一個示例顯示有人入侵了將數(shù)據(jù)類型存入TensorFlow。結(jié)果是237次提交,添加了將近6000行代碼,并在整個代碼庫中觸摸了200多個文件,而這僅僅是添加一種數(shù)據(jù)類型!對于許多研究人員來說,這項工作量是令人望而卻步的。
 為了解決這些問題,提出了“自定義數(shù)據(jù)類型”框架。該框架允許用戶將其模擬數(shù)據(jù)類型插入TVM,從而可以輕松探索深度學習工作負載中的新數(shù)據(jù)類型。與上面的posits-in-Tensorflow示例不同,該示例在編譯器中啟用單個新數(shù)據(jù)類型,而Bring Your Own Datatype框架則支持多種用戶定義的類型。
 自定義數(shù)據(jù)類型
 自定義數(shù)據(jù)類型框架的目標,使用戶能夠使用自定義數(shù)據(jù)類型運行深度學習工作負載。在“自定義數(shù)據(jù)類型”框架中,“數(shù)據(jù)類型”表示標量類型: 例如,float 或uint。不處理更復雜的數(shù)據(jù)格式,例如塊浮點數(shù) 或Intel的Flexpoint。此外,僅聲稱支持 這些標量數(shù)據(jù)類型的軟件仿真版本;不明確支持在自定義數(shù)據(jù)類型硬件上進行編譯和運行。
 TVM中的每個張量都被分配了一個類型代碼,該代碼定義了張量內(nèi)標量的數(shù)據(jù)類型。這些類型代碼,在TVM中具有硬編碼的含義,映射到諸如int和的常見數(shù)據(jù)類型float。但是,絕大多數(shù)類型代碼尚未使用。自定義數(shù)據(jù)類型框架允許用戶聲明這些未使用的類型代碼,并在運行時添加自己的新數(shù)據(jù)類型。
 該框架被實現(xiàn)為一個注冊表,與TVM的常規(guī)數(shù)據(jù)類型設施并排放置。用戶與數(shù)據(jù)類型注冊表進行交互的主要方式有兩種:第一,數(shù)據(jù)類型注冊, 第二,降低功能注冊。
 這些步驟分別類似于數(shù)據(jù)類型的聲明和實現(xiàn)。
 請注意,本文中所有引用的代碼均基于TVM存儲庫的master分支commit 4cad71d。將使用一個示例posit數(shù)據(jù)類型,該數(shù)據(jù)類型可以src/target/datatype/posit/posit-wrapper.cc在TVM下找到,并可以在帶有USE_BYODT_POSIT標志的TVM中進行編譯。4
 數(shù)據(jù)類型注冊
 要注冊數(shù)據(jù)類型,用戶為數(shù)據(jù)類型分配一個名稱和一個類型代碼,其中類型代碼來自可用于自定義數(shù)據(jù)類型的未使用類型代碼的范圍。
 tvm.target.datatype.register(‘posit’, 150)
 上面的代碼’posit’使用類型代碼150注冊數(shù)據(jù)類型。此注冊步驟允許TVM解析使用自定義類型的程序:
 x = relay.var(‘x’, shape=(3, ), dtype=‘float32’)
 y = relay.var(‘y’, shape=(3, ), dtype=‘float32’)
 x_posit = relay.cast(x, dtype=‘custom[posit]16’)
 y_posit = relay.cast(y, dtype=‘custom[posit]16’)
 z_posit = x_posit + y_posit
 z = relay.cast(z_posit, dtype=‘float32’)
 program = relay.Function([x, y], z)
 print(program)
v0.0.4
fn (%x: Tensor[(3), float32], %y: Tensor[(3), float32]) {
%0 = cast(%x, dtype=“custom[posit]16”);
%1 = cast(%y, dtype=“custom[posit]16”);
%2 = add(%0, %1);
cast(%2, dtype=“float32”)
}
上述管型的程序float32的輸入x和y 到positS,將相加,并注塑結(jié)果回float32。一旦posit注冊了類型,TVM便可以解析特殊dtype語法 custom[],其中是為該類型注冊的名稱。此語法還支持通常的 x格式。在這里,16用來表示每個posit都是16位寬。(車道數(shù)默認為1。)
 降低功能注冊
 盡管TVM可以解析上述程序,但它尚不能編譯,TVM尚不了解如何在該posit類型上編譯操作。為了編譯這些程序,為自定義數(shù)據(jù)類型注冊了降級函數(shù),這有助于TVM將操作轉(zhuǎn)換為它可以理解和編譯的內(nèi)容。
 通常,不希望用戶直接將操作降低到LLVM或CUDA。相反,可以通過一些簡單的技巧,將大多數(shù)使用自定義數(shù)據(jù)類型的代碼,簡化為不使用自定義數(shù)據(jù)類型的代碼。可以依靠本機TVM來理解和編譯代碼。
 
圖1:用戶注冊的降低功能的預期結(jié)果。降低功能應將使用自定義數(shù)據(jù)類型的程序轉(zhuǎn)換為本機TVM可以理解和編譯的程序(在這種情況下,需要使用兩個uint16_t來調(diào)用外部庫)。
 圖1顯示了一種常見模式。假設有興趣探索這種posit類型,并選擇通過“自定義數(shù)據(jù)類型”框架將posit仿真庫(例如Stillwater Universal)插入TVM中來運行某些工作負載。工作量是一個簡單的程序,其中添加了兩個posit輸入。本機TVM不了解如何實現(xiàn)posit加法-但有一個實現(xiàn)數(shù)據(jù)類型的庫,所以不是必需的!該庫包含posit加法的實現(xiàn)以及其它運算符,例如乘法和平方根。要實現(xiàn)此posit添加,只想調(diào)用庫。因此,Add節(jié)點應成為Call節(jié)點,并調(diào)出一個函數(shù)(調(diào)用它Posit16es2Add)在庫中。為了將輸入posit的位存儲在TVM可以理解的類型內(nèi),使用16位無符號整數(shù)。生成的程序是TVM可以理解和編譯的程序,它是對外部庫函數(shù)的調(diào)用,使用兩個無符號整數(shù)。
 為了實現(xiàn)上述降低,為以下對象注冊了降低功能posit:
 tvm.target.datatype.register_op(
 tvm.target.datatype.create_lower_func({16: ‘Posit16es2Add’}),
 ‘Add’, ‘llvm’, ‘posit’)
 上面的代碼為特定的運算符(Add),編譯目標(LLVM),數(shù)據(jù)類型(posit)和位長(16)注冊了一個降低函數(shù)。第一個參數(shù)是降低功能。這可以是采用TVM IR節(jié)點并返回新的TVM IR節(jié)點的任何功能。在案例中,使用Bring Your Own Datatypes框架提供的幫助程序功能。 tvm.target.datatype.create_lower_func({16:‘Posit16es2Add’}) 為上述通用模式創(chuàng)建降低功能。結(jié)果函數(shù)將給定節(jié)點的參數(shù)轉(zhuǎn)換為uint16_t,將節(jié)點本身轉(zhuǎn)換為對給定函數(shù)名稱的調(diào)用(在這種情況下,位長度’Posit16es2Add’為posits)。將一個字典傳遞給create_lower_func,以便TVM可以根據(jù)數(shù)據(jù)類型的位長,將其分配給適當?shù)暮瘮?shù)名稱。
 為了實現(xiàn)自定義數(shù)據(jù)類型,用戶將需要為想要運行的工作負載中的每個算子注冊一個降低功能。對于像ResNet這樣的網(wǎng)絡,將大約有10個算子,包括Add,Div,各種Cast和Max。在測試中,注冊數(shù)據(jù)類型和所有降低功能需要大約40行Python。一旦注冊了所有需要的算子,就可以像其它任何TVM程序一樣,輕松地運行自定義數(shù)據(jù)類型的工作負載!
 包起來wrapping up
 自定義數(shù)據(jù)類型框架將用戶定義的數(shù)據(jù)類型引入TVM。鼓勵數(shù)據(jù)類型研究人員在研究中使用TVM;同樣,引起深度學習社區(qū)中對自定義數(shù)據(jù)類型的興趣。有關“攜帶自己的數(shù)據(jù)類型”框架的更多文檔。
總結(jié)
以上是生活随笔為你收集整理的TVM自定义数据类型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: TinyML-TVM如何驯服TinyML
- 下一篇: 向Relay添加算子
