全面解釋人工智慧LLM模型的真實工作原理(二)

果冻人工智能發表於2024-10-27

前一篇:《全面解釋人工智慧LLM模型的真實工作原理(一)》

序言:在上一篇文章中,我們從原理上構建了一個識別“葉子”和“花朵”的神經網路,並詳細講解了它的工作過程。這包括對輸入數字逐個與權重相乘後求和,加上偏置值,最後透過非線性處理和統計分佈計算來得出輸出。這些操作使用了簡單的數學運算(乘法、加法和非線性處理)。本節的重點是解答神經網路的權重和偏置值是如何得到的以及最關鍵的概念:如何讓神經網路輸出chatGPT一樣的句子。為了讓神經網路學到合適的權重和偏置,我們需要提供大量的學習資料(如大量的“葉子”和“花朵”圖片),讓網路在學習過程中調整每個神經元的權重和偏置值,最終實現正確分類。(請動一下您的小手,訂閱作者!

如何訓練這個神經網路(模型)?

在上例中,我們為了測試,給模型預設了合適的權重和偏置,這樣才能得到準確的輸出。但在實際應用中,權重和偏置值是如何獲得的呢?獲得合適的‘權重’和‘偏置’這個過程就稱為“訓練模型”或“訓練神經網路”,也可以理解為“人工智慧的自我學習”;沒錯,這個過程就是“訓練AI”。人類需要做的就是為模型提供優質資料來進行訓練。

假設我們收集了一些資料,包括各種型別的“葉子”和“花朵”。然後,我們用工具將它們的顏色和體積轉換成數字,給每個資料樣本貼上“葉子”或“花朵”的標籤(給資料取名字就稱為“標註資料”),最終這些資料組成了我們的“訓練資料集”。

訓練神經網路的工作原理如下:

  1. 初始化權重

首先,從隨機數開始,將神經元的每個引數/權重設為一個隨機數。(啟動訓練程式時,計算機記憶體中未初始化的都是隨機數,一般無須特別設定)

  1. 輸入資料並獲得初始輸出

我們給神經網路輸入“葉子”的資料表示(如 R=32,G=107,B=56,Vol=11.2),期望輸出層第一個神經元的值大於第二個神經元的值,表示識別出“葉子”。假如預期“葉子”神經元的值是0.8,代表“花”的神經元值是0.2。

  1. 計算損失

因為初始權重是隨機的,實際輸出往往和預期有差異。比如,兩個神經元的初始輸出分別是0.6和0.4。我們可以透過求差並將差值平方相加計算損失:(0.8 - 0.6)² + (0.2 - 0.4)² = 0.04 + 0.04 = 0.08。理想情況下,我們希望損失接近於零,也就是“最小化損失”。

  1. 計算梯度並更新權重

計算每個權重對損失的影響(稱為梯度),看向哪個方向調整才能減少損失。梯度指示了每個引數的變化方向——權重會朝損失減少的方向略微調整一點。這個過程稱為“梯度下降”。

  1. 重複迭代

持續重複這些步驟,透過不斷更新權重,使得損失逐步減少,最終得到一組“訓練好的”權重或引數。這就是神經網路的訓練過程,稱為“梯度下降”。

補充說明

• 多個訓練樣本

訓練中通常會使用多個樣本。微調權重以最小化某個樣本的損失可能會導致其他樣本的損失增大。為了解決這個問題,通常會計算所有樣本的平均損失,並基於平均損失的梯度來更新權重。每次完整的樣本迴圈稱為“一個 epoch”,多個 epoch 的訓練可以幫助逐步找到更優的權重。

• 自動計算梯度

實際上,無需手動微調權重來計算梯度,數學公式可以直接推匯出每個引數的最佳調整方向。例如,如果上一步權重為 0.17,且神經元的輸出希望增大,那麼將權重調整為 0.18 可能更有效。

在實踐中,訓練深度網路是一個複雜的過程,訓練中可能會遇到梯度失控的情況,例如梯度值趨於零或趨向無窮大,這分別稱為“梯度消失”和“梯度爆炸”問題。雖然上述的損失定義有效,但在實際應用中,通常會使用更適合特定任務的損失函式來提高訓練效果。

這些原理怎樣幫助神經網路生成語言?

請記住,神經網路只能接收輸入一組數字,基於訓練好的引數進行數學運算,最後輸出另一組數字。關鍵在於如何解釋這些數字,並透過訓練來自動調整引數。如果我們能夠把兩組數字解釋為“葉子/花朵”或“一小時後是晴天或雨天”,同樣也可以將它們解釋為“句子的下一個字元”。

但是,英語字母遠不止兩個,所以我們需要將輸出層的神經元數量擴充套件,例如擴充套件到26個以上的神經元(再加上一些符號,如空格、句號等)。每個神經元對應一個字母或符號,然後我們在輸出層中找出數值最大的神經元,並將其對應的字元作為輸出字元。現在我們就有了一個可以接收輸入並輸出字元的網路。

如果我們給神經網路輸入“Humpty Dumpt”這個字串,然後讓它輸出一個字元,並將其解釋為“網路預測到的下一個字元”,我們可以透過訓練,確保網路在收到這樣的字串“Humpty Dumpt”輸入時輸出字母“y”,從而達到我們想要的結果“Humpty Dumpty”。

不過,這裡有一個問題:如何將字串輸入到網路中?畢竟,神經網路只接受數字!通常實踐中我們可以透過“one-hot編碼”或其他編碼方法將字串轉換成數值陣列,使其可以被神經網路理解和處理。

這裡我們用一個最簡單的解決方案來編碼:直接為每個字元分配一個數字。例如,a=1,b=2,依此類推。現在我們可以輸入“humpty dumpt”並訓練網路輸出“y”。網路的工作過程如下:

先在神經網路的輸入層輸入一串句子(字串),它將會在輸出層預測下一個字元。這樣的方法可以幫助我們構建完整的句子。例如,當我們預測出“y”後,可以將這個“y”新增到前面輸入的字串尾部,並再次送回神經網路的輸入層,讓它預測下一個字元。如果訓練得當,網路會預測出一個空格;如此迴圈下去,最終生成出完整的句子:“Humpty Dumpty sat on a wall”。這樣,我們就得到了一個生成式 AI(人工智慧語言模型),神經網路現在可以生成人類的自然語言了!

當然,在真實應用中例如chatGPT,我們不會使用這種簡單的字元編號方法。在後文中,我們會介紹一種更合理的編碼方式。如果你迫不及待,可以檢視附錄中的“編碼”部分。

細心的讀者可能會注意到,我們無法直接輸入“Humpty Dumpty”,因為如圖所示,輸入層只有12個神經元,對應於“humpty dumpt”中的每個字元(包括空格),並沒有多餘的神經元留給字母‘y’輸入了。那麼,如何在下一步中加入“y”呢?如果在輸入層加上第13個神經元,就需要重新調整整個網路,這顯然不太現實。解決方案很簡單:我們可以將最早的字元“h”剔除,保留最近的12個字元輸入。例如,我們輸入“umpty dumpty”,網路會預測出一個空格;然後我們輸入“mpty dumpty ”,網路會輸出“s”,如此迴圈下去,過程如下所示:

這種方法有個問題,即當我們輸入“ sat on the wal”時,會丟失之前的許多資訊。那麼,現代頂尖神經網路是如何解決的呢?原理基本相似。神經網路的輸入的長度是固定的(取決於輸入層的大小),這種長度稱為“上下文長度”,即網路用來預測後續內容的參考範圍。現代網路的上下文長度可以很長(通常達到幾萬甚至幾十萬個字元。例如,ChatGPT的4o模型支援12.8萬個字元,Claude則支援25.6萬個字元。這意味著它們在輸入層中使用了超過10萬個神經元來接收使用者的輸入。試想一下,上千億引數意味著有多少神經元在參與運算?),這對提升效果非常有幫助。儘管某些方法允許輸入無限長度的序列,但固定上下文長度較大的模型在效能上已經優於這些方法。

細心的讀者可能還會注意到,我們在輸入和輸出端對同一個字母的解釋方式不同!例如,輸入“h”時我們用數字8表示它,但在輸出層,我們並不直接要求模型輸出數字8來代表“h”,而是生成26個數值,並選擇其中最大值對應的字母作為輸出。如果第8個數值最大,我們將其解釋為“h”。為什麼不在兩端使用相同的表示方式呢?事實上,這是為了構建更有效的模型——不同的輸入和輸出解釋方式為模型效能的提升提供了更多可能。實踐表明,這種不同的表示方式對語言生成更有效。實際上,我們在輸入端的數字表示方式也並非最佳,稍後會介紹更優的方法。

本節是搞明白chatGPT輸出人類自然語言句子的核心原理,希望感興趣的朋友如果沒有搞明白,多讀幾篇或者在評論區留言與作者交流,我會毫無遺漏的回答所有的評論。

未完待續…

相關文章