Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Ciągi Unicode

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Wprowadzenie

Modele przetwarzające język naturalny często obsługują różne języki z różnymi zestawami znaków. Unicode to standardowy system kodowania używany do reprezentowania znaków z prawie wszystkich języków. Każdy znak jest kodowany przy użyciu unikatowego całkowitego punktu kodowego z zakresu od 0 do 0x10FFFF . Ciąg Unicode to sekwencja zera lub większej liczby punktów kodowych.

Ten samouczek pokazuje, jak reprezentować ciągi znaków Unicode w TensorFlow i manipulować nimi przy użyciu odpowiedników Unicode standardowych operacji na ciągach. Oddziela ciągi znaków Unicode na tokeny na podstawie wykrywania skryptów.

 import tensorflow as tf
 

Typ danych tf.string

Podstawowym TensorFlow tf.string dtype pozwala budować tensory ciągów bajtów. Ciągi Unicode są domyślnie zakodowane w formacie UTF-8.

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

Tensor tf.string może przechowywać ciągi bajtów o różnej długości, ponieważ łańcuchy bajtów są traktowane jako jednostki tf.string . Długość struny nie jest uwzględniana w wymiarach tensora.

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

Reprezentujący Unicode

Istnieją dwa standardowe sposoby reprezentowania ciągu znaków Unicode w TensorFlow:

  • string skalar - gdzie sekwencja punktów kodowych jest kodowana przy użyciu znanego kodowania znaków .
  • wektor int32 - gdzie każda pozycja zawiera pojedynczy punkt kodowy.

Na przykład poniższe trzy wartości reprezentują ciąg znaków Unicode "语言处理" (co oznacza „przetwarzanie języka” w języku chińskim):

 # 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 zapewnia operacje konwersji między tymi różnymi reprezentacjami:

 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 z nich może nie być równa. Wynik zwracany to tf.RaggedTensor , gdzie długość najbardziej wewnętrznego wymiaru zmienia się w zależności od liczby znaków w każdym ciągu:

 # 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żesz użyć tego tf.RaggedTensor bezpośrednio lub przekonwertować go na gęsty tf.Tensor z dopełnieniem lub tf.SparseTensor przy użyciu 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()
 

Podczas kodowania wielu ciągów o tej samej długości jako dane wejściowe można użyć tf.Tensor :

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

Podczas kodowania wielu ciągów o różnej długości jako dane wejściowe należy użyć tf.RaggedTensor :

 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 wieloma ciągami w formacie tf.RaggedTensor lub rzadkim, przekonwertuj go na tf.RaggedTensor przed wywołaniem 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ść postaci

Operacja tf.strings.length ma unit parametru, która wskazuje, w jaki sposób powinny być obliczane długości. unit domyślnie ma wartość "BYTE" , ale można ją ustawić na inne wartości, takie jak "UTF8_CHAR" lub "UTF16_CHAR" , aby określić liczbę punktów "UTF16_CHAR" Unicode w każdym zakodowanym string .

 # 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

Podobnie, operacja tf.strings.substr przyjmuje parametr „ unit ” i używa go do określenia rodzaju przesunięć, które zawierają parametry „ pos ” i „ len ”.

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

Podziel ciągi Unicode

Operacja tf.strings.unicode_split dzieli ciągi znaków Unicode 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 znaku wygenerowany przez tf.strings.unicode_decode z oryginalnym ciągiem, warto znać przesunięcie dla początku każdego znaku. Metoda tf.strings.unicode_decode_with_offsets jest podobna do unicode_decode , z tą różnicą, że zwraca drugi tensor zawierający początkowe przesunięcie 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ży do pojedynczej kolekcji punktów kodowych zwanej skryptem . Pismo postaci jest pomocne w określeniu, w jakim języku może być dany znak. Na przykład świadomość, że „Б” jest zapisany 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 udostępnia operację tf.strings.unicode_script celu określenia, którego skryptu używa dany punkt kodowy. Kody skryptów to wartości int32 odpowiadające wartościom International Components for Unicode (ICU) UScriptCode .

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

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

tf.strings.unicode_script można również zastosować do wielowymiarowych tf.Tensor s lub tf.RaggedTensor s tf.RaggedTensor :

 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 przypominające słowa. Często jest to łatwe, gdy do oddzielania słów używa się znaków spacji, ale niektóre języki (takie jak chiński i japoński) nie używają spacji, a niektóre języki (np. Niemiecki) zawierają długie związki, które należy podzielić, aby przeanalizować ich znaczenie. W tekście internetowym często mieszane są różne języki i skrypty, jak w „NY 株 価” (New York Stock Exchange).

Możemy przeprowadzić bardzo zgrubną segmentację (bez implementowania jakichkolwiek modeli ML), używając zmian w skrypcie do przybliżenia granic słów. To zadziała dla łańcuchów, takich jak powyższy przykład „NY 株 価”. Będzie również działać w większości języków, które używają spacji, ponieważ znaki spacji w różnych skryptach są klasyfikowane jako USCRIPT_COMMON, specjalny kod skryptu, który różni się od kodu dowolnego 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 dekodujemy zdania na punkty kodowe znaków i znajdujemy 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]]>

Następnie używamy tych identyfikatorów skryptów, aby określić, gdzie należy dodać granice słów. Dodajemy 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żemy użyć tych przesunięć początkowych, aby zbudować 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]]>

Na koniec możemy RaggedTensor słowo kodowe RaggedTensor powrotem na zdania:

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

Aby końcowy wynik był łatwiejszy do odczytania, możemy zakodować go z powrotem w łańcuchy UTF-8:

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