NLP Course documentation

Xây dựng từng khối tokenizer

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Xây dựng từng khối tokenizer

Open In Colab Open In Studio Lab

Như chúng ta đã thấy trong các phần trước, tokenize bao gồm một số bước:

  • Chuẩn hóa (mọi thao tác dọn dẹp văn bản được cho là cần thiết, chẳng hạn như xóa dấu cách hoặc dấu, chuẩn hóa Unicode, v.v.)
  • Tiền tokenize (chia nhỏ đầu vào thành các từ)
  • Đưa đầu vào thông qua mô hình (sử dụng các từ được tiền tokenize để tạo ra một chuỗi token)
  • Hậu xử lý (thêm token đặc biệt của trình tokenize, tạo attention mask và ID token)
The tokenization pipeline.

Thư viện 🤗 Tokenizers đã được xây dựng để cung cấp nhiều sự lựa chọn cho các bước này, và ta có thể kết hợp và nối chúng với nhau. Trong phần này, chúng ta sẽ xem các có thể xây một tokenizer từ đầu, trái ngược với cách huấn luyện một tokenizer mới từ cái cũ như ta đã làm ở phần 2. Chúng ta sẽ có thể xây bất kì kiểu tokenizer nào ta có thể nghĩ ra!

Chính xác hơn, thư viện được xây dựng tập trung vào lớp Tokenizer với các khối được tập hợp lại trong các mô-đun con:

  • normalizers chứa tất cả các kiểu Normalizer bạn có thể sử dụng (hoàn thiện danh sách tại đây).
  • pre_tokenizers chứa tất cả các kiểu PreTokenizer bạn có thể sử dụng (hoàn thiện danh sách tại đây).
  • models chứa tất cả các kiểu Model bạn có thể sử dụng, như BPE, WordPiece, and Unigram (hoàn thiện danh sách tại đây).
  • trainers chứa tất cả các kiểu Trainer khác nhau bạn có thể sử dụng để huấn luyện mô hình của bạn trên kho ngữ liệu (một cho mỗi loại mô hình; hoàn thiện danh sách tại đây).
  • post_processors chứa tất cả các kiểu PostProcessor bạn có thể sử dụng (hoàn thiện danh sách tại đây).
  • decoders chứa tất cả các kiểu Decoder đa dạng bạn có thể sử dụng để giải mã đầu ra của tokenize (hoàn thiện danh sách tại đây).

Bạn có thể tìm được toàn bộ danh sách các khối tại đây.

Thu thập một kho ngữ liệu

Để huấn luyện tokenizer mới của mình, chúng ta sẽ sử dụng một kho ngữ liệu nhỏ chứa các đoạn văn (cho nhanh). Các bước để có được kho ngữ liệu tương tự như chúng ta đã làm ở phần đầu của chương này, nhưng lần này chúng ta sẽ sử dụng WikiText-2:

from datasets import load_dataset

dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")


def get_training_corpus():
    for i in range(0, len(dataset), 1000):
        yield dataset[i : i + 1000]["text"]

Hàm get_training_corpus() là một hàm tạo có thể trả về các lô với mỗi lô là 1,000 đoạn văn, cái mà ta sẽ sử dụng để huấn luyện tokenizer.

🤗 Tokenizers cũng có thể được huấn luyện trực tiếp trên các tệp văn bản. Đây là cách chúng ta tạo ra một tệp văn bản bao gồm các đoạn văn/đầu vào từ WikiText-2 mà ta có thể sử dụng cục bộ:

with open("wikitext-2.txt", "w", encoding="utf-8") as f:
    for i in range(len(dataset)):
        f.write(dataset[i]["text"] + "\n")

Tiếp theo chúng tôi sẽ hướng dẫn bạn cách tự xây dựng từng khối BERT, GPT-2, và XLNet tokenizer của riêng mình. Điều này sẽ cung cấp cho chúng ta một ví dụ về từng thuật toán trong số ba thuật toán tokenize chính: WordPiece, BPE, và Unigram. Hãy cũng bắt đầu với BERT!

Xây dựng một WordPiece tokenizer từ đầu

Để xây dựng một tokenizer với thư viện 🤗 Tokenizers, chúng ta sẽ bắt đầu với việc khởi tạo một đối tượng Tokenizer với model, sau đó thiết lập normalizer, pre_tokenizer, post_processor, và decoder tới các giá trị ta muốn.

Với ví dụ này, ta sẽ tạo ra một Tokenizer với một mô hình WordPiece:

from tokenizers import (
    decoders,
    models,
    normalizers,
    pre_tokenizers,
    processors,
    trainers,
    Tokenizer,
)

tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))

Chúng ta phải chỉ rõ unk_token để mô hình biết phải trả về gì khi gặp các kí tự chưa từng gặp trước đó. Các tham số khác chúng ta có thể cài đặt gồm vocab của mô hình (ta sẽ huấn luyện mô hình nên không cần thiết lập nó) và max_input_chars_per_word, tương ứng độ dài tối đa cho một từ (từ dài hơn giá trị này sẽ bị chia nhỏ)

Bước đầu tiên để tokenize đó là chuẩn hoá, vì vậy hãy cũng bắt đầu với bước này. Vì BERT được sử dụng rộng tãi, ta có thể sử dụng BertNormalizer với tuỳ chọn kinh điển để thiết lập cho BERT: lowercasestrip_accents, tự cái tên đã giải thích mục đích của chúng; clean_text để loại bỏ tất cả các kí tự kiểm soát và dấu cách lặp lại thành một; và handle_chinese_chars thêm dấu cách giữa các kí tự tiếng Trung. Để , which places spaces around Chinese characters. To tái tạo tokenizer bert-base-uncased, ta có thể thiết lập chuẩn hoá sau:

tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)

Thông thường, khi xây dựng một tokenizer, bạn không cần phải truy cập vào một hàm chuẩn hoá thủ công vì nó đã có sẵn trong thư viện 🤗 Tokenizers library — tuy nhiên, hãy cùng tạo ra chuẩn hoá BERT thủ công. Thư viện cung câp trình chuẩn hoá LowercaseStripAccents, bạn hoàn toàn có thể kết hợp nhiều trình chuẩn hoá với nhau thông qua Sequence:

tokenizer.normalizer = normalizers.Sequence(
    [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
)

Ta cũng có thể sử dụng chuẩn hoá Unicode NFD Unicode normalizer, vì nếu không chuẩn hoá StripAccents sẽ không nhận diện được những kí tự có dấu và không thể tách nó đúng như ta muốn.

Như đã thấy ở trên, ta có thể sử dụng phương thức normalize_str() của normalizer để kiểm tra tác động của nó lên một chuỗi văn bản:

print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
hello how are u?

Đào sâu hơn Nếu bạn muốn kiểm tra xem hai phiên bản chuẩn hoá trước đó trên cũng một chuỗi chứa kí tự unicode u"\u0085", bạn chắc chắn sẽ nhận thấy rằng hai cách chuẩn hoá này không hoàn toàn giống nhau. Để tránh phức tạp hoá phiên bản với normalizers.Sequence quá nhiều, chúng tôi sẽ không bao gồm các sự thay thế theo Regex mà BertNormalizer yêu cầu khi tham số clean_text được thiết lập là True - đây cũng là giá trị mặc định. Nhưng đừng lo: có khả năng ta sẽ nhận được kết quả chuẩn hoá giống nhau mà không cần sử dụng BertNormalizer thủ công bằng cách thêm hai normalizers.Replace vào chuỗi chuẩn hoá.

Tiếp theo là bước pre-tokenization. Một lần nữa, ta có BertPreTokenizer được xây dựng sẵn để dùng:

tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()

Hoặc ta có thể xây từ đầu:

tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()

Lưu ý rằng Whitespace sẽ tách theo dấu cách và các kí tự không phải chữ cái, số, hoặc dấu gạch dưới, nên về mặt kỹ thuật nó sẽ tách theo dấu cách và dấu câu:

tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]

Nêu sbanj chỉ muốn tách theo dấu cách, bạn có thể sử dụng WhitespaceSplit thay thế:

pre_tokenizer = pre_tokenizers.WhitespaceSplit()
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]

Giống như chuẩn hoá, bản có thể sử dụng Sequence để kết hợp các tiền tokenizer với nhau:

pre_tokenizer = pre_tokenizers.Sequence(
    [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
)
pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
 ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]

Bước tiếp theo trong pipeline tokenize là đưa đầu vào qua mô hình. Ta đã chỉ định mô hình của mình khi khởi tạo, nhưng ta vẫn cần huấn luyện nó, điều này cần tới WordPieceTrainer. Vấn đề chính ở đây là khi khởi tạo một trình huấn luyện trong 🤗 Tokenizers thì bạn cần phải truyền tất cả các token đặc biệt bạn có ý định sử dụng, nếu không nó sẽ không thêm vào bộ từ vựng, vì chúng không có trong kho ngữ liệu huấn luyện:

special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)

Cũng như việc chỉ định vocab_sizespecial_tokens, ta cần thiết lập min_frequency (số lần một token phải xuất hiện để được thêm vào bộ từ vựng) hoặc thay đổi continuing_subword_prefix (nếu ta muốn sử dụng thứ gì khác ngoài ##).

Để huấn luyện một mô hình sử dụng trình lặp ta định nghĩa trước đó, ta chỉ cần thực hiện lệnh này:

tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Chúng ta cũng có thể sử dụng các tệp văn bản để huấn luyện tokenizer của mình như sau (ta tái khởi tạo mô hình với một WordPiece rỗng):

tokenizer.model = models.WordPiece(unk_token="[UNK]")
tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Trong cả hai trường hợp, ta có thể kiểm tra xem tokenizer trên một đoạn văn bản bằng cách sử dụng phương thức encode():

encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']

encoding thu được là một Encoding gồm tất cả các đầu ra cần thiết của một tokenizer trong tất cả các thông số đa dạng của nó: ids, type_ids, tokens, offsets, attention_mask, special_tokens_mask, và overflowing.

Bước cuối của quy trình đó là hậu xử lý. Ta cần thêm token [CLS] token at the beginning and the [SEP] ở cuối (hoặc sau mỗi câu, nếu ta có cặp câu). Chúng ta sẽ sử dụng TemplateProcessor để thực hiện điều này, nhưng trước hết ta cần biết các ID của token [CLS][SEP] trong bộ từ vựng.

cls_token_id = tokenizer.token_to_id("[CLS]")
sep_token_id = tokenizer.token_to_id("[SEP]")
print(cls_token_id, sep_token_id)
(2, 3)

Để viết bản mẫu cho TemplateProcessor, chúng ta phải chỉ định cách xử lý một câu đơn và một cặp câu. Đối với cả hai, chúng tôi viết các token đặc biệt muốn sử dụng; câu đầu tiên (hoặc câu đơn) được biểu thị bằng $A, trong khi câu thứ hai (nếu token một cặp) được biểu thị bằng $B. Đối với mỗi loại trong số này (token và câu đặc biệt), chúng ta cũng chỉ định loại token ID tương ứng sau dấu hai chấm.

Do đó, bản mẫu BERT cổ điển được định nghĩa như sau:

tokenizer.post_processor = processors.TemplateProcessing(
    single=f"[CLS]:0 $A:0 [SEP]:0",
    pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
    special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
)

Lưu ý rằng chúng ta cần truyền vào tất cả các IDs của các kí tự đặc biệt, nên các tokenize có thể chuyển đổi chúng thành các cặp ID.

Một khi đã được thêm vào, chúng ta có thể quay lại ví dụ trước đó và sẽ nhận được:

encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']

Và trên một cặp câu, chúng ta có thể có được kết quả sau:

encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
print(encoding.tokens)
print(encoding.type_ids)
['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]

Chúng ta đã gần như hoàn thành việc xây dựng tokenizer này từ đầu — bước cuối cùng là thêm vào một trình giải mã:

tokenizer.decoder = decoders.WordPiece(prefix="##")

Hãy cũng kiểm thử với encoding:

tokenizer.decode(encoding.ids)
"let's test this tokenizer... on a pair of sentences."

Tuyệt vời! Ta có thể lưu tokenizer của mình vào trong một tệp JSON như dưới đây:

tokenizer.save("tokenizer.json")

Ta sau đó có thể tải lại tệp này trong đối tượng Tokenizer với phương thức from_file():

new_tokenizer = Tokenizer.from_file("tokenizer.json")

Để sử dụng tokenizer này trong 🤗 Transformers, chúng ta phải bọc nó trong PreTrainedTokenizerFast. Chúng ta có thể sử dụng lớp chung hoặc, nếu tokenizer của chúng ta tương ứng với một mô hình hiện có, hãy sử dụng lớp đó (ở đây là BertTokenizerFast). Nếu bạn áp dụng bài học này để xây dựng một tokenizer hoàn toàn mới, bạn sẽ phải sử dụng tùy chọn đầu tiên.

Để bọc tokenizer trong một PreTrainedTokenizerFast, chúng ta có thể chuyển tokenizer mà chúng ta đã xây dựng dưới dạng tokenizer_object hoặc truyền tệp tokenizer mà chúng ta đã lưu dưới dạng tokenizer_file. Điều quan trọng cần nhớ là chúng ta phải đặt thủ công tất cả các token đặc biệt, vì lớp đó không thể suy ra từ đối tượng tokenizer token nào là token bị che, [CLS], v.v.:

from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    # tokenizer_file="tokenizer.json", # Bạn có thể tải từ tệp tokenizer
    unk_token="[UNK]",
    pad_token="[PAD]",
    cls_token="[CLS]",
    sep_token="[SEP]",
    mask_token="[MASK]",
)

Nếu bạn đang sự dụng một lớp tokenizer đặc biệt (như BertTokenizerFast), bạn chỉ cần chỉ định một token đặc biết khác so với mặc định (ở đây là không xác định):

from transformers import BertTokenizerFast

wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)

Bạn có thể sử dụng tokenizer như bất kỳ tokenizer nào khác của 🤗 Transformers. Bạn có thể lưu nó với phương thức save_pretrained(), hoặc lại nó lên Hub sử dụng phương thức push_to_hub().

Giờ chúng ta đã thấy cách xây dựng bộ WordPiece tokenizer, hãy làm tương tự đối với BPE tokenizer. Chúng ta sẽ tiến hành nhanh hơn một chút vì bạn đã biết tất cả các bước và chỉ làm nổi bật những điểm khác biệt.

Xây dựng một BPE tokenizer từ đầu

Giờ hãy cũng nhau xây dựng GPT-2 tokenizer. Giống như BERT tokenizer, chúng ta bắt đầu bằng việc khởi tạo Tokenizer với mô hình BPE:

tokenizer = Tokenizer(models.BPE())

Cũng giống như BERT, chúng ta có thể khởi tạo mô hình này với một bộ từ vựng nếu ta đã có (ta sẽ cần truyền vào vocabmerges trong trường hợp này), nhưng vì ta sẽ huấn luyện từ đầu, chúng ta không cần làm vậy. Ta cũng không cần chỉ định unk_token vì GPT-2 sử dụng BPE cấp byte, phương pháp không cần đến nó.

GPT-2 không sử dụng một trình chuẩn hoá, nên ta có thể bỏ qua bước này và đi trực tiếp vào bước pre-tokenization:

tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)

Tuỳ chọn ByteLevel chúng ta thêm vào ở đây không thêm dấu cách vào đầu của một câu (thường nó là mặc định). Ta có thể nhìn các pre-tokenization từ ví dụ tương tự ở trên:

tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
 ('tokenization', (15, 27)), ('!', (27, 28))]

Tiếp theo là mô hình mà ta cần huấn luyện. Với GPT-2, token đặc biệt duy nhất ta cần là token kết thúc văn bản:

trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Như với WordPieceTrainer, cũng như vocab_sizespecial_tokens, ta có thể chỉ định min_frequency nếu muốn, hoặc nếu ta có hậu tố kết thúc từ (như </w>), ta có thể thiết lập nó với end_of_word_suffix.

Tokenizer này cũng có thể được huấn luyện trên các tệp văn bản:

tokenizer.model = models.BPE()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Hãy cũng xem kết quả tokenize trên một văn bản mẫu:

encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']

Ta áp dụng hậu xử lý cấp byte cho GPT-2 tokenizer như sau:

tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)

Tuỳ chọn trim_offsets = False chỉ cho trình hậu xử lý biết rằng ta cần bỏ mốt số offset token bắt đầu với ‘Ġ’: theo cách này, điểm bắt đầu của offset sẽ trỏ vào vùng không gian phía trước của từ, không phải kí tự đầu tiên của từ (vì không gian này về mặt kỹ thuật là một phần của từ). Hãy cùng nhìn xem kết quả với chuỗi văn bản ta vừa mã hoá với 'Ġtest' là token ở chỉ mục 4:

sentence = "Let's test this tokenizer."
encoding = tokenizer.encode(sentence)
start, end = encoding.offsets[4]
sentence[start:end]
' test'

Cuối cùng, ta thêm một trình giải mãi cấp byte:

tokenizer.decoder = decoders.ByteLevel()

và ta kiểm tra lại xem nó hoạt động đúng chưa:

tokenizer.decode(encoding.ids)
"Let's test this tokenizer."

Tuyệt vời! Giờ ta đã xong rồi, ta có thể lưu tokenizer như trên, và bao nó lại trong PreTrainedTokenizerFast hoặc GPT2TokenizerFast nếu ta muốn nó trong 🤗 Transformers:

from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<|endoftext|>",
    eos_token="<|endoftext|>",
)

or:

from transformers import GPT2TokenizerFast

wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)

Như một ví dụ cuối, chúng tôi sẽ chỉ bạn cách xây dựng một Unigram tokenizer từ đầu.

Xây dựng một Unigram tokenizer từ đầu

Hãy cùng nhau xây dựng một XLNet tokenizer. Cũng giống như các tokenizer trước đó, ta có thể bắt đầu khởi tạo Tokenizer với một mô hình Unigram:

tokenizer = Tokenizer(models.Unigram())

Một lần nữa, chúng ta có thể khởi tạo mô hình này với một từ vựng nếu có.

Với sự chuẩn hoá này, XLNet sử dụng một vài phương pháp thay thế (đến từ SentencePiece):

from tokenizers import Regex

tokenizer.normalizer = normalizers.Sequence(
    [
        normalizers.Replace("``", '"'),
        normalizers.Replace("''", '"'),
        normalizers.NFKD(),
        normalizers.StripAccents(),
        normalizers.Replace(Regex(" {2,}"), " "),
    ]
)

Điều này thay thế and bằng và thay thế bất kì chuỗi nào chứa hai hoặc nhiều hơn dấu cách liền nhau thành một dấu duy nhất, cũng như loại bỏ các dấu có trong văn bản để tokenize.

Tiền tokenizer được sử dụng cho bất kỳ SentencePiece tokenizer là Metaspace:

tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()

Ta có thể nhìn vào đầu ra quy trình tiền tokenize qua ví dụ văn bản ở dưới:

tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]

Tiếp theo là mô hình ta cần huấn luyện. XLNet có một số token đặc biệt:

special_tokens = ["<cls>", "<sep>", "<unk>", "<pad>", "<mask>", "<s>", "</s>"]
trainer = trainers.UnigramTrainer(
    vocab_size=25000, special_tokens=special_tokens, unk_token="<unk>"
)
tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)

Một tham số vô cùng quan trong mà ta không thể quên của UnigramTrainerunk_token. Ta có thể truyền vào các tham số cụ thể khác tới thuật toán Unigram, ví dụ shrinking_factor cho các bước mà ta xoá token (mặc định là 0.75) hoặc max_piece_length để chỉ định độ dài tối đa của một token (mặc định là 16).

Tokenizer này có thể được huấn luyện trên các tệp văn bản:

tokenizer.model = models.Unigram()
tokenizer.train(["wikitext-2.txt"], trainer=trainer)

Hãy cùng nhìn xem kết quả tokenize trên tập mẫu:

encoding = tokenizer.encode("Let's test this tokenizer.")
print(encoding.tokens)
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']

Một điểm đặc biệt của XLNet đó là nó thêm token <cls> ở cuối mỗi câu, với kiểu ID là 2 (để phân biết với các token khác). Nó đệm thêm vào phía bên tay trái giống như kết quả ở trên. Ta có thể xử lý tất cả các token đặc biệt và các token kiểu ID với cùng một bản mẫu, như BERT, nhưng đầu tiên ta phải lấy các ID của token <cls><sep>:

cls_token_id = tokenizer.token_to_id("<cls>")
sep_token_id = tokenizer.token_to_id("<sep>")
print(cls_token_id, sep_token_id)
0 1

Bản mẫu sẽ trông như sau:

tokenizer.post_processor = processors.TemplateProcessing(
    single="$A:0 <sep>:0 <cls>:2",
    pair="$A:0 <sep>:0 $B:1 <sep>:1 <cls>:2",
    special_tokens=[("<sep>", sep_token_id), ("<cls>", cls_token_id)],
)

Và ta có thể kiểm tra xem nó hoạt động không bằng cách mã hoá cặp câu:

encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
print(encoding.tokens)
print(encoding.type_ids)
['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '<sep>', '▁', 'on', '▁', 'a', '▁pair', 
  '▁of', '▁sentence', 's', '!', '<sep>', '<cls>']
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]

Cuối cùng, ta sẽ thêm trình giải mã Metaspace:

tokenizer.decoder = decoders.Metaspace()

và ta đã xong với tokenizer này! Ta có thể lưu tokenizer như trên, và bao nó lại trong PreTrainedTokenizerFast hoặc XLNetTokenizerFast nếu ta muốn nó trong 🤗 Transformers. Một điểm cần lưu ý là khi sử dụng PreTrainedTokenizerFast thì trên đầu của các token đặc biệt ta cần nói cho thư viện 🤗 Transformers viết ta cần đệm vào phía bên trái:

from transformers import PreTrainedTokenizerFast

wrapped_tokenizer = PreTrainedTokenizerFast(
    tokenizer_object=tokenizer,
    bos_token="<s>",
    eos_token="</s>",
    unk_token="<unk>",
    pad_token="<pad>",
    cls_token="<cls>",
    sep_token="<sep>",
    mask_token="<mask>",
    padding_side="left",
)

Hoặc một cách khác:

from transformers import XLNetTokenizerFast

wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)

Bây giờ bạn đã thấy cách các khối khác nhau được sử dụng để xây dựng các tokenizer hiện nay, bạn sẽ có thể viết bất kỳ trình tokenize nào mà bạn muốn với thư viện 🤗 Tokenizers và có thể sử dụng nó trong 🤗 Transformers.