ML Community Day คือวันที่ 9 พฤศจิกายน! ร่วมกับเราสำหรับการปรับปรุงจาก TensorFlow, JAX และอื่น ๆ เรียนรู้เพิ่มเติม

สตริง Unicode

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

บทนำ

โมเดล NLP มักจะจัดการภาษาต่างๆ ด้วยชุดอักขระที่ต่างกัน Unicode เป็นระบบการเข้ารหัสมาตรฐานที่ใช้ในการแสดงละครจากเกือบทุกภาษา ตัวละครทุกตัว Unicode จะถูกเข้ารหัสโดยใช้จำนวนเต็มไม่ซ้ำกัน จุดรหัส ระหว่าง 0 และ 0x10FFFF สตริง Unicode เป็นลำดับของศูนย์หรือมากกว่าจุดรหัส

บทช่วยสอนนี้แสดงวิธีแสดงสตริง Unicode ใน TensorFlow และจัดการโดยใช้ Unicode ที่เทียบเท่ากับ ops สตริงมาตรฐาน มันแยกสตริง Unicode เป็นโทเค็นตามการตรวจจับสคริปต์

import tensorflow as tf
import numpy as np

tf.string ชนิดข้อมูล

พื้นฐาน TensorFlow tf.string dtype ช่วยให้คุณสามารถสร้างเทนเซอร์ของสตริงไบต์ สตริง Unicode จะ UTF-8 เข้ารหัสโดยค่าเริ่มต้น

tf.constant(u"Thanks 😊")
2021-08-11 18:50:03.051591: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.060323: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.061297: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.062904: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-11 18:50:03.063621: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.064696: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.065596: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.699747: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.700774: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.701714: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:50:03.702652: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1510] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 14648 MB memory:  -> device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>

tf.string ถือว่าเมตริกซ์ byte สตริงเป็นหน่วยอะตอม ซึ่งช่วยให้สามารถจัดเก็บสตริงไบต์ที่มีความยาวต่างกันได้ ความยาวสตริงไม่รวมอยู่ในมิติเทนเซอร์

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

ถ้าคุณใช้งูหลามสตริงสร้างทราบว่า ตัวอักษรของสตริง จะ Unicode เข้ารหัสโดยค่าเริ่มต้น

เป็นตัวแทนของ 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 : แปลงสตริงเกลาเข้ารหัสเวกเตอร์ของจุดรหัส
  • tf.strings.unicode_encode : แปลงเวกเตอร์ของจุดรหัสไปยังเกลาสตริงเข้ารหัส
  • tf.strings.unicode_transcode : แปลงสตริงเกลาเข้ารหัสการเข้ารหัสที่แตกต่างกัน
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 กับ padding หรือ 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 op ยอมรับ 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 ยกเว้นว่ามันจะกลับเมตริกซ์ที่สองที่มีการเริ่มต้น offset ของตัวละครแต่ละตัว

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 ค่าสอดคล้องกับ ส่วนประกอบระหว่างประเทศเพื่อการ 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']]