Unicode-Strings

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Einführung

NLP-Modelle verarbeiten oft verschiedene Sprachen mit unterschiedlichen Zeichensätzen. Unicode ist ein Standard - Verschlüsselungssystem , das verwendet wird Zeichen aus nahezu allen Sprachen darzustellen. Jeder Unicode - Zeichen codiert ist einen eindeutigen ganze Zahl unter Verwendung von Codepunkt zwischen 0 und 0x10FFFF . Eine Unicode - Zeichenfolge ist eine Sequenz von Null oder mehreren Codepunkten.

Dieses Tutorial zeigt, wie Sie Unicode-Strings in TensorFlow darstellen und mit Unicode-Äquivalenten von Standard-String-Ops bearbeiten. Es trennt Unicode-Strings basierend auf der Skripterkennung in Token.

import tensorflow as tf
import numpy as np

Der tf.string Datentyp

Der Grund TensorFlow tf.string dtype kann Sie Tensoren Byte - Strings bauen. Unicode - Strings sind utf-8 standardmäßig verschlüsselt.

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

Ein tf.string Tensor behandelt Bytefolgen als atomare Einheiten. Dies ermöglicht es, Byte-Strings unterschiedlicher Länge zu speichern. Die Saitenlänge ist nicht in den Tensorabmessungen enthalten.

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

Wenn Sie Python zu konstruieren Strings verwenden, beachten Sie, dass Zeichenketten sind Unicode-codierte standardmäßig.

Stellvertretend für Unicode

Es gibt zwei Standardmethoden, um einen Unicode-String in TensorFlow darzustellen:

  • string skalaren - wobei die Sequenz von Codepunkten codiert ist eine bekannte Verwendung von Zeichencodierung .
  • int32 vector - wobei jede Position einen einzigen Codepunkt enthält.

Zum Beispiel stellen die folgenden drei Werte alle Unicode - String "语言处理" (das bedeutet „Sprachverarbeitung“ in Chinesisch):

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

Konvertieren zwischen Darstellungen

TensorFlow bietet Operationen zum Konvertieren zwischen diesen verschiedenen Darstellungen:

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

Chargenabmessungen

Beim Decodieren mehrerer Zeichenfolgen kann die Anzahl der Zeichen in jeder Zeichenfolge nicht gleich sein. Das Rücker ist ein tf.RaggedTensor , wobei die innerste Dimension Länge variiert in jeder Saite auf der Anzahl von Zeichen , abhängig.

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

Sie können diese verwenden tf.RaggedTensor direkt oder es zu einem dichten umwandeln tf.Tensor mit Polsterung oder ein tf.SparseTensor unter Verwendung der Verfahren tf.RaggedTensor.to_tensor und 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,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _,      _]]

Wenn mehrere Zeichenketten mit den gleichen Längen codieren, eine Verwendung tf.Tensor als Eingang.

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

Wenn mehrere Zeichenfolgen codiert , mit unterschiedlicher Länge, mit einem tf.RaggedTensor als Eingang.

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

Wenn Sie einen Tensor mit mehreren Strings in gepolstertem oder spärlichen Format haben, wandelt sie zunächst in eine tf.RaggedTensor vor dem Aufruf 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-Operationen

Zeichenlänge

Verwenden Sie das unit Parameter des tf.strings.length op um anzuzeigen , wie Zeichenlängen berechnet werden sollen. unit standardmäßig "BYTE" , aber es kann auf andere Werte, wie beispielsweise festgelegt werden "UTF8_CHAR" oder "UTF16_CHAR" , die Anzahl der Unicode - Codepunkte in jeder codierte Zeichenfolge zu bestimmen.

# 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

Zeichen-Teilstrings

Die tf.strings.substr op nimmt die unit Parameter und verwendet es , um zu bestimmen , welche Art von Versetzungen der pos und len paremeters enthalten.

# 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-Strings aufteilen

Der tf.strings.unicode_split Betrieb teilt Unicode - Strings in Teil einzelner Zeichen.

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)

Byte-Offsets für Zeichen

Um den Charakter Tensor durch erzeugte ausrichten tf.strings.unicode_decode mit der ursprünglichen Zeichenfolge, ist es nützlich zu wissen , der Offset für wobei jedes Zeichen beginnt. Die Methode tf.strings.unicode_decode_with_offsets ähnelt unicode_decode , mit der Ausnahme , dass es gibt einen zweiten tensor den Beginn jedes Zeichen - Offset enthalten.

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

Jeder Unicode - Codepunkt gehört zu einer einzigen Sammlung von Codepoints als bekanntes Skript . Die Schrift eines Zeichens ist hilfreich bei der Bestimmung der Sprache des Zeichens. Wenn Sie beispielsweise wissen, dass „Б“ in kyrillischer Schrift steht, bedeutet dies, dass moderner Text, der dieses Zeichen enthält, wahrscheinlich aus einer slawischen Sprache wie Russisch oder Ukrainisch stammt.

TensorFlow bietet die tf.strings.unicode_script Operation , die Skript eine gegebene Codepoint Anwendungen zu bestimmen. Die Script - Codes sind int32 Werte entsprechend International Components for Unicode (ICU) UScriptCode Werte.

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

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

Die tf.strings.unicode_script Operation kann auch auf mehrdimensionale angewendet werden tf.Tensor s oder tf.RaggedTensor s von 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]]>

Beispiel: Einfache Segmentierung

Segmentierung ist die Aufgabe, Text in wortähnliche Einheiten aufzuteilen. Dies ist oft einfach, wenn Leerzeichen verwendet werden, um Wörter zu trennen, aber einige Sprachen (wie Chinesisch und Japanisch) verwenden keine Leerzeichen und einige Sprachen (wie Deutsch) enthalten lange Verbindungen, die geteilt werden müssen, um ihre Bedeutung zu analysieren. In Webtexten werden häufig verschiedene Sprachen und Schriften miteinander vermischt, wie bei "NY株価" (New York Stock Exchange).

Wir können eine sehr grobe Segmentierung (ohne Implementierung von ML-Modellen) durchführen, indem wir Änderungen im Skript verwenden, um Wortgrenzen anzunähern. Dies funktioniert für Zeichenfolgen wie das obige Beispiel "NY株価". Es funktioniert auch für die meisten Sprachen, die Leerzeichen verwenden, da die Leerzeichen verschiedener Skripte alle als USCRIPT_COMMON klassifiziert werden, ein spezieller Skriptcode, der sich von dem jedes tatsächlichen Textes unterscheidet.

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

Decodieren Sie zunächst die Sätze in Zeichencodepunkte und finden Sie die Skriptkennung für jedes Zeichen.

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

Verwenden Sie die Skriptbezeichner, um zu bestimmen, wo Wortgrenzen hinzugefügt werden sollen. Fügen Sie am Anfang jedes Satzes und für jedes Zeichen, dessen Schrift sich vom vorherigen Zeichen unterscheidet, eine Wortgrenze ein.

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

Sie können dann die Start - Offsets verwenden , um eine bauen RaggedTensor enthält die Liste der Wörter aus allen Chargen.

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

Zum Beenden des Vorgangs Segment Codepoints das Wort RaggedTensor zurück in Sätze und codieren in UTF-8 - Zeichenfolgen für die Lesbarkeit.

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