Chuỗi Unicode

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

Giới thiệu

Các mô hình NLP thường xử lý các ngôn ngữ khác nhau với các bộ ký tự khác nhau. Unicode là một hệ thống mã hóa tiêu chuẩn được sử dụng để đại diện cho các nhân vật trong hầu hết các ngôn ngữ. Nhân vật mỗi Unicode được mã hóa bằng một số nguyên duy nhất điểm mã giữa 00x10FFFF . Một Unicode chuỗi là một chuỗi các số không hay nhiều điểm mã.

Hướng dẫn này chỉ ra cách biểu diễn các chuỗi Unicode trong TensorFlow và thao tác với chúng bằng cách sử dụng các mã Unicode tương đương với các mã chuỗi chuẩn. Nó phân tách các chuỗi Unicode thành các mã thông báo dựa trên việc phát hiện tập lệnh.

import tensorflow as tf
import numpy as np

Các tf.string kiểu dữ liệu

Các cơ bản TensorFlow tf.string dtype phép bạn xây dựng tensors các chuỗi byte. Chuỗi Unicode là utf-8 mã hóa theo mặc định.

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

Một tf.string xử lý tensor byte chuỗi như đơn vị nguyên tử. Điều này cho phép nó lưu trữ các chuỗi byte có độ dài khác nhau. Chiều dài chuỗi không được bao gồm trong kích thước tensor.

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

Nếu bạn sử dụng Python để chuỗi xây dựng, lưu ý rằng literals chuỗi là Unicode được mã hóa theo mặc định.

Biểu diễn Unicode

Có hai cách tiêu chuẩn để biểu diễn một chuỗi Unicode trong TensorFlow:

  • string vô hướng - nơi chuỗi các điểm mã được mã hóa bằng cách sử dụng biết mã hóa ký tự .
  • int32 vector - nơi mỗi vị trí chứa một điểm mã duy nhất.

Ví dụ, ba giá trị sau tất cả đại diện cho chuỗi Unicode "语言处理" (mà có nghĩa là "xử lý ngôn ngữ" trong tiếng Trung Quốc):

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

Chuyển đổi giữa các đại diện

TensorFlow cung cấp các hoạt động để chuyển đổi giữa các biểu diễn khác nhau này:

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

Kích thước lô

Khi giải mã nhiều chuỗi, số ký tự trong mỗi chuỗi có thể không bằng nhau. Kết quả trả về là một tf.RaggedTensor , nơi chiều dài chiều trong cùng khác nhau tùy thuộc vào số lượng ký tự trong mỗi chuỗi.

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

Bạn có thể sử dụng này tf.RaggedTensor trực tiếp, hoặc chuyển nó sang một dày đặc tf.Tensor với đệm hoặc một tf.SparseTensor bằng cách sử dụng phương pháp tf.RaggedTensor.to_tensortf.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,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]]

Khi mã hóa nhiều chuỗi với độ dài tương tự, sử dụng một tf.Tensor như đầu vào.

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

Khi mã hóa nhiều chuỗi có chiều dài khác nhau, sử dụng một tf.RaggedTensor như đầu vào.

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

Nếu bạn có một tensor với nhiều chuỗi trong định dạng đệm hoặc thưa thớt, chuyển đổi nó lần đầu tiên vào một tf.RaggedTensor trước khi gọi 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)>

Hoạt động Unicode

Độ dài ký tự

Sử dụng các unit tham số của tf.strings.length op để chỉ cách độ dài vật cần được tính. unit mặc định là "BYTE" , nhưng nó có thể được thiết lập để các giá trị khác, chẳng hạn như "UTF8_CHAR" hoặc "UTF16_CHAR" , để xác định số lượng codepoints Unicode trong mỗi chuỗi được mã hóa.

# 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

Chuỗi ký tự con

Các tf.strings.substr op chấp nhận unit tham số, và sử dụng nó để xác định loại offsets các poslen paremeters chứa.

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

Chia chuỗi Unicode

Các tf.strings.unicode_split hoạt động chia tách chuỗi unicode vào chuỗi con của các nhân vật cá nhân.

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)

Hiệu số byte cho các ký tự

Để sắp xếp các tensor nhân vật được tạo ra bởi tf.strings.unicode_decode với chuỗi gốc, nó là hữu ích để biết bù đắp cho nơi mà mỗi nhân vật bắt đầu. Phương pháp tf.strings.unicode_decode_with_offsets cũng tương tự như unicode_decode , ngoại trừ việc nó trả về một tensor thứ hai chứa đầu bù đắp của mỗi nhân vật.

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

Tập lệnh Unicode

Mỗi mã Unicode điểm thuộc về một bộ sưu tập duy nhất của codepoints biết đến như một kịch bản . Chữ viết của một ký tự rất hữu ích trong việc xác định ngôn ngữ mà nhân vật có thể sử dụng. Ví dụ: khi biết rằng 'Б' là trong hệ thống chữ viết Kirin cho biết rằng văn bản hiện đại chứa ký tự đó có thể là từ ngôn ngữ Slav như tiếng Nga hoặc tiếng Ukraina.

TensorFlow cung cấp tf.strings.unicode_script hoạt động để xác định kịch bản một dụng điểm mã nhất định. Các mã kịch bản là int32 giá trị tương ứng với các thành phần quốc tế Unicode (ICU) UScriptCode giá trị.

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

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

Các tf.strings.unicode_script hoạt động cũng có thể được áp dụng cho đa chiều tf.Tensor s hoặc tf.RaggedTensor s của 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]]>

Ví dụ: Phân đoạn đơn giản

Phân đoạn là nhiệm vụ chia nhỏ văn bản thành các đơn vị giống như từ. Điều này thường dễ dàng khi các ký tự khoảng trắng được sử dụng để phân tách các từ, nhưng một số ngôn ngữ (như tiếng Trung và tiếng Nhật) không sử dụng dấu cách và một số ngôn ngữ (như tiếng Đức) chứa các hợp chất dài phải được tách ra để phân tích ý nghĩa của chúng. Trong văn bản web, các ngôn ngữ và chữ viết khác nhau thường được trộn lẫn với nhau, như trong "NY 株 価" (Sở giao dịch chứng khoán New York).

Chúng tôi có thể thực hiện phân đoạn rất thô (mà không thực hiện bất kỳ mô hình ML nào) bằng cách sử dụng các thay đổi trong tập lệnh để ước lượng ranh giới từ. Điều này sẽ hoạt động đối với các chuỗi như ví dụ "NY 株 価" ở trên. Nó cũng sẽ hoạt động đối với hầu hết các ngôn ngữ sử dụng dấu cách, vì các ký tự khoảng trắng của các tập lệnh khác nhau đều được phân loại là USCRIPT_COMMON, một mã tập lệnh đặc biệt khác với mã của bất kỳ văn bản thực tế nào.

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

Đầu tiên, hãy giải mã các câu thành các điểm mã ký tự và tìm mã nhận dạng tập lệnh cho mỗi ký tự.

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

Sử dụng các mã nhận dạng tập lệnh để xác định vị trí các ranh giới từ sẽ được thêm vào. Thêm ranh giới từ ở đầu mỗi câu và cho mỗi ký tự có chữ viết khác với ký tự trước đó.

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

Sau đó bạn có thể sử dụng những hiệu số bắt đầu xây dựng một RaggedTensor chứa danh sách các từ từ tất cả các lô.

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

Để kết thúc, phân khúc từ codepoints RaggedTensor trở thành câu và mã hóa thành UTF 8-strings cho dễ đọc.

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