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