דף זה תורגם על ידי Cloud Translation API.
Switch to English

מיתרי יוניקוד

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

מבוא

מודלים המעבדים שפה טבעית לעיתים קרובות מטפלים בשפות שונות עם ערכות תווים שונות. יוניקוד היא מערכת קידוד סטנדרטית המשמשת לייצוג אופי כמעט מכל השפות. כל תו מקודד באמצעות נקודת קוד שלם ייחודית בין 0 ל 0x10FFFF . מחרוזת Unicode היא רצף של אפס או יותר נקודות קוד.

מדריך זה מראה כיצד לייצג מחרוזות Unicode ב- TensorFlow ולתפעל אותם באמצעות מקבילות Unicode של אופציות מחרוזות סטנדרטיות. זה מפריד מחרוזות Unicode לאסימונים בהתבסס על זיהוי סקריפט.

import tensorflow as tf

סוג הנתונים tf.string

TensorFlow הבסיסי tf.string dtype מאפשר לך לבנות tensors של מחרוזות בייט. מחרוזות Unicode מקודדות כברירת מחדל ל- utf-8.

tf.constant(u"Thanks 😊")
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>

טנסור tf.string יכול להחזיק מיתרי בתים באורכים שונים מכיוון שמתייחסים למיתרי הבתים כיחידות אטומיות. אורך המיתר אינו כלול במידות הטנסור.

tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])

מייצג את יוניקוד

ישנן שתי דרכים סטנדרטיות לייצג מחרוזת Unicode ב- TensorFlow:

  • סקלר string - כאשר מקודדים את רצף נקודות הקוד באמצעות קידוד תווים ידוע.
  • וקטור int32 - כאשר כל עמדה מכילה נקודת קוד אחת.

לדוגמא, שלושת הערכים הבאים מייצגים את מחרוזת Unicode "语言处理" (שפירושה בסינית "עיבוד שפה"):

# Unicode string, represented as a UTF-8 encoded string scalar.
text_utf8 = tf.constant(u"语言处理")
text_utf8
<tf.Tensor: shape=(), dtype=string, numpy=b'\xe8\xaf\xad\xe8\xa8\x80\xe5\xa4\x84\xe7\x90\x86'>
# Unicode string, represented as a UTF-16-BE encoded string scalar.
text_utf16be = tf.constant(u"语言处理".encode("UTF-16-BE"))
text_utf16be
<tf.Tensor: shape=(), dtype=string, numpy=b'\x8b\xed\x8a\x00Y\x04t\x06'>
# Unicode string, represented as a vector of Unicode code points.
text_chars = tf.constant([ord(char) for char in u"语言处理"])
text_chars
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([35821, 35328, 22788, 29702], dtype=int32)>

המרה בין ייצוגים

TensorFlow מספק פעולות להמרה בין ייצוגים שונים אלה:

tf.strings.unicode_decode(text_utf8,
                          input_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([35821, 35328, 22788, 29702], dtype=int32)>
tf.strings.unicode_encode(text_chars,
                          output_encoding='UTF-8')
<tf.Tensor: shape=(), dtype=string, numpy=b'\xe8\xaf\xad\xe8\xa8\x80\xe5\xa4\x84\xe7\x90\x86'>
tf.strings.unicode_transcode(text_utf8,
                             input_encoding='UTF8',
                             output_encoding='UTF-16-BE')
<tf.Tensor: shape=(), dtype=string, numpy=b'\x8b\xed\x8a\x00Y\x04t\x06'>

מידות אצווה

בעת פענוח מחרוזות מרובות, מספר התווים בכל מחרוזת לא יכול להיות שווה. תוצאת ההחזרה היא tf.RaggedTensor , כאשר אורך הממד הפנימי ביותר משתנה בהתאם למספר התווים בכל מחרוזת:

# A batch of Unicode strings, each represented as a UTF8-encoded string.
batch_utf8 = [s.encode('UTF-8') for s in
              [u'hÃllo',  u'What is the weather tomorrow',  u'Göödnight', u'😊']]
batch_chars_ragged = tf.strings.unicode_decode(batch_utf8,
                                               input_encoding='UTF-8')
for sentence_chars in batch_chars_ragged.to_list():
  print(sentence_chars)
[104, 195, 108, 108, 111]
[87, 104, 97, 116, 32, 105, 115, 32, 116, 104, 101, 32, 119, 101, 97, 116, 104, 101, 114, 32, 116, 111, 109, 111, 114, 114, 111, 119]
[71, 246, 246, 100, 110, 105, 103, 104, 116]
[128522]

אתה יכול להשתמש ב-tf.RaggedTensor ישירות, או להמיר אותו ל- tf.Tensor צפוף עם ריפוד או tf.SparseTensor בשיטות tf.RaggedTensor.to_tensor ו- tf.RaggedTensor.to_sparse .

batch_chars_padded = batch_chars_ragged.to_tensor(default_value=-1)
print(batch_chars_padded.numpy())
[[   104    195    108    108    111     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]
 [    87    104     97    116     32    105    115     32    116    104
     101     32    119    101     97    116    104    101    114     32
     116    111    109    111    114    114    111    119]
 [    71    246    246    100    110    105    103    104    116     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]
 [128522     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1     -1     -1
      -1     -1     -1     -1     -1     -1     -1     -1]]

batch_chars_sparse = batch_chars_ragged.to_sparse()

בעת קידוד מחרוזות מרובות באורכים זהים, ניתן להשתמש ב- tf.Tensor כקלט:

tf.strings.unicode_encode([[99, 97, 116], [100, 111, 103], [ 99, 111, 119]],
                          output_encoding='UTF-8')
<tf.Tensor: shape=(3,), dtype=string, numpy=array([b'cat', b'dog', b'cow'], dtype=object)>

בעת קידוד מחרוזות מרובות באורך משתנה, יש להשתמש ב-tf.RaggedTensor כקלט:

tf.strings.unicode_encode(batch_chars_ragged, output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>

אם יש לך טנסור עם מספר מיתרים בפורמט מרופד או דליל, המיר אותו ל-tf.RaggedTensor לפני שאתה קורא ל- unicode_encode :

tf.strings.unicode_encode(
    tf.RaggedTensor.from_sparse(batch_chars_sparse),
    output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>
tf.strings.unicode_encode(
    tf.RaggedTensor.from_tensor(batch_chars_padded, padding=-1),
    output_encoding='UTF-8')
<tf.Tensor: shape=(4,), dtype=string, numpy=
array([b'h\xc3\x83llo', b'What is the weather tomorrow',
       b'G\xc3\xb6\xc3\xb6dnight', b'\xf0\x9f\x98\x8a'], dtype=object)>

פעולות יוניקוד

אורך הדמות

הפעולה tf.strings.length כוללת unit פרמטר המציינת כיצד יש לחשב אורכים. ברירת המחדל של unit היא "BYTE" , אך ניתן להגדיר אותה לערכים אחרים, כגון "UTF8_CHAR" או "UTF16_CHAR" , כדי לקבוע את מספר נקודות ה- Unicode בכל string מקודדת.

# Note that the final character takes up 4 bytes in UTF8.
thanks = u'Thanks 😊'.encode('UTF-8')
num_bytes = tf.strings.length(thanks).numpy()
num_chars = tf.strings.length(thanks, unit='UTF8_CHAR').numpy()
print('{} bytes; {} UTF-8 characters'.format(num_bytes, num_chars))
11 bytes; 8 UTF-8 characters

משטחי אופי

באופן דומה, פעולת tf.strings.substr מקבלת את הפרמטר " unit ", ומשתמשת בו כדי לקבוע איזה סוג קיזוז מכילים פרמטרים " pos " ו- " len ".

# default: unit='BYTE'. With len=1, we return a single byte.
tf.strings.substr(thanks, pos=7, len=1).numpy()
b'\xf0'
# Specifying unit='UTF8_CHAR', we return a single character, which in this case
# is 4 bytes.
print(tf.strings.substr(thanks, pos=7, len=1, unit='UTF8_CHAR').numpy())
b'\xf0\x9f\x98\x8a'

פיצול מיתרי Unicode

הפעולה tf.strings.unicode_split מחרוזות unicode tf.strings.unicode_split של תווים בודדים:

tf.strings.unicode_split(thanks, 'UTF-8').numpy()
array([b'T', b'h', b'a', b'n', b'k', b's', b' ', b'\xf0\x9f\x98\x8a'],
      dtype=object)

קיזוז בתים לדמויות

כדי ליישר את טנסור התווים שנוצר על ידי tf.strings.unicode_decode למחרוזת המקורית, כדאי לדעת את הקיזוז למקום בו כל דמות מתחילה. השיטה tf.strings.unicode_decode_with_offsets דומה ל- unicode_decode , אלא שהיא מחזירה טנסור שני המכיל את קיזוז ההתחלה של כל תו.

codepoints, offsets = tf.strings.unicode_decode_with_offsets(u"🎈🎉🎊", 'UTF-8')

for (codepoint, offset) in zip(codepoints.numpy(), offsets.numpy()):
  print("At byte offset {}: codepoint {}".format(offset, codepoint))
At byte offset 0: codepoint 127880
At byte offset 4: codepoint 127881
At byte offset 8: codepoint 127882

סקריפטים של Unicode

כל נקודת קוד של Unicode שייכת לאוסף יחיד של נקודות קוד המכונה סקריפט . סקריפט של דמות מועיל בקביעת השפה שבה הדמות עשויה להיות. לדוגמה, הידיעה ש- "Б" נמצאת בכתב קירילי מעידה על כך שטקסט מודרני המכיל תו זה הוא ככל הנראה משפה סלאבית כמו רוסית או אוקראינית.

TensorFlow מספק את פעולת tf.strings.unicode_script כדי לקבוע באיזה סקריפט משתמש קוד נקודתי. קודי סקריפט הם int32 ערכים מתאימים רכיבים בינלאומיים Unicode (ICU) UScriptCode ערכים.

uscript = tf.strings.unicode_script([33464, 1041])  # ['芸', 'Б']

print(uscript.numpy())  # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17  8]

ניתן tf.strings.unicode_script פעולת tf.strings.unicode_script גם על tf.Tensor s אוtf.RaggedTensor s של נקודות tf.Tensor רב ממדיות:

print(tf.strings.unicode_script(batch_chars_ragged))
<tf.RaggedTensor [[25, 25, 25, 25, 25], [25, 25, 25, 25, 0, 25, 25, 0, 25, 25, 25, 0, 25, 25, 25, 25, 25, 25, 25, 0, 25, 25, 25, 25, 25, 25, 25, 25], [25, 25, 25, 25, 25, 25, 25, 25, 25], [0]]>

דוגמה: פילוח פשוט

פילוח הוא המשימה לפצל טקסט ליחידות דמויי מילה. לרוב זה קל כאשר משתמשים בתווי חלל כדי להפריד בין מילים, אך שפות מסוימות (כמו סינית ויפנית) אינן משתמשות ברווחים, ויש שפות (כמו גרמנית) המכילות תרכובות ארוכות שיש לפצל על מנת לנתח את משמעותן. בטקסט אינטרנט, שפות וסקריפטים שונים מעורבבים לעיתים קרובות יחד, כמו "NY 株 価" (הבורסה לניירות ערך בניו יורק).

אנו יכולים לבצע פילוח מחוספס מאוד (מבלי ליישם מודלים של ML) על ידי שימוש בשינויים בסקריפט בכדי לקרב את גבולות המילה. זה יעבוד על מיתרים כמו למשל "NY NY 価" לעיל. זה יעבוד גם עבור רוב השפות המשתמשות ברווחים, מכיוון שתווי הרווח של סקריפטים שונים מסווגים כ- USCRIPT_COMMON, קוד סקריפט מיוחד השונה מזה של כל טקסט בפועל.

# dtype: string; shape: [num_sentences]
#
# The sentences to process.  Edit this line to try out different inputs!
sentence_texts = [u'Hello, world.', u'世界こんにちは']

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

# dtype: int32; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_codepoint[i, j] is the codepoint for the j'th character in
# the i'th sentence.
sentence_char_codepoint = tf.strings.unicode_decode(sentence_texts, 'UTF-8')
print(sentence_char_codepoint)

# dtype: int32; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_scripts[i, j] is the unicode script of the j'th character in
# the i'th sentence.
sentence_char_script = tf.strings.unicode_script(sentence_char_codepoint)
print(sentence_char_script)
<tf.RaggedTensor [[72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 46], [19990, 30028, 12371, 12435, 12395, 12385, 12399]]>
<tf.RaggedTensor [[25, 25, 25, 25, 25, 0, 0, 25, 25, 25, 25, 25, 0], [17, 17, 20, 20, 20, 20, 20]]>

לאחר מכן, אנו משתמשים במזהים אלה של סקריפטים כדי לקבוע היכן יש להוסיף גבולות מילים. אנו מוסיפים גבול מילים בתחילת כל משפט, ולכל דמות שהתסריט שלה שונה מהדמות הקודמת:

# dtype: bool; shape: [num_sentences, (num_chars_per_sentence)]
#
# sentence_char_starts_word[i, j] is True if the j'th character in the i'th
# sentence is the start of a word.
sentence_char_starts_word = tf.concat(
    [tf.fill([sentence_char_script.nrows(), 1], True),
     tf.not_equal(sentence_char_script[:, 1:], sentence_char_script[:, :-1])],
    axis=1)

# dtype: int64; shape: [num_words]
#
# word_starts[i] is the index of the character that starts the i'th word (in
# the flattened list of characters from all sentences).
word_starts = tf.squeeze(tf.where(sentence_char_starts_word.values), axis=1)
print(word_starts)
tf.Tensor([ 0  5  7 12 13 15], shape=(6,), dtype=int64)

לאחר מכן נוכל להשתמש RaggedTensor ההתחלה הללו כדי לבנות RaggedTensor המכיל את רשימת המילים מכל האצוות:

# dtype: int32; shape: [num_words, (num_chars_per_word)]
#
# word_char_codepoint[i, j] is the codepoint for the j'th character in the
# i'th word.
word_char_codepoint = tf.RaggedTensor.from_row_starts(
    values=sentence_char_codepoint.values,
    row_starts=word_starts)
print(word_char_codepoint)
<tf.RaggedTensor [[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46], [19990, 30028], [12371, 12435, 12395, 12385, 12399]]>

ולבסוף, אנו יכולים לחלק את המילה RaggedTensor נקודות RaggedTensor בחזרה למשפטים:

# dtype: int64; shape: [num_sentences]
#
# sentence_num_words[i] is the number of words in the i'th sentence.
sentence_num_words = tf.reduce_sum(
    tf.cast(sentence_char_starts_word, tf.int64),
    axis=1)

# dtype: int32; shape: [num_sentences, (num_words_per_sentence), (num_chars_per_word)]
#
# sentence_word_char_codepoint[i, j, k] is the codepoint for the k'th character
# in the j'th word in the i'th sentence.
sentence_word_char_codepoint = tf.RaggedTensor.from_row_lengths(
    values=word_char_codepoint,
    row_lengths=sentence_num_words)
print(sentence_word_char_codepoint)
<tf.RaggedTensor [[[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46]], [[19990, 30028], [12371, 12435, 12395, 12385, 12399]]]>

כדי להקל על הקריאה בתוצאה הסופית, אנו יכולים לקודד אותה בחזרה למחרוזות UTF-8:

tf.strings.unicode_encode(sentence_word_char_codepoint, 'UTF-8').to_list()
[[b'Hello', b', ', b'world', b'.'],
 [b'\xe4\xb8\x96\xe7\x95\x8c',
  b'\xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf']]