ช่วยปกป้อง Great Barrier Reef กับ TensorFlow บน Kaggle เข้าร่วมท้าทาย

tokenizers คำย่อย

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

กวดวิชานี้จะแสดงให้เห็นถึงวิธีการสร้างคำศัพท์ subword จากชุดข้อมูลและใช้มันเพื่อสร้าง text.BertTokenizer จากคำศัพท์

ข้อได้เปรียบหลักของ tokenizer ของ subword คือมันสอดแทรกระหว่าง tokenization แบบ word-based และ character-based tokenization คำทั่วไปมีช่องว่างในคำศัพท์ แต่ tokenizer สามารถถอยกลับไปเป็นคำและอักขระแต่ละตัวสำหรับคำที่ไม่รู้จัก

ภาพรวม

tensorflow_text แพคเกจรวมถึงการใช้งานของ TensorFlow tokenizers ที่พบบ่อยมาก ซึ่งรวมถึงตัวสร้างโทเค็นแบบคำย่อยสามตัว:

  • text.BertTokenizer - The BertTokenizer ระดับเป็นอินเตอร์เฟซระดับที่สูงขึ้น ซึ่งจะรวมถึงขั้นตอนวิธีการแยกเบิร์ตโทเค็นและ WordPieceTokenizer มันต้องใช้ประโยคเป็น input และส่งกลับ token-รหัส
  • text.WordpieceTokenizer - The WordPieceTokenizer ระดับเป็นอินเตอร์เฟซระดับที่ต่ำกว่า มันก็แค่การดำเนินการ ขั้นตอนวิธีการ WordPiece คุณต้องสร้างมาตรฐานและแบ่งข้อความเป็นคำก่อนเรียก มันต้องใช้คำพูดเป็น input และส่งกลับ token-รหัส
  • text.SentencepieceTokenizer - The SentencepieceTokenizer ต้องมีการตั้งค่าที่ซับซ้อนมากขึ้น ตัวเริ่มต้นของมันต้องใช้แบบจำลองประโยคที่ได้รับการฝึกฝนมาล่วงหน้า ดู พื้นที่เก็บข้อมูล Google / sentencepiece สำหรับคำแนะนำเกี่ยวกับวิธีการสร้างหนึ่งของโมเดลเหล่านี้ มันสามารถยอมรับประโยคเป็น input เมื่อ tokenizing

บทช่วยสอนนี้สร้างคำศัพท์ Wordpiece ในลักษณะจากบนลงล่าง โดยเริ่มจากคำที่มีอยู่ กระบวนการนี้ใช้ไม่ได้กับภาษาญี่ปุ่น จีน หรือเกาหลี เนื่องจากภาษาเหล่านี้ไม่มีหน่วยอักขระหลายตัวที่ชัดเจน เพื่อ tokenize ภาษาเหล่านี้ conside ใช้ text.SentencepieceTokenizer , text.UnicodeCharTokenizer หรือ วิธีการนี้

ติดตั้ง

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()

ดาวน์โหลดชุดข้อมูล

เรียกชุดแปลภาษาโปรตุเกส / ภาษาอังกฤษจาก 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']

ชุดข้อมูลนี้สร้างคู่ประโยคภาษาโปรตุเกส/อังกฤษ:

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 .

สังเกตบางสิ่งเกี่ยวกับประโยคตัวอย่างด้านบน:

  • เป็นตัวพิมพ์เล็ก
  • มีช่องว่างรอบเครื่องหมายวรรคตอน
  • ยังไม่ชัดเจนว่ากำลังใช้การทำให้เป็นมาตรฐานของยูนิโค้ดหรือไม่
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)

สร้างคำศัพท์

ส่วนนี้จะสร้างคำศัพท์ wordpiece จากชุดข้อมูล หากคุณมีไฟล์คำศัพท์และเพียงแค่ต้องการที่จะเห็นวิธีการสร้าง text.BertTokenizer หรือ text.Wordpiece tokenizer กับมันแล้วคุณสามารถข้ามไปข้างหน้าเพื่อ สร้าง tokenizer ส่วน

รหัสรุ่นคำศัพท์ที่จะรวมอยู่ใน tensorflow_text แพคเกจ pip ไม่ได้นำเข้าโดยค่าเริ่มต้น คุณต้องนำเข้าด้วยตนเอง:

from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab

bert_vocab.bert_vocab_from_dataset ฟังก์ชั่นจะสร้างคำศัพท์

มีอาร์กิวเมนต์มากมายที่คุณสามารถตั้งค่าให้ปรับพฤติกรรมได้ สำหรับบทช่วยสอนนี้ คุณจะใช้ค่าเริ่มต้นเป็นส่วนใหญ่ ถ้าคุณต้องการที่จะเรียนรู้เพิ่มเติมเกี่ยวกับตัวเลือกแรกอ่านเกี่ยวกับ ขั้นตอนวิธี และจากนั้นมีลักษณะที่ รหัส

ใช้เวลาประมาณ 2 นาที

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

นี่คือบางส่วนของคำศัพท์ที่ได้

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']
['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']

เขียนไฟล์คำศัพท์:

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)

ใช้ฟังก์ชันนั้นเพื่อสร้างคำศัพท์จากข้อมูลภาษาอังกฤษ:

%%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']
['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']

นี่คือไฟล์คำศัพท์สองไฟล์:

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

สร้างตัวสร้างโทเค็น

text.BertTokenizer สามารถเริ่มต้นได้โดยผ่านเส้นทางของแฟ้มคำศัพท์ที่เป็นอาร์กิวเมนต์แรก (ดูส่วนที่เกี่ยวกับ tf.lookup สำหรับตัวเลือกอื่น ๆ ):

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

ตอนนี้คุณสามารถใช้เพื่อเข้ารหัสข้อความ ยกตัวอย่าง 3 ตัวอย่างจากข้อมูลภาษาอังกฤษ:

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 ."

ดำเนินการโดยใช้ BertTokenizer.tokenize วิธี ในขั้นต้นนี้ส่งกลับ tf.RaggedTensor กับแกน (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]

หากคุณเปลี่ยนรหัสโทเค็นกับการแสดงข้อความของพวกเขา (ใช้ tf.gather ) คุณจะเห็นว่าในตัวอย่างแรกคำว่า "searchability" และ "serendipity" ได้รับการย่อยสลายเป็น "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)>

อีกครั้งประกอบคำจากราชสกุลสกัดใช้ BertTokenizer.detokenize วิธีการ:

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)>

การปรับแต่งและการส่งออก

กวดวิชานี้จะสร้าง tokenizer ข้อความและ detokenizer ใช้โดย หม้อแปลง กวดวิชา ส่วนนี้จะเพิ่มวิธีการและขั้นตอนการประมวลผลเพื่อลดความซับซ้อนของการกวดวิชานั้นและการส่งออกโดยใช้ tokenizers tf.saved_model เพื่อให้พวกเขาสามารถนำเข้าโดยบทเรียนอื่น ๆ

การปรับโทเค็นเอง

บทเรียนปลายน้ำทั้งคาดหวังว่าข้อความที่จะรวม tokenized [START] และ [END] ราชสกุล

reserved_tokens พื้นที่สำรองที่จุดเริ่มต้นของคำศัพท์เพื่อ [START] และ [END] มีดัชนีเดียวกันทั้งภาษา:

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)>

การล้างพิษแบบกำหนดเอง

ก่อนส่งออก tokenizers มีสองสิ่งที่คุณสามารถล้างข้อมูลสำหรับบทช่วยสอนดาวน์สตรีม:

  1. พวกเขาต้องการที่จะสร้างผลลัพธ์ข้อความสะอาดเพื่อวางราชสกุลลิขสิทธิ์เช่น [START] , [END] และ [PAD]
  2. พวกเขากำลังสนใจในสายที่สมบูรณ์เพื่อใช้สตริงเข้าร่วมตาม words แกนของผล
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)

ส่งออก

บล็อกรหัสต่อไปนี้สร้าง CustomTokenizer ระดับเพื่อให้มี text.BertTokenizer กรณีตรรกะที่กำหนดเองและ @tf.function ห่อจำเป็นสำหรับการส่งออก

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)

สร้าง CustomTokenizer สำหรับแต่ละภาษา:

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

ส่งออก tokenizers เป็น 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.

โหลด saved_model และทดสอบวิธีการ:

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 !

เก็บไว้สำหรับ บทเรียนการแปล :

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

ทางเลือก: อัลกอริทึม

เป็นที่น่าสังเกตว่ามีอัลกอริธึม WordPiece สองเวอร์ชัน: จากล่างขึ้นบนและบนลงล่าง ในทั้งสองกรณี เป้าหมายจะเหมือนกัน: "จากคลังข้อมูลการฝึกอบรมและจำนวนโทเค็น D ที่ต้องการ ปัญหาการปรับให้เหมาะสมคือการเลือกคำชิ้น D เพื่อให้คลังผลลัพธ์มีจำนวนน้อยที่สุดเมื่อแบ่งกลุ่มตามแบบจำลองคำที่เลือก "

เดิม อัลกอริทึม WordPiece ล่างขึ้น อยู่บนพื้นฐานของ การเข้ารหัสไบต์คู่ เช่นเดียวกับ BPE มันเริ่มต้นด้วยตัวอักษรและรวม bigrams ทั่วไปซ้ำ ๆ เพื่อสร้างคำและคำ

กำเนิดคำศัพท์ TensorFlow ข้อความดังนี้การดำเนินงานจากบนลงล่างจาก BERT เริ่มต้นด้วยคำและแบ่งออกเป็นส่วนประกอบย่อยๆ จนกว่าจะถึงเกณฑ์ความถี่ หรือจะแยกรายละเอียดเพิ่มเติมไม่ได้ ส่วนถัดไปจะอธิบายรายละเอียดนี้ สำหรับภาษาญี่ปุ่น จีน และเกาหลี วิธีการจากบนลงล่างนี้ใช้ไม่ได้เนื่องจากไม่มีหน่วยคำที่ชัดเจนที่จะเริ่มต้นด้วย สำหรับผู้ที่คุณจำเป็นต้องมี วิธีการที่แตกต่างกัน

การเลือกคำศัพท์

ด้านบนลง WordPiece ขั้นตอนวิธีการใช้เวลาในการสร้างชุดของ (คำนับ) คู่และเกณฑ์ T และผลตอบแทนคำศัพท์ V

อัลกอริทึมเป็นแบบวนซ้ำ มันถูกใช้สำหรับ k ซ้ำที่มักจะ k = 4 แต่เพียงสองคนแรกที่มีความสำคัญจริงๆ ที่สามและสี่ (และอื่น ๆ ) เหมือนกับที่สอง โปรดทราบว่าขั้นตอนของการค้นหาแบบไบนารีแต่ละขั้นตอนวิธีทำงานจากรอยขีดข่วนสำหรับ k ซ้ำ

การทำซ้ำที่อธิบายไว้ด้านล่าง:

ทำซ้ำครั้งแรก

  1. ย้ำกว่าทุกคำพูดและทั้งคู่นับในการป้อนข้อมูลที่แสดงเป็น (w, c)
  2. สำหรับแต่ละคำ w สร้างทุก substring, แสดงเป็น s เช่นคำว่า human เราสร้าง {h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
  3. รักษาแผนที่กัญชาย่อยเพื่อนับและเพิ่มจำนวนของแต่ละ s โดย c เช่นถ้าเรามี (human, 113) และ (humas, 3) ในการป้อนข้อมูลของเรานับ s = huma จะเป็น 113+3=116
  4. เมื่อเราได้รวบรวมข้อหาทุกสตริงย่อยที่ย้ำมากกว่า (s, c) คู่ที่เริ่มต้นด้วยที่ยาวที่สุด s แรก
  5. เก็บใด ๆ s ที่มี c > T เช่นถ้า T = 100 และเรามี (pers, 231); (dogs, 259); (##rint; 76) แล้วเราจะทำให้ pers และ dogs
  6. เมื่อ s จะถูกเก็บไว้ลบออกนับจากทั้งหมดของคำนำหน้าของมัน นี่คือเหตุผลสำหรับการเรียงลำดับทั้งหมดของ s โดยความยาวในขั้นตอนที่ 4 นี้เป็นส่วนหนึ่งที่สำคัญของขั้นตอนวิธีเพราะมิฉะนั้นคำจะนับสองครั้ง ตัวอย่างเช่นสมมุติว่าเราได้เก็บ human และเราได้รับไป (huma, 116) เรารู้ว่า 113 ของผู้ที่ 116 มาจาก human และ 3 มาจาก humas แต่ตอนนี้ที่ human อยู่ในคำศัพท์ของเราเรารู้ว่าเราจะไม่ส่วน human เข้าไปใน huma ##n n ดังนั้นเมื่อ human ได้รับการเก็บรักษาไว้แล้ว huma เพียง แต่มีนับมีประสิทธิภาพของ 3

ขั้นตอนวิธีการนี้จะสร้างชุดของคำชิ้น s (หลายแห่งซึ่งจะมีทั้งคำ w ) ซึ่งเราสามารถใช้เป็นคำศัพท์ WordPiece ของเรา

อย่างไรก็ตาม มีปัญหา: อัลกอริธึมนี้จะสร้างส่วนของคำมากเกินไป เหตุผลก็คือเราลบเฉพาะการนับโทเค็นส่วนนำหน้าเท่านั้น ดังนั้นถ้าเราให้คำว่า human เราจะลบออกนับสำหรับ h, hu, hu, huma แต่ไม่ได้สำหรับ ##u, ##um, ##uma, ##uman และอื่น ๆ ดังนั้นเราจึงอาจสร้างทั้ง human และ ##uman เป็นชิ้นคำแม้ว่า ##uman จะไม่ถูกนำมาใช้

ดังนั้นทำไมไม่ลบออกนับทุก substring ไม่เพียง แต่ทุกคำนำหน้า? เพราะจากนั้นเราสามารถลบการนับออกได้หลายครั้ง สมมติว่าเรากำลังประมวลผล s ความยาว 5 และเราทั้งสอง (##denia, 129) และ (##eniab, 137) ที่ 65 ของการนับเหล่านั้นมาจากคำว่า undeniable ถ้าเราลบออกจากทุก substring เราจะลบ 65 จากอักขระย่อย ##enia สองครั้งแม้ว่าเราควรลบครั้งเดียว อย่างไรก็ตาม หากเราลบเฉพาะคำนำหน้า ก็จะถูกลบเพียงครั้งเดียวเท่านั้น

ครั้งที่สอง (และสาม ... ) การวนซ้ำ

เพื่อแก้ปัญหา overgeneration ที่กล่าวถึงข้างต้น เราทำอัลกอริธึมซ้ำหลายครั้ง

ซ้ำที่ตามมาจะเหมือนกับครั้งแรกที่มีความแตกต่างที่สำคัญอย่างหนึ่ง: ในขั้นตอนที่ 2 แทนการพิจารณาทุก substring เราใช้อัลกอริทึม WordPiece tokenization โดยใช้คำศัพท์จากการย้ำก่อนหน้านี้และมีเพียงพิจารณาสตริงซึ่งเริ่มต้นในจุดแยก

ตัวอย่างเช่นสมมติว่าเรากำลังดำเนินการขั้นตอนที่ 2 ขั้นตอนวิธีการและพบคำ undeniable ในการย้ำแรกเราจะพิจารณาทุกสตริงย่อยเช่น {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...} }

สำหรับการทำซ้ำครั้งที่สอง เราจะพิจารณาเฉพาะส่วนย่อยของสิ่งเหล่านี้ สมมติว่าหลังจากการวนซ้ำครั้งแรก ชิ้นคำที่เกี่ยวข้องคือ:

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

ส่วน WordPiece ขั้นตอนวิธีการนี้จะเข้าสู่ un ##deni ##able (ดูในส่วน การใช้ WordPiece สำหรับข้อมูลเพิ่มเติม) ในกรณีนี้เราจะพิจารณาสตริงที่เริ่มต้นที่จุดแบ่งส่วน เราจะยังคงพิจารณาทุกตำแหน่งสิ้นสุดที่เป็นไปได้ ดังนั้นในช่วงซ้ำสองชุดของ s สำหรับ undeniable คือ

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

อัลกอริทึมจะเหมือนกันทุกประการ ในตัวอย่างนี้ในการย้ำแรกอัลกอริทึมผลิต suprious ราชสกุล ##ndeni และ ##iable ตอนนี้ โทเค็นเหล่านี้จะไม่ถูกพิจารณา ดังนั้นจะไม่ถูกสร้างขึ้นโดยการทำซ้ำครั้งที่สอง เราทำซ้ำหลายครั้งเพื่อให้แน่ใจว่าผลลัพธ์มาบรรจบกัน (แม้ว่าจะไม่มีการรับประกันการลู่เข้าตามตัวอักษร)

การใช้ WordPiece

เมื่อสร้างคำศัพท์ WordPiece แล้ว เราจะต้องสามารถใช้คำศัพท์นั้นกับข้อมูลใหม่ได้ อัลกอริธึมนี้เป็นแอปพลิเคชั่นที่ยาวที่สุดและตรงที่สุดก่อน

ตัวอย่างเช่นพิจารณาแบ่งกลุ่มคำ undeniable

ครั้งแรกที่เราค้นหาแบบ undeniable ในพจนานุกรม WordPiece ของเราและถ้ามันปัจจุบันเรากำลังทำ ถ้าไม่ได้เราพร่องจุดสิ้นสุดโดยหนึ่งในตัวละครและทำซ้ำเช่น undeniabl

ในที่สุด เราจะพบโทเค็นย่อยในคำศัพท์ของเรา หรือค้นหาโทเค็นย่อยที่มีอักขระตัวเดียว (โดยทั่วไปแล้วเราคิดว่าตัวละครทุกตัวที่อยู่ในคำศัพท์ของเราถึงแม้ว่าอาจจะไม่เป็นกรณีสำหรับอักขระ Unicode ที่หายาก. ถ้าเราพบอักขระ Unicode ที่หายากที่ไม่ได้อยู่ในคำศัพท์ที่เราก็ map คำทั้ง <unk> )

ในกรณีนี้เราพบว่า un ในคำศัพท์ของเรา นั่นคือคำแรกของเรา แล้วเราข้ามไปยังจุดสิ้นสุดของการ un และทำซ้ำการประมวลผลเช่นการพยายามที่จะหา ##deniable แล้ว ##deniabl ฯลฯ นี้ซ้ำแล้วซ้ำอีกจนกว่าเราจะได้แบ่งกลุ่มทั้งคำ

ปรีชา

ตามสัญชาตญาณ โทเค็น WordPiece พยายามตอบสนองวัตถุประสงค์สองประการที่แตกต่างกัน:

  1. tokenize ข้อมูลลงในจำนวนน้อยชิ้นที่สุดเท่าที่ทำได้ สิ่งสำคัญคือต้องจำไว้ว่าอัลกอริธึม WordPiece ไม่ได้ "ต้องการ" แยกคำ มิฉะนั้นมันก็จะแยกออกทุกคำเป็นของตัวอักษรเช่น human -> {h, ##u, ##m, ##a, #n} นี่คือสิ่งหนึ่งที่สำคัญที่ทำให้แตกต่างจาก WordPiece แยกก้านซึ่งจะแยก morphemes ภาษาแม้สำหรับคำทั่วไป (เช่น unwanted -> {un, want, ed} )

  2. เมื่อต้องแยกคำออกเป็นชิ้น ๆ ให้แบ่งออกเป็นส่วน ๆ ที่มีจำนวนมากที่สุดในข้อมูลการฝึก ยกตัวอย่างเช่นเหตุผลที่ว่าทำไมคำ undeniable จะถูกแบ่งออกเป็น {un, ##deni, ##able} มากกว่าทางเลือกเช่น {unde, ##niab, ##le} คือว่านับสำหรับการ un และ ##able ใน โดยเฉพาะอย่างยิ่งจะสูงมาก เนื่องจากสิ่งเหล่านี้เป็นคำนำหน้าและคำต่อท้ายทั่วไป แม้ว่าจำนวนสำหรับ ##le ต้องสูงกว่า ##able ที่นับต่ำของ unde และ ##niab จะทำให้เรื่องนี้น้อย tokenization "ที่พึงประสงค์" เพื่ออัลกอริทึม

ไม่บังคับ: tf.lookup

หากคุณต้องการเข้าถึงหรือการควบคุมที่มากกว่าคำศัพท์ที่มันน่าสังเกตว่าคุณสามารถสร้างตารางการค้นหาตัวเองและผ่านที่ BertTokenizer

เมื่อคุณผ่านสตริง BertTokenizer ไม่ต่อไปนี้:

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)

ตอนนี้คุณสามารถเข้าถึงตารางค้นหาที่ใช้ใน tokenizer ได้โดยตรง

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

คุณไม่จำเป็นต้องใช้ไฟล์คำศัพท์, tf.lookup มีตัวเลือกการเริ่มต้นอื่น ๆ หากคุณมีคำศัพท์ที่อยู่ในหน่วยความจำของคุณสามารถใช้ 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)