分詞器文件

分詞管線

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

分詞管線

當呼叫 Tokenizer.encodeTokenizer.encode_batch 時,輸入的文字會經過以下管線:

  • 歸一化
  • 預分詞
  • 模型
  • 後處理

我們將詳細介紹每個步驟中發生的情況,以及當你想要解碼(decode)<decoding>某些詞符 ID 時的處理過程,並說明 🤗 Tokenizers 庫如何允許你根據需求自定義這些步驟。如果你已經熟悉這些步驟,並希望透過程式碼示例來學習,可以直接跳轉到我們的從零開始構建 BERT 示例<example>

對於需要 Tokenizer 的示例,我們將使用在快速入門中訓練好的分詞器,你可以透過以下方式載入:

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

歸一化

簡而言之,歸一化是對原始字串應用的一系列操作,以使其更規整或“更乾淨”。常見的操作包括去除空白、移除重音字元或將所有文字轉換為小寫。如果你熟悉Unicode 歸一化,它也是大多數分詞器中常用的歸一化操作。

在 🤗 Tokenizers 庫中,每個歸一化操作都由一個 Normalizer 表示,你可以透過使用 normalizers.Sequence 來組合多個操作。下面是一個應用 NFD Unicode 歸一化並移除重音的歸一化器示例:

Python
Rust
Node
from tokenizers import normalizers
from tokenizers.normalizers import NFD, StripAccents
normalizer = normalizers.Sequence([NFD(), StripAccents()])

你可以透過將其應用於任何字串來手動測試該歸一化器:

Python
Rust
Node
normalizer.normalize_str("Héllò hôw are ü?")
# "Hello how are u?"

在構建 Tokenizer 時,你可以透過更改相應的屬性來自定義其歸一化器:

Python
Rust
Node
tokenizer.normalizer = normalizer

當然,如果你更改了分詞器應用歸一化的方式,之後可能需要從頭開始重新訓練它。

預分詞

預分詞是將文字分割成更小物件的操作,這些物件為訓練結束時詞符的最終形態設定了上限。一個好的理解方式是,預分詞器會將你的文字分割成“單詞”,然後,你最終的詞符將是這些單詞的一部分。

一種簡單的預分詞方法是按空格和標點符號進行分割,這由 pre_tokenizers.Whitespace 預分詞器完成:

Python
Rust
Node
from tokenizers.pre_tokenizers import Whitespace
pre_tokenizer = Whitespace()
pre_tokenizer.pre_tokenize_str("Hello! How are you? I'm fine, thank you.")
# [("Hello", (0, 5)), ("!", (5, 6)), ("How", (7, 10)), ("are", (11, 14)), ("you", (15, 18)),
#  ("?", (18, 19)), ("I", (20, 21)), ("'", (21, 22)), ('m', (22, 23)), ("fine", (24, 28)),
#  (",", (28, 29)), ("thank", (30, 35)), ("you", (36, 39)), (".", (39, 40))]

輸出是一個元組列表,每個元組包含一個單詞及其在原始句子中的跨度(用於確定我們 Encoding 的最終 offsets)。請注意,按標點符號分割會拆分本例中的縮寫詞,如 "I'm"

你可以將任何 PreTokenizer 組合在一起。例如,下面是一個按空格、標點和數字進行分割,並將數字拆分為單個數字的預分詞器:

Python
Rust
Node
from tokenizers import pre_tokenizers
from tokenizers.pre_tokenizers import Digits
pre_tokenizer = pre_tokenizers.Sequence([Whitespace(), Digits(individual_digits=True)])
pre_tokenizer.pre_tokenize_str("Call 911!")
# [("Call", (0, 4)), ("9", (5, 6)), ("1", (6, 7)), ("1", (7, 8)), ("!", (8, 9))]

正如我們在快速入門中看到的,你可以透過更改相應的屬性來自定義 Tokenizer 的預分詞器:

Python
Rust
Node
tokenizer.pre_tokenizer = pre_tokenizer

當然,如果你更改了預分詞器的方式,之後可能需要從頭開始重新訓練你的分詞器。

模型

一旦輸入文字經過歸一化和預分詞處理,Tokenizer 會在預分詞後的詞符上應用模型。這是管線中需要在你的語料庫上進行訓練的部分(或者如果你使用的是預訓練分詞器,則這部分已經訓練好了)。

模型的作用是使用其學到的規則將你的“單詞”分割成詞符。它還負責將這些詞符對映到模型詞彙表中對應的 ID。

這個模型在初始化 Tokenizer 時傳遞,所以你已經知道如何自定義這部分了。目前,🤗 Tokenizers 庫支援:

  • models.BPE
  • models.Unigram
  • models.WordLevel
  • models.WordPiece

有關每個模型及其行為的更多詳細資訊,你可以檢視這裡

後處理

後處理是分詞管線的最後一步,用於在返回 Encoding 之前對其進行任何額外的轉換,例如新增可能的特殊詞符。

正如我們在快速入門中看到的,我們可以透過設定相應的屬性來自定義 Tokenizer 的後處理器。例如,以下是如何進行後處理以使輸入適用於 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]", 1), ("[SEP]", 2)],
)

請注意,與預分詞器或歸一化器不同,更改後處理器後你不需要重新訓練分詞器。

整合:從零開始構建 BERT 分詞器

讓我們將所有這些部分組合起來,構建一個 BERT 分詞器。首先,BERT 依賴於 WordPiece,所以我們用這個模型例項化一個新的 Tokenizer

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

然後我們知道 BERT 透過移除重音和轉換為小寫來預處理文字。我們還使用一個 Unicode 歸一化器:

Python
Rust
Node
from tokenizers import normalizers
from tokenizers.normalizers import NFD, Lowercase, StripAccents
bert_tokenizer.normalizer = normalizers.Sequence([NFD(), Lowercase(), StripAccents()])

預分詞器只是按空白和標點符號進行分割:

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

後處理使用我們在上一節中看到的模板:

Python
Rust
Node
from tokenizers.processors import TemplateProcessing
bert_tokenizer.post_processor = TemplateProcessing(
    single="[CLS] $A [SEP]",
    pair="[CLS] $A [SEP] $B:1 [SEP]:1",
    special_tokens=[
        ("[CLS]", 1),
        ("[SEP]", 2),
    ],
)

我們可以使用這個分詞器,並像在快速入門中一樣,在 wikitext 上進行訓練:

Python
Rust
Node
from tokenizers.trainers import WordPieceTrainer
trainer = WordPieceTrainer(vocab_size=30522, special_tokens=["[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"])
files = [f"data/wikitext-103-raw/wiki.{split}.raw" for split in ["test", "train", "valid"]]
bert_tokenizer.train(files, trainer)
bert_tokenizer.save("data/bert-wiki.json")

解碼

除了對輸入文字進行編碼,Tokenizer 還提供了用於解碼的 API,即將模型生成的 ID 轉換回文字。這透過 Tokenizer.decode(用於單個預測文字)和 Tokenizer.decode_batch(用於一批預測)方法完成。

decoder 會首先將 ID 轉換回詞符(使用分詞器的詞彙表)並移除所有特殊詞符,然後用空格連線這些詞符:

Python
Rust
Node
output = tokenizer.encode("Hello, y'all! How are you 😁 ?")
print(output.ids)
# [1, 27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35, 2]
tokenizer.decode([1, 27253, 16, 93, 11, 5097, 5, 7961, 5112, 6218, 0, 35, 2])
# "Hello , y ' all ! How are you ?"

如果你使用的模型添加了特殊字元來表示給定“單詞”的子詞(例如 WordPiece 中的 "##"),你需要自定義 decoder 以正確處理它們。如果我們以前面的 bert_tokenizer 為例,預設解碼會得到:

Python
Rust
Node
output = bert_tokenizer.encode("Welcome to the 🤗 Tokenizers library.")
print(output.tokens)
# ["[CLS]", "welcome", "to", "the", "[UNK]", "tok", "##eni", "##zer", "##s", "library", ".", "[SEP]"]
bert_tokenizer.decode(output.ids)
# "welcome to the tok ##eni ##zer ##s library ."

但透過將其更改為適當的解碼器,我們得到:

Python
Rust
Node
from tokenizers import decoders
bert_tokenizer.decoder = decoders.WordPiece()
bert_tokenizer.decode(output.ids)
# "welcome to the tokenizers library."
< > 在 GitHub 上更新

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