近日,一位來自紐西蘭的小哥Brendan Bycroft在科技圈掀起了一股熱潮。他創作的一個名為大模型3D視覺化的項目,不僅登上了Hacker News的榜首,而且其震撼的效果更是讓人瞠目結舌。透過這個項目,你將在短短幾秒鐘內完全理解LLM(Large Language Model)的工作原理。
無論你是否是科技愛好者,這個計畫都會帶給你前所未有的視覺盛宴和認知啟蒙。讓我們一起來探索這個令人驚嘆的創作吧!
本計畫中,Bycroft詳細解析了OpenAI科學家Andrej Karpathy開發的一款輕量級GPT模型,名為Nano-GPT。作為縮小版的GPT模型,模型僅擁有85000個參數。當然,儘管這個模型比OpenAI的GPT-3或GPT-4小得多,但可謂是「麻雀雖小,五臟俱全」。
Nano-GPT GitHub:https://github.com/karpathy/nanoGPT
為了方便示範Transformer模型每一層,Bycroft為Nano-GPT模型安排了一個非常簡單的目標任務:模型輸入是6個字母"CBABBC",輸出是按字母順序排好的序列,例如輸出"ABBBCC".
我們稱每個字母為token ,這些不同的字母構成了詞表vocabulary:
#對於這張表格來說,每個字母分別分配了一個下標token index。這些下標組成的序列可以作為模型的輸入:2 1 0 1 1 2
3D視覺化中,每個綠色的單元代表經過計算的數字,而每個藍色的單元則表示模型的權重。
在序列處理中,每個數字會先轉換為一個C維的向量,這個過程稱為嵌入(embedding)。在Nano-GPT中,這個嵌入的維度通常是48維。透過這種嵌入操作,每個數字被表示為一個在C維空間中的向量,從而能夠更好地進行後續的處理和分析。
embedding要經過一系列中間的模型層計算,這中間的模型層一般稱為Transformers,最後到達底層。
「那麼輸出是什麼?」
模型的輸出就是序列的下一個token。所以在最後,我們得到了下一個token是A B C的機率值。
在這個例子裡,第6個位置模型以較大機率輸出了A。現在我們可以將A作為輸入傳入模型,重複整個過程就可以了。
此外也展示了GPT-2和GPT-3視覺化效果。
要注意的是,本視覺化主要是專注於模型推理(inference),而不是訓練,因此它只是整個機器學習過程的一小部分。並且,這裡假設模型的權重已經經過預先訓練,然後再使用模型推理來產生輸出。
前面有提到,如何使用一個簡單的查找表(Lookup Table)將token映射為一串整數。
這些整數,即標記token index,這是模型中第一次也是唯一一次看到整數。之後,將使用浮點數(十進制數)進行運算。
這裡,以第4個token(index 3)為例,看看其是如何被用來產生輸入嵌入的第4列向量的。
先使用token index (這裡以B=1為例) 從Token Embedding matrix選擇第二列,得到一個大小為C=48(48維)的列向量,稱為token嵌入(token embedding)。
再從position embedding matrix選擇第四列(「因為這裡主要查看第4個位置的(t = 3)token B」),同樣地,得到一個大小為C=48(48維)的列向量,稱為位置嵌入(position embedding)。
要注意的是,position embeddings和token embeddings都是模型訓練得到的(以藍色表示)。現在我們得到了這兩個向量,透過相加我們就可以得到一個新的大小為C=48的列向量。
接下來,以相同的流程處理序列中的所有token,建立一組包含token值及其位置的向量。
由上圖可以看出,對輸入序列中的所有token運行此過程,會產生一個大小為TxC的矩陣。其中,T表示序列長度。 C表示通道(channel),但也稱為特徵或維度或嵌入大小,這裡是48。這個長度C是模型的幾個「超參數」之一,設計者選擇它是為了在模型大小和效能之間進行權衡。
這個維度為TxC的矩陣,即為輸入嵌入(input embedding),並透過模型向下傳遞。
小Tip: 隨意將滑鼠停留在input embedding上的單一儲存格上,可以查看計算及其來源。
#前面得到的input embedding矩陣就是Transformer層的輸入。
Transformer層的第一步是對input embedding矩陣進行層歸一化處理(layer normalization),這是輸入矩陣每一列的值分別進行歸一化的運算。
歸一化是深度神經網路訓練中的重要步驟,它有助於提高模型在訓練過程中的穩定性。
我們可以將矩陣的列單獨分開來看,下面以第四個範例。
歸一化的目標是使得每列的數值平均值為0,標準差為1。為實現這一目標,需要計算每一列的平均值和標準差,然後讓每一列減去對應平均值和除以對應標準差。
這裡使用E[x]來表示平均值, Var[x]來表示變異數(標準差的平方)。 epsilon(ε = 1×10^-5)是防止除0錯誤。
計算並儲存歸一化後的結果,然後乘以學習權重weight(γ)並加上偏置bias(β),進而得到最終的歸一化結果。
最後,在輸入嵌入矩陣(input embedding matrix)的每一列上執行歸一化操作,就得到了歸一化後的輸入嵌入(normalized input embedding ),並將其傳遞給自註意力層(self-attention)。
Self Attention層大概算是Transformer中最核心的部分了,在這個階段,input embedding中的列可以相互“交流”,而其它階段,各列都是獨立存在的。
Self Attention層由多個個自註意力頭組成,本例中有三個自註意力頭。每個頭的輸入是input embedding的1/3部分,我們現在只專注在其中一個。
第一步是從normalized input embedding matrix的C列中為每一列產生3個向量,分別是QKV:
要產生這些向量,需要採用矩陣-向量乘法,外加偏置。每個輸出單元都是輸入向量的線性組合。
例如,對於查詢向量,即是由Q權重矩陣的一行和輸入矩陣的一列之間的點積運算完成的。
點積的運算很簡單,就是對應元素相乘然後相加。
這是一種確保每個輸出元素都能受到輸入向量中所有元素影響的通用而簡單的方法(這種影響由權重決定)。因此,它經常出現在神經網路中。
在神經網路中,這種機制經常出現是因為它允許模型在處理資料時考慮到輸入序列的每個部分。這種全面的注意力機制是許多現代神經網路架構的核心,特別是在處理序列資料(如文字或時間序列)時。
我們對Q, K, V向量中的每個輸出單元重複此操作:
#我們如何使用我們的Q(查詢)、K (鍵)和V(值)向量呢?它們的命名給了我們一個提示:『鍵』和『值』讓人想起字典類型,鍵映射到值。然後‘查詢’是我們用來找出值的手段。
在Self Attention的情況下,我們不是返回單一向量(詞條),而是傳迴向量(詞條)的某種加權組合。為了找到這個權重,我們計算一個Q向量與每個K向量之間的點積,再加權歸一化,最後用它與對應的V向量相乘,再將它們相加。
以第6列為範例(t=5),將從這一列開始查詢:
##由於attention matrix的存在,KV的前6列是可以被查詢到的,Q值是目前時間。 首先計算目前列(t=5)的Q向量與之前各列(前6列)的K向量之間的點積。然後將其儲存在註意力矩陣的對應行(t=5)中。 點積的大小衡量了兩個向量間的相似度,點積越大越相似。 而只將Q向量與過去的K向量運算,使得它成為因果自註意力。也就是說,token無法『看到未來的訊息』。 因此,在求點積之後,要除以sqrt(A),其中A是QKV向量的長度,進行這種縮放是為了防止大值在下一步的歸一化(softmax)中占主導地位。 接下來,又經過了softmax操作,將值域範圍縮小到了0到1。 最後,就可以得到這一列(t=5)的輸出向量。查看歸一化attention matrix的(t=5)行,並將每個元素與其他列的對應V向量相乘。然後,我們可以將這些向量相加,得到輸出向量。因此,輸出向量將以高分列的V向量為主。
現在我們應用到所有列。
這就是Self Attention層中一個頭的處理過程。 「因此,Self Attention的主要目標是每一列都想從其他欄位中找到相關資訊並提取其值,它透過將其Query 向量與那些其他列的Key 進行比較來實現這一點。增加的限制是它只能向過去看。」
在Self Attention操作之後,我們會從每個頭得到一個輸出。這些輸出是受Q和K向量影響而適當混合的V向量。要合併每個頭的輸出向量,我們只需將它們堆疊在一起即可。因此,在t=4時,我們將從3個長度為A=16的向量疊加形成1個長度為C=48的向量。
值得注意的是,在GPT中,頭(A=16)內向量的長度等於 C/num_heads。這確保了當我們將它們重新堆疊在一起時,能得到原來的長度C。
在此基礎上,我們進行投影,得到該層的輸出。這是一個簡單的矩陣-向量乘法,以每列為單位,並加上偏壓。
現在我們有了Self Attention的輸出。
我們沒有將這個輸出直接傳遞到下一階段,而是將它以元素的方式加入input embedding。這個過程,用綠色垂直箭頭表示,被稱為殘差連接(residual connection)或殘差路徑(residual pathway)。
與Layer Normalization一樣,殘差網路對於實現深度神經網路的有效學習至關重要。
現在有了self-attention的結果,我們可以將其傳遞到Transformer的下一層:前饋網路。
在Self Attention之後,Transformer模組的下一部分是MLP(多層感知機),在這裡它是一個有兩層的簡單神經網路。
與Self Attention一樣,在向量進入MLP之前,我們需進行層歸一化處理。
同時,在MLP中,還需對每個長度為C=48的列向量(獨立地)進行以下處理:
讓我們追蹤其中一個向量:
MLP具體處理如下:
先進行矩陣-向量乘法運算並加上偏置,將向量擴展為長度為4*C 的矩陣。 (注意這裡的輸出矩陣是經過轉置的,為了形象化)
#接下來,對向量的每個元素應用GELU激活函數。這是任何神經網路的關鍵部分,我們需要在模型中引入了一些非線性。所使用的特定函數 GELU,看起來很像 ReLU 函數 max(0, x),但它有一個平滑的曲線,而不是尖銳的角度。
然後,再經過另一個帶有偏移的矩陣-向量乘法,將向量投影回長度C。
這裡也有一個殘差網絡,與自註意力 投影部分一樣,我們將MLP的結果按元素順序添加到input中。
重複這些操作。
MLP層到此就結束了,我們最後也得到了transformer的輸出。
這就是一個完整的Transformer模組!
這些若干個模組構成了任何GPT 模型的主體,每個模組的輸出都是下一個模組的輸入。
正如在深度學習中常見的,很難準確地說出這些層各自在做什麼,但我們有一些大致的想法:較早的層傾向於專注於學習低級特徵和模式,而後面的層則學習辨識和理解更高階的抽象和關係。在自然語言處理的背景下,較低的層可能學習語法、句法和簡單的詞彙關聯,而較高的層可能捕捉更複雜的語義關係、話語結構和上下文依賴的含義。
最後就是softmax操作,輸出每個token的預測機率。
最終,我們到達了模型的尾端。最後一個Transfomer的輸出經過一層正規化處理,接著進行一次無偏壓的線性變換。
這個最終變換將我們的每個列向量從長度C轉換為詞彙量大小的長度nvocab。因此,它實際上是為詞彙表中的每個單字產生一個得分logits。
為了將這些分數轉換為更直覺的機率值,需要先透過softmax來處理。如此一來,對於每一列,我們都得到了模型分配給詞彙表中每個單字的機率。
在這個特定模型中,它實際上已經學會了所有關於如何對三個字母進行排序的答案,因此機率極大地傾向於正確答案。
當我們讓模型隨時間推進時,需要使用最後一列的機率來決定序列中下一個新增的token。例如,如果我們向模型中輸入了六個token ,我們會使用第六列的輸出機率。
這一列的輸出是一系列機率值,我們實際上需要從中選出一個作為序列中的下一個token 。我們透過「從分佈中採樣」來實現這一點,即根據其機率隨機選擇一個token 。例如,機率為0.9的token會被選擇的機率是90%。然而,我們也有其他選擇,例如總是選擇機率最高的 token 。
我們也可以透過使用溫度參數來控制分佈的「平滑度」。較高的溫度會使分佈更均勻,而較低的溫度則會使其更集中於機率最高的token 。
我們透過在應用softmax之前,用溫度參數來調整logits(線性變換的輸出),因為softmax中的指數化對較大數值有顯著的放大效果,使所有數值更接近將減少這種效果。
圖片
以上是突破資訊障礙!震撼人心的大型3D視覺化工具問世!的詳細內容。更多資訊請關注PHP中文網其他相關文章!