來源?| OSCHINA 社區(qū)
作者 | OneFlow深度學(xué)習(xí)框架
在 GPT 模型中,tokenization(詞元化)指的是將用戶輸入的文本分割成 token(詞元)的過程,以讓 GPT 能更好地理解輸入文本的詞義、句法和語義,以及生成更連貫的輸出內(nèi)容。這是非常重要的預(yù)處理操作,對(duì)模型的最終效果有重大影響。
而 tokenizer(詞元生成器)是將文本切分成 token 的工具或組件。它將原始文本轉(zhuǎn)換成模型可處理的數(shù)字形式,為 GPT 的生成與推理提供基礎(chǔ)能力。
本文詳細(xì)介紹了 GPT?tokenizer?的工作原理。作者 Simon Willison 是開源 Web 應(yīng)用框架 Django 的共同發(fā)起人,他也開源了用于探索和發(fā)布數(shù)據(jù)的工具 Datasette。
作者|Simon Willison
OneFlow?編譯
翻譯|賈川 語言大模型(如 GPT-3/4、LLaMA 和 PaLM)使用 token 作為基本單位進(jìn)行工作。它們接受文本作為輸入,將其轉(zhuǎn)換為 token(整數(shù)),然后預(yù)測(cè)接下來應(yīng)該出現(xiàn)哪些 token。 通過操作這些 token,可以更好地了解它們?cè)谡Z言模型內(nèi)部的工作原理。 OpenAI 提供了一個(gè) tokenizer,用以探索 token 的工作方式。我自己構(gòu)建了一個(gè)更有意思的工具,是一個(gè) Observable notebook(?https://observablehq.com/@simonw/gpt-tokenizer?)。在這個(gè) Observable notebook 中,你可以將文本轉(zhuǎn)換為 token,將 token 轉(zhuǎn)換為文本,還可以搜索整個(gè) token 表。 這個(gè) Observable notebook 看起來是這樣的:
我在這里切分的文本是:
The dog eats the apples El perro come las manzanas 片仮名
在給定示例中,總共生成了 21 個(gè)整數(shù) token。5 個(gè)對(duì)應(yīng)英文文本,8 個(gè)對(duì)應(yīng)西班牙文本,6 個(gè)(每個(gè)字符兩個(gè))對(duì)應(yīng)三個(gè)日文字符。兩個(gè)換行符也分別被表示為整數(shù) token。 ? Observable notebook 使用了 GPT-2 的?tokenizer(基于 EJ Fox 和 Ian Johnson 所創(chuàng)建的?優(yōu)秀 notebook?),主要作為教育工具使用,不過 GPT-3 及更高版本的最新?tokenizer?與 GPT-2 的?tokenizer?存在些許差異。 ?
1
探索一些有趣的 token
通過與?tokenizer?進(jìn)行交互可以發(fā)現(xiàn)各種有趣的模式。?大?多數(shù)常見的英語單詞都分配一個(gè) token,如上所示:
“The”: 464
“ dog”: 3290
“ eats”: 25365
“ the”: 262
“ apples”: 22514
需要注意的是:字母的大小寫很重要。以單詞 “the” 為例,大寫字母 T 的 “The” 對(duì)應(yīng)的 token 是 464,而以小寫字母 t 開頭且有一個(gè)前導(dǎo)空格的單詞 “the” 對(duì)應(yīng)的 token 卻是 262。 許多單詞的 token 里都包含了一個(gè)前導(dǎo)空格,這樣就不再需要為每個(gè)空格字符使用一個(gè)額外的 token,從而能更有效地對(duì)整個(gè)句子進(jìn)行編碼, 相比英語,在對(duì)其他語言進(jìn)行切分時(shí),效率可能要低點(diǎn)。?西班牙語 “El perro come las manzanas” 這句話的編碼如下:
“El”: 9527
“ per”: 583
“ro”: 305
“ come”: 1282
“ las”: 39990
“ man”: 582
“zan”: 15201
“as”: 292
此處就顯示出了對(duì)英語的偏向。因?yàn)?“man” 是一個(gè)英語單詞,所以它的 token ID 較低,為 582。而 “zan” 不是一個(gè)在英語中獨(dú)立存在的單詞,但也是一個(gè)常見的字符序列,因此仍然值得擁有自己的 token,所以它的 token ID 為 15201。 有些語言甚至?xí)霈F(xiàn)單個(gè)字符編碼為多個(gè) token 的情況,比如以下這些日語:
片: 31965 229
仮: 20015 106
名: 28938 235
2
故障 token
“故障 token”(glitch tokens)是一類令人著迷的 token 子集。其中一個(gè)有趣的例子是 token 23282,即 “davidjl”。 可以通過在 notebook 的搜索框中搜索 “david” 來找到該 token。
Scale AI 的 prompt 工程師 Riley Goodside 指出了與該 token 相關(guān)的一些奇怪行為。
為什么會(huì)發(fā)生這種情況?這是一個(gè)有趣的謎題。 token 23282 可能與 Reddit 上的用戶 “davidjl123” 有關(guān)。該用戶是 /r/counting 子論壇的一位熱情用戶,他經(jīng)常在該論壇上發(fā)布遞增數(shù),并且已經(jīng)發(fā)布了超過 163,000 次這樣的帖子。 據(jù)推測(cè),/r/counting 子論壇中的數(shù)據(jù)最終被用于訓(xùn)練 GPT-2 的 tokenizer。由于用戶 davidjl123 在該子論壇中出現(xiàn)了數(shù)十萬次,所以最終分配到了屬于自己的 token。 為什么這種情況會(huì)導(dǎo)致類似問題呢?到目前為止,我看到最好的解釋來自 Hacker News 上的用戶 @londons_explore
這些故障 token 都位于 token 嵌入空間的中心附近。這意味著,模型在區(qū)分這些 token 和其他位于嵌入空間中心附近的 token 時(shí)存在困難,因此當(dāng)被要求 “重復(fù)” 這些 token 時(shí),模型會(huì)選擇錯(cuò)誤的 token。 ? 這種情況發(fā)生的原因是,這些 token 在互聯(lián)網(wǎng)上出現(xiàn)了很多次(例如,davidjl 用戶在 Reddit 上有 163000 個(gè)帖子,僅僅是計(jì)算遞增的數(shù)字),但是這些 token 本身并不難以預(yù)測(cè)(因此,在訓(xùn)練過程中,梯度變得幾乎為零,并且嵌入向量會(huì)衰減到零,這是某些優(yōu)化器在歸一化權(quán)重時(shí)會(huì)進(jìn)行的操作)。
在 “?SolidGoldMagikarp (plus, prompt generation)?” 這篇帖子下,LessWrong 對(duì)這種現(xiàn)象進(jìn)行了詳細(xì)說明。
3
用 tiktoken 進(jìn)行 token 計(jì)數(shù)
OpenAI 的模型都有 token 限制。有時(shí)在將文本傳遞給 API 之前,需要計(jì)算字符串中的 token 數(shù)量,以確保不超過該限制。 其中,一個(gè)需要計(jì)算 token 數(shù)量的技術(shù)是 “檢索增強(qiáng)生成(Retrieval Augmented Generation)”,通過對(duì)文檔語料庫運(yùn)行搜索(或嵌入搜索)來回答用戶的問題,提取最有可能的內(nèi)容,并將其作為上下文涵蓋在 prompt 中。 成功實(shí)現(xiàn)這種模式的關(guān)鍵是,在 token 限制內(nèi)包含盡可能多的相關(guān)上下文,因此需要能夠計(jì)算 token 數(shù)量。 OpenAI 提供了一個(gè)名為 tiktoken(?https://github.com/openai/tiktoken?)的 Python 庫來實(shí)現(xiàn)這一功能。 如果你深入研究這個(gè)庫,就會(huì)發(fā)現(xiàn)它目前包括五種不同的切分方案:r50k_base、p50k_base、p50k_edit、cl100k_base 和 gpt2。 其中,cl100k_base 是最相關(guān)的,它是 GPT-4 和當(dāng)前 ChatGPT 使用的經(jīng)濟(jì)型 gpt-3.5-turbo 模型的 tokenizer。 text-davinci-003 使用的是 p50k_base 。在 tiktoken/model.py 的 MODEL_TO_ENCODING 詞典中可以找到模型與 tokenizer 的完整映射。 以下是如何使用 tiktoken 的代碼示例:
import tiktoken
encoding = tiktoken.encoding_for_model("gpt-4") # or "gpt-3.5-turbo" or "text-davinci-003" tokens = encoding.encode("Here is some text") token_count?=?len(tokens)
現(xiàn)在token將是一個(gè)包含四個(gè)整數(shù)token?ID的數(shù)組——在該例中是[8586,?374,?1063,?1495]。
使用.decode()方法將一個(gè)token?ID數(shù)組轉(zhuǎn)換回文本:
text?=?encoding.decode(tokens) #?'Here?is?some?text'第一次調(diào)用 encoding_for_model () 時(shí),編碼數(shù)據(jù)將通過 HTTP 從 openaipublic.blob.core.windows.net Azure Blob 存儲(chǔ)桶(storage bucket)獲取(代碼:?https://github.com/openai/tiktoken/blob/0.4.0/tiktoken_ext/openai_public.py?)。這些數(shù)據(jù)會(huì)被緩存在臨時(shí)目錄中,但如果機(jī)器重新啟動(dòng),該目錄將被清除。你可通過設(shè)置 TIKTOKEN_CACHE_DIR 環(huán)境變量來強(qiáng)制使用更持久的緩存目錄。
4
ttok
幾周前,我介紹了?tto?k(https://github.com/simonw/ttok)?,這是 tiktoken 的一個(gè)命令行封裝工具,具有兩個(gè)關(guān)鍵功能:一是可以計(jì)算輸入給它的文本中的 token 數(shù)量,二是可以將該文本截?cái)酁橹付〝?shù)量的 token。 它可以計(jì)算輸入到其中的文本中的 token 數(shù):
?
# Count tokens echo -n "Count these tokens" | ttok # Outputs: 3 (the newline is skipped thanks to echo -n) # Truncation curl 'https://simonwillison.net/' | strip-tags -m | ttok -t 6 # Outputs: Simon Willison’s Weblog # View integer token IDs echo"Show these tokens" | ttok --tokens #?Outputs:?7968?1521?11460?198? 使用 - m gpt2 或類似選項(xiàng)可選擇使用適用于不同模型的編碼。
?
?
5
token 生成過程
一旦你理解了 token,那么 GPT 工具生成文本的方式就會(huì)變得更加明了。 特別有趣的是,觀察到 GPT-4 將其輸出流式化為獨(dú)立的 token(GPT-4 的速度略慢于 3.5 版本,可以更容易觀察到其生成過程)。 以下是使用我的 llm CLI(?https://github.com/simonw/llm?)工具從 GPT-4 生成文本的結(jié)果,命令是 llm -s 'Five names for a pet pelican' -4:
如你所見,不在詞典中的名字(如 “Pelly”)占據(jù)了多個(gè) token,而 “Captain Gulliver” 作為一個(gè)整體輸出了 token “Captain”。 ?
編輯:黃飛
?
評(píng)論
查看更多