Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Unicode-Zeichenfolgen

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

Einführung

Modelle, die natürliche Sprache verarbeiten, verarbeiten häufig unterschiedliche Sprachen mit unterschiedlichen Zeichensätzen. Unicode ist ein Standardcodierungssystem, mit dem Zeichen aus fast allen Sprachen dargestellt werden. Jedes Zeichen wird mit einem eindeutigen ganzzahligen Codepunkt zwischen 0 und 0x10FFFF . Eine Unicode-Zeichenfolge ist eine Folge von null oder mehr Codepunkten.

Dieses Tutorial zeigt, wie Sie Unicode-Zeichenfolgen in TensorFlow darstellen und mithilfe von Unicode-Entsprechungen von Standard-Zeichenfolgenoperationen bearbeiten. Es trennt Unicode-Zeichenfolgen basierend auf der Skripterkennung in Token.

 import tensorflow as tf
 

Der Datentyp tf.string

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

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

Ein tf.string Tensor kann Byte-Strings unterschiedlicher Länge enthalten, da die Byte-Strings als atomare Einheiten behandelt werden. Die Saitenlänge ist in den Tensordimensionen nicht enthalten.

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

Darstellung von Unicode

Es gibt zwei Standardmethoden zur Darstellung einer Unicode-Zeichenfolge in TensorFlow:

  • string Skalar - Hier wird die Folge von Codepunkten mit einer bekannten Zeichenkodierung codiert .
  • int32 vector - wobei jede Position einen einzelnen Codepunkt enthält.

Beispielsweise stellen die folgenden drei Werte alle die Unicode-Zeichenfolge "语言处理" (was auf Chinesisch "Sprachverarbeitung" bedeutet):

 # 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 Dekodieren mehrerer Zeichenfolgen ist die Anzahl der Zeichen in jeder Zeichenfolge möglicherweise nicht gleich. Das Rückgabeergebnis ist ein tf.RaggedTensor , bei dem die Länge der innersten Dimension in Abhängigkeit von der Anzahl der Zeichen in jeder Zeichenfolge variiert:

 # 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 diesen tf.RaggedTensor direkt verwenden oder ihn mit den Methoden tf.RaggedTensor.to_tensor und tf.RaggedTensor.to_sparse in einen dichten tf.Tensor mit Auffüllung oder einen tf.SparseTensor 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()
 

Beim Codieren mehrerer Zeichenfolgen mit derselben Länge kann ein tf.Tensor als Eingabe verwendet werden:

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

Beim Codieren mehrerer Zeichenfolgen mit unterschiedlicher Länge sollte ein tf.RaggedTensor als Eingabe verwendet werden:

 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, wandeln sie dann zu einem tf.RaggedTensor vor dem Aufruf 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

Der tf.strings.length Betrieb hat eine Parameter - unit , die angibt , wie Längen berechnet werden sollen. unit standardmäßig "BYTE" , kann jedoch auf andere Werte wie "UTF8_CHAR" oder "UTF16_CHAR" , um die Anzahl der Unicode-Codepunkte in jeder codierten string 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

Zeichensubstrings

In ähnlicher tf.strings.substr akzeptiert die Operation tf.strings.substr den Parameter " unit " und verwendet ihn, um zu bestimmen, welche Art von Offsets die Parameter " pos " und " len " enthalten.

 # default: unit='BYTE'. With len=1, we return a single byte.
tf.strings.substr(thanks, pos=7, len=1).numpy()
 
b'\xf0'
 # Specifying unit='UTF8_CHAR', we return a single character, which in this case
# is 4 bytes.
print(tf.strings.substr(thanks, pos=7, len=1, unit='UTF8_CHAR').numpy())
 
b'\xf0\x9f\x98\x8a'

Unicode-Zeichenfolgen teilen

Die Operation tf.strings.unicode_split teilt Unicode-Zeichenfolgen in Teilzeichenfolgen einzelner Zeichen auf:

 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 von tf.strings.unicode_decode erzeugten tf.strings.unicode_decode an der ursprünglichen Zeichenfolge auszurichten, ist es hilfreich, den Versatz für den Beginn jedes Zeichens zu kennen. Die Methode tf.strings.unicode_decode_with_offsets ähnelt unicode_decode , gibt jedoch einen zweiten Tensor zurück, der den Startversatz jedes Zeichens enthält.

 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 einzelnen Sammlung von Codepunkten, die als Skript bezeichnet werden . Das Skript eines Zeichens ist hilfreich, um festzustellen, in welcher Sprache sich das Zeichen möglicherweise befindet. 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 stellt die Operation tf.strings.unicode_script , um zu bestimmen, welches Skript ein bestimmter Codepunkt verwendet. Die int32 sind int32 Werte, die den int32 Werten von International Components for Unicode (ICU) UScriptCode .

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

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

Die Operation tf.strings.unicode_script kann auch auf mehrdimensionale tf.Tensor oder tf.RaggedTensor von Codepunkten angewendet werden:

 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

Die Segmentierung ist die Aufgabe, Text in wortähnliche Einheiten aufzuteilen. Dies ist oft einfach, wenn Leerzeichen zum Trennen von Wörtern verwendet werden, einige Sprachen (wie Chinesisch und Japanisch) jedoch keine Leerzeichen verwenden und einige Sprachen (wie Deutsch) lange Verbindungen enthalten, die aufgeteilt werden müssen, um ihre Bedeutung zu analysieren. Im Webtext werden häufig verschiedene Sprachen und Skripte miteinander gemischt, wie in "NY 株 価" (New York Stock Exchange).

Wir können eine sehr grobe Segmentierung durchführen (ohne ML-Modelle zu implementieren), indem wir Änderungen im Skript verwenden, um die Wortgrenzen zu approximieren. 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 sind, ein spezieller Skriptcode, der sich von dem aller tatsächlichen Texte unterscheidet.

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

Zuerst dekodieren wir die Sätze in Zeichencodepunkte und finden die Skriptidentifikation 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]]>

Als Nächstes verwenden wir diese Skriptkennungen, um zu bestimmen, wo Wortgrenzen hinzugefügt werden sollen. Wir fügen am Anfang jedes Satzes und für jedes Zeichen, dessen Skript sich vom vorherigen Zeichen unterscheidet, eine Wortgrenze hinzu:

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

Wir können diese Start-Offsets dann verwenden, um einen RaggedTensor zu RaggedTensor , der die Liste der Wörter aus allen RaggedTensor enthält:

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

Und schließlich können wir die RaggedTensor wieder in Sätze unterteilen:

 # 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.RaggedTensor [[[72, 101, 108, 108, 111], [44, 32], [119, 111, 114, 108, 100], [46]], [[19990, 30028], [12371, 12435, 12395, 12385, 12399]]]>

Um das Endergebnis besser lesbar zu machen, können wir es wieder in UTF-8-Zeichenfolgen codieren:

 tf.strings.unicode_encode(sentence_word_char_codepoint, 'UTF-8').to_list()
 
[[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']]