分詞器文件

快速教程

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

快速入門

讓我們快速瞭解一下 🤗 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 模型例項化一個:

Python
Rust
Node
from tokenizers import Tokenizer
from tokenizers.models import BPE
tokenizer = Tokenizer(BPE(unk_token="[UNK]"))

為了在 wikitext 檔案上訓練我們的分詞器,我們需要例項化一個 [trainer]{.title-ref},在這種情況下是 BpeTrainer

Python
Rust
Node
from tokenizers.trainers import BpeTrainer
trainer = BpeTrainer(special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])

我們可以設定訓練引數,如 vocab_sizemin_frequency(這裡保留它們的預設值 30,000 和 0),但最重要的部分是提供我們計劃稍後使用的 special_tokens(它們在訓練期間根本不使用),以便將它們插入到詞彙表中。

你寫入特殊詞符列表的順序很重要:在這裡 "[UNK]" 將獲得 ID 0,"[CLS]" 將獲得 ID 1,依此類推。

我們現在就可以訓練我們的分詞器,但它還不是最優的。如果沒有一個預分詞器來將我們的輸入分割成單詞,我們可能會得到跨越多個單詞的詞符:例如,我們可能會得到一個 "it is" 詞符,因為這兩個詞經常相鄰出現。使用預分詞器將確保沒有詞符比預分詞器返回的單詞更大。在這裡,我們想要訓練一個子詞 BPE 分詞器,我們將使用最簡單的預分詞器,即按空格分割。

Python
Rust
Node
from tokenizers.pre_tokenizers import Whitespace
tokenizer.pre_tokenizer = Whitespace()

現在,我們可以用任何我們想使用的檔案列表來呼叫 Tokenizer.train 方法:

Python
Rust
Node
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
tokenizer.train(files, trainer)

在完整的 wikitext 資料集上訓練我們的分詞器應該只需要幾秒鐘!要將分詞器儲存在一個包含其所有配置和詞彙表的檔案中,只需使用 Tokenizer.save 方法:

Python
Rust
Node
tokenizer.save("data/tokenizer-wiki.json")

你可以使用 Tokenizer.from_file 類方法從該檔案重新載入你的分詞器:

Python
Rust
Node
tokenizer = Tokenizer.from_file("data/tokenizer-wiki.json")

使用分詞器

現在我們已經訓練好了一個分詞器,我們可以使用 Tokenizer.encode 方法在任何我們想要的文字上使用它:

Python
Rust
Node
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")

這將分詞器的完整流水線應用於文字,返回一個 Encoding 物件。要了解更多關於這個流水線的資訊,以及如何應用(或自定義)其中的部分,請檢視此頁面

這個 Encoding 物件包含了你的深度學習模型(或其他模型)所需的所有屬性。tokens 屬性包含文字分割成的詞符:

Python
Rust
Node
print(output.tokens)
# ["Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?"]

同樣,ids 屬性將包含這些詞符在分詞器詞彙表中的索引:

Python
Rust
Node
print(output.ids)
# [27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35]

🤗 Tokenizers 庫的一個重要特性是它帶有完整的對齊跟蹤,這意味著你總是可以找到原始句子中對應於給定詞符的部分。這些資訊儲存在我們的 Encoding 物件的 offsets 屬性中。例如,假設我們想找回導致 "[UNK]" 詞符出現的原因,它是列表中索引為 9 的詞符,我們只需請求該索引處的偏移量:

Python
Rust
Node
print(output.offsets[9])
# (26, 27)

這些是原始句子中對應於表情符號的索引:

Python
Rust
Node
sentence = "Hello, y'all! How are you 😁 ?"
sentence[26:27]
# "😁"

後處理

我們可能希望分詞器能自動新增特殊詞符,比如 "[CLS]""[SEP]"。為此,我們使用後處理器。TemplateProcessing 是最常用的,你只需指定處理單個句子和句子對的模板,以及特殊詞符及其 ID。

當我們構建分詞器時,我們將 "[CLS]""[SEP]" 放在特殊詞符列表的第 1 和第 2 個位置,所以這應該是它們的 ID。為了仔細檢查,我們可以使用 Tokenizer.token_to_id 方法:

Python
Rust
Node
tokenizer.token_to_id("[SEP]")
# 2

以下是我們如何設定後處理以獲得傳統的 BERT 輸入:

Python
Rust
Node
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。

為了檢查這是否正常工作,讓我們嘗試對與之前相同的句子進行編碼:

Python
Rust
Node
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "How", "are", "you", "[UNK]", "?", "[SEP]"]

要檢查一對句子的結果,我們只需將兩個句子傳遞給 Tokenizer.encode

Python
Rust
Node
output = tokenizer.encode("Hello, y'all!", "How are you 😁 ?")
print(output.tokens)
# ["[CLS]", "Hello", ",", "y", "'", "all", "!", "[SEP]", "How", "are", "you", "[UNK]", "?", "[SEP]"]

然後,你可以用以下方式檢查分配給每個詞符的型別 ID 是否正確:

Python
Rust
Node
print(output.type_ids)
# [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]

如果你使用 Tokenizer.save 儲存你的分詞器,後處理器也會被一起儲存。

批次編碼多個句子

為了充分利用 🤗 Tokenizers 庫的速度,最好使用 Tokenizer.encode_batch 方法批次處理你的文字:

Python
Rust
Node
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])

輸出是 Encoding 物件的列表,就像我們之前看到的一樣。你可以同時處理任意數量的文字,只要它們能裝入記憶體。

要處理一批句子對,請向 Tokenizer.encode_batch 方法傳遞兩個列表:句子 A 的列表和句子 B 的列表:

Python
Rust
Node
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):

Python
Rust
Node
tokenizer.enable_padding(pad_id=3, pad_token="[PAD]")

我們可以設定填充的 direction(預設為右側)或指定的 length,如果我們想將每個樣本填充到該特定長度(這裡我們不設定它,以填充到最長文字的大小)。

Python
Rust
Node
output = tokenizer.encode_batch(["Hello, y'all!", "How are you 😁 ?"])
print(output[1].tokens)
# ["[CLS]", "How", "are", "you", "[UNK]", "?", "[SEP]", "[PAD]"]

在這種情況下,分詞器生成的 attention mask 會考慮填充:

Python
Rust
Node
print(output[1].attention_mask)
# [1, 1, 1, 1, 1, 1, 1, 0]

預訓練

Python
Rust
Node

使用預訓練分詞器

你可以從 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
< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.