Tokenizers từ khóa phụ

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Hướng dẫn này cho thấy làm thế nào để tạo ra một từ vựng subword từ một tập dữ liệu, và sử dụng nó để xây dựng một text.BertTokenizer từ vựng.

Ưu điểm chính của trình mã hóa từ khóa phụ là nó nội suy giữa mã hóa dựa trên từ và mã hóa dựa trên ký tự. Các từ thông dụng có một vị trí trong từ vựng, nhưng trình mã hóa có thể rơi trở lại các mảnh từ và các ký tự riêng lẻ đối với các từ không xác định.

Tổng quat

Các tensorflow_text gói bao gồm TensorFlow triển khai của nhiều tokenizers chung. Điều này bao gồm ba loại tokenizers kiểu từ khóa phụ:

  • text.BertTokenizer - Các BertTokenizer lớp là một giao diện mức độ cao hơn. Nó bao gồm các thuật toán tách thẻ Bert và một WordPieceTokenizer . Phải mất câu như là đầu vào và trả về token-ID.
  • text.WordpieceTokenizer - Các WordPieceTokenizer lớp là một giao diện cấp thấp hơn. Nó chỉ thực hiện các thuật toán WordPiece . Bạn phải chuẩn hóa và tách văn bản thành các từ trước khi gọi nó. Phải mất từ như đầu vào và trả về token-ID.
  • text.SentencepieceTokenizer - Các SentencepieceTokenizer đòi hỏi một thiết lập phức tạp hơn. Bộ khởi tạo của nó yêu cầu một mô hình câu chữ được đào tạo trước. Xem kho google / sentencepiece để được hướng dẫn làm thế nào để xây dựng một trong những mô hình này. Nó có thể chấp nhận câu như là đầu vào khi tokenizing.

Hướng dẫn này xây dựng vốn từ vựng Wordpiece theo cách từ trên xuống, bắt đầu từ các từ hiện có. Quá trình này không hoạt động đối với tiếng Nhật, tiếng Trung hoặc tiếng Hàn vì những ngôn ngữ này không có các đơn vị nhiều ký tự rõ ràng. Tokenize các thứ tiếng conside sử dụng text.SentencepieceTokenizer , text.UnicodeCharTokenizer hoặc cách tiếp cận này .

Thành lập

pip install -q -U tensorflow-text
pip install -q tensorflow_datasets
import collections
import os
import pathlib
import re
import string
import sys
import tempfile
import time

import numpy as np
import matplotlib.pyplot as plt

import tensorflow_datasets as tfds
import tensorflow_text as text
import tensorflow as tf
tf.get_logger().setLevel('ERROR')
pwd = pathlib.Path.cwd()

Tải xuống tập dữ liệu

Fetch bản dịch bộ dữ liệu Bồ Đào Nha / Tiếng Anh từ tfds :

examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
                               as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']

Tập dữ liệu này tạo ra các cặp câu tiếng Bồ Đào Nha / tiếng Anh:

for pt, en in train_examples.take(1):
  print("Portuguese: ", pt.numpy().decode('utf-8'))
  print("English:   ", en.numpy().decode('utf-8'))
Portuguese:  e quando melhoramos a procura , tiramos a única vantagem da impressão , que é a serendipidade .
English:    and when you improve searchability , you actually take away the one advantage of print , which is serendipity .

Lưu ý một số điều về các câu ví dụ ở trên:

  • Chúng là chữ thường.
  • Có khoảng trắng xung quanh dấu câu.
  • Không rõ liệu chuẩn hóa unicode có đang được sử dụng hay không.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)

Tạo từ vựng

Phần này tạo ra một từ vựng ghép từ một tập dữ liệu. Nếu bạn đã có một file từ vựng và chỉ muốn xem làm thế nào để xây dựng một text.BertTokenizer hoặc text.Wordpiece tokenizer với nó thì bạn có thể bỏ qua phần đầu đến xây dựng các tokenizer phần.

Mã thế hệ từ vựng được bao gồm trong tensorflow_text gói pip. Nó không được nhập theo mặc định, bạn cần phải nhập thủ công:

from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab

Các bert_vocab.bert_vocab_from_dataset chức năng sẽ tạo ra các từ vựng.

Có nhiều đối số bạn có thể đặt để điều chỉnh hành vi của nó. Đối với hướng dẫn này, bạn sẽ chủ yếu sử dụng các giá trị mặc định. Nếu bạn muốn tìm hiểu thêm về các tùy chọn, đầu tiên đọc về các thuật toán , và sau đó có một cái nhìn tại các mã .

Quá trình này mất khoảng 2 phút.

bert_tokenizer_params=dict(lower_case=True)
reserved_tokens=["[PAD]", "[UNK]", "[START]", "[END]"]

bert_vocab_args = dict(
    # The target vocabulary size
    vocab_size = 8000,
    # Reserved tokens that must be included in the vocabulary
    reserved_tokens=reserved_tokens,
    # Arguments for `text.BertTokenizer`
    bert_tokenizer_params=bert_tokenizer_params,
    # Arguments for `wordpiece_vocab.wordpiece_tokenizer_learner_lib.learn`
    learn_params={},
)
%%time
pt_vocab = bert_vocab.bert_vocab_from_dataset(
    train_pt.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 30s, sys: 2.21 s, total: 1min 32s
Wall time: 1min 28s

Dưới đây là một số phần của từ vựng kết quả.

print(pt_vocab[:10])
print(pt_vocab[100:110])
print(pt_vocab[1000:1010])
print(pt_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['no', 'por', 'mais', 'na', 'eu', 'esta', 'muito', 'isso', 'isto', 'sao']
['90', 'desse', 'efeito', 'malaria', 'normalmente', 'palestra', 'recentemente', '##nca', 'bons', 'chave']
['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']

Viết tệp từ vựng:

def write_vocab_file(filepath, vocab):
  with open(filepath, 'w') as f:
    for token in vocab:
      print(token, file=f)
write_vocab_file('pt_vocab.txt', pt_vocab)

Sử dụng hàm đó để tạo từ vựng từ dữ liệu tiếng Anh:

%%time
en_vocab = bert_vocab.bert_vocab_from_dataset(
    train_en.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 3s, sys: 2.21 s, total: 1min 6s
Wall time: 1min 2s
print(en_vocab[:10])
print(en_vocab[100:110])
print(en_vocab[1000:1010])
print(en_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['as', 'all', 'at', 'one', 'people', 're', 'like', 'if', 'our', 'from']
['choose', 'consider', 'extraordinary', 'focus', 'generation', 'killed', 'patterns', 'putting', 'scientific', 'wait']
['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']

Đây là hai tệp từ vựng:

write_vocab_file('en_vocab.txt', en_vocab)
ls *.txt
en_vocab.txt  pt_vocab.txt

Xây dựng tokenizer

Các text.BertTokenizer có thể được khởi tạo bằng cách đi qua con đường tập tin từ vựng của như là đối số đầu tiên (xem phần trên tf.lookup cho các tùy chọn khác):

pt_tokenizer = text.BertTokenizer('pt_vocab.txt', **bert_tokenizer_params)
en_tokenizer = text.BertTokenizer('en_vocab.txt', **bert_tokenizer_params)

Bây giờ bạn có thể sử dụng nó để mã hóa một số văn bản. Lấy một loạt 3 ví dụ từ dữ liệu tiếng Anh:

for pt_examples, en_examples in train_examples.batch(3).take(1):
  for ex in en_examples:
    print(ex.numpy())
b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .'
b'but what if it were active ?'
b"but they did n't test for curiosity ."

Chạy nó thông qua BertTokenizer.tokenize phương pháp. Ban đầu, đây trả về một tf.RaggedTensor với trục (batch, word, word-piece) :

# Tokenize the examples -> (batch, word, word-piece)
token_batch = en_tokenizer.tokenize(en_examples)
# Merge the word and word-piece axes -> (batch, tokens)
token_batch = token_batch.merge_dims(-2,-1)

for ex in token_batch.to_list():
  print(ex)
[72, 117, 79, 1259, 1491, 2362, 13, 79, 150, 184, 311, 71, 103, 2308, 74, 2679, 13, 148, 80, 55, 4840, 1434, 2423, 540, 15]
[87, 90, 107, 76, 129, 1852, 30]
[87, 83, 149, 50, 9, 56, 664, 85, 2512, 15]

Nếu bạn thay thế các ID mã thông báo với cơ quan đại diện văn bản của họ (sử dụng tf.gather ), bạn có thể thấy rằng trong ví dụ đầu tiên dòng chữ "searchability""serendipity" đã được phân tách ra thành "search ##ability""s ##ere ##nd ##ip ##ity" :

# Lookup each token id in the vocabulary.
txt_tokens = tf.gather(en_vocab, token_batch)
# Join with spaces.
tf.strings.reduce_join(txt_tokens, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve search ##ability , you actually take away the one advantage of print , which is s ##ere ##nd ##ip ##ity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

Để tái lập lại những lời từ các thẻ chiết xuất, sử dụng BertTokenizer.detokenize phương pháp:

words = en_tokenizer.detokenize(token_batch)
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

Tùy chỉnh và xuất khẩu

Hướng dẫn này được xây dựng các văn bản và tokenizer detokenizer sử dụng bởi các Transformer hướng dẫn. Phần này cho biết thêm các phương pháp và các bước xử lý để đơn giản hóa hướng dẫn đó, và xuất khẩu tokenizers sử dụng tf.saved_model để họ có thể được nhập khẩu bởi các hướng dẫn khác.

Mã hóa tùy chỉnh

Các hướng dẫn hạ lưu cả mong đợi văn bản tokenized bao gồm [START][END] mã thông báo.

Các reserved_tokens không gian dự trữ ở phần đầu của từ vựng, vì vậy [START][END] có các chỉ số tương tự cho cả hai ngôn ngữ:

START = tf.argmax(tf.constant(reserved_tokens) == "[START]")
END = tf.argmax(tf.constant(reserved_tokens) == "[END]")

def add_start_end(ragged):
  count = ragged.bounding_shape()[0]
  starts = tf.fill([count,1], START)
  ends = tf.fill([count,1], END)
  return tf.concat([starts, ragged, ends], axis=1)
words = en_tokenizer.detokenize(add_start_end(token_batch))
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'[START] and when you improve searchability , you actually take away the one advantage of print , which is serendipity . [END]',
       b'[START] but what if it were active ? [END]',
       b"[START] but they did n ' t test for curiosity . [END]"],
      dtype=object)>

Tách rời tùy chỉnh

Trước khi xuất tokenizers, có một số thứ bạn có thể dọn dẹp cho các hướng dẫn hạ lưu:

  1. Họ muốn tạo ra văn bản sạch sẽ, vì vậy thả tokens reserved như [START] , [END][PAD] .
  2. Họ đang quan tâm trong chuỗi hoàn chỉnh, vì vậy áp dụng một chuỗi tham gia dọc theo words trục của kết quả.
def cleanup_text(reserved_tokens, token_txt):
  # Drop the reserved tokens, except for "[UNK]".
  bad_tokens = [re.escape(tok) for tok in reserved_tokens if tok != "[UNK]"]
  bad_token_re = "|".join(bad_tokens)

  bad_cells = tf.strings.regex_full_match(token_txt, bad_token_re)
  result = tf.ragged.boolean_mask(token_txt, ~bad_cells)

  # Join them into strings.
  result = tf.strings.reduce_join(result, separator=' ', axis=-1)

  return result
en_examples.numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n't test for curiosity ."], dtype=object)
token_batch = en_tokenizer.tokenize(en_examples).merge_dims(-2,-1)
words = en_tokenizer.detokenize(token_batch)
words
<tf.RaggedTensor [[b'and', b'when', b'you', b'improve', b'searchability', b',', b'you', b'actually', b'take', b'away', b'the', b'one', b'advantage', b'of', b'print', b',', b'which', b'is', b'serendipity', b'.'], [b'but', b'what', b'if', b'it', b'were', b'active', b'?'], [b'but', b'they', b'did', b'n', b"'", b't', b'test', b'for', b'curiosity', b'.']]>
cleanup_text(reserved_tokens, words).numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)

Xuất khẩu

Khối mã sau xây dựng một CustomTokenizer lớp để chứa các text.BertTokenizer trường hợp, logic tùy chỉnh, và @tf.function giấy gói cần thiết cho xuất khẩu.

class CustomTokenizer(tf.Module):
  def __init__(self, reserved_tokens, vocab_path):
    self.tokenizer = text.BertTokenizer(vocab_path, lower_case=True)
    self._reserved_tokens = reserved_tokens
    self._vocab_path = tf.saved_model.Asset(vocab_path)

    vocab = pathlib.Path(vocab_path).read_text().splitlines()
    self.vocab = tf.Variable(vocab)

    ## Create the signatures for export:   

    # Include a tokenize signature for a batch of strings. 
    self.tokenize.get_concrete_function(
        tf.TensorSpec(shape=[None], dtype=tf.string))

    # Include `detokenize` and `lookup` signatures for:
    #   * `Tensors` with shapes [tokens] and [batch, tokens]
    #   * `RaggedTensors` with shape [batch, tokens]
    self.detokenize.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.detokenize.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    self.lookup.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.lookup.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    # These `get_*` methods take no arguments
    self.get_vocab_size.get_concrete_function()
    self.get_vocab_path.get_concrete_function()
    self.get_reserved_tokens.get_concrete_function()

  @tf.function
  def tokenize(self, strings):
    enc = self.tokenizer.tokenize(strings)
    # Merge the `word` and `word-piece` axes.
    enc = enc.merge_dims(-2,-1)
    enc = add_start_end(enc)
    return enc

  @tf.function
  def detokenize(self, tokenized):
    words = self.tokenizer.detokenize(tokenized)
    return cleanup_text(self._reserved_tokens, words)

  @tf.function
  def lookup(self, token_ids):
    return tf.gather(self.vocab, token_ids)

  @tf.function
  def get_vocab_size(self):
    return tf.shape(self.vocab)[0]

  @tf.function
  def get_vocab_path(self):
    return self._vocab_path

  @tf.function
  def get_reserved_tokens(self):
    return tf.constant(self._reserved_tokens)

Xây dựng một CustomTokenizer cho mỗi ngôn ngữ:

tokenizers = tf.Module()
tokenizers.pt = CustomTokenizer(reserved_tokens, 'pt_vocab.txt')
tokenizers.en = CustomTokenizer(reserved_tokens, 'en_vocab.txt')

Xuất tokenizers như một saved_model :

model_name = 'ted_hrlr_translate_pt_en_converter'
tf.saved_model.save(tokenizers, model_name)
2021-11-02 15:20:31.762976: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.

Nạp lại các saved_model và thử nghiệm các phương pháp:

reloaded_tokenizers = tf.saved_model.load(model_name)
reloaded_tokenizers.en.get_vocab_size().numpy()
7010
tokens = reloaded_tokenizers.en.tokenize(['Hello TensorFlow!'])
tokens.numpy()
array([[   2, 4006, 2358,  687, 1192, 2365,    4,    3]])
text_tokens = reloaded_tokenizers.en.lookup(tokens)
text_tokens
<tf.RaggedTensor [[b'[START]', b'hello', b'tens', b'##or', b'##f', b'##low', b'!', b'[END]']]>
round_trip = reloaded_tokenizers.en.detokenize(tokens)

print(round_trip.numpy()[0].decode('utf-8'))
hello tensorflow !

Lưu trữ nó cho hướng dẫn dịch :

zip -r {model_name}.zip {model_name}
adding: ted_hrlr_translate_pt_en_converter/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/saved_model.pb (deflated 91%)
  adding: ted_hrlr_translate_pt_en_converter/variables/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.data-00000-of-00001 (deflated 51%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.index (deflated 33%)
  adding: ted_hrlr_translate_pt_en_converter/assets/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/assets/pt_vocab.txt (deflated 57%)
  adding: ted_hrlr_translate_pt_en_converter/assets/en_vocab.txt (deflated 54%)
du -h *.zip
184K    ted_hrlr_translate_pt_en_converter.zip

Tùy chọn: Thuật toán

Điều đáng chú ý ở đây là có hai phiên bản của thuật toán WordPiece: Bottom-up và top-down. Trong cả hai trường hợp, mục tiêu đều giống nhau: "Với một kho ngữ liệu đào tạo và một số mã thông báo D mong muốn, bài toán tối ưu hóa là chọn các phôi chữ D sao cho số lượng văn bản thu được là tối thiểu về số lượng mẫu chữ khi được phân đoạn theo mô hình phôi chữ đã chọn. "

Bản gốc thuật toán WordPiece từ dưới lên , dựa trên mã hóa byte cặp . Giống như BPE, Nó bắt đầu với bảng chữ cái và kết hợp lặp đi lặp lại các bigrams thông thường để tạo thành các mảnh từ và từ.

Máy phát điện từ vựng TensorFlow Tiêu đề của sau việc thực hiện từ trên xuống từ Bert . Bắt đầu với các từ và chia nhỏ chúng thành các thành phần nhỏ hơn cho đến khi chúng đạt đến ngưỡng tần suất hoặc không thể chia nhỏ hơn nữa. Phần tiếp theo mô tả chi tiết điều này. Đối với tiếng Nhật, tiếng Trung và tiếng Hàn, cách tiếp cận từ trên xuống này không hoạt động vì không có đơn vị từ rõ ràng nào để bắt đầu. Đối với những bạn cần một cách tiếp cận khác nhau .

Chọn từ vựng

Các từ trên xuống WordPiece thuật toán thế hệ mất trong một bộ (word, count) theo cặp và một ngưỡng T , và trả về một vốn từ vựng V .

Thuật toán là lặp đi lặp lại. Nó được điều hành cho k lặp, nơi thường k = 4 , nhưng chỉ có hai đầu tiên là thực sự quan trọng. Cái thứ ba và thứ tư (và xa hơn nữa) giống hệt cái thứ hai. Lưu ý rằng mỗi bước của việc tìm kiếm nhị phân chạy các thuật toán từ đầu cho k lặp.

Các bước lặp được mô tả bên dưới:

Lần lặp đầu tiên

  1. Lặp qua từng lời nói và cặp đếm trong đầu vào, ký hiệu là (w, c) .
  2. Đối với mỗi từ w , tạo ra mỗi chuỗi, ký hiệu là s . Ví dụ, cho từ human , chúng ta tạo ra {h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n} .
  3. Duy trì một bản đồ chuỗi-to-count băm, và làm tăng số lần của mỗi s bởi c . Ví dụ, nếu chúng ta có (human, 113)(humas, 3) ở đầu vào của chúng tôi, số lượng của s = huma sẽ 113+3=116 .
  4. Một khi chúng ta đã thu thập các tội danh mỗi chuỗi con, lặp qua (s, c) cặp bắt đầu với dài nhất s đầu tiên.
  5. Giữ bất kỳ s mà có một c > T . Ví dụ, nếu T = 100 và chúng tôi có (pers, 231); (dogs, 259); (##rint; 76) , sau đó chúng tôi sẽ tiếp tục persdogs .
  6. Khi một s được giữ, trừ tắt tính của nó từ tất cả các tiền tố của nó. Đây là lý do để phân loại tất cả các s theo chiều dài trong bước 4. Đây là một phần quan trọng của thuật toán, bởi vì nếu không lời sẽ được tính gấp đôi. Ví dụ, chúng ta hãy nói rằng chúng tôi đã giữ human và chúng tôi có được (huma, 116) . Chúng ta biết rằng 113 trong những 116 đến từ human , và 3 đến từ humas . Tuy nhiên, bây giờ mà human là trong vốn từ vựng của chúng tôi, chúng tôi biết chúng tôi sẽ không bao giờ phân khúc human vào huma ##n . Vì vậy, một khi human đã được lưu giữ, sau đó huma chỉ có một số hiệu quả của 3 .

Thuật toán này sẽ tạo ra một tập hợp các chữ mảnh s (nhiều trong số đó sẽ là toàn bộ từ w ), mà chúng tôi có thể sử dụng như từ vựng WordPiece của chúng tôi.

Tuy nhiên, có một vấn đề: Thuật toán này sẽ tạo ra quá nhiều các mẩu từ. Lý do là chúng tôi chỉ trừ đi số lượng mã thông báo tiền tố. Do đó, nếu chúng tôi tiếp tục từ human , chúng tôi sẽ trừ ra khỏi đếm cho h, hu, hu, huma , nhưng không phải cho ##u, ##um, ##uma, ##uman và vân vân. Vì vậy, chúng ta có thể tạo ra cả human##uman như mảnh từ, mặc dù ##uman sẽ không bao giờ được áp dụng.

Vậy tại sao không trừ ra khỏi tính cho mỗi chuỗi con, không chỉ mỗi tiền tố? Bởi vì sau đó chúng ta có thể trừ đi số lượng nhiều lần. Chúng ta hãy nói rằng chúng tôi chế biến đang s chiều dài 5 và chúng tôi giữ cả hai (##denia, 129)(##eniab, 137) , nơi 65 của những tội đến từ từ undeniable . Nếu chúng ta trừ khỏi tất cả các chuỗi con, chúng tôi sẽ trừ đi 65 từ chuỗi ##enia hai lần, mặc dù chúng ta chỉ nên trừ một lần. Tuy nhiên, nếu chúng ta chỉ trừ các tiền tố, thì nó sẽ chỉ bị trừ một lần.

Lần lặp thứ hai (và thứ ba ...)

Để giải quyết vấn đề phát sinh quá mức được đề cập ở trên, chúng tôi thực hiện nhiều lần lặp lại thuật toán.

Lặp tiếp theo là giống hệt nhau để là người đầu tiên, với một khác biệt quan trọng: Trong bước 2, thay vì xem xét tất cả các chuỗi con, chúng tôi áp dụng các thuật toán WordPiece tokenization bằng cách sử dụng từ vựng từ phiên trước đó, và chỉ xem xét chuỗi con mà bắt đầu vào một điểm chia.

Ví dụ, chúng ta hãy nói rằng chúng tôi đang thực hiện bước 2 của thuật toán và bắt gặp từ undeniable . Trong phiên đầu tiên, chúng tôi sẽ xem xét tất cả các chuỗi con, ví dụ như, {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...} .

Bây giờ, đối với lần lặp thứ hai, chúng ta sẽ chỉ xem xét một tập hợp con trong số này. Giả sử rằng sau lần lặp đầu tiên, các từ liên quan là:

un, ##deni, ##able, ##ndeni, ##iable

Thuật toán WordPiece chí phân khúc này vào un ##deni ##able (xem phần Áp dụng WordPiece để biết thêm thông tin). Trong trường hợp này, chúng ta sẽ chỉ xem xét chuỗi con mà bắt đầu tại một điểm phân khúc. Chúng tôi vẫn sẽ xem xét tất cả các vị trí cuối có thể. Vì vậy, trong lần lặp thứ hai, bộ s cho undeniable là:

{u, un, und, unden, undeni, undenia, undeniab, undeniabl, undeniable, ##d, ##de, ##den, ##deni, ##denia, ##deniab, ##deniabl , ##deniable, ##a, ##ab, ##abl, ##able}

Thuật toán giống hệt nhau. Trong ví dụ này, trong phiên đầu tiên, thuật toán tạo ra suprious tokens ##ndeni##iable . Bây giờ, những mã thông báo này không bao giờ được xem xét, vì vậy chúng sẽ không được tạo ra bởi lần lặp thứ hai. Chúng tôi thực hiện một số lần lặp chỉ để đảm bảo kết quả hội tụ (mặc dù không có đảm bảo hội tụ theo nghĩa đen).

Áp dụng WordPeces

Sau khi từ vựng WordPeces đã được tạo, chúng ta cần có thể áp dụng nó vào dữ liệu mới. Thuật toán là một ứng dụng đầu tiên phù hợp dài nhất tham lam đơn giản.

Ví dụ, hãy xem xét phân đoạn từ undeniable .

Đầu tiên chúng ta tra cứu undeniable trong từ điển WordPiece của chúng tôi, và nếu nó hiện tại, chúng tôi đã hoàn tất. Nếu không, chúng ta giảm các điểm cuối của một nhân vật, và lặp lại, ví dụ như, undeniabl .

Cuối cùng, chúng tôi sẽ tìm thấy một ký tự phụ trong từ vựng của mình hoặc nhận được một ký tự phụ. (Nói chung, chúng tôi giả định rằng tất cả các nhân vật trong vốn từ vựng của chúng tôi, mặc dù sức mạnh đây không phải là trường hợp cho các ký tự Unicode hiếm. Nếu chúng ta gặp phải một ký tự Unicode hiếm hoi mà không có trong từ vựng, chúng tôi chỉ đơn giản là bản đồ toàn bộ từ để <unk> ).

Trong trường hợp này, chúng ta thấy un trong vốn từ vựng của chúng tôi. Vì vậy, đó là phần từ đầu tiên của chúng tôi. Sau đó, chúng tôi nhảy đến cuối un và lặp lại quá trình xử lý, ví dụ như, cố gắng tìm ##deniable , sau đó ##deniabl , vv Điều này được lặp lại cho đến khi chúng tôi đã phân đoạn toàn bộ từ.

Trực giác

Về mặt trực quan, mã hóa WordP mảnh đang cố gắng đáp ứng hai mục tiêu khác nhau:

  1. Tokenize dữ liệu vào số nhỏ nhất của mảnh càng tốt. Điều quan trọng cần ghi nhớ là thuật toán WordPiece không "muốn" tách các từ. Nếu không, nó sẽ chỉ chia từng chữ vào nó ký tự, ví dụ như, human -> {h, ##u, ##m, ##a, #n} . Đây là một điều quan trọng là làm cho WordPiece khác nhau từ splitter hình thái, mà sẽ chia hình vị ngôn ngữ ngay cả đối với những từ thông thường (ví dụ, unwanted -> {un, want, ed} ).

  2. Khi một từ thực sự phải chia thành nhiều phần, hãy chia từ đó thành các phần có số lượng tối đa trong dữ liệu đào tạo. Ví dụ, lý do tại sao từ undeniable sẽ được chia thành {un, ##deni, ##able} chứ không phải là lựa chọn thay thế như {unde, ##niab, ##le} là tính cho un##able ở cụ thể sẽ rất cao, vì đây là những tiền tố và hậu tố phổ biến. Mặc dù số lượng cho ##le phải cao hơn ##able , đếm thấp unde##niab sẽ làm cho một ít "mong muốn" tokenization cho thuật toán.

Tùy chọn: tf.lookup

Nếu bạn cần truy cập vào, hoặc kiểm soát nhiều hơn các từ vựng của nó đáng chú ý là bạn có thể xây dựng các bảng tra cứu bản thân và vượt qua đó để BertTokenizer .

Khi bạn vượt qua một chuỗi, BertTokenizer nào sau đây:

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.TextFileInitializer(
        filename='pt_vocab.txt',
        key_dtype=tf.string,
        key_index = tf.lookup.TextFileIndex.WHOLE_LINE,
        value_dtype = tf.int64,
        value_index=tf.lookup.TextFileIndex.LINE_NUMBER)) 
pt_tokenizer = text.BertTokenizer(pt_lookup)

Bây giờ bạn có quyền truy cập trực tiếp vào bảng tra cứu được sử dụng trong tokenizer.

pt_lookup.lookup(tf.constant(['é', 'um', 'uma', 'para', 'não']))
<tf.Tensor: shape=(5,), dtype=int64, numpy=array([7765,   85,   86,   87, 7765])>

Bạn không cần phải sử dụng một tập tin từ vựng, tf.lookup có các tùy chọn khởi tạo khác. Nếu bạn có các từ vựng trong bộ nhớ, bạn có thể sử dụng lookup.KeyValueTensorInitializer :

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.KeyValueTensorInitializer(
        keys=pt_vocab,
        values=tf.range(len(pt_vocab), dtype=tf.int64))) 
pt_tokenizer = text.BertTokenizer(pt_lookup)