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