עזרה להגן על שונית המחסום הגדולה עם TensorFlow על Kaggle הצטרפו אתגר

אסימון באמצעות טקסט TF

הצג באתר TensorFlow.org הפעל בגוגל קולאב הצג ב-GitHub הורד מחברת ראה דגמי TF Hub

סקירה כללית

טוקניזציה היא תהליך של פירוק מחרוזת לאסימונים. בדרך כלל, אסימונים אלו הם מילים, מספרים ו/או סימני פיסוק. tensorflow_text החבילה מספקת מספר tokenizers הזמין עבור עיבוד מקדים טקסט נדרש על ידי המודלים מבוססי הטקסט שלך. על ידי ביצוע הטוקניזציה בגרף TensorFlow, לא תצטרך לדאוג לגבי הבדלים בין זרימות העבודה של האימון וההסקה וניהול סקריפטים לעיבוד מקדים.

מדריך זה דן באפשרויות האסימון הרבות שמספק TensorFlow Text, כאשר אולי תרצה להשתמש באפשרות אחת על פני אחרת, וכיצד נקראים האסימון הללו מתוך המודל שלך.

להכין

pip install -q tensorflow-text
import requests
import tensorflow as tf
import tensorflow_text as tf_text

API של ספליטר

הממשקים העיקריים הם Splitter ו SplitterWithOffsets אשר יש שיטות יחידות split ו split_with_offsets . SplitterWithOffsets וריאנט (המשתרע Splitter ) כולל אופציה לקבלת קיזוז בייט. זה מאפשר למתקשר לדעת מאילו בתים במחרוזת המקורית נוצר האסימון שנוצר.

Tokenizer ו TokenizerWithOffsets גרסאות מיוחדות של Splitter המספקים שיטות נוחות tokenize ו tokenize_with_offsets בהתאמה.

באופן כללי, עבור כל קלט N-ממדי, אסימונים חזר נמצאים N + 1 ממדי RaggedTensor עם המימד הפנימי ביותר של אסימונים מיפוי מחרוזות הפרט המקורי.

class Splitter {
  @abstractmethod
  def split(self, input)
}

class SplitterWithOffsets(Splitter) {
  @abstractmethod
  def split_with_offsets(self, input)
}

יש גם Detokenizer ממשק. כל אסימון המיישם ממשק זה יכול לקבל טנזור מרופט של אסימונים ב-N-ממד, ובדרך כלל מחזיר טנזור N-1-ממדי או טנזור מרופט שהאסימונים הנתונים מורכבים יחדיו.

class Detokenizer {
  @abstractmethod
  def detokenize(self, input)
}

אסימונים

להלן חבילת האסימונים המסופקת על ידי TensorFlow Text. ההנחה היא שכניסות המחרוזות הן UTF-8. עיין מדריך Unicode להמרת מחרוזות UTF-8.

סימני מילה שלמים

אסימונים אלו מנסים לפצל מחרוזת לפי מילים, וזו הדרך האינטואיטיבית ביותר לפצל טקסט.

WhitespaceTokenizer

text.WhitespaceTokenizer הוא tokenizer הבסיסי ביותר אשר מפצל מחרוזות על דמויות רווחים מוגדרות טיפול נמרץ (למשל. רווח, טאב, קו חדש). לרוב זה טוב לבנייה מהירה של דגמי אב טיפוס.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
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.
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

ייתכן שתבחין שחסרון של האסימון הזה הוא שסימני פיסוק כלולים במילה המרכיבה אסימון. כדי לפצל את המילים וסימני פיסוק לתוך אסימונים נפרדים, UnicodeScriptTokenizer צריך לשמש.

UnicodeScriptTokenizer

UnicodeScriptTokenizer מפצלת מחרוזות מבוססות על גבולות תסריט Unicode. קודי הסקריפט שבהם נעשה שימוש תואמים לערכי UScriptCode International Components for Unicode (ICU). ראה: http://icu-project.org/apiref/icu4c/uscript_8h.html

בפועל, זה דומה WhitespaceTokenizer עם ההוויה ההבדל הכי ברור שזה יהיה לפצל פיסוק (USCRIPT_COMMON) מטקסטים השפה (לדוגמא. USCRIPT_LATIN, USCRIPT_CYRILLIC, וכו ') תוך הפרדת טקסטים בשפה זו מזו. שימו לב שזה גם יפצל מילות התכווצות לאסימונים נפרדים.

tokenizer = tf_text.UnicodeScriptTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b'can', b"'", b't', b'explain', b',', b'but', b'you', b'feel', b'it', b'.']]

אסימוני מילות משנה

ניתן להשתמש באסימוני מילות משנה עם אוצר מילים קטן יותר, ולאפשר למודל לקבל קצת מידע על מילים חדשות מתוך מילות המשנה שיוצרות אותו.

אנו דנים באפשרויות tokenization Subword בקצרה להלן, אך הדרכת Subword Tokenization הולכת יותר לעומק וגם מסבירה איך ליצור את קבצי vocab.

WordpieceTokenizer

אסימון WordPiece הוא סכמת אסימון מונעת נתונים אשר מייצרת קבוצה של אסימוני משנה. אסימוני משנה אלה עשויים להתאים למורפמות לשוניות, אך לרוב זה לא המקרה.

WordpieceTokenizer מצפה שהקלט כבר יפוצל לאסימונים. בגלל תנאי זה, תוכלו בדרך כלל רוצה לפצל באמצעות WhitespaceTokenizer או UnicodeScriptTokenizer מראש.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

לאחר מחרוזת מחולק אסימונים, את WordpieceTokenizer יכול לשמש כדי לפצל subtokens.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_wp_en_vocab.txt?raw=true"
r = requests.get(url)
filepath = "vocab.txt"
open(filepath, 'wb').write(r.content)
52382
subtokenizer = tf_text.UnicodeScriptTokenizer(filepath)
subtokens = tokenizer.tokenize(tokens)
print(subtokens.to_list())
[[[b'What'], [b'you'], [b'know'], [b'you'], [b"can't"], [b'explain,'], [b'but'], [b'you'], [b'feel'], [b'it.']]]

BertTokenizer

ה-BertTokenizer משקף את היישום המקורי של טוקניזציה מנייר BERT. זה מגובה על ידי WordpieceTokenizer, אך גם מבצע משימות נוספות כגון נורמליזציה ו-Tokenizing למילים תחילה.

tokenizer = tf_text.BertTokenizer(filepath, token_out_type=tf.string, lower_case=True)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[[b'what'], [b'you'], [b'know'], [b'you'], [b'can'], [b"'"], [b't'], [b'explain'], [b','], [b'but'], [b'you'], [b'feel'], [b'it'], [b'.']]]

SentencepieceTokenizer

ה-SenencepieceTokenizer הוא אסימון משנה שניתן להגדרה. זה מגובה על ידי ספריית Sentencepiece. כמו BertTokenizer, הוא יכול לכלול נורמליזציה ופיצול אסימון לפני פיצול לתת-אסימונים.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_oss_model.model?raw=true"
sp_model = requests.get(url).content
tokenizer = tf_text.SentencepieceTokenizer(sp_model, out_type=tf.string)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'\xe2\x96\x81What', b'\xe2\x96\x81you', b'\xe2\x96\x81know', b'\xe2\x96\x81you', b'\xe2\x96\x81can', b"'", b't', b'\xe2\x96\x81explain', b',', b'\xe2\x96\x81but', b'\xe2\x96\x81you', b'\xe2\x96\x81feel', b'\xe2\x96\x81it', b'.']]

מפצלים אחרים

UnicodeCharTokenizer

זה מפצל מחרוזת לתווי UTF-8. זה שימושי עבור שפות CJK שאין בהן רווחים בין מילים.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]

הפלט הוא נקודות קוד של Unicode. זה יכול להיות שימושי גם ליצירת ngrams של תווים, כגון bigrams. כדי להמיר בחזרה לתווי UTF-8.

characters = tf.strings.unicode_encode(tf.expand_dims(tokens, -1), "UTF-8")
bigrams = tf_text.ngrams(characters, 2, reduction_type=tf_text.Reduction.STRING_JOIN, string_separator='')
print(bigrams.to_list())
[[b'Wh', b'ha', b'at', b't ', b' y', b'yo', b'ou', b'u ', b' k', b'kn', b'no', b'ow', b'w ', b' y', b'yo', b'ou', b'u ', b' c', b'ca', b'an', b"n'", b"'t", b't ', b' e', b'ex', b'xp', b'pl', b'la', b'ai', b'in', b'n,', b', ', b' b', b'bu', b'ut', b't ', b' y', b'yo', b'ou', b'u ', b' f', b'fe', b'ee', b'el', b'l ', b' i', b'it', b't.']]

HubModuleTokenizer

זהו מעטפת סביב דגמים שנפרסו ל- TF Hub כדי להקל על השיחות מכיוון ש- TF Hub אינו תומך כרגע בטנזורים מרופטים. שימוש במודל לביצוע טוקניזציה שימושי במיוחד עבור שפות CJK כאשר אתה רוצה לפצל למילים, אך אין לך רווחים לספק מדריך היוריסטי. בשלב זה, יש לנו מודל פילוח יחיד לסינית.

MODEL_HANDLE = "https://tfhub.dev/google/zh_segmentation/1"
segmenter = tf_text.HubModuleTokenizer(MODEL_HANDLE)
tokens = segmenter.tokenize(["新华社北京"])
print(tokens.to_list())
[[b'\xe6\x96\xb0\xe5\x8d\x8e\xe7\xa4\xbe', b'\xe5\x8c\x97\xe4\xba\xac']]

ייתכן שיהיה קשה לראות את התוצאות של מחרוזות בתים מקודדות UTF-8. פענח את ערכי הרשימה כדי להקל על הצפייה.

def decode_list(x):
  if type(x) is list:
    return list(map(decode_list, x))
  return x.decode("UTF-8")

def decode_utf8_tensor(x):
  return list(map(decode_list, x.to_list()))

print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

SplitMergeTokenizer

SplitMergeTokenizer & SplitMergeFromLogitsTokenizer יש מטרה ממוקדת של פיצול מחרוזת מבוססת על ערכים ובלבד מציינים היכן המחרוזת אמורה להיות מפוצלת. זה שימושי בעת בניית מודלים של פילוח משלך, כמו דוגמה לפילוח הקודם.

עבור SplitMergeTokenizer , ערך של 0 משמש כדי לציין את תחילתה של שרשרת חדשה, ואת הערך של 1 מציין את האופי הוא חלק המחרוזת הנוכחית.

strings = ["新华社北京"]
labels = [[0, 1, 1, 0, 1]]
tokenizer = tf_text.SplitMergeTokenizer()
tokens = tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

SplitMergeFromLogitsTokenizer דומה, אבל זה במקום מקבלת זוגות ערך logit מרשת עצבית כי לנבא אם כל תו יש לפצל מחרוזת חדשה או ימוזגו שהנוכחי.

strings = [["新华社北京"]]
labels = [[[5.0, -3.2], [0.2, 12.0], [0.0, 11.0], [2.2, -1.0], [-3.0, 3.0]]]
tokenizer = tf_text.SplitMergeFromLogitsTokenizer()
tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

RegexSplitter

RegexSplitter הוא מסוגל מחרוזות קטע ב נקודה עצירה שרירותיות שהוגדרו על ידי ביטוי רגולרי מסופק.

splitter = tf_text.RegexSplitter("\s")
tokens = splitter.split(["What you know you can't explain, but you feel it."], )
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

קיזוזים

בעת ביצוע אסימון מחרוזות, לעתים קרובות רצוי לדעת מאיפה במחרוזת המקורית מקורו של האסימון. מסיבה זו, כל tokenizer אשר מיישם TokenizerWithOffsets יש שיטה tokenize_with_offsets כי יחזיר את קיזוז בייט יחד עם אסימונים. ה-start_offsets מפרט את הבתים במחרוזת המקורית שבה כל אסימון מתחיל, וה-end_offsets מפרט את הבתים מיד אחרי הנקודה שבה כל אסימון מסתיים. כדי לנסח מחדש, קיזוז ההתחלה הם כולל וקיזוז הסיום הם בלעדיים.

tokenizer = tf_text.UnicodeScriptTokenizer()
(tokens, start_offsets, end_offsets) = tokenizer.tokenize_with_offsets(['Everything not saved will be lost.'])
print(tokens.to_list())
print(start_offsets.to_list())
print(end_offsets.to_list())
[[b'Everything', b'not', b'saved', b'will', b'be', b'lost', b'.']]
[[0, 11, 15, 21, 26, 29, 33]]
[[10, 14, 20, 25, 28, 33, 34]]

ביטול טוקן

Tokenizers אשר ליישם את Detokenizer לספק detokenize שיטה המנסה לשלב את המיתרים. יש לזה סיכוי להיות אובדן, אז ייתכן שהמחרוזת המפורקת לא תמיד תתאים בדיוק למחרוזת המקורית, שנוספה מראש.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
strings = tokenizer.detokenize(tokens)
print(strings.numpy())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]
[b"What you know you can't explain, but you feel it."]

TF Data

TF Data הוא API רב עוצמה ליצירת צינור קלט עבור מודלים להדרכה. Tokenizers עובדים כצפוי עם ה-API.

docs = tf.data.Dataset.from_tensor_slices([['Never tell me the odds.'], ["It's a trap!"]])
tokenizer = tf_text.WhitespaceTokenizer()
tokenized_docs = docs.map(lambda x: tokenizer.tokenize(x))
iterator = iter(tokenized_docs)
print(next(iterator).to_list())
print(next(iterator).to_list())
[[b'Never', b'tell', b'me', b'the', b'odds.']]
[[b"It's", b'a', b'trap!']]