揭示GPT Tokenizer的工作原理

來源 | OSCHINA 社區

作者 | OneFlow深度學習框架

原文鏈接:https://my.oschina.net/oneflow/blog/10082815

在 GPT 模型中,tokenization(詞元化)指的是將用戶輸入的文本分割成 token(詞元)的過程,以讓 GPT 能更好地理解輸入文本的詞義、句法和語義,以及生成更連貫的輸出內容。這是非常重要的預處理操作,對模型的最終效果有重大影響。

而 tokenizer(詞元生成器)是將文本切分成 token 的工具或組件。它將原始文本轉換成模型可處理的數字形式,爲 GPT 的生成與推理提供基礎能力。

本文詳細介紹了 GPT tokenizer 的工作原理。作者 Simon Willison 是開源 Web 應用框架 Django 的共同發起人,他也開源了用於探索和發佈數據的工具 Datasette。(以下內容由 OneFlow 編譯,轉載請聯繫 OneFlow 獲得授權。來源:https://simonwillison.net/2023/Jun/8/gpt-tokenizers/)

作者|Simon Willison

OneFlow 編譯

翻譯|賈川

語言大模型(如 GPT-3/4、LLaMA 和 PaLM)使用 token 作爲基本單位進行工作。它們接受文本作爲輸入,將其轉換爲 token(整數),然後預測接下來應該出現哪些 token。

通過操作這些 token,可以更好地瞭解它們在語言模型內部的工作原理。

OpenAI 提供了一個 tokenizer,用以探索 token 的工作方式。我自己構建了一個更有意思的工具,是一個 Observable notebook( https://observablehq.com/@simonw/gpt-tokenizer )。在這個 Observable notebook 中,你可以將文本轉換爲 token,將 token 轉換爲文本,還可以搜索整個 token 表。

這個 Observable notebook 看起來是這樣的:

我在這裡切分的文本是:

在給定示例中,總共生成了 21 個整數 token。5 個對應英文文本,8 個對應西班牙文本,6 個(每個字符兩個)對應三個日文字符。兩個換行符也分別被表示爲整數 token。

Observable notebook 使用了 GPT-2 的 tokenizer(基於 EJ Fox 和 Ian Johnson 所創建的 優秀 notebook ),主要作爲教育工具使用,不過 GPT-3 及更高版本的最新 tokenizer 與 GPT-2 的 tokenizer 存在些許差異。

1探索一些有趣的 token

通過與 tokenizer 進行交互可以發現各種有趣的模式。 大 多數常見的英語單詞都分配一個 token,如上所示:

“The”: 464

“ dog”: 3290

“ eats”: 25365

“ the”: 262

“ apples”: 22514

需要注意的是:字母的大小寫很重要。以單詞 “the” 爲例,大寫字母 T 的 “The” 對應的 token 是 464,而以小寫字母 t 開頭且有一個前導空格的單詞 “the” 對應的 token 卻是 262。

許多單詞的 token 裡都包含了一個前導空格,這樣就不再需要爲每個空格字符使用一個額外的 token,從而能更有效地對整個句子進行編碼,

相比英語,在對其他語言進行切分時,效率可能要低點。 西班牙語 “El perro come las manzanas” 這句話的編碼如下:

“El”: 9527

“ per”: 583

“ro”: 305

“ come”: 1282

“ las”: 39990

“ man”: 582

“zan”: 15201

“as”: 292

此處就顯示出了對英語的偏向。因爲 “man” 是一個英語單詞,所以它的 token ID 較低,爲 582。而 “zan” 不是一個在英語中獨立存在的單詞,但也是一個常見的字符序列,因此仍然值得擁有自己的 token,所以它的 token ID 爲 15201。

有些語言甚至會出現單個字符編碼爲多個 token 的情況,比如以下這些日語:

片: 31965 229

仮: 20015 106

名: 28938 235

2故障 token

“故障 token”(glitch tokens)是一類令人着迷的 token 子集。其中一個有趣的例子是 token 23282,即 “davidjl”。

可以通過在 notebook 的搜索框中搜索 “david” 來找到該 token。

Scale AI 的 prompt 工程師 Riley Goodside 指出了與該 token 相關的一些奇怪行爲。

爲什麼會發生這種情況?這是一個有趣的謎題。

token 23282 可能與 Reddit 上的用戶 “davidjl123” 有關。該用戶是 /r/counting 子論壇的一位熱情用戶,他經常在該論壇上發佈遞增數,並且已經發布了超過 163,000 次這樣的帖子。

據推測,/r/counting 子論壇中的數據最終被用於訓練 GPT-2 的 tokenizer。由於用戶 davidjl123 在該子論壇中出現了數十萬次,所以最終分配到了屬於自己的 token。

爲什麼這種情況會導致類似問題呢?到目前爲止,我看到最好的解釋來自 Hacker News 上的用戶 @londons_explore

在 “ SolidGoldMagikarp (plus, prompt generation) ” 這篇帖子下,LessWrong 對這種現象進行了詳細說明。

3用 tiktoken 進行 token 計數

OpenAI 的模型都有 token 限制。有時在將文本傳遞給 API 之前,需要計算字符串中的 token 數量,以確保不超過該限制。

其中,一個需要計算 token 數量的技術是 “檢索增強生成(Retrieval Augmented Generation)”,通過對文檔語料庫運行搜索(或嵌入搜索)來回答用戶的問題,提取最有可能的內容,並將其作爲上下文涵蓋在 prompt 中。

成功實現這種模式的關鍵是,在 token 限制內包含儘可能多的相關上下文,因此需要能夠計算 token 數量。

OpenAI 提供了一個名爲 tiktoken( https://github.com/openai/tiktoken )的 Python 庫來實現這一功能。

如果你深入研究這個庫,就會發現它目前包括五種不同的切分方案:r50k_base、p50k_base、p50k_edit、cl100k_base 和 gpt2。

其中,cl100k_base 是最相關的,它是 GPT-4 和當前 ChatGPT 使用的經濟型 gpt-3.5-turbo 模型的 tokenizer。

text-davinci-003 使用的是 p50k_base 。在 tiktoken/model.py 的 MODEL_TO_ENCODING 詞典中可以找到模型與 tokenizer 的完整映射。

以下是如何使用 tiktoken 的代碼示例:

現在token將是一個包含四個整數token ID的數組——在該例中是[8586, 374, 1063, 1495]。

使用.decode()方法將一個token ID數組轉換回文本:

第一次調用 encoding_for_model () 時,編碼數據將通過 HTTP 從 openaipublic.blob.core.windows.net Azure Blob 存儲桶(storage bucket)獲取(代碼: https://github.com/openai/tiktoken/blob/0.4.0/tiktoken_ext/openai_public.py )。這些數據會被緩存在臨時目錄中,但如果機器重新啓動,該目錄將被清除。你可通過設置 TIKTOKEN_CACHE_DIR 環境變量來強制使用更持久的緩存目錄。

4ttok

幾周前,我介紹了 tto k(https://github.com/simonw/ttok) ,這是 tiktoken 的一個命令行封裝工具,具有兩個關鍵功能:一是可以計算輸入給它的文本中的 token 數量,二是可以將該文本截斷爲指定數量的 token。

它可以計算輸入到其中的文本中的 token 數:

使用 - m gpt2 或類似選項可選擇使用適用於不同模型的編碼。

5token 生成過程

一旦你理解了 token,那麼 GPT 工具生成文本的方式就會變得更加明瞭。

特別有趣的是,觀察到 GPT-4 將其輸出流式化爲獨立的 token(GPT-4 的速度略慢於 3.5 版本,可以更容易觀察到其生成過程)。

以下是使用我的 llm CLI( https://github.com/simonw/llm )工具從 GPT-4 生成文本的結果,命令是 llm -s 'Five names for a pet pelican' -4:

如你所見,不在詞典中的名字(如 “Pelly”)佔據了多個 token,而 “Captain Gulliver” 作爲一個整體輸出了 token “Captain”。