解讀大模型原理最通透的一篇文章
生成式 AI 太火爆了,可以說無處不在,頻頻刷爆我們的朋友圈。你可能已經試用過 ChatGPT 了,甚至已經把它當作你的智能小助手。
但我知道很多人心裏會有個疑問:這些 AI 模型的智能究竟是怎麼來的?今天咱們就來聊聊這個話題。我會用大家都能懂的,而不是那些讓人頭大的高等數學術語來解釋生成式文本模型的工作原理,揭開它的神祕面紗,把它變成簡單的計算機算法。
LLM 的功能
首先,我要澄清人們對 LLM 工作原理的一個重大誤解。大多數人認爲這些模型可以回答問題或與你聊天,但實際上它們只能接收你提供的一些文本作爲輸入,然後猜測下一個詞(更準確地說,下一個 Token)是什麼。
讓我們從 Token 開始瞭解 LLM 的奧祕。
Token
Token 是 LLM 理解的文本基本單位。雖然將 Token 看作單詞很方便,但對 LLM 來說,目標是儘可能高效地編碼文本,所以在許多情況下,Token 代表的字符序列比整個單詞都要短或長。標點符號和空格也被表示爲 Token,可能是單獨或與其他字符組合表示。
LLM 使用的所有 Token 統稱爲其詞彙,因爲它可以用來表示任何可能的文本。字節對編碼(BPE)算法通常用於 LLM 生成 Token 詞彙。爲了讓你對規模有個大致的瞭解,GPT-2 語言模型是開源的,可以詳細研究,其詞彙量爲 50,257 個 Token。
LLM 詞彙中的每個 Token 都有一個唯一的標識符,通常是一個數字。LLM 使用分詞器在常規文本字符串和等效的 Token 數列表之間進行轉換。如果你熟悉 Python 並想嘗試 Token,可以安裝 OpenAI 的 tiktoken 包:
$ pip install tiktoken
然後在 Python 提示符中嘗試以下內容:
>>> import tiktoken
>>> encoding = tiktoken.encoding_for_model("gpt-2")
>>> encoding.encode("The quick brown fox jumps over the lazy dog.")
[464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13]
>>> encoding.decode([464, 2068, 7586, 21831, 18045, 625, 262, 16931, 3290, 13])
'The quick brown fox jumps over the lazy dog.'
>>> encoding.decode([464])
'The'
>>> encoding.decode([2068])
' quick'
>>> encoding.decode([13])
'.'
在這個實驗中,你可以看到對於 GPT-2 語言模型,Token 464 代表單詞 “The”,而 Token 2068 代表單詞 “quick”,包括一個前導空格。該模型使用 Token 13 表示句號。
由於 Token 是通過算法確定的,你可能會發現一些奇怪的現象,比如這三個變體的單詞 “the”,在 GPT-2 中都編碼爲不同的 Token:
>>> encoding.encode('The')
[464]
>>> encoding.encode('the')
[1169]
>>> encoding.encode(' the')
[262]
BPE 算法並不總是將整個單詞映射到 Token。事實上,使用頻率較低的單詞不能成爲獨立的 Token,必須使用多個 Token 進行編碼。以下是一個使用兩個 Token 編碼的單詞示例:
>>> encoding.encode("Payment")
[19197, 434]
>>> encoding.decode([19197])
'Pay'
>>> encoding.decode([434])
'ment'
下一個 Token 預測
如上所述,給定一些文本,語言模型會預測下一個緊跟其後的 Token。如果用 Python 僞代碼展示可能會更清晰,下面是如何運行這些模型以獲取下一個 Token 的預測:
predictions = get_token_predictions(['The', ' quick', ' brown', ' fox'])
該函數接收一個由用戶提供的提示詞編碼而來的輸入 Token 列表。在這個例子中,我假設每個單詞都是一個獨立的 Token。爲了簡化,我使用每個 Token 的文本表示,但正如你之前看到的,實際上每個 Token 會作爲一個數字傳遞給模型。
這個函數的返回值是一個數據結構,它爲詞彙表中的每個 Token 分配一個緊隨輸入文本之後的概率。如果基於 GPT-2,這個函數的返回值將是一個包含 50,257 個浮點數的列表,每個浮點數預測相應 Token 將會出現的概率。
在上述例子中,你可以想象,一個訓練良好的語言模型會給 Token “jumps” 一個較高的概率來緊跟提示詞 “The quick brown fox” 後面。同樣假設模型訓練得當,你也可以想象,隨機單詞如 “potato” 繼續這個短語的概率會非常低,接近於 0。
爲了能夠生成合理的預測,語言模型必須經過訓練過程。在訓練期間,它會被提供大量文本以進行學習。訓練結束時,模型能夠使用它在訓練中見到的所有文本構建的數據結構來計算給定 Token 序列的下一個 Token 概率。
這與你的預期有何不同?我希望這現在看起來不再那麼神奇了。
生成長文本序列
由於模型只能預測下一個 Token 是什麼,因此生成完整句子的唯一方法是多次循環運行模型。每次循環迭代都會生成一個新的 Token,從返回的概率中選擇該 Token。然後將該 Token 添加到下一次循環迭代的輸入中,直到生成足夠的文本爲止。
讓我們看一個更完整的 Python 僞代碼,展示這種方法的工作原理:
def generate_text(prompt, num_tokens, hyperparameters):
tokens = tokenize(prompt)
for i in range(num_tokens):
predictions = get_token_predictions(tokens)
next_token = select_next_token(predictions, hyperparameters)
tokens.append(next_token)
return ''.join(tokens)
generate_text() 函數將用戶提示作爲參數。這可能是一個問題。
tokenize() 輔助函數使用 tiktoken 或類似庫將提示轉換爲等效的 Token 列表。在 for 循環中,get_token_predictions() 函數是調用 AI 模型以獲取下一個 Token 的概率,如前面的例子所示。
select_next_token() 函數的作用是獲取下一個 Token 的概率(或預測)並選擇最佳 Token 以繼續輸入序列。函數可以只選擇概率最高的 Token,這在機器學習中稱爲 “貪心選擇(greedy selection)”。更好的是,它可以使用隨機數生成器來選擇一個符合模型返回概率的 Token,從而爲生成的文本添加一些變化。這也會使模型在多次給出相同提示時產生不同的響應。
爲了使 Token 選擇過程更加靈活,LLM 返回的概率可以使用超參數進行修改,這些超參數作爲參數傳遞給文本生成函數。超參數允許你控制 Token 選擇過程的 “貪婪” 程度。
如果你使用過 LLM,你可能熟悉 temperature 超參數。temperature 越高,Token 概率越平坦,這增加了選擇不太可能的 Token 的機會,最終使生成的文本看起來更有創造性或更不尋常。你可能還使用了另外兩個超參數,稱爲 top_p 和 top_k,它們控制被考慮選擇的最高概率的 Token 數量。
一旦選定了一個 Token,循環就會迭代,模型會接收到一個包含新 Token 在末尾的輸入,並生成另一個緊隨其後的 Token。num_tokens 參數控制循環運行的迭代次數,換句話說,就是要控制生成多少文本。生成的文本可能(而且經常)在句子中間斷開,因爲 LLM 沒有句子或段落的概念,它只處理一個 Token。爲了防止生成的文本在句子中間斷開,我們可以將 num_tokens 參數視爲最大值而不是確切的 Token 數,在這種情況下,我們可以在生成句號 Token 時停止循環。
如果你已經理解了這些內容,那麼恭喜你,你現在已經大致瞭解了 LLM 是如何工作的。在下一部分,我會更深入一些,但仍然儘量避免涉及支撐這一技術的數學原理,因爲它相當複雜。
模型訓練
在不使用數學表達式的情況下討論如何訓練模型實際上是很困難的。我將從一個非常簡單的訓練方法開始展示。
鑑於任務是預測 Token 的後續 Token,一種簡單的訓練模型的方法是獲取訓練數據集中所有連續 Token,並用它們構建一個概率表。
讓我們用一個簡短的詞彙表和數據集來做這個。假設模型的詞彙表包含以下五個詞元:
['I', 'you', 'like', 'apples', 'bananas']
爲了使這個例子簡短而簡單,我不會將空格或標點符號作爲 Token。
我們使用一個由三句話組成的訓練數據集:
-
I like apples
-
I like bananas
-
you like bananas
我們可以構建一個 5x5 的表格,並在每個單元格中寫下表示單元格所在行的 Token 被單元格所在列的 Token 跟隨的次數。以下是根據數據集中的三句話構建的表格:
希望這很清楚。數據集中有兩次出現 “I like”,一次出現 “you like”,一次出現 “like apples” 和兩次出現 “like bananas”。
現在我們知道每對 Token 在訓練數據集中出現的次數,我們可以計算每個 Token 跟隨另一個 Token 的概率。爲此,我們將每行中的數字轉換爲概率。例如,表格中間行的 Token “like” 後面跟一次 “apples” 和兩次 “bananas”。這意味着 “like” 後面 33。3% 的概率是 “apples”,剩下的 66。7% 的概率是 “bananas”。
以下是計算出的所有概率的完整表格。空單元格的概率爲 0%。
對於 “I”、“you” 和 “like” 行來說,計算很簡單,但 “apples” 和 “bananas” 行卻出現了問題,因爲它們沒有任何數據。由於數據集中沒有任何示例顯示這些 Token 後面跟隨其他 Token,這裏我們的訓練中存在一個 “漏洞”。
爲了確保模型在缺乏訓練的情況下仍能生成預測,我決定將 “apples” 和 “bananas” 後續 Token 的概率平均分配到其他四個可能的 Token 上,這顯然可能會產生奇怪的結果,但至少模型在遇到這些 Token 時不會卡住。
訓練數據中的漏洞問題實際上非常重要。在真正的 LLM 中,訓練數據集非常龐大,因此你不會發現像我上面這個小例子中那樣明顯的訓練漏洞。但由於訓練數據覆蓋率低而導致的小的、更難檢測到的漏洞確實存在,並且相當普遍。
在這些訓練不足的區域中,LLM 對 Token 的預測質量可能會很差,但通常是難以察覺的。這是 LLM 有時會產生幻覺的原因之一,這種情況發生在生成的文本讀起來很流暢但包含事實錯誤或不一致時。
使用上面的概率表,你現在可以想象 get_token_predictions()函數的實現方式。在 Python 僞代碼中它可能是這樣的:
def get_token_predictions(input_tokens):
last_token = input_tokens[-1]
return probabilities_table[last_token]
比想象的更簡單,對吧?這個函數接受一個由用戶提示詞生成的序列。它取序列中的最後一個 Token,並返回該 Token 在概率表中對應的那一行。
例如,如果你用 ['you', 'like'] 作爲輸入 Token 調用這個函數,那麼該函數會返回 “like” 的那一行,“like” 會給予 “apples” 33.3% 的概率來繼續句子,而 “bananas” 則是另外的 66.7%。根據這些概率,上面展示的 select_next_token() 函數每三次應該會選擇一次 “apples”。
當 “apples” 被選爲 “you like” 的延續時,句子 “you like apples” 就會形成。這是一個在訓練數據集中不存在,但完全合理的原創句子。希望你開始瞭解這些模型如何通過重用模式和拼接它們在訓練中學到的不同部分來生成看似原創的想法或概念。
上下文窗口
我在上一節中訓練小模型的方法稱爲馬爾可夫鏈。
這種技術的一個問題是,只使用一個 Token(輸入的最後一個)來進行預測。任何出現在最後一個 Token 之前的文本在選擇如何繼續時都沒有影響,所以我們可以說這種解決方案的上下文窗口等於一個 Token,這個窗口非常小。由於上下文窗口如此小,模型會不斷 “忘記” 思路,從一個詞跳到另一個詞,缺乏一致性。
可以通過構建一個更大的概率矩陣來改進模型的預測。爲了使用兩個 Token 的上下文窗口,需要增加額外的錶行,這些行代表所有可能的兩個 Token 序列。在示例中使用的五個 Token 中,每一對 Token 將在概率表中新增 25 行,加上已經存在的 5 個單 Token 行。模型將不得不再次訓練,這次不僅看 Token 對,還要看 Token 組的三元組。在每次 get_token_predictions() 函數的循環迭代中,當可用時,將使用輸入的最後兩個 Token 來查找較大概率表中的對應行。
但是,2 個 Token 的上下文窗口仍然不夠。爲了生成一致且至少有基本意義的文本,需要更大的上下文窗口。沒有足夠大的上下文,新生成的 Token 不可能與之前 Token 中表達的概念或想法相關聯。那麼我們該怎麼辦呢?將上下文窗口增加到 3 個 Token 將爲概率表增加 125 行,並且質量仍然很差。我們需要將上下文窗口擴大到多大呢?
OpenAI 開源的 GPT-2 模型使用了一個 1024 個 Token 的上下文窗口。爲了使用馬爾可夫鏈實現這麼大的上下文窗口,每行概率表都必須代表一個長度在 1 到 1024 個 Token 長的序列。使用上面示例中的 5 個 Token 詞彙表,有 5 的 1024 次方種可能的序列長度爲 1024 Token。需要多少錶行來表示這些?我在 Python 會話中做了計算(向右滾動以查看完整數字):
>>> pow(5, 1024)
55626846462680034577255817933310101605480399511558295763833185422180110870347954896357078975312775514101683493275895275128810854038836502721400309634442970528269449838300058261990253686064590901798039126173562593355209381270166265416453973718012279499214790991212515897719252957621869994522193843748736289511290126272884996414561770466127838448395124802899527144151299810833802858809753719892490239782222290074816037776586657834841586939662825734294051183140794537141608771803070715941051121170285190347786926570042246331102750604036185540464179153763503857127117918822547579033069472418242684328083352174724579376695971173152319349449321466491373527284227385153411689217559966957882267024615430273115634918212890625
這行數太多了!而這只是表的一部分,因爲我們還需要長度爲 1023 的序列,1022 的序列,等等,一直到 1,因爲我們想確保在輸入中沒有足夠 Token 時也能處理較短的序列。馬爾可夫鏈是有趣的,但它們確實存在一個很大的可擴展性問題。
而且一個 1024 Token 的上下文窗口已經不再那麼大了。隨着 GPT-3,上下文窗口增加到 2048 個 Token,然後在 GPT-3.5 中增加到 4096。GPT-4 開始時爲 8192 個 Token,後來增加到 32K,然後又增加到 128K(沒錯,128,000 個 Tokens!)。現在開始出現上下文窗口爲 1M 或更大的模型,這允許在做 Token 預測時有更好的一致性和回憶能力。
總之,馬爾可夫鏈使我們以正確的方式思考文本生成問題,但它們有很大的問題,阻止我們考慮其作爲可行的解決方案。
從馬爾可夫鏈到神經網絡
顯然,我們不能再考慮使用概率表的方案,因爲合理上下文窗口的概率表會需要龐大的 RAM。我們可以做的是用一個函數來替代概率表,該函數返回 Token 概率的近似值,這些概率是通過算法生成的,而不是存儲在一個龐大的表格中。事實上,這正是神經網絡擅長的事情。
神經網絡是一種特殊的函數,它接受一些輸入,對這些輸入進行計算,然後返回一個輸出。對於語言模型來說,輸入是表示提示詞的 Token,輸出是下一個 Token 的預測概率列表。
神經網絡之所以被稱爲 “特殊” 的函數,是因爲它們除了函數邏輯外,還受一組外部定義的參數控制。最初,網絡的參數是未知的,因此函數產生的輸出完全沒有用。神經網絡的訓練過程在於找到能使函數在訓練數據集上表現最佳的參數,假設如果函數在訓練數據上表現良好,那麼在其他數據上也會表現良好。
在訓練過程中,參數會使用一種稱爲反向傳播的算法進行小幅度的迭代調整,這個算法涉及大量數學運算,所以本文不會詳細討論。每次調整後,神經網絡的預測結果會略有改善。參數更新後,網絡會再次根據訓練數據集進行評估,評估結果用於指導下一輪調整。這個過程會持續進行,直到函數在訓練數據集上表現出良好的下一個 Token 預測爲止。
爲了讓你瞭解神經網絡工作的規模,可以考慮 GPT-2 模型有大約 15 億個參數,而 GPT-3 將參數數量增加到 1750 億。據說 GPT-4 有大約 1.76 萬億個參數。以現有硬件條件訓練這種規模的神經網絡需要很長時間,通常是數週或數月。
有意思的是,由於參數衆多,都是在沒有人爲干預的情況下通過漫長的迭代過程計算出來的,因此很難理解模型的工作原理。一個訓練有素的 LLM 就像一個黑匣子,非常難以調試,因爲模型的大部分 “思考” 都隱藏在參數中。即使是訓練該模型的人也難以解釋其內部工作原理。
層、Transformer 和注意力機制
你可能會好奇,在神經網絡函數內部發生了哪些神祕的計算,在參數調優的幫助下,可以將一列輸入 Token 轉換爲合理的下一個 Token 的概率。
一個神經網絡被配置爲執行一系列操作,每個操作稱爲一個 “層”。第一層接收輸入,並進行某種轉換。轉換後的輸入進入下一層,再次被轉換。如此反覆,直到數據到達最後一層,並進行最後一次轉換,生成輸出或預測結果。
機器學習專家設計出不同類型的層,對輸入數據進行數學轉換,並找出如何組織和組合層以達到預期的結果。有些層是通用的,而有些層則專爲處理特定類型的輸入數據而設計,如圖像或在 LLM 中的 Token 化文本。
當前在 LLM 中用於文本生成最流行的神經網絡架構稱爲 Transformer。使用這種設計的 LLM 被稱爲 GPT,即生成式預訓練 Transformer。
Transformer 模型的顯著特點是其執行的一種稱爲注意力機制的層計算,這使得它們能夠在上下文窗口中的 Token 之間推導出關係和模式,並將這些關係和模式反映在下一個 Token 的概率中。
注意力機制最初用於語言翻譯器,作爲一種找到輸入序列中最重要的 Token 以提取其含義的方法。這種機制使得現代翻譯器能夠在基本層面上 “理解” 一個句子,通過關注(或將 “注意力” 引向)重要的詞或 Token。
LLM 是否具有智能?
到現在,你可能已經開始對 LLM 在生成文本的方式上是否表現出某種形式的智能形成一種看法。
我個人並不認爲 LLM 具備推理能力或提出原創思想的能力,但這並不意味着它們毫無用處。由於它們在上下文窗口中的 Token 上進行的巧妙計算,LLM 能夠發現用戶提示中存在的模式,並將這些模式與訓練期間學到的類似模式相匹配。它們生成的文本主要由訓練數據的片段組成,但它們將詞(實際上是 Token)拼接在一起的方式非常複雜,在許多情況下,產生的結果感覺是原創且有用的。
鑑於 LLM 容易產生幻覺現象,我不會信任任何由 LLM 生成,未經人工驗證就直接傳遞給終端用戶的工作流。
未來幾個月或幾年內出現的更大規模的 LLM 會實現類似於真正智能的東西嗎?我覺得這在 GPT 架構下不會發生,因爲它有很多限制,但誰知道呢,也許通過未來的一些創新,我們會實現這一目標。
作者:李木子
來源:AI 大模型實驗室
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/pZ7Ejm3ziBbNMfqHiuNIiw