Ver en TensorFlow.org | Ejecutar en Google Colab | Ver fuente en GitHub | Descargar cuaderno |
Introducción
Los modelos de PNL a menudo manejan diferentes idiomas con diferentes conjuntos de caracteres. Unicode es un sistema de codificación estándar que se utiliza para representar los caracteres de casi todos los idiomas. Carácter Cada Unicode se codifica mediante un número entero único punto de código entre 0
y 0x10FFFF
. Una cadena Unicode es una secuencia de cero o más puntos de código.
Este instructivo muestra cómo representar cadenas Unicode en TensorFlow y manipularlas con equivalentes Unicode de operaciones de cadenas estándar. Separa las cadenas Unicode en tokens según la detección de secuencias de comandos.
import tensorflow as tf
import numpy as np
El tf.string
tipo de datos
La básica TensorFlow tf.string
dtype
le permite construir tensores de cadenas de bytes. Son cadenas Unicode UTF-8 codificada por defecto.
tf.constant(u"Thanks 😊")
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>
A tf.string
trata tensor byte cadenas como unidades atómicas. Esto le permite almacenar cadenas de bytes de diferentes longitudes. La longitud de la cuerda no está incluida en las dimensiones del tensor.
tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])
Si utiliza Python a cadenas de construir, nota que los literales de cadena son Unicode con codificación por defecto.
Representando Unicode
Hay dos formas estándar de representar una cadena Unicode en TensorFlow:
-
string
escalar - donde la secuencia de puntos de código se codifica utilizando una conocida codificación de caracteres . -
int32
vector - donde cada posición contiene un único punto de código.
Por ejemplo, los siguientes tres valores todos representan la cadena Unicode "语言处理"
(que significa "el procesamiento del lenguaje" en chino):
# 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)>
Conversión entre representaciones
TensorFlow proporciona operaciones para convertir entre estas diferentes representaciones:
-
tf.strings.unicode_decode
: Convierte una cadena codificada escalares a un vector de puntos de código. -
tf.strings.unicode_encode
: Convierte un vector de puntos de código a un escalar cadena codificada. -
tf.strings.unicode_transcode
: convierte una cadena escalares codificada a una codificación diferente.
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'>
Dimensiones del lote
Al decodificar varias cadenas, es posible que el número de caracteres de cada cadena no sea igual. El resultado de retorno es un tf.RaggedTensor
, donde la longitud de la dimensión más interna varía en función del número de caracteres en cada cadena.
# 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]
Se puede utilizar esta tf.RaggedTensor
directamente, o convertirla en una densa tf.Tensor
con relleno o una tf.SparseTensor
utilizando los métodos tf.RaggedTensor.to_tensor
y 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, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]]
Al codificar múltiples cadenas con las mismas longitudes, utilizar un tf.Tensor
que la 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)>
Al codificar varias cadenas con longitud variable, utilice un tf.RaggedTensor
que la 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)>
Si usted tiene un tensor con múltiples cadenas en formato acolchada o escasa, convertirlo primero en un tf.RaggedTensor
antes de llamar 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)>
Operaciones Unicode
Longitud del carácter
Utilice la unit
parámetro de la tf.strings.length
op para indicar cómo deben calcularse longitudes de caracteres. unit
por defecto a "BYTE"
, pero puede ser ajustado a otros valores, tales como "UTF8_CHAR"
o "UTF16_CHAR"
, para determinar el número de puntos de código Unicode en cada cadena 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
Subcadenas de caracteres
El tf.strings.substr
op acepta la unit
parámetro, y lo utiliza para determinar qué tipo de compensaciones de los pos
y len
paremeters contienen.
# 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'
Dividir cadenas Unicode
El tf.strings.unicode_split
operación se divide cadenas Unicode en subcadenas de caracteres individuales.
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)
Desplazamientos de bytes para caracteres
Para alinear el tensor personaje generado por tf.strings.unicode_decode
con la cadena original, es útil conocer el offset de inicio de cada carácter. El método tf.strings.unicode_decode_with_offsets
es similar a unicode_decode
, excepto que devuelve un segundo tensor que contiene el desplazamiento de cada carácter de inicio.
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
Secuencias de comandos Unicode
Cada punto de código Unicode pertenece a una sola colección de puntos de código conocidos como secuencia de comandos . La escritura de un personaje es útil para determinar en qué idioma podría estar. Por ejemplo, saber que 'Б' está en escritura cirílica indica que el texto moderno que contiene ese carácter probablemente provenga de un idioma eslavo como el ruso o el ucraniano.
TensorFlow proporciona la tf.strings.unicode_script
operación para determinar que la escritura a usos codepoint dadas. Los códigos de secuencia de comandos son int32
valores correspondientes a Componentes internacionales para Unicode (ICU) UScriptCode
valores.
uscript = tf.strings.unicode_script([33464, 1041]) # ['芸', 'Б']
print(uscript.numpy()) # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17 8]
El tf.strings.unicode_script
operación también se puede aplicar a multidimensional tf.Tensor
s o tf.RaggedTensor
s de puntos 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]]>
Ejemplo: segmentación simple
La segmentación es la tarea de dividir el texto en unidades similares a palabras. A menudo, esto es fácil cuando se usan espacios para separar palabras, pero algunos idiomas (como el chino y el japonés) no usan espacios, y algunos idiomas (como el alemán) contienen compuestos largos que deben dividirse para analizar su significado. En el texto web, con frecuencia se mezclan diferentes idiomas y escrituras, como en "NY 株 価" (Bolsa de Valores de Nueva York).
Podemos realizar una segmentación muy aproximada (sin implementar ningún modelo de AA) mediante el uso de cambios en el script para aproximar los límites de las palabras. Esto funcionará para cadenas como el ejemplo anterior "NY 株 価". También funcionará para la mayoría de los idiomas que utilizan espacios, ya que los caracteres de espacio de varios scripts se clasifican como USCRIPT_COMMON, un código de script especial que difiere del de cualquier 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'世界こんにちは']
Primero, decodifique las oraciones en puntos de código de caracteres y busque el identificador de secuencia de comandos para cada carácter.
# 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]]>
Utilice los identificadores de secuencia de comandos para determinar dónde se deben agregar los límites de las palabras. Agregue un límite de palabra al principio de cada oración y para cada carácter cuyo guión difiera del carácter 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)
A continuación, puede utilizar esos desplazamientos comenzar a construir una RaggedTensor
que contiene la lista de palabras de todos los 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]]>
Para terminar, el segmento de la palabra puntos de código RaggedTensor
de nuevo en frases y codificar en UTF-8 cuerdas para facilitar la lectura.
# 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']]