自主数据类型:在TVM中启用自定义数据类型探索
自主數據類型:在TVM中啟用自定義數據類型探索
介紹
在設計加速器時,一個重要的決定是如何在硬件中近似地表示實數。這個問題有一個長期的行業標準解決方案:IEEE 754浮點標準.1。然而,當試圖通過構建高度專業化的設計來最大限度地利用硬件時,使用通用IEEE 754浮點有意義嗎?如果知道工作負載的數字需求,是否可以構建一個更小、更快或更省電的數據類型?答案是肯定的!研究人員已經開始在學術和工業加速器設計中嘗試新的數據類型。例如,Google的張量處理單元(TPU)使用bfloat類型:一個被截斷為16位的單精度IEEE浮點。由于許多深度學習工作負載對數值要求不嚴格,這種截斷通常對模型精度沒有影響,同時立即將存儲成本降低一半。
然而,在研究人員開始為數據類型構建硬件之前,首先需要確定數據類型在工作負載中的數值行為。這通常需要首先構建其數據類型的軟件仿真版本(例如Berkeley SoftFloat或libposit),然后將數據類型直接插入工作負載,以查看工作負載如何使用數據類型執行。更好的方法是將數據類型直接集成到編譯器本身中,這樣就可以編譯許多不同的工作負載來使用該數據類型。這兩種路徑都可能是乏味的,鑒于現代編譯器的規模和復雜性,后一種路徑往往變得不可管理。一個來自GitHub的例子顯示有人將posit數據類型侵入TensorFlow。結果是237次提交,添加了近6000行代碼,并在代碼庫中處理了200多個文件,這僅僅是添加了一個數據類型!對許多研究人員來說,這樣的工作量是令人望而卻步的。
為了解決這些問題,提出了自帶數據類型框架。該框架允許用戶將模擬的數據類型插入TVM,從而可以輕松地在深度學習工作負載中探索新的數據類型。與上面的Tensorflow示例中的positions不同,它支持編譯器中的單個新數據類型,而Bring Your Own datatype框架支持各種各樣的用戶定義類型。
自主數據類型
“自主數據類型”框架的目標是使用戶能夠使用自定義數據類型運行深入學習的工作負載。在Bring Your Own Datatypes框架中,“datatype”表示標量類型:例如float或uint。不處理像Intel Flexpoint這樣復雜的數據塊格式(floating point or Intel’s Flexpoint)。此外,只聲明支持這些標量數據類型的軟件仿真版本;不明確支持在自定義數據類型硬件上編譯和運行。
TVM中的每個張量都分配了一個類型代碼,它定義了張量中標量的數據類型。這些類型代碼在TVM中具有硬編碼的含義,映射到諸如int和float之類的常見數據類型。然而,絕大多數類型代碼都是未使用的。“自帶數據類型”框架允許用戶聲明這些未使用的類型代碼,并在運行時添加自己的新數據類型。
該框架是作為注冊表來實現的,它位于TVM的常規數據類型架構上。用戶與數據類型注冊表交互的主要方式有兩種:第一種是數據類型注冊,第二種是降低函數注冊。這些步驟分別類似于數據類型的聲明和實現。
本文中引用的所有代碼都基于TVM存儲庫的主分支提交4cad71d。將使用一個示例posit數據類型,該數據類型位于src/target/datatype/posit/posit-wrapper.cc并且可以在TVM中使用USE_BYODT_POSIT標志進行編譯。
Datatype Registration
為了注冊數據類型,用戶為數據類型分配一個名稱和一個類型代碼,其中類型代碼來自自定義數據類型可用的未使用類型代碼范圍。
tvm.target.datatype.register(‘posit’, 150)
The above code registers the ‘posit’ datatype with type code 150. This registration step allows TVM to parse programs which use the custom type:
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來表示每個posite的寬度是16位。(通道數默認為1。)
Lowering Function Registration
雖然TVM可以解析上述程序,但它還不能編譯它,因為TVM還不知道如何在posit類型上編譯操作。為了編譯這些程序,為自定義數據類型注冊了降低函數,這有助于TVM將操作轉換成它可以理解和編譯的內容。
一般來說,用戶不需要直接將操作降低到LLVM或CUDA。相反,大多數使用自定義數據類型的代碼可以通過一些簡單的技巧被簡化為不使用自定義數據類型的代碼。然后,可以依賴于本機TVM來理解和編譯代碼。
圖1:用戶注冊的降低函數的預期結果。
降低函數應該將使用自定義數據類型的程序轉換為本機TVM可以理解和編譯的程序(在本例中,調用外部庫,使用兩個uint16)。
圖1顯示了一個通用模式。假設對探索posit類型感興趣,并選擇通過將posit仿真庫(例如Stillwater Universal)通過自帶的數據類型框架插入TVM來運行一些工作負載。工作量是一個簡單的程序,它添加了兩個posite輸入。Native-TVM不知道如何實現posit加法,但它不需要,因為有一個實現數據類型的庫!這個庫包含一個正數加法的實現,以及乘法和平方根等其他運算符。為了實現這個posit加法,只想調用庫。因此,Add節點應該成為一個調用節點,調用庫中的一個函數(稱之為position16es2add)。為了將輸入位置的位存儲在TVM理解的類型中,使用16位無符號整數。生成的程序是TVM可以理解和編譯的程序,它只是調用一個外部庫函數,取兩個無符號整數。
為了實現上述降低,為posit注冊了一個降低功能:
tvm.target.datatype.register_op(
tvm.target.datatype.create_lower_func({16: ‘Posit16es2Add’}),
‘Add’, ‘llvm’, ‘posit’)
上面的代碼為特定運算符(Add)、編譯目標(LLVM)、數據類型(posit)和位長度(16)注冊了一個降低函數。第一個參數是lowering函數。這可以是任何獲取TVM IR節點并返回新TVM IR節點的函數。在例子中,使用了Bring Your Own Datatypes框架提供的helper函數。tvm.target.datatype.create_lower_func({16:‘Posit16es2Add’})為上述公共模式創建一個降低函數。結果函數將給定節點的參數轉換為uint16_t,然后將節點本身轉換為對給定函數名的調用(在本例中,對于位長度為16的position16es2add)。我們傳遞一個字典來create_lower_func,以便TVM可以根據數據類型的位長度將其分派到適當的函數名。
要實現自定義數據類型,用戶將需要為他們希望運行的工作負載中的每個運算符注冊一個降低函數。對于像ResNet這樣的網絡,大約有10個操作符,包括Add、Div、各種類型轉換和Max。在測試中,注冊一個數據類型和所有的降低函數需要大約40行Python。一旦注冊了所有需要的算子,自定義數據類型工作負載就可以像任何其他TVM程序一樣輕松運行!
結束
“自帶數據類型”框架將用戶定義的數據類型引入TVM。希望這將鼓勵數據類型研究人員在研究中使用TVM;同樣,希望這將激發深度學習社區中對定制數據類型的興趣。有關“自帶數據類型”框架的更多文檔,請訪問“將自己的數據類型帶到TVM開發人員”。
總結
以上是生活随笔為你收集整理的自主数据类型:在TVM中启用自定义数据类型探索的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TinyML-TVM是如何驯服Tiny的
- 下一篇: Tesla T4视频编码性能分析