رشته های یونیکد

با مجموعه‌ها، منظم بمانید ذخیره و دسته‌بندی محتوا براساس اولویت‌های شما.

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

معرفی

مدل های NLP اغلب زبان های مختلف را با مجموعه کاراکترهای مختلف مدیریت می کنند. یونیکد را پشتیبانی می کند یک سیستم استاندارد است که برای نشان دادن شخصیتهای تقریبا از همه زبان ها است. شخصیت هر یونیکد با استفاده از یک عدد صحیح منحصر به فرد کد گذاری شده است نقطه کد بین 0 و 0x10FFFF . رشته یونیکد دنباله ای از صفر یا چند نقطه فعال است.

این آموزش نحوه نمایش رشته های یونیکد در TensorFlow و دستکاری آنها را با استفاده از معادل های یونیکد عملیات رشته استاندارد نشان می دهد. رشته های یونیکد را بر اساس تشخیص اسکریپت به توکن ها جدا می کند.

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'>

tf.string رفتار تانسور بایت رشته را به عنوان واحد اتمی است. این به آن امکان می دهد رشته های بایتی با طول های مختلف را ذخیره کند. طول رشته در ابعاد تانسور گنجانده نشده است.

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

اگر شما استفاده از پایتون به رشته سازه، توجه داشته باشید که رشته هستند یونیکد کد گذاری به طور پیش فرض.

نمایندگی یونیکد

دو روش استاندارد برای نمایش یک رشته یونیکد در TensorFlow وجود دارد:

  • string اسکالر - که در آن دنباله ای از نقاط با استفاده از یک کد شناخته شده کد گذاری شده است رمزگذاری کاراکتر .
  • int32 بردار - که در آن هر یک از موقعیت شامل یک نقطه کد تک.

به عنوان مثال، سه مقدار زیر همه نشان دهنده رشته یونیکد "语言处理" (که به معنی "پردازش زبان" در زبان چینی):

# 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 یونیکد در هر رشته کد گذاری.

# 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

اسکریپت های یونیکد

هر نقطه کد یونیکد متعلق به یک مجموعه واحد از codepoints شناخته شده به عنوان یک اسکریپت . خط یک کاراکتر در تعیین زبانی که ممکن است کاراکتر باشد کمک کننده است. برای مثال، دانستن اینکه "B" به خط سیریلیک است، نشان می دهد که متن مدرن حاوی آن کاراکتر احتمالاً از زبان اسلاوی مانند روسی یا اوکراینی است.

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