大規(guī)模 Transformer 模型 8 比特矩陣乘簡(jiǎn)介

引言
語(yǔ)言模型一直在變大。截至撰寫本文時(shí),PaLM 有 5400 億參數(shù),OPT、GPT-3 和 BLOOM 有大約 1760 億參數(shù),而且我們?nèi)栽诶^續(xù)朝著更大的模型發(fā)展。下圖總結(jié)了最近的一些語(yǔ)言模型的尺寸。

由于這些模型很大,因此它們很難在一般的設(shè)備上運(yùn)行。舉個(gè)例子,僅推理 BLOOM-176B 模型,你就需要 8 個(gè) 80GB A100 GPU (每個(gè)約 15,000 美元)。而如果要微調(diào) BLOOM-176B 的話,你需要 72 個(gè)這樣的 GPU!更大的模型,如 PaLM,還需要更多資源。
由于這些龐大的模型需要大量 GPU 才能運(yùn)行,因此我們需要找到降低資源需求而同時(shí)保持模型性能的方法。目前已有一些試圖縮小模型尺寸的技術(shù),比如你可能聽說過的量化和蒸餾等技術(shù)。
完成 BLOOM-176B 的訓(xùn)練后,Hugging Face 和 BigScience 一直在尋找能讓這個(gè)大模型更容易在更少的 GPU 上運(yùn)行的方法。通過我們的 BigScience 社區(qū),我們了解到一些有關(guān) Int8 推理的研究,它不會(huì)降低大模型的預(yù)測(cè)性能,而且可以將大模型的內(nèi)存占用量減少 2 倍。很快我們就開始合作進(jìn)行這項(xiàng)研究,最終將其完全整合到 Hugging Face?transformers
?中。本文我們將詳述我們集成在 Hugging Face 中的 LLM.int8() 方案,它適用于所有 Hugging Face 模型。如果你想了解更多研究細(xì)節(jié),可以閱讀我們的論文 LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale。
本文將主要介紹 LLM.int8() 量化技術(shù),討論將其納入?transformers
?庫(kù)的過程中經(jīng)歷的困難,并對(duì)后續(xù)工作進(jìn)行了計(jì)劃。
在這里,你將了解到究竟是什么讓一個(gè)大模型占用這么多內(nèi)存?是什么讓 BLOOM 占用了 350GB 內(nèi)存?我們先從一些基礎(chǔ)知識(shí)開始,慢慢展開。
機(jī)器學(xué)習(xí)中常用的數(shù)據(jù)類型
我們從理解不同浮點(diǎn)數(shù)據(jù)類型開始,這些數(shù)據(jù)類型在機(jī)器學(xué)習(xí)中也被稱為“精度”。
模型的大小由其參數(shù)量及其精度決定,精度通常為 float32、float16 或 bfloat16 之一 (下圖來源)。

Float32 (FP32) 是標(biāo)準(zhǔn)的 IEEE 32 位浮點(diǎn)表示。使用該數(shù)據(jù)類型,可以表示大范圍的浮點(diǎn)數(shù)。在 FP32 中,為“指數(shù)”保留了 8 位,為“尾數(shù)”保留了 23 位,為符號(hào)保留了 1 位。因?yàn)槭菢?biāo)準(zhǔn)數(shù)據(jù)類型,所以大部分硬件都支持 FP32 運(yùn)算指令。
而在 Float16 (FP16) 數(shù)據(jù)類型中,指數(shù)保留 5 位,尾數(shù)保留 10 位。這使得 FP16 數(shù)字的數(shù)值范圍遠(yuǎn)低于 FP32。因此 FP16 存在上溢 (當(dāng)用于表示非常大的數(shù)時(shí)) 和下溢 (當(dāng)用于表示非常小的數(shù)時(shí)) 的風(fēng)險(xiǎn)。
例如,當(dāng)你執(zhí)行?10k * 10k
?時(shí),最終結(jié)果應(yīng)為?100M
,F(xiàn)P16 無法表示該數(shù),因?yàn)?FP16 能表示的最大數(shù)是?64k
。因此你最終會(huì)得到?NaN
?(Not a Number,不是數(shù)字),在神經(jīng)網(wǎng)絡(luò)的計(jì)算中,因?yàn)橛?jì)算是按層和 batch 順序進(jìn)行的,因此一旦出現(xiàn)?NaN
,之前的所有計(jì)算就全毀了。一般情況下,我們可以通過縮放損失 (loss scaling) 來緩解這個(gè)問題,但該方法并非總能奏效。
于是我們發(fā)明了一種新格式 Bfloat16 (BF16) 來規(guī)避這些限制。BF16 為指數(shù)保留了 8 位 (與 FP32 相同),為小數(shù)保留了 7 位。這意味著使用 BF16 我們可以保留與 FP32 相同的動(dòng)態(tài)范圍。但是相對(duì)于 FP16,我們損失了 3 位精度。因此,在使用 BF16 精度時(shí),大數(shù)值絕對(duì)沒有問題,但是精度會(huì)比 FP16 差。
在 Ampere 架構(gòu)中,NVIDIA 還引入了 TensorFloat-32(TF32) 精度格式,它使用 19 位表示,結(jié)合了 BF16 的范圍和 FP16 的精度。目前,它僅在某些操作的內(nèi)部使用 [譯者注: 即 TF32 是一個(gè)計(jì)算數(shù)據(jù)類型而不是存儲(chǔ)數(shù)據(jù)類型]。
在機(jī)器學(xué)習(xí)術(shù)語(yǔ)中,F(xiàn)P32 稱為全精度 (4 字節(jié)),而 BF16 和 FP16 稱為半精度 (2 字節(jié))。除此以外,還有 Int8 (INT8) 數(shù)據(jù)類型,它是一個(gè) 8 位的整型數(shù)據(jù)表示,可以存儲(chǔ)??個(gè)不同的值 (對(duì)于有符號(hào)整數(shù),區(qū)間為 [-128, 127],而對(duì)于無符號(hào)整數(shù),區(qū)間為 [0, 255])。
雖然理想情況下訓(xùn)練和推理都應(yīng)該在 FP32 中完成,但 FP32 比 FP16/BF16 慢兩倍,因此實(shí)踐中常常使用混合精度方法,其中,使用 FP32 權(quán)重作為精確的 “主權(quán)重 (master weight)”,而使用 FP16/BF16 權(quán)重進(jìn)行前向和后向傳播計(jì)算以提高訓(xùn)練速度,最后在梯度更新階段再使用 FP16/BF16 梯度更新 FP32 主權(quán)重。
在訓(xùn)練期間,主權(quán)重始終為 FP32。而在實(shí)踐中,在推理時(shí),半精度權(quán)重通常能提供與 FP32 相似的精度 —— 因?yàn)橹挥性谀P吞荻雀聲r(shí)才需要精確的 FP32 權(quán)重。這意味著在推理時(shí)我們可以使用半精度權(quán)重,這樣我們僅需一半 GPU 顯存就能獲得相同的結(jié)果。

以字節(jié)為單位計(jì)算模型大小時(shí),需要將參數(shù)量乘以所選精度的大小 (以字節(jié)為單位)。例如,如果我們使用 BLOOM-176B 模型的 Bfloat16 版本,其大小就應(yīng)為?字節(jié)!如前所述,這個(gè)大小需要多個(gè) GPU 才能裝得下,這是一個(gè)相當(dāng)大的挑戰(zhàn)。
但是,如果我們可以使用另外的數(shù)據(jù)類型來用更少的內(nèi)存存儲(chǔ)這些權(quán)重呢?深度學(xué)習(xí)社區(qū)已廣泛使用的方法是量化。
模型量化簡(jiǎn)介
通過實(shí)驗(yàn),我們發(fā)現(xiàn)不使用 4 字節(jié) FP32 精度轉(zhuǎn)而使用 2 字節(jié) BF16/FP16 半精度可以獲得幾乎相同的推理結(jié)果,同時(shí)模型大小會(huì)減半。這促使我們想進(jìn)一步削減內(nèi)存,但隨著我們使用更低的精度,推理結(jié)果的質(zhì)量也開始急劇下降。
為了解決這個(gè)問題,我們引入了 8 位量化。僅用四分之一精度,因此模型大小也僅需 1/4!但這次,我們不能簡(jiǎn)單地丟棄另一半位寬了。
基本上講,量化過程是從一種數(shù)據(jù)類型“舍入”到另一種數(shù)據(jù)類型。舉個(gè)例子,如果一種數(shù)據(jù)類型的范圍為?0..9
,而另一種數(shù)據(jù)類型的范圍為?0..4
,則第一種數(shù)據(jù)類型中的值?4
?將舍入為第二種數(shù)據(jù)類型中的?2
?。但是,如果在第一種數(shù)據(jù)類型中有值?3
,它介于第二種數(shù)據(jù)類型的?1
?和 ?2
?之間,那么我們通常會(huì)四舍五入為?2
。也就是說,第一種數(shù)據(jù)類型的值?4
?和?3
?在第二種數(shù)據(jù)類型中具有相同的值?2
。這充分表明量化是一個(gè)有噪過程,會(huì)導(dǎo)致信息丟失,是一種有損壓縮。
兩種最常見的 8 位量化技術(shù)是零點(diǎn)量化 (zero-point quantization) 和最大絕對(duì)值 (absolute maximum quantization,absmax) 量化。它們都將浮點(diǎn)值映射為更緊湊的 Int8 (1 字節(jié)) 值。這些方法的第一步都是用量化常數(shù)對(duì)輸入進(jìn)行歸一化縮放。
在零點(diǎn)量化中,如果我的數(shù)值范圍是?-1.0…1.0
,我想量化到?-127…127
,我需要先縮放?127
倍,然后四舍五入到?8
?位精度。要恢復(fù)原始值,我需要將 Int8 值除以相同的量化因子?127
。在這個(gè)例子中,值?0.3
?將縮放為?0.3*127 = 38.1
。四舍五入后得到值?38
。恢復(fù)時(shí),我們會(huì)得到?38/127=0.2992
?—— 因此最終會(huì)有?0.008
?的量化誤差。這些看似微小的誤差在沿著模型各層傳播時(shí)往往會(huì)累積和增長(zhǎng),從而導(dǎo)致最終的精度下降。
譯者注: 這個(gè)例子舉得不好,因?yàn)楦↑c(diǎn)范圍和整型范圍都是對(duì)稱的,所以不存在零點(diǎn)調(diào)整了,而零點(diǎn)調(diào)整是零點(diǎn)量化中最能體現(xiàn)其命名原因的部分。簡(jiǎn)而言之,零點(diǎn)量化分為兩步,第一步值域映射,即通過縮放將原始的數(shù)值范圍映射為量化后的數(shù)值范圍; 第二步零點(diǎn)調(diào)整,即通過平移將映射后的數(shù)據(jù)的最小值對(duì)齊為目標(biāo)值域的最小值

(圖源)
現(xiàn)在我們?cè)倏聪?absmax 量化的細(xì)節(jié)。要計(jì)算 absmax 量化中 fp16 數(shù)與其對(duì)應(yīng)的 int8 數(shù)之間的映射,你必須先除以張量的最大絕對(duì)值,然后再乘以數(shù)據(jù)類型的最大可表示值。
例如,假設(shè)你要用 absmax 對(duì)向量?[1.2, -0.5, -4.3, 1.2, -3.1, 0.8, 2.4, 5.4]
?進(jìn)行量化。首先需要計(jì)算該向量元素的最大絕對(duì)值,在本例中為?5.4
。Int8 的范圍為?[-127, 127]
,因此我們將?127
?除以?5.4
,得到縮放因子?23.5
。最后,將原始向量乘以縮放因子得到最終的量化向量?[28, -12, -101, 28, -73, 19, 56, 127]
。

要恢復(fù)原向量,可以將 int8 量化值除以縮放因子,但由于上面的過程是“四舍五入”的,我們將丟失一些精度。

對(duì)于無符號(hào) Int8,我們可以先減去最小值然后再用最大絕對(duì)值來縮放,這與零點(diǎn)量化的做法相似。其做法也與最小 - 最大縮放 (min-max scaling) 類似,但后者在縮放時(shí)會(huì)額外保證輸入中的?0
?始終映射到一個(gè)整數(shù),從而保證?0
?的量化是無誤差的。
當(dāng)進(jìn)行矩陣乘法時(shí),我們可以通過組合各種技巧,例如逐行或逐向量量化,來獲取更精確的結(jié)果。舉個(gè)例子,對(duì)矩陣乘法?,我們不會(huì)直接使用常規(guī)量化方式,即用整個(gè)張量的最大絕對(duì)值對(duì)張量進(jìn)行歸一化,而會(huì)轉(zhuǎn)而使用向量量化方法,找到 A 的每一行和 B 的每一列的最大絕對(duì)值,然后逐行或逐列歸一化 A 和 B 。最后將 A 與 B 相乘得到 C。最后,我們?cè)儆?jì)算與 A 和 B 的最大絕對(duì)值向量的外積,并將此與 C 求哈達(dá)瑪積來反量化回 FP16。有關(guān)此技術(shù)的更多詳細(xì)信息可以參考 LLM.int8() 論文 或 Tim 的博客上的 關(guān)于量化和涌現(xiàn)特征的博文。
雖然這些基本技術(shù)能夠幫助我們量化深度學(xué)習(xí)模型,但它們通常會(huì)導(dǎo)致大模型準(zhǔn)確性的下降。我們集成到 Hugging Face Transformers 和 Accelerate 庫(kù)中的 LLM.int8() 是第一個(gè)適用于大模型 (如 BLOOM-176B) 且不會(huì)降低準(zhǔn)確性的量化技術(shù)。
簡(jiǎn)要總結(jié) LLM.int8(): 大語(yǔ)言模型的零退化矩陣乘法
在 LLM.int8() 中,我們已經(jīng)證明理解 transformer 模型表現(xiàn)出的與模型規(guī)模相關(guān)的涌現(xiàn)特性對(duì)于理解為什么傳統(tǒng)量化對(duì)大模型失效至關(guān)重要。我們證明性能下降是由離群特征 (outlier feature) 引起的,下一節(jié)我們會(huì)詳細(xì)解釋。LLM.int8() 算法本身如下。
本質(zhì)上,LLM.int8() 通過三個(gè)步驟完成矩陣乘法計(jì)算:
從輸入的隱含狀態(tài)中,按列提取異常值 (即大于某個(gè)閾值的值)。
對(duì) FP16 離群值矩陣和 Int8 非離群值矩陣分別作矩陣乘法。
反量化非離群值的矩陣乘結(jié)果并其與離群值矩陣乘結(jié)果相加,獲得最終的 FP16 結(jié)果。
該過程可以總結(jié)為如下動(dòng)畫:

離群特征的重要性
超出某個(gè)分布范圍的值通常稱為離群值。離群值檢測(cè)已得到廣泛應(yīng)用,在很多文獻(xiàn)中也有涉及,且獲取特征的先驗(yàn)分布對(duì)離群值檢測(cè)任務(wù)很有助益。更具體地說,我們觀察到對(duì)于參數(shù)量大于 6B 的 transformer 模型,經(jīng)典的量化方法會(huì)失效。雖然離群值特征也存在于較小的模型中,但在大于 6B 的 transformer 模型中,我們觀察到幾乎每層都會(huì)出現(xiàn)超出特定閾值的離群點(diǎn),而且這些離群點(diǎn)呈現(xiàn)出一定的系統(tǒng)性模式。有關(guān)該現(xiàn)象的更多詳細(xì)信息,請(qǐng)參閱 LLM.int8() 論文 和 涌現(xiàn)特征的博文。
如前所述,8 位精度的動(dòng)態(tài)范圍極其有限,因此量化具有多個(gè)大值的向量會(huì)產(chǎn)生嚴(yán)重誤差。此外,由于 transformer 架構(gòu)的固有特性,它會(huì)將所有元素互相關(guān)聯(lián)起來,這樣的話,這些誤差在傳播幾層后往往會(huì)混雜在一起。因此,我們發(fā)明了混合精度分解的方法,以對(duì)此類極端離群值進(jìn)行有效量化。接下來我們對(duì)此方法進(jìn)行討論。
MatMul 內(nèi)部
計(jì)算隱含狀態(tài)后,我們使用自定義閾值提取離群值,并將矩陣分解為兩部分,如上所述。我們發(fā)現(xiàn),以這種方式提取所有幅度大于等于 6 的離群值可以完全恢復(fù)推理精度。離群值部分使用 FP16 表示,因此它是一個(gè)經(jīng)典的矩陣乘法,而 8 位矩陣乘法是通過使用向量量化將權(quán)重和隱含狀態(tài)分別量化為 8 位精度 - 即按行量化權(quán)重矩陣,并按列量化隱含狀態(tài),然后再進(jìn)行相應(yīng)向量乘加操作。最后,將結(jié)果反量化至半精度,以便與第一個(gè)矩陣乘法的結(jié)果相加。

0 退化是什么意思?
我們?nèi)绾握_評(píng)估該方法是否會(huì)對(duì)性能造成下降?使用 8 位模型時(shí),我們的生成質(zhì)量損失了多少?
我們使用?lm-eval-harness
?在 8 位和原始模型上運(yùn)行了幾個(gè)常見的基準(zhǔn)測(cè)試,結(jié)果如下。
對(duì) OPT-175B 模型:

對(duì) BLOOM-176 模型:

我們切實(shí)地看到上述這些模型的性能下降為 0,因?yàn)橹笜?biāo)的絕對(duì)差異均低于原始模型的標(biāo)準(zhǔn)誤差 (BLOOM-int8 除外,它在 lambada 上略好于原始模型)。如果想要知道 LLM.int8() 與當(dāng)前其他先進(jìn)方法的更詳細(xì)的性能比較,請(qǐng)查看 論文!
比原始模型更快嗎?
LLM.int8() 方法的主要目的是在不降低性能的情況下降低大模型的應(yīng)用門檻。但如果速度非常慢,該方法用處也不會(huì)很大。所以我們對(duì)多個(gè)模型的生成速度進(jìn)行了基準(zhǔn)測(cè)試。
我們發(fā)現(xiàn)使用了 LLM.int8() 的 BLOOM-176B 比 FP16 版本慢了大約 15% 到 23% —— 這應(yīng)該是完全可以接受的。我們發(fā)現(xiàn)較小模型 (如 T5-3B 和 T5-11B) 的降速幅度更大。我們還在努力優(yōu)化這些小模型的推理速度。在一天之內(nèi),我們可以將 T5-3B 的每詞元推理延遲從 312 毫秒降低到 173 毫秒,將 T5-11B 從 45 毫秒降低到 25 毫秒。此外,我們 已經(jīng)找到原因,在即將發(fā)布的版本中,LLM.int8() 在小模型上的推理速度可能會(huì)更快。下表列出了當(dāng)前版本的一些性能數(shù)據(jù)。

Hugging Face?Transformers
?集成細(xì)節(jié)
接下來讓我們討論在 Hugging Face?transformers
?集成該方法的細(xì)節(jié),向你展示常見的用法及在使用過程中可能遇到的常見問題。
用法
所有的操作都集成在?Linear8bitLt
?模塊中,你可以輕松地從?bitsandbytes
?庫(kù)中導(dǎo)入它。它是?torch.nn.modules
?的子類,你可以仿照下述代碼輕松地將其應(yīng)用到自己的模型中。
下面以使用?bitsandbytes
?將一個(gè)小模型轉(zhuǎn)換為 int8 為例,并給出相應(yīng)的步驟。
首先導(dǎo)入模塊,如下。
然后就可以定義自己的模型了。請(qǐng)注意,我們支持將任何精度的 checkpoint 或模型轉(zhuǎn)換為 8 位 (FP16、BF16 或 FP32),但目前,僅當(dāng)模型的輸入張量數(shù)據(jù)類型為 FP16 時(shí),我們的 Int8 模塊才能工作。因此,這里我們稱模型為 fp16 模型。
假設(shè)你已經(jīng)在你的數(shù)據(jù)集和任務(wù)上訓(xùn)完了你的模型!現(xiàn)在需要保存模型:
至此,
state_dict
?已保存,我們需要定義一個(gè) int8 模型:
此處標(biāo)志變量?has_fp16_weights
?非常重要。默認(rèn)情況下,它設(shè)置為?True
,用于在訓(xùn)練時(shí)使能 Int8/FP16 混合精度。但是,因?yàn)樵谕评碇形覀儗?duì)內(nèi)存節(jié)省更感興趣,因此我們需要設(shè)置?has_fp16_weights=False
。
現(xiàn)在加載 8 位模型!
請(qǐng)注意,一旦將模型的設(shè)備設(shè)置為 GPU,量化過程就會(huì)在第二行代碼中完成。如果在調(diào)用?
.to
?函數(shù)之前打印?int8_model[0].weight
,你會(huì)看到:
而如果你在第二行之后打印它,你會(huì)看到:
正如我們?cè)谇懊娌糠纸忉屃炕椒〞r(shí)所講,權(quán)重值被“截?cái)唷绷?。此外,這些值的分布看上去在 [-127, 127] 之間。
你可能還想知道如何獲取 FP16 權(quán)重以便在 FP16 中執(zhí)行離群值的矩陣乘?很簡(jiǎn)單:
你會(huì)看到:
這跟第一次打印的原始 FP16 值很接近!
現(xiàn)在你只需將輸入推給正確的 GPU 并確保輸入數(shù)據(jù)類型是 FP16 的,你就可以使用該模型進(jìn)行推理了:
你可以查看 示例腳本,獲取完整的示例代碼!
多說一句,?Linear8bitLt
?與 ?nn.Linear
?模塊略有不同,主要在?Linear8bitLt
?的參數(shù)屬于?bnb.nn.Int8Params
?類而不是?nn.Parameter
?類。稍后你會(huì)看到這給我們帶來了一些小麻煩!
現(xiàn)在我們開始了解如何將其集成到?transformers
?庫(kù)中!
accelerate
?足矣
在處理大模型時(shí),?accelerate
?庫(kù)包含許多有用的工具。init_empty_weights
?方法特別有用,因?yàn)槿魏文P停瑹o論大小,都可以在此方法的上下文 (context) 內(nèi)進(jìn)行初始化,而無需為模型權(quán)重分配任何內(nèi)存。
初始化過的模型將放在 PyTorch 的 ?meta
?設(shè)備上,這是一種用于表征向量的形狀和數(shù)據(jù)類型而無需實(shí)際的內(nèi)存分配的超酷的底層機(jī)制。
最初,我們?cè)?.from_pretrained
?函數(shù)內(nèi)部調(diào)用?init_empty_weights
,并將所有參數(shù)重載為?torch.nn.Parameter
。這不是我們想要的,因?yàn)樵谖覀兊那闆r中,我們希望為?Linear8bitLt
?模塊保留?Int8Params
?類,如上所述。我們最后成功使用 此 PR 修復(fù)了該問題,它將下述代碼:
修改成:
現(xiàn)在這個(gè)問題已經(jīng)解決了,我們可以輕松地在一個(gè)自定義函數(shù)中利用這個(gè)上下文管理器將所有?nn.Linear
?模塊替換為?bnb.nn.Linear8bitLt
?而無需占用內(nèi)存!
此函數(shù)遞歸地將?meta
?設(shè)備上初始化的給定模型的所有?nn.Linear
?層替換為?Linear8bitLt
?模塊。這里,必須將?has_fp16_weights
?屬性設(shè)置為?False
,以便直接將權(quán)重加載為?Int8
,并同時(shí)加載其量化統(tǒng)計(jì)信息。
我們放棄了對(duì)某些模塊 (這里時(shí)?lm_head
) 進(jìn)行替換,因?yàn)槲覀兿M3州敵鰧拥脑季纫垣@得更精確、更穩(wěn)定的結(jié)果。
但還沒完!上面的函數(shù)在?init_empty_weights
?上下文管理器中執(zhí)行,這意味著新模型將仍在?meta
?設(shè)備中。
對(duì)于在此上下文管理器中初始化的模型,?accelerate
?將手動(dòng)加載每個(gè)模塊的參數(shù)并將它們拷貝到正確的設(shè)備上。因此在?bitsandbytes
?中,設(shè)置?Linear8bitLt
?模塊的設(shè)備是至關(guān)重要的一步 (感興趣的讀者可以查看 此代碼),正如你在我們上面提供的腳本中所見。
而且,第二次調(diào)用量化過程時(shí)會(huì)失??!我們必須想出一個(gè)與?accelerate
?的 ?set_module_tensor_to_device
?函數(shù)相應(yīng)的實(shí)現(xiàn) (稱為?set_module_8bit_tensor_to_device
),以確保我們不會(huì)調(diào)用兩次量化。我們將在下面的部分中詳細(xì)討論這個(gè)問題!
在 ?accelerate
?設(shè)置設(shè)備要當(dāng)心
這方面,我們對(duì)?accelerate
?庫(kù)進(jìn)行了精巧的修改,以取得平衡!
在模型被加載且設(shè)置到正確的設(shè)備上后,有時(shí)你仍需調(diào)用?set_module_tensor_to_device
?以便向所有設(shè)備分派加了 hook 的模型。該操作在用戶調(diào)用?accelerate
?的 ?dispatch_model
?函數(shù)時(shí)會(huì)被觸發(fā),這意味著我們有可能多次調(diào)用?.to
,我們需要避免該行為。
我們通過兩個(gè) PR 實(shí)現(xiàn)了目的,這里 的第一個(gè) PR 破壞了一些測(cè)試,但 這個(gè) PR 成功修復(fù)了所有問題!
總結(jié)
因此,最終我們完成了:
使用正確的模塊在?
meta
?設(shè)備上初始化模型。不重不漏地對(duì)目標(biāo) GPU 逐一設(shè)置參數(shù),確保不要對(duì)同一個(gè) GPU 重復(fù)設(shè)置!
將新加的參數(shù)變量更新到所有需要的地方,并添加好文檔。
添加高覆蓋度的測(cè)試!你可以從 此處 查看更多關(guān)于測(cè)試的詳細(xì)信息。
知易行難,在此過程中,我們經(jīng)歷了許多艱難的調(diào)試局,其中很多跟 CUDA 核函數(shù)有關(guān)!
總而言之,這次集成的過程充滿了冒險(xiǎn)和趣味; 從深入研究并對(duì)不同的庫(kù)做一些“手術(shù)”,到整合一切并最終使其發(fā)揮作用,每一步都充滿挑戰(zhàn)!
現(xiàn)在,我們看看如何在?transformers
?中成功使用它并從中獲益!
如何在?transformers
?中使用它
硬件要求
CPU 不支持 8 位張量核心 [*]。bitsandbytes 可以在支持 8 位張量核心的硬件上運(yùn)行,這些硬件有 Turing 和 Ampere GPU (RTX 20s、RTX 30s、A40-A100、T4+)。例如,Google Colab GPU 通常是 NVIDIA T4 GPU,而最新的 T4 是支持 8 位張量核心的。我們后面的演示將會(huì)基于 Google Colab!
*: 譯者注: Intel 最新的 Sapphire Rapids CPU 已支持 8 位張量指令集: AMX
安裝
使用以下命令安裝最新版本的庫(kù) (確保你的 python>=3.8)。
演示示例 - 在 Google Colab 上運(yùn)行 T5 11B
以下是運(yùn)行 T5-11B 的演示。T5-11B 模型的 checkpoint 精度為 FP32,需要 42GB 內(nèi)存,Google Colab 里跑不動(dòng)。使用我們的 8 位模塊,它僅需 11GB 內(nèi)存,因此能輕易跑通:
T5-11B 的 Colab 演示:https://colab.research.google.com/drive/1YORPWx4okIHXnjW7MSAidXN29mPVNT7F?usp=sharing
或者,你還可以看看下面這個(gè)使用 8 位 BLOOM-3B 模型進(jìn)行推理的演示!
BLOOM-3B 的 Colab 演示:https://colab.research.google.com/github/huggingface/blog/blob/main/notebooks/HuggingFace_int8_demo.ipynb
影響范圍
我們認(rèn)為,該方法讓超大模型不再是陽(yáng)春白雪,而是人人皆可觸及。在不降低性能的情況下,它使擁有較少算力的用戶能夠使用以前無法使用的模型。
我們已經(jīng)發(fā)現(xiàn)了幾個(gè)可以在繼續(xù)改進(jìn)的領(lǐng)域,以使該方法對(duì)大模型更友好!
較小模型的推理加速
正如我們?cè)?[基準(zhǔn)測(cè)試部分](# 比原始模型更快嗎?) 中看到的那樣,我們可以將小模型 (<=6B 參數(shù)) 的運(yùn)行速度提高近 2 倍。然而,雖然推理速度對(duì)于像 BLOOM-176B 這樣的大模型來說比較穩(wěn)定,但對(duì)小模型而言仍有改進(jìn)的余地。我們已經(jīng)定位到了問題并有希望恢復(fù)與 FP16 相同的性能,甚至還可能會(huì)有小幅加速。我們將在接下來的幾周內(nèi)合入這些改進(jìn)。
支持 Kepler GPU (GTX 1080 等)
雖然我們只支持過去四年的所有 GPU,但現(xiàn)實(shí)是某些舊的 GPU (如 GTX 1080) 現(xiàn)在仍然被大量使用。雖然這些 GPU 沒有 Int8 張量核心,但它們有 Int8 向量單元 (一種“弱”張量核心)。因此,這些 GPU 也可以體驗(yàn) Int8 加速。然而,它需要一個(gè)完全不同的軟件棧來優(yōu)化推理速度。雖然我們確實(shí)計(jì)劃集成對(duì) Kepler GPU 的支持以使 LLM.int8() 的應(yīng)用更廣泛,但由于其復(fù)雜性,實(shí)現(xiàn)這一目標(biāo)需要一些時(shí)間。
在 Hub 上保存 8 位 checkpoint
目前 8 位模型無法直接加載被推送到 Hub 上的 8 位 checkpoint。這是因?yàn)槟P陀?jì)算所需的統(tǒng)計(jì)數(shù)據(jù) (還記得上文提到的?weight.CB
?和 ?weight.SCB
?嗎?) 目前沒有存儲(chǔ)在 state_dict 中,而且 state_dict 的設(shè)計(jì)也未考慮這一信息的存儲(chǔ),同時(shí)?Linear8bitLt
?模塊也還尚未支持該特性。
但我們認(rèn)為保存它并將其推送到 Hub 可能有助于提高模型的可訪問性。
CPU 的支持
正如本文開頭所述,CPU 設(shè)備不支持 8 位張量核。然而,我們能克服它嗎?在 CPU 上運(yùn)行此模塊可以顯著提高可用性和可訪問性。[譯者注: 如上文,最新的 Intel CPU 已支持 8 位張量核]
擴(kuò)展至其他模態(tài)
目前,大模型以語(yǔ)言模型為主。在超大視覺、音頻和多模態(tài)模型上應(yīng)用這種方法可能會(huì)很有意思,因?yàn)殡S著這些模型在未來幾年變得越來越多,它們的易用性也會(huì)越來越重要。
致謝
非常感謝以下為提高文章的可讀性以及在?transformers
?中的集成過程做出貢獻(xiàn)的人 (按字母順序列出): JustHeuristic (Yozh), Michael Benayoun, Stas Bekman, Steven Liu, Sylvain Gugger, Tim Dettmers
英文原文:?https://hf.co/blog/hf-bitsandbytes-integration
原文作者: Younes Belkada,Tim Dettmers
譯者: Matrix Yao (姚偉峰),英特爾深度學(xué)習(xí)工程師,工作方向?yàn)?transformer-family 模型在各模態(tài)數(shù)據(jù)上的應(yīng)用及大規(guī)模模型的訓(xùn)練推理。
排版: zhongdongy (阿東)