3月28日,智東西公開課組織的「自動(dòng)駕駛新青年講座」第16講順利完結(jié)。在這一講中,地平線工具鏈核心開發(fā)者楊志剛以《基于征程5芯片的Transformer量化部署實(shí)踐與經(jīng)驗(yàn)》為主題進(jìn)行了直播講解。
楊志剛首先介紹了Transformer發(fā)展趨勢(shì)及在嵌入式智能芯片上部署的問題,之后重點(diǎn)講解了以征程5為例的嵌入式智能芯片的算法開發(fā)流程,并對(duì)以SwinT為例的量化精度提升和部署性能優(yōu)化做了詳細(xì)解讀,最后分析了如何在征程5上既快又好地部署Transformer模型。
(資料圖)
本次講座分為主講和Q&A兩個(gè)環(huán)節(jié),以下則是主講回顧:
大家好,我叫楊志剛,在地平線主要負(fù)責(zé)天工開物工具鏈的開發(fā),比如征程2、征程3、征程5上的系列量化工具和算法工具的一些開發(fā)和驗(yàn)證工作。因此和我們公司內(nèi)部的算法團(tuán)隊(duì)、編譯器團(tuán)隊(duì)都有比較深入的接觸。
今天我分享的主題是《基于征程5芯片的Transformer量化部署實(shí)踐與經(jīng)驗(yàn)》,然后也會(huì)從量化和部署兩個(gè)方面分析如何讓Swin-Transformer在征程5上跑得既快又好。
以下是本次講座的主要內(nèi)容,大概分為4個(gè)部分:
1、Transformer發(fā)展趨勢(shì)及在嵌入式智能芯片上部署的問題
2、以征程5為例的嵌入式智能芯片的算法開發(fā)流程
3、以SwinT為例的量化精度提升和部署性能優(yōu)化
4、如何在征程5上既快又好地部署Transformer模型
01
Transformer發(fā)展趨勢(shì)
及在嵌入式智能芯片上部署的問題
第一部分是Transformer的發(fā)展趨勢(shì)以及它在嵌入式智能芯片上的部署問題。最近,我估計(jì)大家都對(duì)Transformer勢(shì)不可擋的趨勢(shì)有所了解,它確實(shí)已經(jīng)在NLP領(lǐng)域甚至在圖像領(lǐng)域都起到了不可替代的作用。比如從2017年Transformer被提出來以后,因?yàn)樗瑥?qiáng)的序列建模和全局建模的能力,所以Transformer模型結(jié)構(gòu)其實(shí)已經(jīng)在整個(gè)智能模型結(jié)構(gòu)里有著越來越重要的地位。
一方面,它引領(lǐng)了一個(gè)大模型的潮流(當(dāng)然這個(gè)潮流主要是指NLP領(lǐng)域),比如最近比較火的BERT、GPT等這樣以Transformer為基礎(chǔ)的模型其實(shí)在NLP領(lǐng)域已經(jīng)起到了一些根本性的變革,還有像GPT這種模型的參數(shù)量從億級(jí)別到千億級(jí)別,我們能看到 Transformer的容量還有模型發(fā)展的趨勢(shì)都朝著越來越大的方向發(fā)展。當(dāng)然越來越大的前提是我們可以通過更大的模型去獲取更高的精度,所以這個(gè)量級(jí)基本上已經(jīng)從億級(jí)別到了千億級(jí)別、萬億級(jí)別。
另外一方面,Transformer不僅在NLP領(lǐng)域引領(lǐng)了大模型的潮流,而且在圖像領(lǐng)域也有著越來越重要的地位。我這里截的圖(如圖一所示)主要是它在Backbone也就是分類ImageNet上面的一個(gè)趨勢(shì)圖,可以看到隨著它的計(jì)算量、參數(shù)量越來越大,它的正確率也會(huì)越來越高。
事實(shí)上它在常見的基礎(chǔ)任務(wù)中(比如說常見的檢測(cè)、分割、跟蹤等這樣的任務(wù))制作刷榜的時(shí)候,可以看到前幾名里基本上已經(jīng)遍地都是Transformer的影子了。所以比如常見的以Swin-Transformer為例的encoder,以DETR為例的decoder,還有時(shí)序、BEV等這種用Transformer做特征融合的,不管在圖像領(lǐng)域的哪一個(gè)階段,我們都可以把Transformer的特性和CNN結(jié)合,甚至替代CNN的模型結(jié)構(gòu)。無論是替代CNN還是和CNN結(jié)合,這兩個(gè)發(fā)展方向都已經(jīng)成為視覺領(lǐng)域的常用做法,所以整體上來說Transformer在現(xiàn)在的圖像領(lǐng)域里已經(jīng)是無法繞開的模型結(jié)構(gòu)了。
其實(shí)我在標(biāo)題里面新加了一句話“通向通用人工智能的一扇門”,當(dāng)然這個(gè)話我不敢說,我也是在一些別的信息上看到的。現(xiàn)在基本上認(rèn)為,在我們做特征提取的階段中,Transformer是通用人工智能的一種組件,所以也被稱為一扇門,不過這個(gè)不是我們今天要分享的重點(diǎn)。
Transformer確實(shí)在模型結(jié)構(gòu)上起著越來越重要的作用,但是另一方面,它在嵌入式端部署的問題也會(huì)受到越來越多的重視。具體來說,Transformer越來越大和嵌入式智能芯片部署這兩個(gè)方向的出發(fā)點(diǎn)是有區(qū)別的,比如說Transformer模型在發(fā)展上是越做越大、越做越寬,但是嵌入式智能芯片因?yàn)槭艿匠杀尽⒐牡确矫娴南拗疲瑢?dǎo)致它在算力、帶寬等很多功能方面受限,這就導(dǎo)致當(dāng)前的嵌入式智能芯片不管是部署稍微大一點(diǎn)的還是小一點(diǎn)的Transformer模型,都會(huì)有一些吃力。
這里我講三個(gè)主要的例子。第一個(gè)因?yàn)榍度胧街悄苄酒艿匠杀竞凸牡南拗疲运乃懔Α挕?nèi)存等方面都會(huì)受到一定的限制,這就直接導(dǎo)致像Transformer這樣的大模型的部署會(huì)受到限制。因?yàn)槿绻靡粋€(gè)大模型去部署一個(gè)小算力的平臺(tái),就算不是Transformer哪怕只是普通的CNN,性能顯而易見的可能會(huì)極差,更何況是Transformer這樣的大模型在小算力平臺(tái)上的部署很明顯會(huì)有一些缺陷。
第二個(gè)特征是目前市面上比較流行的嵌入式智能芯片通常都會(huì)以低精度的方式來處理部署的模型。當(dāng)然低精度之外也會(huì)少量的去支持一定精度的浮點(diǎn),這個(gè)原因和算力、帶寬受限是一樣的,主要還是從成本、功耗等這方面的情況考慮的,所以這就直接導(dǎo)致了如果想要在嵌入式智能芯片上部署的話,那么這個(gè)模型可能要經(jīng)過一些量化,但同時(shí)量化不能有一定的精度損失。否則如果精度損失比較大的話,這個(gè)部署就是沒有意義的。
第三點(diǎn)是芯片的發(fā)展其實(shí)是滯后于算法的。關(guān)于這一點(diǎn),在我們公司羅老師之前的分享當(dāng)中(地平線羅恒博士:如何打造一顆好的自動(dòng)駕駛AI芯片)有比較詳細(xì)的描述,大家如果有興趣可以去看一下。簡(jiǎn)單來說,就是芯片從設(shè)計(jì)到正式量產(chǎn)需要經(jīng)過一個(gè)漫長(zhǎng)的過程,這個(gè)過程可能是2-4年。因此現(xiàn)在市面上流行的嵌入式智能芯片基本上是源自于1-2年甚至更長(zhǎng)時(shí)間之前的設(shè)計(jì),而那時(shí)候設(shè)計(jì)的嵌入式智能芯片很大概率沒有考慮Transformer的情況,因?yàn)槟菚r(shí)候可能大部分市面上流行的還是以CNN為主的模型,所以這樣就會(huì)造成現(xiàn)在大部分嵌入式智能芯片對(duì)CNN的部署非常友好,但是對(duì)Transformer的部署存在一定的gap。今天我們就要討論這個(gè)gap到底來自哪里。
下面我們?cè)敿?xì)拆解一下剛剛講到的問題:Transformer部署過程中會(huì)遇到哪些問題?
第一個(gè)是量化問題,其實(shí)Transformer的量化問題現(xiàn)在我們能在很多社區(qū)的論文或者一些博客當(dāng)中看到。首先,它為什么要經(jīng)過量化?我剛剛簡(jiǎn)單講了一下,它是從成本、功耗等方面考慮的。如果用int8或者低比特的量化部署,它的好處是顯而易見的,比如可以降低功耗、提高計(jì)算速度、減少內(nèi)存和存儲(chǔ)的占用。這里有個(gè)數(shù)據(jù)對(duì)比,Transformer部署的時(shí)候其實(shí)會(huì)有一些常見的問題,如果熟悉量化訓(xùn)練的同學(xué)應(yīng)該比較清楚,Transformer模型當(dāng)中有大量的非線性函數(shù),比如說像GeLU、LayerNorm這樣的東西。所以它激活值的輸出和高斯分布會(huì)有比較大的差異,這就直接導(dǎo)致了很大一部分之前在CNN中最常用的對(duì)稱量化的方法,可能會(huì)出現(xiàn)很明顯的精度問題。
如果要解決Transformer的量化精度問題,社區(qū)有很多常見的經(jīng)驗(yàn)。我這里舉兩個(gè)例子,比如用非對(duì)稱量化等方法去處理分布不均衡或高斯分布差異較大的情況,還有一些情況可能會(huì)直接在硬件上使用浮點(diǎn)的SoftMax或LayerNorm,這種情況肯定是可以解決量化問題的,但實(shí)際上我們需要和硬件結(jié)合,而硬件上到底能不能支持浮點(diǎn)或者能不能支持非對(duì)稱性量化是我們需要考慮的另一個(gè)問題。我們今天要講的征程5的平臺(tái),它就是一個(gè)純int8的嵌入式智能平臺(tái),如果要在一個(gè)純int8的嵌入式智能平臺(tái)上去部署一個(gè)浮點(diǎn)的SoftMax或者LayerNorm顯然是不合理的。甚至有一些情況就算它是純int8的,可能也不支持非對(duì)稱量化,所以我們?nèi)绻鉀QTransformer量化不友好的問題,還需要結(jié)合硬件的特點(diǎn)來考慮。
Transformer模型部署的第二個(gè)問題是Transformer對(duì)算力的要求比較高。開始也講到,Transformer是近年來最受關(guān)注的神經(jīng)網(wǎng)絡(luò)模型,而Transformer在機(jī)器視覺領(lǐng)域最重要也是最徹底的應(yīng)用就是Swin Transformer,這個(gè)工作也得到了機(jī)器視覺領(lǐng)域最高的獎(jiǎng)項(xiàng),馬爾獎(jiǎng)。這里我們以Swin-Transformer為例。我們考慮Swin-Transformer這個(gè)最小的模型,它的計(jì)算量大概是4.5G左右。
說4.5G可能很多人沒有直觀概念,我做了兩個(gè)簡(jiǎn)單的對(duì)比,這就約等于我們常用模型里的EffcientNetB4和ResNet50。說到ResNet50,很多人就有概念了,如果我們用ResNet50的水平去做部署的話,其實(shí)市面上很多算力稍微低一點(diǎn)的嵌入式智能芯片部署就會(huì)有點(diǎn)吃力了。如果有人知道地平線的歷史,比如地平線的上一代芯片跑ResNet50是可以跑的,但它的效率不是很高,而且這還是CNN的部署效率,如果在Transformer上效率會(huì)進(jìn)一步降低。這樣考慮的話,整個(gè)SwinT部署的前提條件就是芯片的算力達(dá)到一定的要求。
除了剛才提到的SwinT的基礎(chǔ)還有量化問題之外,還有一個(gè)比較重要的問題就是我們一直在講的Transformer和CNN模型到底有哪些區(qū)別?為什么說我的芯片可以部署ResNet50,但是沒法部署Transformer呢?其實(shí)這就是CNN模型和Transformer模型之間一個(gè)比較重要的區(qū)別。如果我們比較熟悉CNN模型,就會(huì)知道CNN基本上從頭到尾只有一個(gè)卷積,或者有少量的非卷積算子,如RoiAlign。所以整個(gè)CNN模型實(shí)際上是以卷積和矩陣乘為主的。換句話說,這類算子的特征是以計(jì)算密集型算子為主。我們?cè)缙诘闹悄苄酒瑸槭裁床l(fā)能力強(qiáng),因?yàn)橹悄苄酒O(shè)計(jì)之初就是以這樣的CNN模型為出發(fā)點(diǎn)的,它的重點(diǎn)是利用并發(fā)去解決計(jì)算密集型的問題。
但在Transformer里情況是不一樣的,Transformer里除了我們剛剛說到的卷積和矩陣乘以外,還有大量像Elementwise、Reduce這樣的訪存密集型算子。訪存密集型算子和計(jì)算密集型會(huì)有明顯的區(qū)別,會(huì)要求我的訪存帶寬或者訪存本身的存儲(chǔ)容量比較高,同時(shí)不規(guī)則的數(shù)據(jù)搬運(yùn)比較多,不像CNN中,一個(gè)4d-tensor可以從頭到尾,而且我的4d-tensor的規(guī)則可能非常明顯:W/H維度做下載樣,C維度做特征變長(zhǎng),這種4d-tensor的特征對(duì)整個(gè)嵌入式智能平臺(tái)是非常友好的。
但Transformer中不規(guī)則的數(shù)據(jù)搬運(yùn)會(huì)明顯多很多,比如像Swin-Transformer,我們做window partition和window reverse時(shí)會(huì)有很多Reshape和Transpose的操作,這種操作帶來的問題是效率會(huì)進(jìn)一步降低。事實(shí)上這個(gè)問題是整個(gè)Transformer或者說整個(gè)芯片行業(yè)都會(huì)遇到的一個(gè)問題,不僅是嵌入式智能芯片會(huì)有這樣的問題,訓(xùn)練芯片也會(huì)有類似的問題。
我記得早幾年前英偉達(dá)在測(cè)試上做過一個(gè)OPS的簡(jiǎn)單統(tǒng)計(jì),這個(gè)細(xì)節(jié)就不說了,大體上的結(jié)論是純粹計(jì)算型的算子,比如卷積和矩陣乘這樣的算子在計(jì)算量上占比大概99.8%,但實(shí)際上它在英偉達(dá)芯片(就訓(xùn)練芯片上而言)的執(zhí)行時(shí)間只有60%。換句話說,訓(xùn)練芯片本身有大量的占比很低的非計(jì)算型算子,但這些算子卻花費(fèi)了40%的時(shí)間。這個(gè)問題在Transformer部署嵌入式智能芯片時(shí),會(huì)被很大程度的放大。常見的嵌入式智能芯片可能會(huì)有大量的時(shí)間浪費(fèi)在訪存算子和不規(guī)則數(shù)據(jù)搬運(yùn)上面。
總結(jié)一下第一部分,就是嵌入式智能芯片由于受到成本、功耗等方面的限制,設(shè)計(jì)思路和實(shí)際上需要部署的Transformer模型之間有較大的區(qū)別。
02
以征程5為例的
嵌入式智能芯片的算法開發(fā)流程
第二部分重點(diǎn)講一下嵌入式智能芯片的開發(fā)流程,這里雖然是以征程5為例,但實(shí)際上我們通過目前的調(diào)研或者就目前大部分嵌入式智能芯片總體上看,開發(fā)流程基本上是一致的,所以換句話說,大家要解決的問題基本上類似。
首先簡(jiǎn)單講一下征程5的基本情況,這在之前的系列課里有比較充分的描述,是講征程5是怎么設(shè)計(jì)出來的,然后針對(duì)智駕平臺(tái)有怎樣的創(chuàng)新或者怎樣的用處,我就不多講了,這里我主要講這幾個(gè)基本情況是如何符合Transformer部署的前提條件的。然后這個(gè)也和我們剛才說的常見的嵌入式智能芯片部署的缺陷對(duì)應(yīng)上。
第一點(diǎn)是大算力計(jì)算平臺(tái),首先我們得有一個(gè)大算力計(jì)算平臺(tái)作為前提,才有可能去部署Transformer系列模型。如果是小算力的話,剛剛也講了,比如上一代征程3想部署Transformer可能就比較困難。
第二個(gè)重點(diǎn)是豐富的算子支持。我們?cè)趧偛臫ransformer的結(jié)構(gòu)圖中也能看到這點(diǎn)為什么比較重要,CNN模型的主體是以卷積為主,配合少量其他算子,如RoiAlign等。但Transformer中其實(shí)有很多很雜的算子,比如說像LayerNorm、SoftMax,還有Reshape、Transpose等,所以說智能芯片部署Swin-Transformer或者其他Transformer的前提條件除了大算力之外,還需要非常豐富的算子知識(shí)。
另外是最強(qiáng)的計(jì)算性能,我覺得在我們Transformer的部署中其實(shí)沒有太多的參考價(jià)值,因?yàn)樗且訡NN為基礎(chǔ)的模型進(jìn)行統(tǒng)計(jì)的,也就是以計(jì)算密集型的模型統(tǒng)計(jì)的,但Transformer的能力跟這個(gè)還是有比較明顯的差距。
最后一點(diǎn)是超低功耗,這點(diǎn)也需要多講,因?yàn)樗旧硪彩钦鞒?的亮點(diǎn)之一。地平線的征程5和天工開物工具鏈,其實(shí)已經(jīng)積累了一套比較完善的軟件工具,這套軟件工具從用戶訓(xùn)練的浮點(diǎn)模型開始,然后做量化、訓(xùn)練、編譯、部署、優(yōu)化等,最終部署到嵌入式端。以量化為例,基本上整個(gè)芯片工具鏈會(huì)提供PTQ的后量化和QAT的量化訓(xùn)練這兩種量化方式。在優(yōu)化編譯階段,可以提供Checker、Calibrator和分析、仿真等工具,最終可以保證用戶的模型經(jīng)過量化、優(yōu)化后,能部署到嵌入式端。這里需要說一下早期的天工開物整個(gè)工具鏈的積累其實(shí)是基于CNN模型的,后面我也會(huì)講為什么基于CNN模型積累下的整個(gè)芯片工具鏈在處理Transformer模型時(shí),不管是量化還是優(yōu)化部署方面都有一定缺陷。
下面是如何利用整個(gè)天工開物工具鏈幫助用戶把浮點(diǎn)模型快速部署到嵌入式芯片上。這就是我一開始講的,各家的芯片工具鏈、各家的嵌入式智能芯片的部署流程已經(jīng)趨于相同了,整體上都是從算法遷移代價(jià)足夠小的角度考慮,所以基本上已經(jīng)是一個(gè)標(biāo)準(zhǔn)流程了。然后我們來看一下這個(gè)流程,從浮點(diǎn)訓(xùn)練開始,經(jīng)過PTQ后量化的校準(zhǔn),如果后量化的精度滿足要求我們就可以直接編譯優(yōu)化、最終部署;如果不滿足要求可以反過來去做量化感知訓(xùn)練,量化感知訓(xùn)練的目的是使精度達(dá)到要求,并最終去做模型定義。那么如果我們要處理這種Transformer部署優(yōu)化的流程,要處理的兩個(gè)重點(diǎn)就是量化調(diào)優(yōu)和編譯優(yōu)化,主要是利用量化公式去提升量化精度。第二個(gè)是在編譯過程中,用手動(dòng)或自動(dòng)的方式去獲取更好的部署性能。
天工開物工具鏈?zhǔn)状伟裇win-Transformer部署在征程5上,其實(shí)沒有遇到太多困難,當(dāng)然這個(gè)前提我剛剛已經(jīng)講了,首先它有大算力,然后豐富的算子知識(shí),這兩點(diǎn)我們?cè)谡鞒?上的部署過程比較簡(jiǎn)單。這里簡(jiǎn)單講一下支持哪些算子,其實(shí)了解Swin-Transformer的人應(yīng)該都了解,比如說有Reshape、roll、LayerNorm、matmul等。這里為什么需要算子完全支持?我們一開始做這個(gè)事情的時(shí)候發(fā)現(xiàn) ONNX opset上面沒有完全支持roll,所以當(dāng)時(shí)測(cè)Swin-Transformer在其他品牌上的結(jié)果時(shí),還需要單獨(dú)處理roll的情況。最近,我們發(fā)現(xiàn)opset上已經(jīng)支持roll了,但另一個(gè)方面說明一些嵌入式智能芯片的平臺(tái)不管是由于使用的工具還是最后部署的芯片的限制,想做到算子完全支持有一定的門檻。
第二點(diǎn)就是量化精度,總體上來看,量化精度經(jīng)過QAT之后,大體上量化精度損失了4個(gè)點(diǎn),這里需要說明一下就是量化損失4個(gè)點(diǎn)可能看起來不是那么糟糕,但是我們?cè)谥暗墓ぞ哝湲?dāng)中已經(jīng)沉淀了一系列的精度debug工具和提升方法。不過它的局限性可能是以CNN為主的模型,而CNN模型我們通常可以快速定位出它的量化損失來自哪里,但是當(dāng)有一些經(jīng)驗(yàn)映射到Transformer上時(shí)沒那么通用。因此在CNN經(jīng)驗(yàn)積累的基礎(chǔ)上,我們才得到了損失4個(gè)點(diǎn)的結(jié)果。
最后一個(gè)是首次部署的FPS小于1,可以說性能極低,基本上不可以用。這里我們參考一下征程5上其他一些CNN的模型數(shù)據(jù),比如ResNet50大概會(huì)超過600FPS,EffcientNet LiteB4大概會(huì)超過1000FPS。實(shí)際上如果是地平線自己設(shè)計(jì)的高效模型這個(gè)數(shù)字還會(huì)更大。但我們要解決的不是CNN的問題,而是CNN和Transformer之間的gap沒有解決的問題,所以Transformer部署的瓶頸顯然不能直接使用CNN的經(jīng)驗(yàn)。
第二部分總結(jié)來說就是在征程5上沉淀出的天工開物工具鏈的標(biāo)準(zhǔn)流程,雖然可以很好的解決CNN的問題,但并不能完整的解決Transformer量化部署的問題。所以接下來以Swin-Transformer為例,講解如何結(jié)合征程5平臺(tái)做量化精度提升和部署性能優(yōu)化。
03
以SwinT為例的
量化精度提升和部署性能優(yōu)化
首先第一點(diǎn)是我們說在CNN上積累了一套基礎(chǔ)配置或者說標(biāo)準(zhǔn)流程,那這個(gè)標(biāo)準(zhǔn)流程到底是指什么?這就需要講一下天工開物中量化訓(xùn)練的基礎(chǔ)配置,不過這里不需要講PTQ的后量化,因?yàn)镻TQ后量化除了方法的選擇上有一些空間外,訓(xùn)練的空間不是很大,所以我們重點(diǎn)講一下量化訓(xùn)練的技術(shù)配置。在之前的演示中,我們把PTQ跟QAT分開看,即要么執(zhí)行PTQ的后量化,要么使用 QAT的量化訓(xùn)練。
但事實(shí)上我們?cè)谝恍┙?jīng)驗(yàn)中發(fā)現(xiàn),如果我們使用PTQ的后量化參數(shù)去給QAT做初始化時(shí),就可以給QAT的初始狀態(tài)提供一個(gè)更高的起點(diǎn),這也可以保證QAT的量化訓(xùn)練收斂的更快。所以目前的量化訓(xùn)練,不管是CNN還是Transformer都是PTQ+QAT這樣的流程,這基本上已經(jīng)變成一種標(biāo)準(zhǔn)化操作了。另外,常見的一些CNN配置,比如全局使用int8,只在輸出階段使用int32。還有像QAT過程中有一些超參的配置,比如說Lr我們一般是10?3、10??,Epoch大概是浮點(diǎn)的10%-20%,這個(gè)我不需要多講,如果有量化訓(xùn)練經(jīng)驗(yàn)的同學(xué),可能對(duì)這個(gè)比較了解。
然后講一下量化精度如何進(jìn)行調(diào)優(yōu),其實(shí)我們非常不建議在PTQ嘗試更多的方法或者盲目的調(diào)整QAT的參數(shù),而是使用更加合理的可以快速分析出是什么導(dǎo)致量化損失誤差的方法。比如說我們內(nèi)部一般會(huì)把量化損失的誤差從工具角度分為三個(gè)部分,比如說是算子誤差、模型誤差還是精度誤差?可能大家都比較了解單純的一個(gè)算子經(jīng)過量化標(biāo)準(zhǔn)之后的誤差是怎樣的。模型誤差是指,比如在相同的輸入情況下,我們?nèi)プR(shí)別出有一些FeatureMap比較大或者說分布非常不均衡,這種FeatureMap其實(shí)對(duì)量化會(huì)非常不友好。還有包括模型本身的誤差,可能還包括weight這些參數(shù),比如訓(xùn)練下來如果也有一些比較大的weight,其實(shí)這樣對(duì)量化也是不友好的。另外就是精度誤差,我們一般使用分布量化的方式,看模型中哪一個(gè)模塊對(duì)最終整個(gè)數(shù)據(jù)集+模型的誤差最大,這就是精度誤差。
下面這兩個(gè)圖是我們量化損失分析工具提供的一個(gè)可視化結(jié)果,我們可以從非常直觀的角度看到算子或模型本身的誤差,還有一些weight分布的誤差。第二個(gè)問題是如果我們識(shí)別出誤差比較大的計(jì)算應(yīng)該怎么處理?簡(jiǎn)單的方法是我一開始講的浮點(diǎn)操作,但如果芯片不支持浮點(diǎn)操作的話,那就應(yīng)該尋求其他更高精度的表達(dá),比如征程5上是支持一些少量int16的,但int16可能不是原生支持,因?yàn)槲覀兛梢酝ㄟ^簡(jiǎn)單的操作用一些int8拼湊出來一個(gè)int16。所以如果我們想對(duì)一些算子做更高精度的表達(dá)時(shí),優(yōu)先采取的就是int16。
另一方面,如果我們要在下一代芯片或者一個(gè)對(duì)Transformer部署友好的嵌入式智能芯片上更好的部署Transformer,可能有些算子用浮點(diǎn)會(huì)有更好的結(jié)果。我們這里面主要使用int16的方法來解決量化誤差的問題。以LayerNorm為例,在量化過程中我們其實(shí)是將LayerNorm拆成具體的算子,比如加減乘除、開方、add等操作,然后所有的中間結(jié)果除了輸入輸出之外,像mean、加減乘除等全部采用int16的方法,這樣可以使LayerNorm或SoftMax這兩個(gè)誤差較大的算子獲得更高的精度表達(dá)。
可能很多人會(huì)說SoftMax和LayerNorm不需要我們這樣做,也能識(shí)別出量化損失誤差,因?yàn)槲乙婚_始就講了,他們?cè)谳敵龇植挤秶矫婢兔黠@不符合高斯分布,或者說像之前GeLU的情況。但其實(shí)我們?cè)诤髞淼囊恍z測(cè)實(shí)驗(yàn)中得出一些結(jié)論,一些特殊的linear或者matmul,如果有針對(duì)性地使用int16,比如在linear輸入的情況下使用int16,在有些matmul輸出的情況下使用int16,其實(shí)可以得到更好的精度結(jié)果。
第二部分是量化精度解決之后,編譯部署優(yōu)化的問題。這里我放出一些基本的統(tǒng)計(jì)數(shù)據(jù),這是編譯器或Swin-Transformer首次在征程5上部署時(shí)的基本情況。第一個(gè)是張量Tensor和向量Vector的計(jì)算比例大概是218:1,所謂的張量計(jì)算、張量Tensor實(shí)際上是指矩陣乘、卷積等操作,而向量Vector的計(jì)算,其實(shí)是指Normalization等操作,它的計(jì)算比大概是218:1。
直觀的看這個(gè)數(shù)字可能看不出來,但我們可以對(duì)比CNN的情況,如果絕大部分模型都是張量Tensor的計(jì)算結(jié)果,那這個(gè)比例其實(shí)可以接近無窮大,而在無窮大的情況下,專門針對(duì)并發(fā)設(shè)計(jì)的嵌入式智能計(jì)算平臺(tái)其實(shí)可以更好的發(fā)揮它的并行效率。而這樣一個(gè)比例,可能就需要芯片本身有更多的時(shí)間去處理向量Vector。另一方面,Reshape和Transpose的數(shù)據(jù)搬運(yùn)算子占比比較高,這個(gè)可能也和一些歷史遺留問題有關(guān),比如在之前的CNN中其實(shí)都沒有遇到這類算子本身的實(shí)踐方式,所以它的功能或者性能優(yōu)化等做的不是特別完善,這也是導(dǎo)致Swin-Transformer部署效率極低的原因之一。
另外講一下我們整個(gè)編譯器部署的優(yōu)化,總的前提是我們不應(yīng)該改變整個(gè)模型結(jié)構(gòu)和計(jì)算邏輯,這樣不管我們?cè)趺磧?yōu)化,用戶都不需要重新訓(xùn)練浮點(diǎn)模型和量化模型。如果這個(gè)成本降下來的話,不管用戶怎么改變,我們只要等價(jià)替換就可以了。優(yōu)化方向是編譯器優(yōu)化常見的思路,比如提高數(shù)據(jù)復(fù)用,減少數(shù)據(jù)加載的帶寬,然后還有一些算子合并等。
下面重點(diǎn)講一下具體有哪些操作,我們大概總結(jié)了5個(gè)優(yōu)化方向,有一些需要算法側(cè)感知,有一些由編譯器側(cè)直接完成不需要算法感知。在說明過程中我們也會(huì)一一提到這些。
第一個(gè)是算子的映射優(yōu)化,簡(jiǎn)單來說,是編譯器端去豐富算子實(shí)現(xiàn)的功能,這樣對(duì)于他們來說,每個(gè)計(jì)算單元、計(jì)算組件可以覆蓋更多的功能。這樣對(duì)部署而言,單獨(dú)的算子就可以完成多個(gè)功能,從而在算子內(nèi)部增加并行的機(jī)會(huì)。比如以matmul為例,一般來說,我們做QAT這種自注意力時(shí)會(huì)用matmul,然后在matmul前后會(huì)配合一些Transpose來使用。在這種情況下,從編譯器的角度來說,matmul可以把Transpose吃進(jìn)去,這樣就是Transpose+matmul,即可以通過一個(gè)matmul算子來完成并且提高了算子并行的機(jī)會(huì)。
另一個(gè)LayerNorm的例子中也是類似的,LayerNorm前后如果有view或者Transpose操作的話,可以把前后維度變化融合到上層內(nèi)部,這樣我們就可以通過一個(gè)自定義的算子支持豐富的維度,那么view操作可以達(dá)到前后加Transpose的效果。這樣的話,這個(gè)接口的修改需要用戶感知,比如使用天工開物工具鏈對(duì)外釋放的包中提供的算子接口,才可以使用這樣的功能。當(dāng)然這里其實(shí)是單純?yōu)榱藘?yōu)化性能而做的,使用原來的接口支持或部署是沒有障礙的。
第二個(gè)部分是算子融合。算子融合其實(shí)比較簡(jiǎn)單,整個(gè)征程5的數(shù)據(jù)排布是一種多維的表示,而Reshape和Transpose操作是對(duì)CPU和GPU的數(shù)字行為作描述,所以如果我想在征程5上做多次的、連續(xù)的、線性的重排布,理論上征程5可以一次性統(tǒng)一排布。
這里比較明顯的是window partition和window reverse這兩個(gè)算子,這兩個(gè)算子內(nèi)部主要是一些Reshape、view、permute等操作,簡(jiǎn)單來說就是不規(guī)則的數(shù)據(jù)搬運(yùn)。對(duì)于征程5來說,我們優(yōu)化的方向就是把這些東西融合成一個(gè)算子去操作完成,這樣我們?nèi)プ远x一個(gè)window partition的時(shí)候,內(nèi)部就不需要感知view、permute等這樣細(xì)致的邏輯。對(duì)于編譯器來說,一條指令就可以完成window partition的操作。換句話說,像這種多個(gè)算子融合在一起的操作能不能用圖優(yōu)化或者編譯器默認(rèn)去做,而不在算法側(cè)去做感知?理論上是可以的。
但圖優(yōu)化的另一個(gè)問題是需要維護(hù)大量pattern,也就是說只有在這樣的優(yōu)化規(guī)則下我才能得到最終的優(yōu)化效果。我們大概看一下window partition的維度信息和多種格式的變化,這我可以衍生出一堆的寫法。這種寫法如果要在后面的編譯階段把所有的pattern都維護(hù)起來,對(duì)于自動(dòng)的圖優(yōu)化來說還不如直接在算子層面使用一個(gè)固定格式、提供參數(shù)的方式讓用戶使用,而且這種替換幾乎沒有成本,也不需要重訓(xùn)。除了window partition和window reverse之外,還有一些常見的圖優(yōu)化,比如連續(xù)的Reshape和連續(xù)的Transpose的融合,這就可以直接用自動(dòng)的圖優(yōu)化來做而不需要用戶側(cè)來感知。
另外一個(gè)重點(diǎn)是算子的實(shí)現(xiàn)優(yōu)化,這看起來可能不是那么重要,但它對(duì)性能的提升是非常有效的。我們剛才講,有一些歷史遺留因素比如Reshape、Transpose之前在CNN模型中用的比較少,所以早期征程5上是使用DDR方式去實(shí)現(xiàn)算子的。為了性能的提升,把DDR的實(shí)現(xiàn)挪到SRAM的時(shí)候,可以發(fā)現(xiàn)整個(gè)Transformer部署有比較明顯的性能提升,后面我們有詳細(xì)的數(shù)據(jù)去說明。
另外一個(gè)特征是Batch MatMul的優(yōu)化是普通循環(huán)做的tile優(yōu)化。除此之外,還有一些其他比較瑣碎的圖優(yōu)化,比如做elementwise算子或者concat/split算子。如果涉及到我前后需要用Reshape和Transpose得到一個(gè)具體維度的時(shí)候,而且只是GPU或者CPU上需要得到這個(gè)維度,但對(duì)于編譯器或者對(duì)于征程5來說,這個(gè)維度信息可以穿透到需要計(jì)算的算子內(nèi)部。比如我concat某一個(gè)維度,需要通過Reshape得到這個(gè)維度的話,其實(shí)Reshape的操作有可能只需要融合到concat,然后一次性做完就可以了。
另外,我們一直在說征程5對(duì)CNN模型是非常友好的,但是到Transformer模型上的話除了上面的圖優(yōu)化之外,還有一些比較明顯的gap:征程5上以CNN為基礎(chǔ)的模型仍然是最高效的模型,這一點(diǎn)表現(xiàn)在規(guī)則的4d-Tensor仍然是最高效的支持方式。為什么要講規(guī)則的4d-Tensor呢?因?yàn)橄馛NN中的一些維度信息,比如W/H是做2倍下載樣,C維度做4倍的擴(kuò)充等,這些是非常有規(guī)則的。
所以在常見的嵌入式智能芯片平臺(tái)上,我們會(huì)有一些基本的對(duì)齊操作,而征程5上做的是4d-Tensor的對(duì)齊。簡(jiǎn)單來說,我們支持三維的方式就是把一個(gè)非四維Tensor轉(zhuǎn)成四維Tensor,我們也可以把一些不是特別重要的維度或者用來判定的維度使用一來做,然后4d-Tensor再結(jié)合嵌入式智能芯片常見的對(duì)齊操作。比如Conv的話我后面會(huì)講,再比如征程5上channel維度基本上是8對(duì)齊,W維度是16對(duì)齊,如果這兩個(gè)是非4d-Tensor轉(zhuǎn)成4d-Tensor,結(jié)合對(duì)齊的情況來看,很可能在一些維度上做復(fù)雜對(duì)齊,這就會(huì)導(dǎo)致計(jì)算的效率極致降低,也會(huì)導(dǎo)致多了很多無用的計(jì)算。
所以我們基本上建議,如果可能性比較高的話可以使用一些4d的計(jì)算邏輯去替代任意維度上的操作,但這一點(diǎn)不是必須的,因?yàn)槔碚撋夏壳霸谡鞒?上也是支持任意維度的操作的。如果想手動(dòng)獲取更高性能的話,可以使用目的性比較強(qiáng)的對(duì)齊4d-Tensor的規(guī)則去替換原來任意維度的計(jì)算。這里舉個(gè)例子,比如我們可以用nn.Conv2d去替換nn.Linear,這個(gè)替換是等價(jià)的。比如我們把weight做一些Reshape操作,然后把2D、3D或者任意維度的東西去做一些維度融合或者維度擴(kuò)充,經(jīng)過Conv也是等價(jià)的,其他像BatchNorm、LayerNorm等是要結(jié)合Conv來看的。
最后是SwinT在征程5上優(yōu)化的一個(gè)結(jié)論。通過上面一系列優(yōu)化,可以把Swin-Transformer的量化損失降低到1%左右,同時(shí)部署的效率可以達(dá)到143FPS。我們可以看一下這里的優(yōu)化選項(xiàng),我們 Reshape和Transpose的優(yōu)化非常明顯,當(dāng)然其他一些優(yōu)化也是重要的,因?yàn)橄鄬?duì)于FPS小于1的情況, Reshape和Transpose可能只是跨出了第一步而已。這里需要說明一下143這個(gè)數(shù)字可能跟之前在微信公眾號(hào)發(fā)布的一些文檔里的133有些區(qū)別,不過還是以143為主,因?yàn)榻?jīng)過我們?cè)谄渌恍㏕ransformer上面的探索發(fā)現(xiàn)這個(gè)數(shù)字在最近的最新版本上其實(shí)有一定的提升,相對(duì)來說可能比原來多了10FPS。然后我們也做了一個(gè)對(duì)比,這個(gè)數(shù)字跟端側(cè)最強(qiáng)的GPU相比不會(huì)差太多。但另一方面,我們的功耗大概只有端側(cè)最強(qiáng)GPU功耗的50%,這個(gè)數(shù)字還是非常可觀的。
04
如何在征程5上既快又好地
部署Transformer模型
最后講一下如何在征程5上既快又好地部署Transformer模型,并且把Swin-Transformer的經(jīng)驗(yàn)推廣到其它所有Transformer模型當(dāng)中。
第一部分是量化精度的調(diào)優(yōu),其實(shí)剛剛已經(jīng)講到一些了,比如我們非常不建議盲目的使用PTQ方法,而建議使用一些量化工具、分析工具來分析誤差本身的來源。另外,我們也非常不建議QAT本身的調(diào)參,因?yàn)閷?duì)于工具本身的誤差來源我們可以分析算子誤差、模型誤差、精度誤差等這些方面。
我們后來發(fā)現(xiàn),在Swin-Transformer模型當(dāng)中一般的模型誤差可能很難分析。這里可以舉一個(gè)最簡(jiǎn)單的format的例子:比如我們?cè)贑NN模型當(dāng)中,Conv+BN其實(shí)已經(jīng)是一個(gè)標(biāo)準(zhǔn)的format了。Conv+BN的量化一般會(huì)在Conv前面和BN后面插入量化節(jié)點(diǎn),這會(huì)導(dǎo)致如果Conv的輸出范圍比較大,那這個(gè)范圍就不需要量化。因?yàn)檎w上經(jīng)過BN之后的Normalization就可以做量化了,這部分其實(shí)是不需要考慮的。但實(shí)際上這部分在Transformer里會(huì)出現(xiàn)一個(gè)很奇怪的現(xiàn)象,就是Linear的輸出分布非常大,然后后面接的Normalization的方法是LayerNorm,而LayerNorm本身又不能和Linear一起去做量化。
所以這時(shí)候我們又會(huì)發(fā)現(xiàn)一個(gè)非常奇怪的現(xiàn)象,當(dāng)我們用相似度分析模型誤差時(shí),會(huì)發(fā)現(xiàn)Linear前面的相似度誤差可能不高,然而Linear后面的相似度誤差急劇上升、誤差很大,但是經(jīng)過LayerNorm之后誤差又降了下來。這種情況就需要分辨原來在CNN上面的結(jié)論,比如量化節(jié)點(diǎn)的誤差或者模型的誤差是否對(duì)Transformer非常有效?另外,我們更建議使用一些分布量化的方法去分析整個(gè)數(shù)據(jù)集或者精度上的誤差,這樣做可能更合理。遇到比較明顯的量化誤差,我們建議使用更高精度的量化方法。其實(shí)不僅是在征程5上使用int16更加方便,如果其他平臺(tái)使用浮點(diǎn)或者FP16、BF16這樣的浮點(diǎn)操作也是一個(gè)合理的解決方法。
最后講一下,量化精度的前提是輸入的浮點(diǎn)模型。我們發(fā)現(xiàn)量化模型相對(duì)于浮點(diǎn)模型來說,并不可以做到完全無損,這主要原因是當(dāng)浮點(diǎn)模型中有一些非常大的weight或者輸出的分布非常大甚至非常不均衡時(shí),這本身對(duì)于量化來說就是非常不友好的。所以針對(duì)這些量化不友好的模型,我們建議對(duì)浮點(diǎn)模型做一定的調(diào)整,比如在分布不均衡的情況上加上一些Normalization,使它對(duì)輸出的分布更加友好。
然后第二個(gè)是部署方面的建議,我們比較建議使用已經(jīng)封裝好的算子,比如像window reverse、window partition,還有matmul、LayerNorm等算子。可以通過天工開物芯片工具鏈里的QAT工具或者PTQ工具相應(yīng)的接口去獲取這些算子,如果有興趣之后可以嘗試使用一下。另外一個(gè)比較常用的算子天工開物工具鏈也是具備的,但是在Swin-Transformer中可能沒有用到,就是MultiHeadAttention這個(gè)算子。目前,量化工具本身也是支持算子以及算子的部署優(yōu)化的。
第三個(gè)是Tensor的對(duì)齊建議,其實(shí)我剛剛也在Swin-Transformer的部署優(yōu)化中簡(jiǎn)單提到了,這里我們把征程5的基本情況列出來,包括Conv的一些基本運(yùn)算部件、支持什么樣的對(duì)齊策略。這個(gè)對(duì)齊策略如果想用細(xì)膩度優(yōu)化去提升征程5的利用率的話,我們可以結(jié)合4d-tensor的優(yōu)化思路,再結(jié)合pattern就可以進(jìn)一步提高部署的利用率。Conv一般是H/W維度是26或者 channel維度是8對(duì)齊。如果對(duì)對(duì)齊策略有興趣的話可以直接看。
另一個(gè)需要講的是,除了算子在單個(gè)計(jì)算部件內(nèi)部的對(duì)齊浪費(fèi)開銷之外,不同部件切換的時(shí)候也會(huì)有一些Reorder的算子開銷。這個(gè)開銷的原理比較簡(jiǎn)單:如果上一個(gè)計(jì)算部件的判定值比較小,比如說Conv在channel維度的判定值是8,當(dāng)后面接的是一個(gè)ReduceSum算子時(shí),channel維度的判定要求是256。所以當(dāng)我們把一個(gè)Conv判定要求的算子以最小單位8塞到ReduceSum當(dāng)中時(shí),我們?cè)诤艽蠓秶锸亲鰺o效計(jì)算的。這個(gè)分析是一個(gè)比較特殊的例子,比較建議的方法是一個(gè)Conv接一個(gè)ReduceSum,這樣正好在channel維度上有一個(gè)比較大的判定。
這樣當(dāng)Conv跟ReduceSum這兩個(gè)組件切換的時(shí)候,就會(huì)有比較明顯的Reorder的開銷。有一個(gè)解決方法是我們用卷積來替代ReduceSum,這樣的話計(jì)算方法也比較簡(jiǎn)單,其實(shí)跟我們之前做Conv替代linear這樣的操作是類似的。比如我用reduce在C維度上去做ReduceSum的話,它的kernel大小就可以用(C,1,1,1)來表示,這樣我在C維度就從原來的C變成1,而Reduce on H就是(C,C,H,1)。
最后分享一下未來的一些工作。在征程5上部署SwinT其實(shí)是我們?nèi)ツ甑墓ぷ鳎S著工具鏈參考模型的發(fā)布,我們會(huì)有更多Transformer模型發(fā)布,比如說像DETR,DETR3d,PETR等,同時(shí)我們也會(huì)有更多Transformer相關(guān)的算子,比如量化Debug工具,還有一些經(jīng)驗(yàn)等也會(huì)沉淀到工具鏈當(dāng)中。另外,征程5上的一些生產(chǎn)模型也會(huì)探索更多Transformer模型的可能性。
其實(shí)像Swin-Transformer更多是做了一個(gè)驗(yàn)證的過程——驗(yàn)證征程5的可行性,但實(shí)際在生產(chǎn)模型上,如果FPS要求極高的話,我們更建議的做法是在一些CNN操作中內(nèi)嵌一些Transformer操作,比如我們可以參考現(xiàn)在比較流行的MobileNet、ViT的優(yōu)化,或者在BEV、時(shí)序上采用Transformer的方法做一些特征融合,而不使用以前那些卷積的方法。這樣少部分使用Transformer不僅能提高模型性能,而且在征程5上的部署效率也會(huì)更高。最后推廣到其他非CV任務(wù)上,事實(shí)上我們已經(jīng)在做語(yǔ)音方面的Transformer在征程5上的部署。
總體上我的分享就這么多,如果大家有興趣的話,可以去訪問地平線的開發(fā)者社區(qū)(https://developer.horizon.ai/),里面會(huì)有更多工具鏈的細(xì)節(jié),開放的相關(guān)文檔與參考算法,大家如果有什么問題的話也可以在里面交流。