سلاسل يونيكود

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

مقدمة

غالبًا ما تتعامل نماذج البرمجة اللغوية العصبية مع لغات مختلفة بمجموعات أحرف مختلفة. يونيكود هو نظام التشفير القياسية التي تستخدم لتمثيل الحروف من اللغات كلها تقريبا. يتم ترميز حرف كل يونيكود باستخدام عدد صحيح فريد نقطة رمز بين 0 و 0x10FFFF . سلسلة A يونيكود هي سلسلة من الصفر أو المزيد من النقاط التعليمات البرمجية.

يوضح هذا البرنامج التعليمي كيفية تمثيل سلاسل Unicode في TensorFlow ومعالجتها باستخدام معادلات Unicode من عمليات السلسلة القياسية. يفصل سلاسل Unicode إلى رموز بناءً على اكتشاف البرنامج النصي.

import tensorflow as tf
import numpy as np

و tf.string نوع البيانات

وTensorFlow الأساسي tf.string dtype يسمح لك لبناء التنسورات سلاسل بايت. سلاسل يونيكود هي UTF-8 المشفرة بشكل افتراضي.

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

A tf.string يعامل موتر بايت سلاسل وحدات ذرية. وهذا يمكّنها من تخزين سلاسل البايت ذات الأطوال المتفاوتة. لم يتم تضمين طول السلسلة في أبعاد الموتر.

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

إذا كنت تستخدم بايثون إلى سلاسل بناء، علما بأن سلسلة حرفية هي يونيكود ترميز افتراضيا.

تمثل Unicode

هناك طريقتان قياسيتان لتمثيل سلسلة 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)>

عمليات يونيكود

طول الحرف

استخدام 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'

انقسام سلاسل يونيكود

و 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 تنتمي إلى مجموعة واحدة من codepoints يعرف النصي . يعد نص الحرف مفيدًا في تحديد اللغة التي قد تكون بها الشخصية. على سبيل المثال ، معرفة أن "Б" مكتوبة بخط سيريلي يشير إلى أن النص الحديث الذي يحتوي على هذا الحرف من المحتمل أن يكون من لغة سلافية مثل الروسية أو الأوكرانية.

يوفر TensorFlow و tf.strings.unicode_script عملية لتحديد أي سيناريو معين الاستخدامات التمثيل البرمجي. رموز النصي هي int32 القيم المقابلة ل مكونات الدولي لليونيكود (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']]