給 ?大模型初學者? 的 LLaMA 3 核心技術剖析 原創 精華
編者按: 本文旨在帶領讀者深入了解 LLaMA 3 的核心技術 —— 使用 RMSNorm 進行預歸一化、SwiGLU 激活函數、旋轉編碼(RoPE)和字節對編碼(BPE)算法。RMSNorm 技術讓模型能夠識別文本中的重點,SwiGLU 激活函數則如同“神筆”,讓模型生成的文本更加突出重點且易于理解;RoPE 賦予了模型處理序列中詞語位置的靈活性,而 BPE 算法則有效提升了模型處理長文本的能力。
從開發環境配置到項目邏輯梳理,各組件的介紹與構建,再到模型組件的整合,本文將帶你一步步走過從文本數據分詞到創建嵌入、從注意力機制到多頭注意力實現的全過程。僅需具備一定的 Python 編程基礎,并對神經網絡和 Transformer 架構有基本的認識,便能跟隨本文的指引,觀察 LLaMA 3 如何根據輸入生成輸出,見證它如何基于輸入生成連貫且有意義的文本。
本文作者提供了完整的代碼示例和詳細的指導文檔,無需 GPU ,僅需 17 GB RAM 即可開始實踐。無論是希望深化理論認識還是渴望實踐技能提升,本文都將是不可多得的優質內容。
作者 | Fareed Khan
編譯 | 岳揚
LLaMA 3[1] 是繼 Mistral[2] 之后最有前途的開源模型之一,具備應對多種任務的強大能力。早前,我曾發布過一篇文章,詳述了如何基于 LLaMA 架構,從頭構建一個參數量超 230 萬的大語言模型(LLM)。目前 LLaMA-3 已經發布,我們將以更加簡便的方法重新構建這一模型。
在本篇博客中無需使用 GPU,但需要不少于 17 GB 的 RAM,因為我們將加載數個超過 15 GB 的文件。
為便于實踐,我已在 GitHub 上創建了一個代碼倉庫[3],內含全部操作代碼及詳細指南的 notebook 文件,避免了逐行從本博客復制粘貼的繁瑣。
想了解如何從零構建一個參數量超過 230 萬的大語言模型(LLM)嗎?請參考這篇指南[4],帶你從零開始踏入大模型創建之旅。
目錄
01 預備知識概覽
02 LLaMA 2 與 LLaMA 3 的區別
03 LLaMA 3 架構探秘
- 使用 RMSNorm 進行預歸一化
- SwiGLU 激活函數
- 旋轉編碼 (RoPE)
- 字節對編碼 (BPE) 算法
04 配置開發環境
05 理清項目組織邏輯
06 對輸入數據進行分詞
07 為每個 token 創建嵌入
08 使用 RMSNorm 進行歸一化
09 注意力頭(Query,Key,Values)
10 實現 RoPE
11 實現 Self Attention
12 實現 Multi-Head Attention
13 實現 SwiGLU 激活函數
14 整合上述模型組件
15 見證模型如何基于輸入生成輸出
01 預備知識概覽
本文不涉及面向對象編程(OOP),僅需掌握簡單的 Python 語法即可。不過,要想順暢閱讀這篇博客,具備對神經網絡和 Transformer 架構的基本認識是必不可少的前提。這兩點便是學習本文的全部要求。
02 LLaMA 2 與 LLaMA 3 的區別
在探討相關技術的細枝末節之前,需要了解的一點是,LLaMA 3的架構設計與 LLaMA 2 相同。因此,即便你尚未深入了解 LLaMA 3 的技術細節,閱讀這篇博客也沒有啥問題。即便不了解 LLaMA 2 的架構也別著急,我們也會對其技術細節進行概述,盡力確保所有感興趣的讀者都能閱讀本文。
以下是有關 LLaMA 2 與 LLaMA 3 的幾個核心要點:
03 LLaMA 3 架構探秘
在著手編寫代碼之前,了解 LLaMA 3 架構非常重要。為了幫助各種讀者更形象地理解這一概念,特附上一張對比圖,直觀展示原始 Transformer 架構與 LLaMA 2/3 及 Mistral 之間的異同。
From Rajesh Kavadiki
現在,讓我們深入了解一下 LLaMA 3 中幾個核心要素的細節:
3.1 使用 RMSNorm 進行預歸一化
沿襲 LLaMA 2 的做法,LLaMA 3 采取了一項名為 RMSNorm 的技術,來對每一個 Transformer 子層的輸入數據進行歸一化處理(normalizing)。
設想一下,你正在備戰一場大型考試,手邊是一本“大部頭”教科書,內容分成了多個章節。每一章都覆蓋了不同知識點,但其中某些章節對理解整本書的主題更為重要。在通讀整本教科書之前,你決定評估一下每一章的重要性,你不想在每一章節上都花同樣多的時間,而是希望將精力更多地集中在那些核心章節上。
類比到像 ChatGPT 這樣的大語言模型(LLMs),使用 RMSNorm 進行預歸一化就如同依據內容的重要性為各章節“加權”。對理解書籍主題至關重要的章節會獲得更高的“權重”,而次要章節則反之。
因此,在深入學習之前,我們會根據各章節的重要性調整學習計劃。在權重較高的章節上分配更多時間與精力,確保全面而深刻地掌握其核心概念。
《Root Mean Square Layer Normalization》(??https://arxiv.org/abs/1910.07467??)
與此類似,采用 RMSNorm 技術進行預歸一化能幫助大語言模型(LLMs)識別出文本中哪些部分對于理解上下文及其語義更為重要。它通過為關鍵要素賦予更高權重、非關鍵要素賦予較低權重的方式,引導模型將注意力集中于那些對上下文的準確解讀最需要的地方。對此感興趣的讀者可在此處[5]深入了解 RMSNorm 的具體實現方式。
3.2 SwiGLU 激活函數
LLaMA 從 PaLM 模型中汲取靈感,引入了 SwiGLU 激活函數。
想象一下,假如你是一名教師,正試圖向學生解釋一個復雜的話題。有一塊大白板,你在上面寫下了一些要點,并繪制圖像盡量使得內容講解更為清晰。但有時,你的字跡可能不太工整,或者圖像可能畫得不夠完美。這會大大增加學生理解教材的難度。
假如你擁有一支“神筆”,它能夠根據每個要點的重要性自動調整字的大小和樣式。如若某一要點非常重要,這支筆就會把它寫得更大、更清晰,讓其更加顯眼。如果不那么重要,這支“神筆”就會把字寫得小一些,但仍清晰可辨識。
SwiGLU 對于像 ChatGPT 這樣的大語言模型(LLMs)來說就像那支“神筆”。在生成文本之前,SwiGLU 會根據每個單詞(word)或短語(phrase)與上下文的相關性(relevance)調整其重要性(importance)。就像“神筆”會調整你寫字??時的字體大小和風格一樣,SwiGLU 也會調整每個詞或短語的重要程度。
《SwiGLU: GLU Variants Improve Transformer》(??https://kikaben.com/swiglu-2020/??)
這樣,當大語言模型生成文本時,就可以更加突出重要部分,并確保這些內容對文本的整體理解貢獻更大。這樣,SwiGLU 就能幫助大語言模型生成更清晰、更易于理解的文本,就像“神筆”能夠幫助你在白板上為學生創造更為清晰的內容講解一樣。若想進一步了解 SwiGLU 的相關細節,請查閱相關論文[6]。
3.3 旋轉編碼 (RoPE)
旋轉編碼(Rotary Embeddings),簡稱 RoPE ,是 LLaMA 3 中采用的一種位置編碼方式(position embedding)。
想象一下,你正在教室里組織小組討論,需要為學生們分配座位。常規做法是按行和列安排座位,每位學生都有一個固定的座位。但在某些情況下,我們可能希望設計一種更為靈活的座位布局安排,讓學生們能更自如地走動與交流。
RoPE 就像一種特別的座位布局方案,它讓每位學生既能旋轉又能改變位置,同時還能保持與其他學生的相對位置不變。學生們不再受制于某一個固定的位置,而是能做圓周運動(circular motion),從而實現更加順暢的互動。
在這種情況下,每一位學生都代表著文本序列里的一個詞語或 token ,他們的位置與他們在序列中的位置相對應。如同 RoPE 讓學生們能旋轉與改變位置一樣,RoPE 也允許文本序列中各詞語的位置編碼(positional embeddings)能夠根據彼此間的相對位置進行動態調整。
因此,在處理文本的過程中,RoPE 并未簡單地將位置編碼視作固定、靜態的(fixed and static)元素,而是巧妙地融入了旋轉(rotational)這一概念,使得表示方式更加靈活、多樣化,能夠更精準地把握文本序列內詞語間的變化關系。 這種靈活性賦予了 ChatGPT 等模型更強的能力,使其能更深刻地理解和生成自然流暢、邏輯連貫的文本內容,就如同在教室中采用動態座位布局(dynamic seating arrangement)能夠激發更多互動式的討論一樣。若想深入了解其背后的數學原理,可查閱 RoPE 的相關論文[7]。
3.4 字節對編碼 (BPE) 算法
LLaMA 3 采用由 OpenAI 推出的 tiktoken 庫中的字節對編碼(Byte Pair Encoding, BPE),而 LLaMA 2 的 BPE 分詞機制基于 sentencepiece 庫。兩者雖有微妙差異,但目前的首要任務是理解 BPE 究竟是什么。
先從一個簡單的例子開始:假設有一個文本語料庫(text corpus),內含 "ab", "bc", "bcd", 和 "cde" 這些詞語。我們將語料庫中所有單詞拆分為單個字符納入詞匯表,此時的詞匯表為 {"a", "b", "c", "d", "e"}。
接下來,計算各字符在文本語料庫中的出現次數。在本例中,統計結果為 {"a": 1, "b": 3, "c": 3, "d": 2, "e": 1}。
- 隨后,進入核心環節 —— 合并階段(merging process)。重復執行以下操作直至詞匯表達到預定規模:第一步,找出頻次最高的連續字符組合。在本例中,頻次最高的一對字符是“bc”,頻次為 2。然后,我們將這對字符合并,生成新的子詞單元(subword unit)“bc”。合并后,更新字符頻次,更新后的頻次為 {"a": 1, "b": 2, "c": 2, "d": 2, "e": 1, "bc": 2}。我們將新的子詞單元 “bc” 加入詞匯表,使之擴充至 {"a", "b", "c", "d", "e", "bc"}。
- 重復循環這一過程。下一個出現頻次最高的詞對是“cd”,將其合并生成新的子詞單元 “cd”,并同步更新頻次。更新后為 {"a": 1, "b": 2, "c": 1, "d": 1, "e": 1, "bc": 2, "cd": 2}。然后我們將 “cd” 加入詞匯表,得到 {"a", "b", "c", "d", "e", "bc", "cd"}。
- 延續此流程,下一個頻繁出現的詞對是 “de”,將其合并為子詞單元 “de”,并將頻次更新至 {"a": 1, "b": 2, "c": 1, "d": 1, "e": 0, "bc": 2, "cd": 1, "de": 1}。然后將 “de” 添加到詞匯表中,使其更新為 {"a", "b", "c", "d", "e", "bc", "cd", "de"}。
- 接下來,我們發現 “ab” 是出現頻次最高的詞對,將其合并為子詞單元 “ab”,同步更新頻次為 {"a": 0, "b": 1, "c": 1, "d": 1, "e": 0, "bc": 2, "cd": 1, "de": 1, "ab": 1}。再將 “ab” 添加至詞匯表中,使其擴容至 {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab"}。
- 再往后,“bcd”成為了下一個出現頻次最高的詞對,將其合并為子詞單元 “bcd”,更新頻次至 {"a": 0, "b": 0, "c": 0, "d": 0, "e": 0, "bc": 1, "cd": 0, "de": 1, "ab": 1, "bcd": 1}。將 “bcd” 添入詞匯表,使其升級為 {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab", "bcd"}。
- 最后,出現頻次最高的詞對是 “cde”,將其合并為子詞單元 “cde”,更新頻次至 {"a": 0, "b": 0, "c": 0, "d": 0, "e": 0, "bc": 1, "cd": 0, "de": 0, "ab": 1, "bcd": 1, "cde": 1}。將 “cde” 添加入詞匯表,這樣詞匯表就變為了 {"a", "b", "c", "d", "e", "bc", "cd", "de", "ab", "bcd", "cde"}。
此方法能夠顯著提升大語言模型(LLMs)的性能,同時能夠有效處理生僻詞及詞匯表之外的詞匯。TikToken BPE 與 sentencepiece BPE 的主要區別在于:TikToken BPE 不會盲目將已知的完整詞匯分割。 比如,若 “hugging” 已存在于詞匯表中,它會保持原樣,不會被拆解成 ["hug","ging"]。
04 環境配置
我們將使用到少量 Python 庫,為了防止出現 “no module found” 的錯誤,建議運行下面這行命令提前安裝好這些庫。
安裝完所需庫后,下一步是下載相關文件。因為我們要復現的是 llama-3-8B 模型的架構,所以我們需要在 HuggingFace 平臺上注冊一個賬戶。另外,鑒于 llama-3 是一款使用受限的模型,訪問模型內容前需同意其使用條款。
具體步驟如下:
- 點擊此處[8]注冊 HuggingFace 賬戶
- 點擊此處[9]同意 llama-3-8B 的使用條款
完成以上兩個步驟后,接下來就會下載一些必要的文件。要實現此目的,有兩條途徑可選:
- 手動下載 (Option 1: Manual) :通過此鏈接[10]進入 llama-3–8B 的 HuggingFace 目錄,逐一手動下載以下三個文件。
LLaMA-3 配置文件下載
- 通過編程的方式下載 (options 2: Coding) :使用之前安裝的 hugging_face 庫,我們可以一鍵下載所有必需的文件。不過,在此之前,需要先用 HF Token 在當前工作環境中登錄HuggingFace Hub。你可以創建一個新 token ,或直接通過此鏈接[11]獲取。
當我們運行該代碼單元時,系統會要求我們輸入 token 。如若登錄過程中遇到問題,請重試,但一定要記得取消選中 “token as git credential” 這一選項。隨后,我們僅需運行一段簡單的 Python 腳本代碼,便能順利下載 llama-3-8B 架構的三個主要文件。
下載完所有必要文件后,我們需要導入本博客中將要使用的 Python 庫。
接下來,我們需要了解所下載的每個文件的具體用途。
05 理清項目組織邏輯
由于我們的目標是精確復制 llama-3 ,這意味著無論輸入的文本是什么,都應當獲得有實際意義的輸出。舉例來說,當我們輸入“太陽的顏色是什么?(the color of the sun is?)”這樣的問題時,期望的回答自然是“白色(white)”。然而,要達成這一目標,通常需要在海量數據集上訓練大語言模型(LLMs),而這往往對計算資源的要求極高,因此對于我們來說并不可行。
不過,Meta 已經公開發布了他們的 llama-3 架構文件(或者更確切地說,是他們預訓練模型權重)供公眾使用。我們剛剛下載的正是這些文件,這意味著我們無需自行訓練模型或搜集龐大的數據集,這樣就可以復制它們的架構。一切準備工作都已就緒,接下來,我們只需確保在恰當的位置正確地運用這些組件。
現在,讓我們逐一了解這些文件及其各自的重要作用:
tokenizer.model —— 如前文所述,LLaMA-3 采用的是 tiktoken 庫中的字節對編碼(BPE)分詞技術,這項技術是在一個包含了 15 萬億個 tokens 的超大數據集上訓練得來的,比 LLaMA-2 使用的數據集足足大了7倍之多。現在,讓我們加載這個文件,一探究竟,看看它背后藏著哪些奧秘。
??length?
?? 這一屬性代表的是詞匯表的總體規模,具體指代的是訓練數據中的所有不同(唯一)字符的總數。而 ??tokenizer_model?
? 本身,則是一種字典類型的數據結構。
當我們隨機抽取并展示其中的 10 項內容,會注意到,這些內容都是通過 BPE 算法精心構造的字符串,與我們之前探討的示例頗為相似。此處的字典鍵(key)代表著 BPE 算法訓練過程中的字節序列(Byte sequences),而字典值(values)則反映了依據出現頻率確定的合并排序級別(merge ranks)。
consolidated.00.pth —— 這個文件內藏玄機,它保存了 Llama-3-8B 模型在訓練過程中學到的所有參數,即所謂的模型權重。這些參數深度揭示了模型的工作機制,比如它如何對 tokens 進行編碼、如何計算注意力權重、如何執行前饋神經網絡的轉換,以及最終如何對輸出結果進行歸一化處理,等等。
對于熟悉 transformer 架構的人來說,諸如查詢(query)、鍵(key)矩陣等概念一定不會陌生。稍后,我們在 Llama-3 的體系結構中借助這些模型層/權重構建出相應的矩陣。
params.json —— 這個文件內容豐富,記錄了各類參數的具體數值,包括但不限于:
這些數值將幫助我們一步步復制 Llama-3 架構,它們詳細記錄了模型架構中的關鍵參數,比如注意力頭的數量、嵌入向量的維度等。
現在,讓我們妥善保存這些數據值,以備后續環節中調用使用。
有了分詞模型(tokenizer model)、載有關鍵權重的架構模型(architecture model containing weights)以及詳盡的配置參數(configuration parameters)在手,萬事俱備,只欠東風。現在,讓我們滿懷熱情,從最基礎的部分做起,動手搭建屬于我們自己的 Llama-3 模型吧!
06 對輸入數據進行分詞
該步驟的首要任務是將輸入的文本信息轉化為詞元形式,而這一步驟的關鍵在于,我們必須先生成一系列特殊詞元(token)。這些特殊詞元如同導航標,鑲嵌在分詞之后的文本中,它們賦予分詞器識別與處理特定條件或指令的能力,是整個流程中不可或缺的一環。
接下來,我們可以通過設定不同的模式來識別輸入文本中各種類型的子字符串,以此來制定文本分割規則。讓我們來看看具體操作方法。
此工具能夠從輸入文本中提取單詞(words)、縮寫(contractions)、數字(numbers)(最多三位數) ,以及由非空白字符組成的字符序列,我們可以根據自身需求對其進行個性化設置。
我們需利用 TikToken 的 BPE 算法來編寫一個簡易的分詞函數,該函數接收三項參數:t0okenizer_model 、 tokenize_breaker 和 special_tokens 。該函數會按照需求對輸入文本進行相應的編碼或解碼處理。
為了驗證該編碼函數是否能夠正常工作,我們先以“Hello World”作為測試文本傳入該函數處理。首先,該函數將文本編碼,將其轉化為一系列數字。隨后,再將這些數字解碼回原始文本,最終得到 “hello world!” —— 這一過程證明了函數功能正常。現在,讓我們開始對輸入內容進行分詞處理。
我們從一個特殊詞元(譯者注:此處應當為??<|begin_of_text|>?
?)開始,對輸入文本 “the answer to the ultimate question of life, the universe, and everything is ” 進行編碼處理。
07 為每個 token 創建嵌入
如果我們檢查輸入向量的長度,其長度應為:
目前,輸入向量的維度為 (17x1) ,下一步需要將每個經過分詞后的單詞轉換為其對應的嵌入表征。這樣一來,原本的 (17x1) token 將擴展為 (17x4096) 維度的嵌入矩陣,即每個 token 都將擁有一個長度為 4096 的嵌入向量。
有一點需要注意,這些嵌入向量并未經過歸一化處理,若不對其進行歸一化處理,可能會產生嚴重負面影響。在下一節,我們將著手對輸入向量進行歸一化操作。
08 使用 RMSNorm 進行歸一化
為了確保輸入向量完成歸一化,我們將采用前文的 RMSNorm 公式來進行處理。
《Root Mean Square Layer Normalization》 (??https://arxiv.org/abs/1910.07467??)
我們將使用來自 layers_0 的注意力權重,對尚未歸一化的嵌入向量進行歸一化處理。選擇 layer_0 的原因在于,我們正著手構建 LLaMA-3 transformer 架構的第一層。
由于我們僅對向量進行歸一化處理,并不涉及其他操作,所以向量的維度并不會發生變化。
09 注意力頭(Query,Key,Values)
首先,我們從模型中加載 query、key、value 和 output 向量。
從向量的維度上可以看出,我們下載的模型權重并非為單獨的注意力頭設計,因為采用了并行處理或并行訓練的方式,可以同時服務于多個注意力頭。不過,我們能夠分解這些矩陣,讓它們只適用于單個注意力頭。
在此,??32?
?? 代表 LLama-3 中注意力頭的數量,??128?
?? 是查詢向量的維度大小,??4096?
? 則是 token 嵌入的維度大小。
我們可以通過以下方式,獲取到第一層中首個注意力頭的 query 權重矩陣:
要計算每個 token 對應的 query 向量,我們需要將該 token 的嵌入向量與 query 權重進行相乘運算。
由于 query 向量本身無法識別自己在提示詞文本中的具體位置,因此我們將借助 RoPE 技術,讓這些向量能夠感知其所在位置。
10 實現 RoPE
我們將 query 向量分成兩個一組,接著對每一組實施旋轉角度(rotational angle)的調整,以此來區分它們。
此處要處理的是一個大小為 [17x64x2] 的向量,其實質是將每個提示詞內的 128 個長度單位(128-length) 的 query 信息,劃分成 64 對。每一個 query 對都將依據 m*θ 角度進行旋轉,這里的 m 即為 token 在序列中的位置。
為了實現向量的旋轉操作,我們會采用復數點積(the dot product of complex numbers)的計算方法。
完成分割過程后,接下來我們將對分割后的數據進行頻率計算。
現在,我們已經為每個 token 的 query 部分賦予了對應的復數值。接下來,我們就可以把這些 query 轉換為復數,再依據它們各自在序列中的位置,運用點積運算實現旋轉處理。
得到旋轉后的向量后,我們可以通過將之前的復數重新解釋為實數,從而恢復到最初以配對形式表示的 query 向量。
現在,旋轉后的數據將進行合并處理,然后得到一個全新的 query 向量(rotated query vector),其 shape 為 [17x128] 。此處的數字 17 代表 token 總數,而 128 則是 query 向量的維度大小。
處理 key 向量的方式與 query 向量的處理方式相似,不過要記得,key 向量也是 128 維的。由于 key 向量的權重在 4 個注意力頭(head)間共享,以盡量減少運算量,因此其權重數量僅是 query 向量的四分之一。就像 query 向量一樣,key 向量也會通過旋轉(rotated)來融入位置信息(positional information),以此增強模型對序列位置的理解。
現在,我們已經獲得了每個 token 對應的旋轉查詢向量與鍵向量(rotated queries and keys),其大小均為 [17x128] 。
11 實現 Self Attention
通過將 query 矩陣與 key 矩陣相乘,我們會得到一組 score(相似性分數),這些 score 對應著每個 token 與其他 token 之間的關聯度。具體來說,這些 score 表示每個 token 的 query 向量與其 key 向量之間的相互關系。
[17x17] 這個 Shape 表示的是注意力分數(qk_per_token),其中數字 17 指的是提示詞文本中包含的 token 數量。
我們有必要對 query-key scores (譯者注:此處應當指在計算注意力權重時,Query 矩陣和 Key 矩陣之間的匹配程度或相關性得分。)進行屏蔽處理。在模型訓練階段,為了確保模型僅利用歷史信息進行預測,我們會屏蔽掉未來 token 的 query-key scores。這一策略導致我們在進行推理時,會將所有未來 token 的 query-key scores 值都設定為零。
現在,我們需對每個 token 的 query 向量和 key 向量實施遮掩操作。接著,我們打算在此之上應用 softmax 函數,將得到的分數轉化為概率值。這樣做有利于從模型的詞匯表(vocabulary)中挑選出可能性最高的 tokens 或 token 序列,進而讓模型的預測結果更易于理解,也更適用于語言生成、分類等應用場景。
在 value 矩陣這里,自注意力機制告一段落。為了節約計算資源,類似地,value 矩陣權重在每四個注意力頭中被共享。最終,value 權重矩陣呈現出的 shape 為 [8x128x4096]。
與 query 矩陣和 key 矩陣類似,我們可以通過特定方法得到第一層及首個注意力頭的 value 矩陣。
通過值矩陣權重,我們計算出每一個 token 的注意力值,最終得到一個 shape 為 [17x128] 的矩陣。這里,17 指的是提示詞文本中的 token 總數,128 則是單個 token 的值向量維度。
要獲取最終的注意力矩陣,我們只需進行如下所示的乘法操作:
我們現在已經獲得了第一層和第一個注意力頭的注意力值,這實際上就是所謂的自注意力值(self attention)。
12 實現 Multi-Head Attention
接下來將通過一個循環過程,對第一層中的所有注意力頭重復進行上述的計算步驟。
現在,第一層的所有 32 個注意力頭中的 QKV 注意力矩陣都已被計算出來,接下來,這些注意力分數將被整合進一個大小為 [17x4096] 的大矩陣中。
在 layer 0 attention (譯者注:可能是整個 Transformer 模型理解和處理序列這一過程的第一步)中,收尾步驟是使用權重矩陣(weight matrix)去乘以堆疊起來的 QKV 矩陣。
我們現在已經得到了應用注意力機制處理之后的嵌入值(embedding values),這些變化應當被添加至原先的詞元嵌入(token embeddings)上。
接下來,我們會對嵌入值的變化進行歸一化處理,然后將其送入前饋神經網絡(feedforward neural network)中進一步加工。
13 實現 SwiGLU 激活函數
由于我們已經對前文介紹的 SwiGLU 激活函數有所了解,現在我們將把之前探討的那個公式運用到這里。
SwiGLU: GLU Variants Improve Transformer (??https://kikaben.com/swiglu-2020/??)
14 整合上述模型組件
現在一切準備就緒,我們需要合并代碼,從而構建另外 31 個模型層。
15 見證模型如何基于文本輸入生成輸出
現在,我們已經得到了最終的嵌入表征,這是模型預測下一個詞元(token)的依據。這一嵌入的結構與普通的詞元嵌入(token embeddings)一致,為 [17x4096] ,意味著由 17 個 token 構成,每個 token 的嵌入向量維度為 4096 。
接下來,我們可以把得到的嵌入表征轉換回具體的 token 值,完成從抽象表征到文本內容的解碼過程。
在預測后續內容時,我們會使用上一個 token 的嵌入表征作為依據,以此推理出最可能的下一個 token 值。
為了將一串 token IDs 轉換成可讀的文本,我們需要進行生成文本的解碼過程,將 token IDs 對應到具體的字符或詞匯上。
所以,我們輸入的是“the answer to the ultimate question of life, the universe, and everything is(“生命、宇宙以及萬物的終極問題的答案是”)”,而得到的模型輸出正是“42”,這正是正確答案。
各位讀者可以嘗試各種不同的提示詞文本,進行各種各樣的實驗。在整個程序中,只需修改這兩行代碼即可,其他部分可保持不變!
Thanks for reading! Hope you have enjoyed and learned new things from this blog!
Fareed Khan
MSc Data Science, I write on AI
??https://www.linkedin.com/in/fareed-khan-dev/??
END
參考資料
[1]??https://llama.meta.com/llama3/??
[3]??https://github.com/FareedKhan-dev/Building-llama3-from-scratch??
[5]??https://github.com/bzhangGo/rmsnorm/blob/master/rmsnorm_torch.py??
[6]??https://arxiv.org/pdf/2002.05202v1.pdf??
[7]??https://arxiv.org/pdf/2104.09864v4.pdf??
[8]??https://huggingface.co/join?next=%2Fmeta-llama%2FMeta-Llama-3-8B??
[9]??https://huggingface.co/meta-llama/Meta-Llama-3-8B??
[10]??https://huggingface.co/meta-llama/Meta-Llama-3-8B/tree/main/original??
[11]??https://huggingface.co/settings/tokens??
原文鏈接:
??https://levelup.gitconnected.com/building-llama-3-from-scratch-with-python-e0cf4dbbc306??
