String Unicode

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

pengantar

Model NLP sering menangani bahasa yang berbeda dengan set karakter yang berbeda. Unicode adalah sistem pengkodean standar yang digunakan untuk mewakili karakter dari hampir semua bahasa. Karakter setiap Unicode dikodekan menggunakan bilangan bulat yang unik titik kode antara 0 dan 0x10FFFF . Sebuah Unicode string adalah urutan nol atau lebih poin kode.

Tutorial ini menunjukkan cara merepresentasikan string Unicode di TensorFlow dan memanipulasinya menggunakan operasi string standar yang setara dengan Unicode. Ini memisahkan string Unicode menjadi token berdasarkan deteksi skrip.

import tensorflow as tf
import numpy as np

The tf.string tipe data

Dasar TensorFlow tf.string dtype memungkinkan Anda untuk membangun tensor string byte. String Unicode yang utf-8 dikodekan secara default.

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

Sebuah tf.string memperlakukan tensor byte string sebagai unit atom. Ini memungkinkannya untuk menyimpan string byte dengan panjang yang bervariasi. Panjang senar tidak termasuk dalam dimensi tensor.

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

Jika Anda menggunakan Python untuk string konstruk, catatan bahwa string literal adalah Unicode-encoded secara default.

Mewakili Unicode

Ada dua cara standar untuk merepresentasikan string Unicode di TensorFlow:

  • string skalar - di mana urutan poin kode dikodekan menggunakan diketahui pengkodean karakter .
  • int32 vector - di mana setiap posisi berisi titik kode tunggal.

Misalnya, tiga nilai berikut semua mewakili string Unicode "语言处理" (yang berarti "pemrosesan bahasa" dalam bahasa Cina):

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

Mengonversi antar representasi

TensorFlow menyediakan operasi untuk mengonversi antara berbagai representasi berikut:

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

Dimensi batch

Saat mendekode beberapa string, jumlah karakter di setiap string mungkin tidak sama. Hasil pengembalian adalah tf.RaggedTensor , dimana panjang dimensi terdalam bervariasi tergantung pada jumlah karakter dalam setiap string.

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

Anda dapat menggunakan ini tf.RaggedTensor langsung, atau mengubahnya menjadi padat tf.Tensor dengan bantalan atau tf.SparseTensor menggunakan metode tf.RaggedTensor.to_tensor dan 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,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]]

Ketika pengkodean beberapa string dengan panjang yang sama, menggunakan tf.Tensor sebagai masukan.

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

Ketika pengkodean beberapa string dengan panjang bervariasi, menggunakan tf.RaggedTensor sebagai masukan.

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

Jika Anda memiliki tensor dengan beberapa string dalam format empuk atau jarang, mengubahnya pertama menjadi tf.RaggedTensor sebelum memanggil 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)>

Operasi Unicode

Panjang karakter

Gunakan unit parameter dari tf.strings.length op untuk menunjukkan bagaimana panjang karakter harus dihitung. unit default ke "BYTE" , tetapi dapat diatur untuk nilai-nilai lain, seperti "UTF8_CHAR" atau "UTF16_CHAR" , untuk menentukan jumlah codepoints Unicode di setiap string disandikan.

# 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

Substring karakter

The tf.strings.substr op menerima unit parameter, dan menggunakannya untuk menentukan jenis offset pos dan len paremeters mengandung.

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

Pisahkan string Unicode

The tf.strings.unicode_split operasi membagi string unicode ke substring dari karakter individu.

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)

Offset byte untuk karakter

Untuk menyelaraskan tensor karakter yang dihasilkan oleh tf.strings.unicode_decode dengan string asli, ini berguna untuk mengetahui offset untuk di mana masing-masing karakter dimulai. Metode tf.strings.unicode_decode_with_offsets mirip dengan unicode_decode , kecuali bahwa ia mengembalikan tensor kedua berisi awal offset masing-masing karakter.

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

Skrip Unicode

Setiap titik kode Unicode milik koleksi tunggal codepoints dikenal sebagai naskah . Skrip karakter sangat membantu dalam menentukan bahasa yang digunakan karakter tersebut. Misalnya, mengetahui bahwa 'Б' dalam skrip Cyrillic menunjukkan bahwa teks modern yang berisi karakter tersebut kemungkinan berasal dari bahasa Slavia seperti Rusia atau Ukraina.

TensorFlow menyediakan tf.strings.unicode_script operasi untuk menentukan script yang diberikan menggunakan codepoint. Kode Script yang int32 nilai-nilai yang sesuai dengan Komponen Internasional untuk Unicode (ICU) UScriptCode nilai-nilai.

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

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

The tf.strings.unicode_script operasi juga dapat diterapkan untuk multidimensi tf.Tensor s atau tf.RaggedTensor s dari 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]]>

Contoh: Segmentasi sederhana

Segmentasi adalah tugas memecah teks menjadi unit-unit seperti kata. Ini seringkali mudah ketika karakter spasi digunakan untuk memisahkan kata, tetapi beberapa bahasa (seperti Cina dan Jepang) tidak menggunakan spasi, dan beberapa bahasa (seperti Jerman) mengandung senyawa panjang yang harus dipisahkan untuk menganalisis artinya. Dalam teks web, bahasa dan skrip yang berbeda sering dicampur bersama, seperti dalam "NY株価" (Bursa Saham New York).

Kami dapat melakukan segmentasi yang sangat kasar (tanpa menerapkan model ML apa pun) dengan menggunakan perubahan skrip untuk memperkirakan batas kata. Ini akan bekerja untuk string seperti contoh "NY株価" di atas. Ini juga akan berfungsi untuk sebagian besar bahasa yang menggunakan spasi, karena karakter spasi dari berbagai skrip semuanya diklasifikasikan sebagai USCRIPT_COMMON, kode skrip khusus yang berbeda dari teks sebenarnya.

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

Pertama, dekode kalimat menjadi titik kode karakter, dan temukan pengidentifikasi skrip untuk setiap karakter.

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

Gunakan pengidentifikasi skrip untuk menentukan di mana batas kata harus ditambahkan. Tambahkan batas kata di awal setiap kalimat, dan untuk setiap karakter yang skripnya berbeda dari karakter sebelumnya.

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

Anda kemudian dapat menggunakan mereka offset awal untuk membangun RaggedTensor yang berisi daftar kata-kata dari semua batch.

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

Untuk finish, segmen kata codepoints RaggedTensor kembali ke kalimat dan encode ke UTF-8 string untuk dibaca.

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