הצג באתר TensorFlow.org | הפעל בגוגל קולאב | צפה במקור ב-GitHub | הורד מחברת |
הדרכה זו מדגימה כיצד ניתן ליצור אוצר מילים subword מן הנתונים, ולהשתמש בו כדי לבנות text.BertTokenizer
מאוצר המילים.
היתרון העיקרי של טוקנייזר מילות משנה הוא בכך שהוא משלב בין טוקניזציה מבוססת מילים ומבוססת תווים. מילים נפוצות מקבלות משבצת באוצר המילים, אך הטוקנייזר יכול לחזור לחלקי מילים ולתווים בודדים עבור מילים לא ידועות.
סקירה כללית
tensorflow_text
החבילה כוללת הטמעות TensorFlow של tokenizers הנפוצה רבה. זה כולל שלושה אסימונים בסגנון מילות משנה:
-
text.BertTokenizer
- TheBertTokenizer
בכיתה הוא ממשק ברמה גבוהה. היא כוללת אלגוריתם פיצול האסימון של ברט לביןWordPieceTokenizer
. זה לוקח משפטים כקלט ומחזירה-ID האסימון. -
text.WordpieceTokenizer
- TheWordPieceTokenizer
בכיתה הוא ממשק ברמה נמוכה. זה רק מיישם את אלגוריתם WordPiece . עליך לתקן ולפצל את הטקסט למילים לפני שתקרא לו. זה לוקח מילים כקלט ומחזירה-ID האסימון. -
text.SentencepieceTokenizer
- TheSentencepieceTokenizer
דורש התקנה מורכבת יותר. האתחול שלו דורש מודל משפטי מאומן מראש. עיין מאגר Google / sentencepiece כדי לקבל הוראות כיצד לבנות אחד מן המודלים האלה. זה יכול לקבל משפטים כקלט כאשר 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 .
שימו לב לכמה דברים לגבי המשפטים לדוגמה למעלה:
- הם באותיות קטנות.
- יש רווחים מסביב לסימני הפיסוק.
- לא ברור אם או באיזו נורמליזציה של Unicode נעשה שימוש.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)
צור את אוצר המילים
קטע זה יוצר אוצר מילים מתוך מערך נתונים. אם כבר יש לך קובץ אוצר מילים רק רוצה לראות איך לבנות 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 שבשימוש Transformer הדרכה. סעיף זה מוסיף שיטות וצעדים עיבוד לפשט הדרכה כי, והן על היצוא, 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)>
ניתוק מותאם אישית
לפני ייצוא האסימונים יש כמה דברים שתוכלו לנקות עבור ההדרכות במורד הזרם:
- הם רוצים ליצור פלט טקסט נקי, כך טיפת אסימונים שמור כמו
[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, זה מתחיל באלפבית, ומשלב באופן איטרטיבי ביגרמות נפוצות ליצירת חלקי מילים ומילים.
TensorFlow גנרטור אוצר המילים של הטקסט מלווה את היישום מלמעלה למטה מן ברט . מתחילים במילים ומפרקים אותן לרכיבים קטנים יותר עד שהן מגיעות לסף התדר, או שלא ניתן לפרק אותן יותר. הסעיף הבא מתאר זאת בפירוט. עבור יפנית, סינית וקוריאנית גישה זו מלמעלה למטה לא עובדת מכיוון שאין יחידות מילים מפורשות מלכתחילה. למי אתה צריך גישה שונה .
בחירת אוצר המילים
החלק העליון כלפי מטה WordPiece אלגוריתם הדור לוקח סט של (מילה, ספירה) בזוגות סף T
, וחוזר אוצר מילים V
.
האלגוריתם הוא איטרטיבי. היא מנוהלת על k
איטרציות, שבו בדרך כלל k = 4
, אבל רק שני הראשונים הם באמת חשובים. השלישי והרביעי (ומעבר לו) פשוט זהות לשני. שים לב שכל שלב של חיפוש בינארי פועל אלגוריתם מאפס עבור k
איטרציות.
האיטרציות המתוארות להלן:
איטרציה ראשונה
- וחזור על פני כל מילה לספור זוג בקלט, מסומן בתור
(w, c)
. - עבור כל מילה
w
, ליצור כל מחרוזת, מסומן כמו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
. אז פעםhuman
נשמר, אזhuma
יש רק ספירה יעילה של3
.
אלגוריתם זה יפיק סדרה של המילה חתיכות s
(שרבים מהם יהיו מילים שלמות w
), אשר נוכל להשתמש כמו אוצר המילים WordPiece שלנו.
עם זאת, יש בעיה: אלגוריתם זה יגרום ליצירת יתר חמורה של חלקי מילים. הסיבה היא שאנו מחסירים רק ספירות של אסימוני קידומת. לכן, אם אנחנו שומרים את המילה human
, נקזז את הספירה עבור h, hu, hu, huma
, אך לא עבור ##u, ##um, ##uma, ##uman
וכן הלאה. אז נוכל לייצר הן human
ו ##uman
כמו חתיכות מילה, למרות ##uman
לעולם להיות מיושם.
אז למה לא להחסיר את ספירת עבור כל מחרוזת, לא רק כל קידומת? כי אז נוכל בסופו של דבר להחסיר את הספירות מספר פעמים. תניחו שאנחנו עיבוד כן את s
באורך 5 ואנחנו שומרים שניהם (##denia, 129)
ו (##eniab, 137)
, שבו 65
של ספירה אלה בא מהמילה undeniable
. אם נחסיר את מכל מחרוזת, היינו להחסיר 65
מן המחרוזת ##enia
פעמים, למרות שאנחנו צריכים להחסיר פעם אחת בלבד. עם זאת, אם נגרע רק מקידומות, זה ייגרע בצורה נכונה רק פעם אחת.
איטרציה שנייה (ושלישית...).
כדי לפתור את בעיית יצירת היתר שהוזכרה לעיל, אנו מבצעים איטרציות מרובות של האלגוריתם.
חזרות עוקבות זהה לראשון, עם הבחנה חשובה אחת: בשלב 2, במקום בהתחשב בכל מחרוזת, אנו מיישמים את האלגוריתם tokenization WordPiece באמצעות אוצר המילים מן איטרציה הקודם, ורק לשקול מחרוזות אשר להתחיל בנקודה מפוצל.
לדוגמא, נניח כי אנו מבצעים צעד 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 נדיר. אם אנו נתקלים תו יוניקוד נדיר זה לא בלקסיקון אנחנו פשוט למפות את המילה כולה <unk>
).
במקרה זה, אנו מוצאים un
ב אוצר המילים שלנו. אז זו המילה הראשונה שלנו. אז אנחנו לקפוץ לסוף un
וחוזרים על העיבוד, למשל, לנסות למצוא ##deniable
, אז ##deniabl
, וכו 'זה חוזר על עצמו עד שאנחנו שפלחנו המילה כול.
אינטואיציה
באופן אינטואיטיבי, אסימון WordPiece מנסה לספק שתי מטרות שונות:
Tokenize את הנתונים הפחות מספר חתיכות ככל האפשר. חשוב לזכור שאלגוריתם WordPiece לא "רוצה" לפצל מילים. אחרת, זה היה פשוט לפצל כל מילה לתוך הדמויות שלה, למשל,
human -> {h, ##u, ##m, ##a, #n}
. זהו דבר קריטי אחד שעושה שונה WordPiece מ מפצל מורפולוגי, אשר יהיה לפצל מורפמות לשוניות אפילו עבור מילות נפוצות (למשל,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)