語言模型在 NLP 領域裡面,長期以來一直是個倍受重視的主題。拜 ChatGPT 所賜,這半年多以來大型語言模型的相關議題迅速爆紅,其影響力相信大家都感同身受。筆者有幸在這段期間,獲得深入研究這個領域的機會,近距離親身感受 LLM 的魅力,在此與各位分享學習 Large-Scale Language Model 大型語言模型相關知識的過程。
(Powered By Microsoft Designer)
本篇筆記簡單介紹不同類型的語言模型,包含傳統的統計式語言模型,與後來以機器學習方法為主的神經網路模型。並嘗試實做一個 Bigram 語言模型,以三國演義的文本為資料,簡單製作一個可以模仿三國演義的文本產生器。
語言模型基本上是一種用來描述文字分佈的統計機率,廣義來說,任何透過統計文字機率的來處理自然語言問題的方法,都可以算是一種語言模型。一個常見的應用是計算某句話出現的機率,以語音辨識的角度來說,使用者的發音可能可以組成以下兩種句子:
- 再給我兩份蔥,讓我把記憶煎成餅
- 再給我兩分鐘,讓我把記憶結成冰
語言模型可以計算這兩句話出現的機率,用來判斷哪句話更有可能是使用者實際想說的話。語言模型也有可能因為統計的資料不同,而有不同的計算結果。以上面這兩句話來說,如果統計來源是一個蔥油餅店的老闆,那有可能第一句話的機率比較高。如果統計來源是一位歌手,那有可能第二句話的機率比較高。
語言模型也可以用來預測下一個字詞出現的機率,例如看見一句話「今天天氣真 ...」下一字有可能是「真好」、「真棒」、「真壞」、「真差」,這也是能透過語言模型去統計出來的。
N-Gram 是一種相當單純且直觀的統計模型,主要透過統計連續字詞的頻率來建立機率模型,其中 Unigram、Bigram 和 Trigram 是 N-Gram 模型常見的具體例子。
Unigram 是 N-Gram 模型中最簡單的一種,將每個字詞當作一個獨立的元素。一個典型的 Unigram 模型的統計結果可能如下:
{
"今天": 0.35,
"天氣": 0.25,
"心情": 0.20,
"真好": 0.15,
"真差": 0.05,
}
Bigram 則是將兩個連續字詞當作一個元素,描述了在給定一個字詞情況下,下一個可能出現的字詞的機率分佈。這樣不僅能捕捉字詞的分佈機率,也能捕捉語法架構。一個典型的 Bigram 模型統計結果可能如下:
{
"今天": {"天氣": 0.5, "心情": 0.5},
"天氣": {"真好": 0.7, "真差": 0.3},
"心情": {"真好": 0.8, "真差": 0.2},
}
依此類推 Trigram 模型則是將三個連續字詞作為一個單元來分析,以獲取更豐富的文本信息。越大的 N-Gram 可以捕捉越完整的語言資訊,但伴隨而來的是更大的記憶體消耗。
綜上所述,Unigram、Bigram 和 Trigram 模型是自然語言處理中用於文本分析的基本工具。透過這些模型,我們可以更準確地捕捉語言模式和結構,從而進行更精確的分析和預測。但 N-Gram 模型存在嚴重的稀疏問題與上下文捕捉能力不好等等,因此能夠實際應用的場景並不多。
這類模型筆者相對不熟悉一些,基本上 HMM 會考慮一些隱藏狀態的問題,例如 Bigram 只考慮前一個詞與下一個詞之間的機率,而 HMM 可能還會考慮詞性之類的。
RNN 開啟了語言模型踏入深度學習領域的先河,著名的 LSTM, GRU 等都是 RNN 的一種。循環神經網路的運作方式為:給定一段輸入序列,神經網路會從頭到尾一個一個看,每看過一個元素就更新一次隱藏狀態。也有 Bidirectional RNN 的變體,就是從頭看到尾之後,會再從尾看到頭。使用 RNN 的語言模型比起過往 N-Gram 與 HMM 的效果都好上不少。但 RNN 的計算成本非常高昂,平行運算不易,有訓練效率不佳的問題。
2017 年 Google 在 Attention Is All You Need 論文中提出 Transformers 架構進行機器翻譯的改善,從此以後 Vaswani et al., 2017
便深深烙印在每個 NLP 研究者的腦海裡(誤)。
在 Transformers 裡非常重要的 Attention 機制,在 Bahdanau et al., 2014 和 Luong et al., 2015 時就已經被提出來探討過了,因此 Attention 的算法也分成 Bahdanau Attention 和 Luong Attention 兩種。關於 Transformers 的描述,會在後續章節提及。
其後 2018 年來自 Google 的 Devlin 等人提出 BERT 模型,在 NLP 領域引起了相當大的迴響,此後自然語言相關的任務皆不斷向上突破,模型大小蒸蒸日上,即便到了 OpenAI 提出的超大型語言模型 GPT-3 之後,模型的參數量還在不斷攀升。
過去很長一段時間,語言模型對筆者而言都是一個很理論性質的存在。但細思其本質,實際上並沒有那麼遙不可及。於是筆者決定嘗試實做一個小型的傳統 Bigram 模型,在此之前我們需要一份文本。這裡推薦維基文庫或開放文學,裡面有許多自由內容的中文文本可以使用,比較沒有版權問題。筆者這邊使用開放文學的三國演義,完整的程式碼放在 Colab 上給大家參考。
透過 F12 觀察開放文學網站的運作方式,主要透過 POST 到這段網址 http://open-lit.com/GETbook.php
取得文本資料,並以 bid
代表哪本書,以 id
代表那本書的第幾回。簡單觀察後發現三國演義 120 回的 ID 分佈在 67 ~ 186 之間。且回傳的內容只有文本本身,對爬蟲非常友善,因此撰寫爬蟲程式如下:
import requests
from bs4 import BeautifulSoup as BS
from tqdm import tqdm
corpus = list()
for pid in tqdm(range(67, 186 + 1)):
data = {"bid": 12, "id": pid}
res = requests.post("http://open-lit.com/GETbook.php", data=data)
res = BS(res.text)
corpus.append(res.text)
我們將這種純文本資料集稱呼為 Corpus 語料庫。這邊提醒一下 id
是 Python 的內建函式,所以盡量不要直接使用 id
當作變數名稱。
透過以下程式碼進行機率建模:
from collections import Counter, defaultdict
# \u3000 為全形空白
skip_symbols = ["\u3000", "\r", "\t", "\n"]
bigram = defaultdict(Counter)
for data in corpus:
# 將空白字元移除
for sym in skip_symbols:
data = data.replace(sym, "")
# 以 <BOS> 代表文本開頭,開始統計整份文本
prev_char = "<BOS>"
for ch in data:
bigram[prev_char][ch] += 1
prev_char = ch
# 以 <EOS> 代表文本結尾
ch = "<EOS>"
bigram[prev_char][ch] += 1
基本上就是在統計目前這個字,他的下一個字是什麼,又出現了幾次。常見的語言模型通常會將這種資訊換算成機率,但因為我們是 Bigram 文本生成模型,所以只需要知道下個詞的權重分佈比例即可。
如果是轉換成機率的模型,容易因為浮點數除法的原因,導致每個詞的機率都是一個非常非常小的浮點數。因此,這類模型都會對機率取 Log 對數,當語言模型在進行機率相乘時,只需要將對數相加即可。
我們在處理文本時,也一併將空格移除,這是因為我很確信三國演義只會出現中文字。倘若訓練的文本是中英夾雜時,必須特別注意空格的處理。我在文本裡面觀察到大量排版用的空格,於是將空格移除,來減少語言模型學到過於偏頗的機率分佈。
透過以下程式碼進行文本生成:
import random
generate = str()
curr_ch = "<BOS>"
while curr_ch != "<EOS>":
generate += curr_ch
prob = bigram[curr_ch]
elements = [k for k in prob]
weights = [prob[k] for k in prob]
curr_ch = random.choices(elements, weights=weights, k=1)[0]
print(generate + curr_ch)
這裡很單純的根據模型權重進行取樣,然後將文字拼接在一起,以下節錄一段生成結果:
<BOS>
第五百姓疲乏。」問時魏皇叔季玉龍鳳雛妙算。布陣。舊抵安樂進。臣才和,關津,引五十騎將愕然歸本大功可防。若興兵三路。五路,皆曰:「如此機會稽妖,斷機密丞相爭戰,尚多有怠;又恐國將軍器,復至別宮室,受君耶?」言解酒畢,忽然後苑中,乃天下奇功,忽然人協,事。」
乍看之下似乎有點樣子,反正文言文其實也看不是很懂 ((x
但其實稍微仔細看一下就會發現很多問題:
<BOS>
後面接的必定是「第」。在這種 Bigram 生成模型裡面,每兩個字之間的組合可能還合理,但將它們串聯在一起後,整體文本就顯得不成章法。這是因為 Bigram 模型捕捉上下文能力薄弱的關係,僅能考慮前一個字。也因此造成了上下括號不成對的問題,因為模型根本沒有理解句法結構的能力。
在 <BOS>
後面必定是「第」的問題,主要是因為我們在統計文本時,有在每個回目之前加上一個 <BOS>
標記。然而因為三國演義每個回目的文本,開頭必定是「第 XXX 回」的關係,所以在機率分佈上 <BOS>
後面必定是「第」,這顯現了語言模型在這份資料上產生了嚴重的偏見 (Bias) 而造成的重複性。
在動手實做一個簡單的 Bigram 模型實驗後,可以瞭解許多語言模型值得學習的觀點與必須考量的問題。這些觀點即使到了千億參數級的大型語言模型,依然有很大的影響。
以上只是個 Bigram 模型非常基礎的概念,實際上機率模型在運作時還有很多細節需要處理,例如遇到未知詞時,需要做 Fallback 之類的。雖然 Bigram 模型的生成效果並不理想,但筆者覺得其實蠻有趣的。筆者認為這些「有趣但是沒用」的遊戲性實驗,可以激發研究者的好奇心,也是不斷推動理論進步的基石。
在這次參賽的系列文裡面,筆者規劃了許多「有趣但是沒用」的小型應用,希望可以帶大家見微知著,一起從小處著手 ((雖然真相是筆者也沒有很強的機器做大型實驗 QQ