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

מחרוזות Unicode

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

מבוא

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

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

import tensorflow as tf
import numpy as np

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])

אם אתה משתמש Python למחרוזות מבנה, פתק כי literals מחרוזת הם יוניקוד בקידוד כברירת מחדל.

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

ישנן שתי דרכים סטנדרטיות לייצוג מחרוזת 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()

nrows, ncols = batch_chars_sparse.dense_shape.numpy()
elements = [['_' for i in range(ncols)] for j in range(nrows)]
for (row, col), value in zip(batch_chars_sparse.indices.numpy(), batch_chars_sparse.values.numpy()):
  elements[row][col] = str(value)
# max_width = max(len(value) for row in elements for value in row)
value_lengths = []
for row in elements:
  for value in row:
    value_lengths.append(len(value))
max_width = max(value_lengths)
print('[%s]' % '\n '.join(
    '[%s]' % ', '.join(value.rjust(max_width) for value in row)
    for row in elements))
[[   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.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 לפני פניית tf.strings.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)>

פעולות Unicode

אורך הדמות

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

# 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 paremeters מכילים.

# Here, unit='BYTE' (default). Returns a single byte with len=1
tf.strings.substr(thanks, pos=7, len=1).numpy()
b'\xf0'
# Specifying unit='UTF8_CHAR', returns a single 4 byte character in this case
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(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 שייכת לאוסף יחיד של codePoints ידוע בתור סקריפט . התסריט של דמות מועיל בקביעה באיזו שפה הדמות עשויה להימצא. לדוגמה, הידיעה ש-'Б' הוא בכתב קירילי מצביע על כך שטקסט מודרני המכיל את הדמות הזו כנראה משפה סלאבית כמו רוסית או אוקראינית.

TensorFlow מספק את tf.strings.unicode_script פעולה כדי לקבוע איזה תסריט שימושים codepoint נתון. קודי סקריפט הם 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.Tensor ים או tf.RaggedTensor ים של codePoints:

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株価" למעלה. זה יעבוד גם עבור רוב השפות המשתמשות ברווחים, שכן תווי הרווח של סקריפטים שונים מסווגים כולם כ-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 המכיל את רשימת המילים מכל הקבוצות.

# 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]]>

ועד סופו, קטע את המילה codePoints RaggedTensor בחזרה לתוך משפטים לקודד לתוך UTF-8 מחרוזות עבור הקריאות.

# 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.strings.unicode_encode(sentence_word_char_codepoint, 'UTF-8').to_list()
<tf.RaggedTensor [[[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46]], [[19990, 30028], [12371, 12435, 12395, 12385, 12399]]]>
[[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']]