הצג באתר TensorFlow.org | הפעל בגוגל קולאב | הצג ב-GitHub | הורד מחברת |
סקירה כללית
עיבוד מקדים של טקסט הוא טרנספורמציה מקצה לקצה של טקסט גולמי לקלט מספרים שלמים של מודל. מודלים של NLP מלווים לרוב בכמה מאות (אם לא אלפי) שורות של קוד Python לעיבוד מוקדם של טקסט. עיבוד מוקדם של טקסט הוא לעתים קרובות אתגר עבור דגמים מכיוון:
הטיית אימון-הגשה. קשה יותר ויותר להבטיח שהלוגיקת העיבוד המקדים של תשומות המודל תהיה עקבית בכל שלבי פיתוח המודל (למשל אימון מקדים, כוונון עדין, הערכה, מסקנות). שימוש בפרמטרים שונים, טוקניזציה, אלגוריתמים של עיבוד מקדים של מחרוזות או פשוט אריזה של תשומות מודל באופן לא עקבי בשלבים שונים עלולים להניב השפעות קשות לניפוי והרס למודל.
יעילות וגמישות. בעוד שניתן לבצע עיבוד מקדים במצב לא מקוון (למשל על ידי כתיבת פלטים מעובדים לקבצים בדיסק ולאחר מכן צריכת נתונים מעובדים מראש בצינור הקלט), שיטה זו כרוכה בעלות קריאה וכתיבה נוספת של קבצים. עיבוד מוקדם במצב לא מקוון הוא גם לא נוח אם יש החלטות עיבוד מקדמות שצריכות להתרחש באופן דינמי. ניסוי עם אפשרות אחרת ידרוש יצירה מחדש של מערך הנתונים מחדש.
ממשק דגם מורכב. מודלים של טקסט הם הרבה יותר מובנים כאשר הקלט שלהם הוא טקסט טהור. קשה להבין מודל כאשר הקלט שלו דורש שלב קידוד נוסף ועקיף. צמצום מורכבות העיבוד המקדים מוערך במיוחד עבור ניפוי באגים, הגשה והערכה של מודל.
בנוסף, ממשקי מודל פשוטים יותר גם הופכים את זה לנוח יותר לנסות את המודל (למשל הסקה או הדרכה) על מערכי נתונים שונים שלא נחקרו.
עיבוד מקדים של טקסט עם TF.Text
באמצעות ממשקי ה-API של עיבוד טקסט מראש של TF.Text, אנו יכולים לבנות פונקציית עיבוד מקדימה שיכולה להפוך את מערך הנתונים של הטקסט של המשתמש לקלט המספרים השלמים של המודל. משתמשים יכולים לארוז עיבוד מקדים ישירות כחלק מהמודל שלהם כדי להקל על הבעיות שהוזכרו לעיל.
הדרכה זו תציג כיצד להשתמש ops עיבוד מקדים TF.Text להפוך נתוני טקסט לתוך תשומות עבור המודל ברט תשומות עבור השפה מיסוך pretraining המשימה המתוארת "רעול LM ו מיסוך נוהל" של ברט: טרום הכשרה של רובוטריקים כיוונית עמוק עבור שפה הבנה . התהליך כולל אסימון של טקסט ליחידות של מילות משנה, שילוב משפטים, חיתוך תוכן לגודל קבוע וחילוץ תוויות עבור משימת דוגמנות השפה המסוכה.
להכין
בואו לייבא תחילה את החבילות והספריות שאנו צריכים.
pip install -q -U tensorflow-text
import tensorflow as tf
import tensorflow_text as text
import functools
הנתונים שלנו מכילים שתי תכונות טקסט נוכל ליצור דוגמא tf.data.Dataset
. מטרתנו היא ליצור פונקציה כי אנו יכולים לספק Dataset.map()
עם שישמש אימונים.
examples = {
"text_a": [
b"Sponge bob Squarepants is an Avenger",
b"Marvel Avengers"
],
"text_b": [
b"Barack Obama is the President.",
b"President is the highest office"
],
}
dataset = tf.data.Dataset.from_tensor_slices(examples)
next(iter(dataset))
{'text_a': <tf.Tensor: shape=(), dtype=string, numpy=b'Sponge bob Squarepants is an Avenger'>, 'text_b': <tf.Tensor: shape=(), dtype=string, numpy=b'Barack Obama is the President.'>}
אסימונים
הצעד הראשון שלנו הוא להפעיל כל עיבוד מקדים של מחרוזת וליצור אסימון של מערך הנתונים שלנו. ניתן לעשות זאת באמצעות text.BertTokenizer
, שהינה text.Splitter
שיכולים tokenize משפטים subwords או wordpieces עבור דגם ברט קיבל אוצר המילים שנוצר מן האלגוריתם Wordpiece . אתה יכול ללמוד עוד על subword האחר tokenizers הזמין TF.Text מ כאן .
אוצר המילים יכול להיות ממחסום BERT שנוצר בעבר, או שאתה יכול ליצור אחד בעצמך על פי הנתונים שלך. למטרות דוגמה זו, בואו ניצור אוצר מילים של צעצוע:
_VOCAB = [
# Special tokens
b"[UNK]", b"[MASK]", b"[RANDOM]", b"[CLS]", b"[SEP]",
# Suffixes
b"##ack", b"##ama", b"##ger", b"##gers", b"##onge", b"##pants", b"##uare",
b"##vel", b"##ven", b"an", b"A", b"Bar", b"Hates", b"Mar", b"Ob",
b"Patrick", b"President", b"Sp", b"Sq", b"bob", b"box", b"has", b"highest",
b"is", b"office", b"the",
]
_START_TOKEN = _VOCAB.index(b"[CLS]")
_END_TOKEN = _VOCAB.index(b"[SEP]")
_MASK_TOKEN = _VOCAB.index(b"[MASK]")
_RANDOM_TOKEN = _VOCAB.index(b"[RANDOM]")
_UNK_TOKEN = _VOCAB.index(b"[UNK]")
_MAX_SEQ_LEN = 8
_MAX_PREDICTIONS_PER_BATCH = 5
_VOCAB_SIZE = len(_VOCAB)
lookup_table = tf.lookup.StaticVocabularyTable(
tf.lookup.KeyValueTensorInitializer(
keys=_VOCAB,
key_dtype=tf.string,
values=tf.range(
tf.size(_VOCAB, out_type=tf.int64), dtype=tf.int64),
value_dtype=tf.int64),
num_oov_buckets=1
)
המבנה של לתת text.BertTokenizer
להשתמש באוצר המילים שלעיל tokenize תשומות הטקסט לתוך RaggedTensor
.`.
bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.string)
bert_tokenizer.tokenize(examples["text_a"])
<tf.RaggedTensor [[[b'Sp', b'##onge'], [b'bob'], [b'Sq', b'##uare', b'##pants'], [b'is'], [b'an'], [b'A', b'##ven', b'##ger']], [[b'Mar', b'##vel'], [b'A', b'##ven', b'##gers']]]>
bert_tokenizer.tokenize(examples["text_b"])
<tf.RaggedTensor [[[b'Bar', b'##ack'], [b'Ob', b'##ama'], [b'is'], [b'the'], [b'President'], [b'[UNK]']], [[b'President'], [b'is'], [b'the'], [b'highest'], [b'office']]]>
פלט טקסט text.BertTokenizer
מאפשר לנו לראות איך הטקסט מתבצע tokenized, אבל המודל דורש מזהים שלמים. אנו יכולים להגדיר את token_out_type
param כדי tf.int64
להשיג שלם מזהים (המהווים את המדדים לתוך אוצר המילים).
bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.int64)
segment_a = bert_tokenizer.tokenize(examples["text_a"])
segment_a
<tf.RaggedTensor [[[22, 9], [24], [23, 11, 10], [28], [14], [15, 13, 7]], [[18, 12], [15, 13, 8]]]>
segment_b = bert_tokenizer.tokenize(examples["text_b"])
segment_b
<tf.RaggedTensor [[[16, 5], [19, 6], [28], [30], [21], [0]], [[21], [28], [30], [27], [29]]]>
text.BertTokenizer
מחזירה RaggedTensor
עם הצורה [batch, num_tokens, num_wordpieces]
. מכיוון שאנחנו לא צריכים את תוספת num_tokens
ממדים המתאימה למקרה השימוש הנוכחי שלנו, אנחנו יכולים למזג את שני הממדים האחרונים להשיג RaggedTensor
עם צורה [batch, num_wordpieces]
:
segment_a = segment_a.merge_dims(-2, -1)
segment_a
<tf.RaggedTensor [[22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7], [18, 12, 15, 13, 8]]>
segment_b = segment_b.merge_dims(-2, -1)
segment_b
<tf.RaggedTensor [[16, 5, 19, 6, 28, 30, 21, 0], [21, 28, 30, 27, 29]]>
חיתוך תוכן
הקלט העיקרי ל- BERT הוא שרשור של שני משפטים. עם זאת, BERT דורש שהקלטים יהיו בגודל ובצורה קבועים וייתכן שיש לנו תוכן שחורג מהתקציב שלנו.
אנחנו יכולים להתמודד עם זה באמצעות text.Trimmer
לקצץ התוכן שלנו גודל קבוע מראש (פעם בשרשור לאורך הציר האחרון). ישנם שונים text.Trimmer
סוגים בוחרים בתוכן לשמר באמצעות אלגוריתמים שונים. text.RoundRobinTrimmer
למשל יקצה מכסה שווה לכול מגזר, אך עשוי לקצץ את הקצוות של משפטים. text.WaterfallTrimmer
יהיה לקצץ החל סוף המשפט האחרון.
לדוגמה שלנו, נשתמש RoundRobinTrimmer
אשר בוחר פריטים מכל מגזר באופן שמאל לימין.
trimmer = text.RoundRobinTrimmer(max_seq_length=[_MAX_SEQ_LEN])
trimmed = trimmer.trim([segment_a, segment_b])
trimmed
[<tf.RaggedTensor [[22, 9, 24, 23], [18, 12, 15, 13]]>, <tf.RaggedTensor [[16, 5, 19, 6], [21, 28, 30, 27]]>]
trimmed
מכיל כעת המגזרים שבהם מספר אלמנטים פני אצווה הוא 8 אלמנטים (כאשר בשרשור לאורך ציר = -1).
שילוב קטעים
עכשיו יש לנו קטעים גזוז, נוכל לשלב אותם יחד כדי לקבל אחת RaggedTensor
. ברט משתמשת אסימונים מיוחדים כדי לציין את תחילתה ( [CLS]
) וסיום של קטע ( [SEP]
). אנחנו גם צריכים RaggedTensor
המציין אילו פריטים ב בשילוב Tensor
שייכים לאיזו פלח. אנו יכולים להשתמש text.combine_segments()
כדי לקבל את שני אלה Tensor
עם אסימונים מיוחדים המוכנסים.
segments_combined, segments_ids = text.combine_segments(
[segment_a, segment_b],
start_of_sequence_id=_START_TOKEN, end_of_segment_id=_END_TOKEN)
segments_combined, segments_ids
(<tf.RaggedTensor [[3, 22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7, 4, 16, 5, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 8, 4, 21, 28, 30, 27, 29, 4]]>, <tf.RaggedTensor [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]]>)
משימה מודל שפה מסכה
עכשיו שיש לנו התשומות הבסיסיות שלנו, אנו יכולים להתחיל לחלץ התשומות דרוש "LM הרעול הפנים נוהלו מיסוך" משימה מתוארת ברט: טרום הכשרה של רובוטריקים כיוונית העמוקים להבנת שפה
למשימת מודל השפה המסוכה יש שתי בעיות משנה שעליהן עלינו לחשוב: (1) אילו פריטים לבחור למיסוך ו-(2) אילו ערכים מוקצים להם?
בחירת פריט
מכיוון שאנחנו נבחר לבחור פריטים באופן אקראי עבור מיסוך, נשתמש text.RandomItemSelector
. RandomItemSelector
בוחר פריטים באופן אקראי נושא אצווה למגבלות נתון ( max_selections_per_batch
, selection_rate
ו unselectable_ids
) וחוזר מסכה בוליאני המציין אילו פריטים נבחרו.
random_selector = text.RandomItemSelector(
max_selections_per_batch=_MAX_PREDICTIONS_PER_BATCH,
selection_rate=0.2,
unselectable_ids=[_START_TOKEN, _END_TOKEN, _UNK_TOKEN]
)
selected = random_selector.get_selection_mask(
segments_combined, axis=1)
selected
<tf.RaggedTensor [[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, True, True, True, False, False], [False, False, False, False, False, True, False, False, False, False, False, True, False]]>
בחירת ערך המסכה
המתודולוגיה שתיארה את נייר BERT המקורי לבחירת הערך למיסוך היא כדלקמן:
עבור mask_token_rate
הזמן, להחליף את הפריט עם [MASK]
האסימון:
"my dog is hairy" -> "my dog is [MASK]"
עבור random_token_rate
הזמן, להחליף את הפריט עם מילה אקראית:
"my dog is hairy" -> "my dog is apple"
עבור 1 - mask_token_rate - random_token_rate
הזמן, לשמור את הפריט ללא שינוי:
"my dog is hairy" -> "my dog is hairy."
text.MaskedValuesChooser
מתמצת ההיגיון הזה יכול לשמש הפונקציה המקדימה שלנו. הנה דוגמה של מה MaskValuesChooser
מחזירה נתון mask_token_rate
של 80% ו מחדל random_token_rate
:
input_ids = tf.ragged.constant([[19, 7, 21, 20, 9, 8], [13, 4, 16, 5], [15, 10, 12, 11, 6]])
mask_values_chooser = text.MaskValuesChooser(_VOCAB_SIZE, _MASK_TOKEN, 0.8)
mask_values_chooser.get_mask_values(input_ids)
<tf.RaggedTensor [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1], [1, 10, 1, 1, 6]]>
כאשר מסופק עם RaggedTensor
קלט, text.MaskValuesChooser
מחזירה RaggedTensor
של אותה צורה גם עם _MASK_VALUE
(0), מזהה אקראי, או באותו מזהה שינוי.
הפקת קלט עבור מטלת מודל שפה מסכה
עכשיו שיש לנו RandomItemSelector
כדי לעזור לנו לבחור פריטים עבור מיסוך text.MaskValuesChooser
להקצות את הערכים, אנו יכולים להשתמש text.mask_language_model()
כדי להרכיב את כל התשומות של משימה זו עבור דגם ברט שלנו.
masked_token_ids, masked_pos, masked_lm_ids = text.mask_language_model(
segments_combined,
item_selector=random_selector, mask_values_chooser=mask_values_chooser)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: batch_gather (from tensorflow.python.ops.array_ops) is deprecated and will be removed after 2017-10-25. Instructions for updating: `tf.batch_gather` is deprecated, please use `tf.gather` with `batch_dims=-1` instead.
צלילה בואו עמוק ולבחון את התפוקות של mask_language_model()
. הפלט של masked_token_ids
הוא:
masked_token_ids
<tf.RaggedTensor [[3, 22, 1, 24, 23, 1, 10, 28, 1, 15, 1, 7, 4, 16, 5, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 1, 4, 21, 28, 30, 27, 1, 4]]>
זכור שהקלט שלנו מקודד באמצעות אוצר מילים. אם אנחנו לפענח masked_token_ids
באמצעות אוצר המילים שלנו, אנחנו מקבלים:
tf.gather(_VOCAB, masked_token_ids)
<tf.RaggedTensor [[b'[CLS]', b'Sp', b'[MASK]', b'bob', b'Sq', b'[MASK]', b'##pants', b'is', b'[MASK]', b'A', b'[MASK]', b'##ger', b'[SEP]', b'Bar', b'##ack', b'Ob', b'##ama', b'is', b'the', b'President', b'[UNK]', b'[SEP]'], [b'[CLS]', b'Mar', b'##vel', b'A', b'##ven', b'[MASK]', b'[SEP]', b'President', b'is', b'the', b'highest', b'[MASK]', b'[SEP]']]>
שים לב שחלק אסימוני wordpiece הוחלפו או [MASK]
, [RANDOM]
או ערך מזהה אחר. masked_pos
פלט נותן לנו את המדדים (ב אצווה בהתאמה) של הז'יטונים הוחלפו.
masked_pos
<tf.RaggedTensor [[2, 5, 8, 10], [5, 11]]>
masked_lm_ids
נותן לנו את הערך המקורי של האסימון.
masked_lm_ids
<tf.RaggedTensor [[9, 11, 14, 13], [8, 29]]>
אנחנו יכולים שוב לפענח את המזהים כאן כדי לקבל ערכים קריאים אנושיים.
tf.gather(_VOCAB, masked_lm_ids)
<tf.RaggedTensor [[b'##onge', b'##uare', b'an', b'##ven'], [b'##gers', b'office']]>
כניסות דגם ריפוד
עכשיו יש לנו את כול התשומות עבור המודל שלנו, השלב האחרון המקדים שלנו הוא לארוז אותם לתוך 2-ממדי קבוע Tensor
ים עם ריפוד וגם ליצור מסכה Tensor
המציינת את הערכים שהם ערכי כרית. אנו יכולים להשתמש text.pad_model_inputs()
כדי לעזור לנו עם משימה זו.
# Prepare and pad combined segment inputs
input_word_ids, input_mask = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
input_type_ids, _ = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
# Prepare and pad masking task inputs
masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
masked_lm_ids, _ = text.pad_model_inputs(
masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
model_inputs = {
"input_word_ids": input_word_ids,
"input_mask": input_mask,
"input_type_ids": input_type_ids,
"masked_lm_ids": masked_lm_ids,
"masked_lm_positions": masked_lm_positions,
"masked_lm_weights": masked_lm_weights,
}
model_inputs
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23, 1, 10, 28], [ 3, 18, 12, 15, 13, 1, 4, 21]])>, 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]])>, 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23, 1, 10, 28], [ 3, 18, 12, 15, 13, 1, 4, 21]])>, 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 9, 11, 14, 13, 0], [ 8, 29, 0, 0, 0]])>, 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23], [ 3, 18, 12, 15, 13]])>, 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])>}
סקירה
בואו נסקור את מה שיש לנו עד כה ונרכיב את פונקציית העיבוד המקדים שלנו. הנה מה שיש לנו:
def bert_pretrain_preprocess(vocab_table, features):
# Input is a string Tensor of documents, shape [batch, 1].
text_a = features["text_a"]
text_b = features["text_b"]
# Tokenize segments to shape [num_sentences, (num_words)] each.
tokenizer = text.BertTokenizer(
vocab_table,
token_out_type=tf.int64)
segments = [tokenizer.tokenize(text).merge_dims(
1, -1) for text in (text_a, text_b)]
# Truncate inputs to a maximum length.
trimmer = text.RoundRobinTrimmer(max_seq_length=6)
trimmed_segments = trimmer.trim(segments)
# Combine segments, get segment ids and add special tokens.
segments_combined, segment_ids = text.combine_segments(
trimmed_segments,
start_of_sequence_id=_START_TOKEN,
end_of_segment_id=_END_TOKEN)
# Apply dynamic masking task.
masked_input_ids, masked_lm_positions, masked_lm_ids = (
text.mask_language_model(
segments_combined,
random_selector,
mask_values_chooser,
)
)
# Prepare and pad combined segment inputs
input_word_ids, input_mask = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_SEQ_LEN)
input_type_ids, _ = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_SEQ_LEN)
# Prepare and pad masking task inputs
masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
masked_lm_ids, _ = text.pad_model_inputs(
masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
model_inputs = {
"input_word_ids": input_word_ids,
"input_mask": input_mask,
"input_type_ids": input_type_ids,
"masked_lm_ids": masked_lm_ids,
"masked_lm_positions": masked_lm_positions,
"masked_lm_weights": masked_lm_weights,
}
return model_inputs
אנחנו בעבר בנה tf.data.Dataset
ואנחנו יכולים כעת להשתמש בפונקציה המקדימה התאסף שלנו bert_pretrain_preprocess()
ב Dataset.map()
. זה מאפשר לנו ליצור צינור קלט להפיכת נתוני המחרוזת הגולמיים שלנו לכניסות שלמים ולהזנה ישירות למודל שלנו.
dataset = tf.data.Dataset.from_tensors(examples)
dataset = dataset.map(functools.partial(
bert_pretrain_preprocess, lookup_table))
next(iter(dataset))
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4, 16, 5, 19], [ 3, 18, 1, 15, 4, 1, 28, 30]])>, 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]])>, 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4, 16, 5, 19], [ 3, 18, 1, 15, 4, 1, 28, 30]])>, 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[24, 19, 0, 0, 0], [12, 21, 0, 0, 0]])>, 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4], [ 3, 18, 1, 15, 4]])>, 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])>}
הדרכות קשורות
לסווג טקסט עם ברט - הדרכה על אופן השימוש במודל ברט pretrained לטקסט לסווג. זהו המשך נחמד עכשיו כשאתה מכיר כיצד לעבד מראש את התשומות המשמשות את מודל BERT.
Tokenizing עם טקסט TF - מדריך המפרט את הסוגים השונים של tokenizers שקיימים TF.Text.
טיפול בטקסט עם
RaggedTensor
- מדריך מפורט על איך ליצור, שימוש ולטפלRaggedTensor
ים.