Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
Wstęp
Modele NLP często obsługują różne języki z różnymi zestawami znaków. Unicode to standardowy system kodowania, który jest używany do reprezentowania znaków z prawie wszystkich językach. Każdy znak Unicode jest kodowany za pomocą unikalnego całkowitą punkt kodu pomiędzy 0
i 0x10FFFF
. Ciąg Unicode to sekwencja zero lub więcej punktów kodowych.
Ten samouczek pokazuje, jak reprezentować ciągi Unicode w TensorFlow i manipulować nimi za pomocą odpowiedników Unicode standardowych operacji ciągów. Rozdziela ciągi Unicode na tokeny na podstawie wykrywania skryptów.
import tensorflow as tf
import numpy as np
tf.string
typ danych
Podstawowym TensorFlow tf.string
dtype
pozwala budować tensory ciągów bajtów. Ciągi są Unicode UTF-8 zakodowany domyślnie.
tf.constant(u"Thanks 😊")
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>
A tf.string
traktuje tensor bajt sznurki jak jednostkach atomowych. Umożliwia to przechowywanie ciągów bajtów o różnej długości. Długość struny nie jest uwzględniona w wymiarach napinacza.
tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])
Jeśli używasz Python ciągów zbudować, uwaga, że literały łańcuchowe są kodowane Unicode domyślnie.
Reprezentowanie Unicode
Istnieją dwa standardowe sposoby reprezentowania ciągu Unicode w TensorFlow:
-
string
skalarne - gdzie sekwencja punktów kodów jest kodowane przy użyciu znanego kodowania znaków . -
int32
wektor - gdzie każde stanowisko zawiera pojedynczy punkt kodu.
Na przykład, następujące trzy wartości wszystkich reprezentacji ciąg Unicode "语言处理"
(co oznacza „przetwarzanie języka” po chińsku):
# 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)>
Konwersja między reprezentacjami
TensorFlow udostępnia operacje do konwersji między tymi różnymi reprezentacjami:
-
tf.strings.unicode_decode
: konwertuje zakodowany ciąg skalarne do wektora punktów kodowych. -
tf.strings.unicode_encode
: konwertuje wektor punktów kodowych do zakodowany ciąg skalar. -
tf.strings.unicode_transcode
: Konwersja zakodowany ciąg skalarne do innego kodowania.
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'>
Wymiary partii
Podczas dekodowania wielu ciągów liczba znaków w każdym ciągu może nie być równa. Wynik powrót jest tf.RaggedTensor
, gdzie najbardziej wewnętrzna długość wymiar zależy od liczby znaków w każdej struny.
# 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]
Można użyć tej tf.RaggedTensor
bezpośrednio lub przekształcić go w gęstym tf.Tensor
z wyściółką lub tf.SparseTensor
wykorzystaniem metod tf.RaggedTensor.to_tensor
i 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, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]]
Przy kodowaniu wielu ciągów o tych samych długościach, należy użyć tf.Tensor
jako wejście.
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)>
Przy kodowaniu wielu ciągów z różnej długości, użyj tf.RaggedTensor
jako wejście.
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)>
Jeśli masz tensor z wielu ciągów w formacie wyściełane lub rozrzedzony, przekształcić go najpierw do tf.RaggedTensor
przed wywołaniem 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)>
Operacje Unicode
Długość znaków
Użyj unit
parametru tf.strings.length
op aby wskazać, jak długość postać powinna być obliczona. unit
domyślnie "BYTE"
, ale mogą być ustawione dla innych wartości, takich jak "UTF8_CHAR"
lub "UTF16_CHAR"
, w celu określenia liczby codepoints unikodowymi każdego zakodowanego ciągu.
# 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
Podciągi znaków
tf.strings.substr
op akceptuje unit
parametr, i używa go do określenia, jakiego rodzaju kompensuje pos
i len
parametrach, zawierają.
# 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'
Podziel ciągi Unicode
tf.strings.unicode_split
operacja dzieli unicode ciągi znaków na podciągi poszczególnych znaków.
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)
Przesunięcia bajtów dla znaków
Aby wyrównać tensor znaków generowanych przez tf.strings.unicode_decode
z oryginalnego łańcucha, to warto wiedzieć, gdzie offset dla zaczyna każda postać. Sposób ten tf.strings.unicode_decode_with_offsets
jest podobna do unicode_decode
, z wyjątkiem tego, że zwraca drugi tensor zawierający początek przesunięcia każdego znaku.
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
Skrypty Unicode
Każdy punkt kodowy Unicode należący do jednej kolekcji codepoints znanych postaci skryptu . Pismo postaci jest pomocne w ustaleniu, w jakim języku może ona być. Na przykład wiedza, że „Б” jest zapisane cyrylicą, wskazuje, że współczesny tekst zawierający ten znak prawdopodobnie pochodzi z języka słowiańskiego, takiego jak rosyjski lub ukraiński.
TensorFlow zapewnia tf.strings.unicode_script
operację celu określenia, które scenariusz danego zastosowania punkt kodowy. Kody skryptów są int32
wartości odpowiadające Międzynarodowym Komponentów do Unicode (ICU) UScriptCode
wartości.
uscript = tf.strings.unicode_script([33464, 1041]) # ['芸', 'Б']
print(uscript.numpy()) # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17 8]
tf.strings.unicode_script
operacja może być również stosowany do wielowymiarowego tf.Tensor
S lub tf.RaggedTensor
s 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]]>
Przykład: Prosta segmentacja
Segmentacja to zadanie polegające na podzieleniu tekstu na jednostki podobne do wyrazów. Jest to często łatwe, gdy do oddzielania słów używa się spacji, ale niektóre języki (np. chiński i japoński) nie używają spacji, a niektóre języki (np. niemiecki) zawierają długie związki złożone, które należy rozdzielić, aby przeanalizować ich znaczenie. W tekście internetowym różne języki i pismo często są ze sobą mieszane, jak w przypadku „NY株価” (New York Stock Exchange).
Możemy przeprowadzić bardzo zgrubną segmentację (bez implementowania jakichkolwiek modeli ML), używając zmian w skrypcie w celu przybliżenia granic słów. To zadziała w przypadku ciągów, takich jak w powyższym przykładzie „NY株価”. Będzie również działać w większości języków używających spacji, ponieważ znaki spacji w różnych skryptach są klasyfikowane jako USCRIPT_COMMON, specjalny kod skryptu, który różni się od każdego rzeczywistego tekstu.
# dtype: string; shape: [num_sentences]
#
# The sentences to process. Edit this line to try out different inputs!
sentence_texts = [u'Hello, world.', u'世界こんにちは']
Najpierw zdekoduj zdania na punkty kodowe znaków i znajdź identyfikator skryptu dla każdego znaku.
# 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]]>
Użyj identyfikatorów skryptu, aby określić, gdzie należy dodać granice słów. Dodaj granicę słowa na początku każdego zdania i dla każdego znaku, którego pismo różni się od poprzedniego znaku.
# 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)
Następnie można korzystać z tych przesunięć zacząć budować RaggedTensor
zawierający listę słów ze wszystkich partii.
# 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]]>
Do wykończenia segment słowo codepoints RaggedTensor
powrotem do zdań i zakodować do UTF-8 ciągi dla czytelności.
# 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']]