分詞器文件
快速教程
並獲得增強的文件體驗
開始使用
快速入門
讓我們快速瞭解一下 🤗 Tokenizers 庫的特性。該庫提供了當今最常用分詞器的實現,既易於使用又速度飛快。
從零開始構建分詞器
為了說明 🤗 Tokenizers 庫的速度有多快,讓我們在 wikitext-103 資料集(516M 文字)上訓練一個新的分詞器,只需幾秒鐘。首先,你需要下載該資料集並解壓縮:
wget https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-103-raw-v1.zip unzip wikitext-103-raw-v1.zip
訓練分詞器
在本教程中,我們將構建和訓練一個位元組對編碼(BPE)分詞器。有關不同型別分詞器的更多資訊,請檢視 🤗 Transformers 文件中的這份指南。在這裡,訓練分詞器意味著它將透過以下方式學習合併規則:
- 從訓練語料庫中存在的所有字元作為初始詞符開始。
- 識別最常見的詞符對並將其合併為一個詞符。
- 重複此過程,直到詞彙表(例如,詞符數量)達到我們想要的大小。
該庫的主要 API 是 Tokenizer
類,以下是我們如何用 BPE 模型例項化一個:
from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))
為了在 wikitext 檔案上訓練我們的分詞器,我們需要例項化一個 [trainer]{.title-ref},在這種情況下是 BpeTrainer
:
from tokenizers.trainers import BpeTrainer
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
我們可以設定訓練引數,如 vocab_size
或 min_frequency
(這裡保留它們的預設值 30,000 和 0),但最重要的部分是提供我們計劃稍後使用的 special_tokens
(它們在訓練期間根本不使用),以便將它們插入到詞彙表中。
你寫入特殊詞符列表的順序很重要:在這裡 "[UNK]"
將獲得 ID 0,"[CLS]"
將獲得 ID 1,依此類推。
我們現在就可以訓練我們的分詞器,但它還不是最優的。如果沒有一個預分詞器來將我們的輸入分割成單詞,我們可能會得到跨越多個單詞的詞符:例如,我們可能會得到一個 "it is"
詞符,因為這兩個詞經常相鄰出現。使用預分詞器將確保沒有詞符比預分詞器返回的單詞更大。在這裡,我們想要訓練一個子詞 BPE 分詞器,我們將使用最簡單的預分詞器,即按空格分割。
from tokenizers.pre_tokenizers import Whitespace
tokenizer.pre_tokenizer = Whitespace()
現在,我們可以用任何我們想使用的檔案列表來呼叫 Tokenizer.train
方法:
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
tokenizer.train(files, trainer)
在完整的 wikitext 資料集上訓練我們的分詞器應該只需要幾秒鐘!要將分詞器儲存在一個包含其所有配置和詞彙表的檔案中,只需使用 Tokenizer.save
方法:
tokenizer.save("data/tokenizer-wiki.json")
你可以使用 Tokenizer.from_file
類方法從該檔案重新載入你的分詞器:
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")
使用分詞器
現在我們已經訓練好了一個分詞器,我們可以使用 Tokenizer.encode
方法在任何我們想要的文字上使用它:
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
這將分詞器的完整流水線應用於文字,返回一個 Encoding
物件。要了解更多關於這個流水線的資訊,以及如何應用(或自定義)其中的部分,請檢視此頁面。
這個 Encoding
物件包含了你的深度學習模型(或其他模型)所需的所有屬性。tokens
屬性包含文字分割成的詞符:
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]
同樣,ids
屬性將包含這些詞符在分詞器詞彙表中的索引:
print(output.ids)
# [27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35]
🤗 Tokenizers 庫的一個重要特性是它帶有完整的對齊跟蹤,這意味著你總是可以找到原始句子中對應於給定詞符的部分。這些資訊儲存在我們的 Encoding
物件的 offsets
屬性中。例如,假設我們想找回導致 "[UNK]"
詞符出現的原因,它是列表中索引為 9 的詞符,我們只需請求該索引處的偏移量:
print(output.offsets[9])
# (26, 27)
這些是原始句子中對應於表情符號的索引:
sentence = "Hello, y'all! How are you 😁 ?"
sentence[26:27]
# "😁"
後處理
我們可能希望分詞器能自動新增特殊詞符,比如 "[CLS]"
或 "[SEP]"
。為此,我們使用後處理器。TemplateProcessing
是最常用的,你只需指定處理單個句子和句子對的模板,以及特殊詞符及其 ID。
當我們構建分詞器時,我們將 "[CLS]"
和 "[SEP]"
放在特殊詞符列表的第 1 和第 2 個位置,所以這應該是它們的 ID。為了仔細檢查,我們可以使用 Tokenizer.token_to_id
方法:
tokenizer.token_to_id("[SEP]")
# 2
以下是我們如何設定後處理以獲得傳統的 BERT 輸入:
from tokenizers.processors import TemplateProcessing
tokenizer.post_processor = TemplateProcessing(
single="[CLS] $A [SEP]",
pair="[CLS] $A [SEP] $B:1 [SEP]:1",
special_tokens=[
("[CLS]", tokenizer.token_to_id("[CLS]")),
("[SEP]", tokenizer.token_to_id("[SEP]")),
],
)
讓我們更詳細地看一下這段程式碼。首先,我們指定單個句子的模板:它們應具有 "[CLS] $A [SEP]"
的形式,其中 $A
代表我們的句子。
然後,我們指定句子對的模板,它應該具有 "[CLS] $A [SEP] $B [SEP]"
的形式,其中 $A
代表第一個句子,$B
代表第二個句子。模板中新增的 :1
代表我們希望輸入各部分的 type IDs
:它預設為 0(這就是為什麼我們沒有 $A:0
),在這裡我們將其設定為 1,用於第二個句子的詞符和最後一個 "[SEP]"
詞符。
最後,我們指定我們使用的特殊詞符及其在分詞器詞彙表中的 ID。
為了檢查這是否正常工作,讓我們嘗試對與之前相同的句子進行編碼:
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?", "[SEP]"]
要檢查一對句子的結果,我們只需將兩個句子傳遞給 Tokenizer.encode
:
output = tokenizer.encode("Hello, y'all!", "How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "[SEP]", "How", "are", "you", "[UNK]", "?", "[SEP]"]
然後,你可以用以下方式檢查分配給每個詞符的型別 ID 是否正確:
print(output.type_ids)
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
如果你使用 Tokenizer.save
儲存你的分詞器,後處理器也會被一起儲存。
批次編碼多個句子
為了充分利用 🤗 Tokenizers 庫的速度,最好使用 Tokenizer.encode_batch
方法批次處理你的文字:
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
輸出是 Encoding
物件的列表,就像我們之前看到的一樣。你可以同時處理任意數量的文字,只要它們能裝入記憶體。
要處理一批句子對,請向 Tokenizer.encode_batch
方法傳遞兩個列表:句子 A 的列表和句子 B 的列表:
output = tokenizer.encode_batch(
[["Hello, y'all!", "How are you 😁 ?"], ["Hello to you too!", "I'm fine, thank you!"]]
)
在編碼多個句子時,你可以使用 Tokenizer.enable_padding
自動將輸出填充到最長句子的長度,需要提供 pad_token
及其 ID(我們可以像之前一樣使用 Tokenizer.token_to_id
來仔細檢查填充詞符的 ID):
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")
我們可以設定填充的 direction
(預設為右側)或指定的 length
,如果我們想將每個樣本填充到該特定長度(這裡我們不設定它,以填充到最長文字的大小)。
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
print(output[1].tokens)
# ["[CLS]", "How", "are", "you", "[UNK]", "?", "[SEP]", "[PAD]"]
在這種情況下,分詞器生成的 attention mask
會考慮填充:
print(output[1].attention_mask)
# [1, 1, 1, 1, 1, 1, 1, 0]
預訓練
使用預訓練分詞器
你可以從 Hugging Face Hub 載入任何分詞器,只要倉庫中存在 tokenizer.json
檔案。
from tokenizers import Tokenizer
tokenizer = Tokenizer.from_pretrained("bert-base-uncased")
從舊版詞彙表文件匯入預訓練分詞器
你也可以直接匯入一個預訓練的分詞器,只要你有它的詞彙表文件。例如,以下是如何匯入經典的預訓練 BERT 分詞器:
from tokenizers import BertWordPieceTokenizer
tokenizer = BertWordPieceTokenizer("bert-base-uncased-vocab.txt", lowercase=True)
只要你已經用以下命令下載了 bert-base-uncased-vocab.txt
檔案:
wget https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-vocab.txt