ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูแหล่งที่มาบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค |
กวดวิชานี้จะแสดงให้เห็นถึงวิธีการสร้างคำศัพท์ subword จากชุดข้อมูลและใช้มันเพื่อสร้าง text.BertTokenizer
จากคำศัพท์
ข้อได้เปรียบหลักของ tokenizer ของ subword คือมันสอดแทรกระหว่าง tokenization แบบ word-based และ character-based tokenization คำทั่วไปมีช่องว่างในคำศัพท์ แต่ tokenizer สามารถถอยกลับไปเป็นคำและอักขระแต่ละตัวสำหรับคำที่ไม่รู้จัก
ภาพรวม
tensorflow_text
แพคเกจรวมถึงการใช้งานของ TensorFlow tokenizers ที่พบบ่อยมาก ซึ่งรวมถึงตัวสร้างโทเค็นแบบคำย่อยสามตัว:
-
text.BertTokenizer
- TheBertTokenizer
ระดับเป็นอินเตอร์เฟซระดับที่สูงขึ้น ซึ่งจะรวมถึงขั้นตอนวิธีการแยกเบิร์ตโทเค็นและWordPieceTokenizer
มันต้องใช้ประโยคเป็น input และส่งกลับ token-รหัส -
text.WordpieceTokenizer
- TheWordPieceTokenizer
ระดับเป็นอินเตอร์เฟซระดับที่ต่ำกว่า มันก็แค่การดำเนินการ ขั้นตอนวิธีการ WordPiece คุณต้องสร้างมาตรฐานและแบ่งข้อความเป็นคำก่อนเรียก มันต้องใช้คำพูดเป็น input และส่งกลับ token-รหัส -
text.SentencepieceTokenizer
- TheSentencepieceTokenizer
ต้องมีการตั้งค่าที่ซับซ้อนมากขึ้น ตัวเริ่มต้นของมันต้องใช้แบบจำลองประโยคที่ได้รับการฝึกฝนมาล่วงหน้า ดู พื้นที่เก็บข้อมูล 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 มีสองสิ่งที่คุณสามารถล้างข้อมูลสำหรับบทช่วยสอนดาวน์สตรีม:
- พวกเขาต้องการที่จะสร้างผลลัพธ์ข้อความสะอาดเพื่อวางราชสกุลลิขสิทธิ์เช่น
[START]
,[END]
และ[PAD]
- พวกเขากำลังสนใจในสายที่สมบูรณ์เพื่อใช้สตริงเข้าร่วมตาม
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
ซ้ำ
การทำซ้ำที่อธิบายไว้ด้านล่าง:
ทำซ้ำครั้งแรก
- ย้ำกว่าทุกคำพูดและทั้งคู่นับในการป้อนข้อมูลที่แสดงเป็น
(w, c)
- สำหรับแต่ละคำ
w
สร้างทุก substring, แสดงเป็นs
เช่นคำว่าhuman
เราสร้าง{h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
- รักษาแผนที่กัญชาย่อยเพื่อนับและเพิ่มจำนวนของแต่ละ
s
โดยc
เช่นถ้าเรามี(human, 113)
และ(humas, 3)
ในการป้อนข้อมูลของเรานับs = huma
จะเป็น113+3=116
- เมื่อเราได้รวบรวมข้อหาทุกสตริงย่อยที่ย้ำมากกว่า
(s, c)
คู่ที่เริ่มต้นด้วยที่ยาวที่สุดs
แรก - เก็บใด ๆ
s
ที่มีc > T
เช่นถ้าT = 100
และเรามี(pers, 231); (dogs, 259); (##rint; 76)
แล้วเราจะทำให้pers
และdogs
- เมื่อ
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 พยายามตอบสนองวัตถุประสงค์สองประการที่แตกต่างกัน:
tokenize ข้อมูลลงในจำนวนน้อยชิ้นที่สุดเท่าที่ทำได้ สิ่งสำคัญคือต้องจำไว้ว่าอัลกอริธึม WordPiece ไม่ได้ "ต้องการ" แยกคำ มิฉะนั้นมันก็จะแยกออกทุกคำเป็นของตัวอักษรเช่น
human -> {h, ##u, ##m, ##a, #n}
นี่คือสิ่งหนึ่งที่สำคัญที่ทำให้แตกต่างจาก WordPiece แยกก้านซึ่งจะแยก morphemes ภาษาแม้สำหรับคำทั่วไป (เช่นunwanted -> {un, want, ed}
)เมื่อต้องแยกคำออกเป็นชิ้น ๆ ให้แบ่งออกเป็นส่วน ๆ ที่มีจำนวนมากที่สุดในข้อมูลการฝึก ยกตัวอย่างเช่นเหตุผลที่ว่าทำไมคำ
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)