Esta página foi traduzida pela API Cloud Translation.
Switch to English

Strings Unicode

Ver em TensorFlow.org Executar no Google Colab Ver fonte no GitHub Download do caderno

Introdução

Os modelos que processam a linguagem natural geralmente lidam com diferentes idiomas com diferentes conjuntos de caracteres. Unicode é um sistema de codificação padrão usado para representar caracteres de quase todos os idiomas. Cada caractere é codificado usando um ponto de código inteiro exclusivo entre 0 e 0x10FFFF . Uma cadeia Unicode é uma sequência de zero ou mais pontos de código.

Este tutorial mostra como representar seqüências de caracteres Unicode no TensorFlow e manipulá-las usando equivalentes Unicode de operações de sequência padrão. Ele separa seqüências de caracteres Unicode em tokens com base na detecção de script.

 import tensorflow as tf
 

O tipo de dados tf.string

O básico TensorFlow tf.string dtype permite que você construa tensores de strings de bytes. As strings Unicode são utf-8 codificadas por padrão.

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

Um tensor tf.string pode conter cadeias de bytes de comprimentos variados porque as cadeias de bytes são tratadas como unidades atômicas. O comprimento da corda não está incluído nas dimensões do tensor.

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

Representando Unicode

Existem duas maneiras padrão de representar uma seqüência de caracteres Unicode no TensorFlow:

  • string escalar - onde a sequência de pontos de código é codificada usando uma codificação de caracteres conhecida.
  • Vetor int32 - onde cada posição contém um único ponto de código.

Por exemplo, todos os três valores a seguir representam a string Unicode "语言处理" (que significa" processamento de idioma "em chinês):

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

Convertendo entre representações

O TensorFlow fornece operações para converter entre estas diferentes representações:

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

Dimensões do lote

Ao decodificar várias strings, o número de caracteres em cada string pode não ser igual. O resultado do retorno é um tf.RaggedTensor , em que o comprimento da dimensão mais interna varia dependendo do número de caracteres em cada sequência:

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

Você pode usar esse tf.RaggedTensor diretamente ou convertê-lo em um denso tf.Tensor com preenchimento ou em um tf.SparseTensor usando os métodos tf.RaggedTensor.to_tensor e 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()
 

Ao codificar várias strings com os mesmos comprimentos, um tf.Tensor pode ser usado como entrada:

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

Ao codificar várias seqüências de caracteres com comprimento variável, um tf.RaggedTensor deve ser usado como entrada:

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

Se você tiver um tensor com várias cadeias de caracteres no formato acolchoado ou esparso, converta-o em um tf.RaggedTensor antes de chamar 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)>

Operações Unicode

Comprimento do caractere

A operação tf.strings.length possui uma unit parâmetro, que indica como os comprimentos devem ser calculados. unit padrão da unit é "BYTE" , mas pode ser definido com outros valores, como "UTF8_CHAR" ou "UTF16_CHAR" , para determinar o número de pontos de código Unicode em cada string codificada.

 # 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

Substrações de caracteres

Da mesma forma, a operação tf.strings.substr aceita o parâmetro " unit " e o utiliza para determinar que tipo de compensação os parâmetros " pos " e " len " contêm.

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

Dividir seqüências Unicode

A operação tf.strings.unicode_split divide cadeias unicode em substrings de caracteres individuais:

 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)

Deslocamentos de bytes para caracteres

Para alinhar o tensor de caractere gerado por tf.strings.unicode_decode com a string original, é útil conhecer o deslocamento de onde cada caractere começa. O método tf.strings.unicode_decode_with_offsets é semelhante ao unicode_decode , exceto que ele retorna um segundo tensor contendo o deslocamento inicial de cada caractere.

 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

Scripts Unicode

Cada ponto de código Unicode pertence a uma única coleção de pontos de código conhecidos como script . O script de um personagem é útil para determinar em qual idioma o personagem pode estar. Por exemplo, saber que 'Б' está em script cirílico indica que o texto moderno que contém esse personagem provavelmente é de um idioma eslavo como o russo ou o ucraniano.

O TensorFlow fornece a operação tf.strings.unicode_script para determinar qual script um determinado ponto de código usa. Os códigos de script são valores int32 correspondentes aos valores UScriptCode International Components for Unicode (ICU).

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

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

A operação tf.strings.unicode_script também pode ser aplicada a tf.Tensor s multidimensional ou tf.RaggedTensor s de pontos de código:

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

Exemplo: Segmentação Simples

A segmentação é a tarefa de dividir o texto em unidades semelhantes a palavras. Isso geralmente é fácil quando caracteres de espaço são usados ​​para separar palavras, mas alguns idiomas (como chinês e japonês) não usam espaços e alguns idiomas (como alemão) contêm compostos longos que devem ser divididos para analisar seu significado. No texto da web, diferentes idiomas e scripts são frequentemente misturados, como em "NY 株 価" (New York Stock Exchange).

Podemos realizar uma segmentação muito grosseira (sem implementar nenhum modelo de ML) usando alterações no script para aproximar os limites das palavras. Isso funcionará para seqüências de caracteres como o exemplo "NY 株 価" acima. Ele também funcionará para a maioria dos idiomas que usam espaços, pois os caracteres de espaço de vários scripts são classificados como USCRIPT_COMMON, um código de script especial que difere do de qualquer texto real.

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

Primeiro, decodificamos as frases em pontos de código de caractere e encontramos o identificador de script para cada caractere.

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

Em seguida, usamos esses identificadores de script para determinar onde os limites de palavras devem ser adicionados. Nós adicionamos um limite de palavras no início de cada frase e para cada caractere cujo script difere do caractere anterior:

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

Em seguida, podemos usar esses deslocamentos iniciais para criar um RaggedTensor contendo a lista de palavras de todos os lotes:

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

E, finalmente, podemos segmentar a palavra codepoints RaggedTensor novamente em frases:

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

Para facilitar a leitura do resultado final, podemos codificá-lo de volta para cadeias 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']]