徹底理解GPT tokenizers
你可能已經(jīng)聽說過GPT這個(gè)詞,它是一種人工智能模型,可以生成各種各樣的文本,比如小說、詩歌、對(duì)話、新聞等等。GPT的全稱是Generative Pre-trained Transformer,意思是生成式預(yù)訓(xùn)練變換器。生成式表示它可以根據(jù)一些輸入(比如一個(gè)單詞或一句話)來創(chuàng)造新的內(nèi)容,預(yù)訓(xùn)練表示它在使用之前已經(jīng)在大量的文本數(shù)據(jù)上進(jìn)行了學(xué)習(xí),變換器表示它使用了一種叫做Transformer的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。
要理解GPT的工作原理,我們需要先了解一個(gè)重要的概念:token。token是文本的最小單位,可以是一個(gè)字母、一個(gè)單詞、一個(gè)標(biāo)點(diǎn)符號(hào)或者一個(gè)符號(hào)。比如,這句話:
Hello, world!
可以被分成五個(gè)token:
Hello , world !
GPT模型在處理文本時(shí),需要先把文本分割成token,然后把每個(gè)token轉(zhuǎn)換成一個(gè)數(shù)字,這個(gè)數(shù)字就代表了這個(gè)token的含義。這個(gè)數(shù)字叫做token ID。比如,我們可以用下面的表格來表示每個(gè)token和它對(duì)應(yīng)的token ID:
token | token ID |
Hello | 1 |
, | 2 |
world | 3 |
! | 4 |
那么,這句話就可以被轉(zhuǎn)換成一個(gè)數(shù)字序列:
1 2 3 4
GPT模型就是通過學(xué)習(xí)大量的這樣的數(shù)字序列,來掌握文本的規(guī)律和語義。
然后,當(dāng)我們給它一個(gè)輸入(比如一個(gè)token ID或者一個(gè)數(shù)字序列),它就可以根據(jù)它學(xué)到的知識(shí),來生成一個(gè)合理的輸出(比如一個(gè)新的token ID或者一個(gè)新的數(shù)字序列)。
但是,如果我們只用單個(gè)字母或單詞作為token,會(huì)有一些問題。首先,不同的語言有不同的詞匯量,有些語言可能有幾萬個(gè)單詞,有些語言可能有幾十萬甚至幾百萬個(gè)單詞。如果我們要給每個(gè)單詞分配一個(gè)唯一的token ID,那么我們需要很大的內(nèi)存空間來存儲(chǔ)這些ID。其次,有些單詞可能很少出現(xiàn)在文本中,或者有些單詞可能是新造出來的,比如一些專有名詞、縮寫、網(wǎng)絡(luò)用語等等。如果我們要讓GPT模型能夠處理這些單詞,那么我們需要不斷地更新我們的token ID表格,并且重新訓(xùn)練模型。
為了解決這些問題,GPT模型使用了一種叫做BPE(Byte Pair Encoding)的方法來分割文本。BPE是一種數(shù)據(jù)壓縮技術(shù),它可以把一段文本分割成更小的子單元(subword),這些子單元可以是單個(gè)字母、字母組合、部分單詞或完整單詞。
BPE的原理是基于統(tǒng)計(jì)頻率來合并最常見的字母對(duì)或子單元對(duì)。比如,如果我們有下面這四個(gè)單詞:
lowlowernewestwidest
我們可以先把它們分割成單個(gè)字母:
l o wl o w e rn e w e s tw i d e s t
然后,我們可以統(tǒng)計(jì)每個(gè)字母對(duì)出現(xiàn)的次數(shù),比如:
pair | count |
l o | 2 |
o w | 2 |
w e | 2 |
e r | 1 |
n e | 1 |
e w | 1 |
w i | 1 |
i d | 1 |
d e | 1 |
e s | 1 |
s t | 1 |
我們可以看到,l o,o w和w e都出現(xiàn)了兩次,是最常見的字母對(duì)。我們可以把它們合并成一個(gè)新的子單元,比如:
lowlow ern e westw i dest
這樣,我們就減少了一些token的數(shù)量。我們可以重復(fù)這個(gè)過程,直到達(dá)到我們想要的token的數(shù)量或者沒有更多的可合并的字母對(duì)。比如,我們可以繼續(xù)合并e r,n e,e w等等,得到:
lowlowernewestwidest
這樣,我們就把四個(gè)單詞分割成了六個(gè)子單元:
lowernewestwidest
這些子單元就是BPE的token。我們可以給它們分配token ID,比如:
token | token ID |
low | 5 |
er | 6 |
new | 7 |
est | 8 |
wid | 9 |
那么,這四個(gè)單詞就可以被轉(zhuǎn)換成下面的數(shù)字序列:
55 67 89 8
你可能會(huì)問,為什么要用BPE來分割文本呢?有什么好處呢?其實(shí),BPE有以下幾個(gè)優(yōu)點(diǎn):
- 它可以減少token的數(shù)量,從而節(jié)省內(nèi)存空間和計(jì)算資源。
- 它可以處理未知或罕見的單詞,只要把它們分割成已知的子單元就行了。比如,如果我們遇到一個(gè)新單詞lowerest,我們可以把它分割成low er est,然后用對(duì)應(yīng)的token ID表示它。
- 它可以捕捉單詞的形態(tài)變化,比如復(fù)數(shù)、時(shí)態(tài)、派生等等。比如,如果我們遇到一個(gè)單詞lowering,我們可以把它分割成low er ing,然后用對(duì)應(yīng)的token ID表示它。這樣,GPT模型就可以學(xué)習(xí)到這個(gè)單詞和其他形式的關(guān)系。
當(dāng)然,BPE也有一些缺點(diǎn),比如:
- 它可能會(huì)破壞一些有意義的子單元,比如把一個(gè)完整的單詞分割成兩個(gè)或多個(gè)部分。比如,如果我們遇到一個(gè)單詞tower,我們可能會(huì)把它分割成t ow er,而不是保留它作為一個(gè)整體。
- 它可能會(huì)導(dǎo)致一些歧義或混淆,比如把兩個(gè)不同的單詞分割成相同的子單元序列。比如,如果我們遇到兩個(gè)單詞tow er和tower,我們可能會(huì)把它們都分割成t ow er,而不是區(qū)分它們。
- 它可能會(huì)影響一些特殊的符號(hào)或標(biāo)記的處理,比如HTML標(biāo)簽、URL、郵箱地址等等。比如,如果我們遇到一個(gè)URLhttps://www.bing.com/, 我們可能會(huì)把它分割成多個(gè)子單元,比如:
https : / / www . bing . com /
這樣,可能會(huì)丟失一些原本的含義或格式。
所以,BPE并不是一種完美的方法,它只是一種權(quán)衡的方法,它在減少token數(shù)量和保留token含義之間尋找一個(gè)平衡點(diǎn)。不同的BPE方法可能會(huì)有不同的分割規(guī)則和結(jié)果,比如,我們可以設(shè)置一個(gè)最大的token數(shù)量,或者一個(gè)最小的合并頻率,來影響B(tài)PE的過程和輸出。
那么,GPT模型是如何使用BPE來分割文本的呢?實(shí)際上,GPT模型并不是直接使用BPE來分割文本,而是使用了一種叫做GPT-2 tokenizer的工具,這個(gè)工具是基于BPE的一種改進(jìn)版本。GPT-2 tokenizer有以下幾個(gè)特點(diǎn):
- 它使用了Unicode編碼來表示每個(gè)字符,而不是ASCII編碼。這樣,它可以支持更多的語言和符號(hào),比如中文、日文、阿拉伯文、表情符號(hào)等等。
- 它使用了一個(gè)固定的token數(shù)量,即50257個(gè)。這個(gè)數(shù)字是根據(jù)GPT-2模型的輸入層的大小來確定的,每個(gè)輸入層可以容納50257個(gè)不同的token ID。
- 它使用了一個(gè)預(yù)先訓(xùn)練好的BPE模型來分割文本,這個(gè)BPE模型是在一個(gè)大規(guī)模的文本數(shù)據(jù)集上訓(xùn)練得到的,它包含了各種各樣的文本類型和語言。
上手實(shí)踐
如果你想使用GPT-2 tokenizer來分割文本,你可以參考以下的步驟:
- 首先,你需要安裝和導(dǎo)入transformers庫,這是一個(gè)提供了各種預(yù)訓(xùn)練模型和工具的開源庫12。
- 然后,你需要從預(yù)訓(xùn)練的gpt2模型中加載tokenizer和model,你可以使用AutoTokenizer和GPT2DoubleHeadsModel類來實(shí)現(xiàn)這一功能12。
- 接著,你需要給tokenizer添加一些特殊的token,比如[CLS]和[SEP],這些token可以幫助模型識(shí)別文本的開始和結(jié)束12。
- 最后,你可以使用tokenizer的encode或encode_plus方法來把文本轉(zhuǎn)換成token ID的序列,并且使用model的forward方法來得到模型的輸出123。
下面是一個(gè)簡單的Python代碼示例:
# 導(dǎo)入transformers庫
from transformers import AutoTokenizer, GPT2DoubleHeadsModel
import torch
# 加載tokenizer和model
tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = GPT2DoubleHeadsModel.from_pretrained("gpt2")
# 添加特殊的token
num_added_tokens = tokenizer.add_special_tokens({"cls_token": "[CLS]", "sep_token": "[SEP]"})
# 分割文本
text = "Hello, my dog is cute"
inputs = tokenizer.encode_plus(text, add_special_tokens=True, return_tensors="pt")
# 得到模型的輸出
outputs = model(**inputs)
last_hidden_states = outputs.last_hidden_state
一旦您了解了令牌,GPT 工具生成文本的方式就會(huì)變得更加有意義。
特別是,觀看 GPT-4 將其輸出作為獨(dú)立令牌流式傳輸回很有趣(GPT-4 比 3.5 略慢,因此更容易看到發(fā)生了什么)。
這是我得到的 - 使用我的 llm CLI 工具從 GPT-4 生成文本:llm -s 'Five names for a pet pelican' -4。
字典中不存在的“Pelly” 占用了多個(gè)token,而字典中存在的“Captain Gulliver”則能一次性輸出。
本文轉(zhuǎn)載自 ??AI小智??,作者: AI小智
