Pre-tokenization
上一步我們讓 BPE 自由地把常見的東西黏起來。但如果真的讓它「自由」,它會黏出你不想要的東西。
先用一條固定規則把文字剁開。BPE 只能在每一塊裡面合併,永遠不准跨過柵欄。
把一小段英文直接丟給 BPE,不做任何切割。它學到的前十二條規則是這樣:
第一步就跨過了空格。到第八步,一個句點加一個空格加一個詞加一個空格被黏成單一 token。這種 token 換個標點就失效,詞表會被各種組合吃光。
這是 GPT-2 公開的那一條 — 後來幾乎所有人都從它改。
's 't 're 've 'm 'll 'd英文縮寫,一個一個寫死在規則裡 ?\p{L}+可有可無的一個空格,加上一串字母 ?\p{N}+可有可無的一個空格,加上一串數字 ?[^\s\p{L}\p{N}]+可有可無的一個空格,加上一串符號\s+(?!\S) \s+剩下的空白,自成一塊注意每一條前面那個 「可有可無的空格」:空格被黏在詞的前面。所以 the 和 the 在模型眼裡是兩個不同的東西。
貼任何文字進去,看柵欄落在哪。切出來的塊數,就是 BPE 之後最多能合併到的下限。
「今天天氣很好。」交給同一條柵欄規則。柵欄落在哪,差很多。
位元組數用 UTF-8 計算。塊數 = 這條規則切出幾塊。
\p{L}「字母」。但母音符號、聲調符號在 Unicode 裡屬於標記不是字母 — 於是每遇到一個母音符號就切一刀。मौसम 被切成 म/ौ/सम,一個音節硬生生剖成兩半。it's 乾淨切成 it + 's,因為 's 被寫死在規則第一行。法文的 l'eau 沒人照顧,被切成 l/'/eau 三塊。規則裡有英語的母語知識,沒有別人的。evlerimizden(從我們的房子)= ev+ler+imiz+den,四個詞素。靠空格切,它就是一整塊,接著被 BPE 切成看不出詞素的碎片。同樣一句「今天天氣很好」,英文 26 個位元組,印地文要 45 個。切得越碎、位元組越多,同一個意思花掉的 token 就越多。
token 花得多,代表三件事同時發生:context window 變小、API 帳單變貴、模型看到的訓練訊號更破碎。同一個模型,換一種語言就變笨又變貴 — 其中一部分的原因,早在 BPE 開跑之前的這條正規表達式裡就決定了。
最不起眼的那一步,
把每種語言的待遇先寫死了。