Voir sur TensorFlow.org | Exécuter dans Google Colab | Voir la source sur GitHub | Télécharger le cahier |
introduction
Les modèles de PNL gèrent souvent différentes langues avec différents jeux de caractères. Unicode est un système de codage standard qui est utilisé pour représenter les caractères de presque toutes les langues. Chaque caractère Unicode est codé en utilisant un nombre entier unique point de code entre 0
et 0x10FFFF
. Une chaîne de caractères Unicode est une séquence de zéro ou plusieurs points de code.
Ce didacticiel montre comment représenter des chaînes Unicode dans TensorFlow et les manipuler à l'aide des équivalents Unicode des opérations de chaîne standard. Il sépare les chaînes Unicode en jetons en fonction de la détection de script.
import tensorflow as tf
import numpy as np
Le tf.string
type de données
Le tensorflow de base tf.string
dtype
vous permet de créer tenseurs de chaînes d'octets. Chaînes Unicode sont utf-8 codé par défaut.
tf.constant(u"Thanks 😊")
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>
A tf.string
traite tensoriels octet chaînes comme unités atomiques. Cela lui permet de stocker des chaînes d'octets de différentes longueurs. La longueur de la corde n'est pas incluse dans les dimensions du tenseur.
tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])
Si vous utilisez Python pour les chaînes de construct, notez que les littéraux de chaîne sont codés en Unicode par défaut.
Représenter Unicode
Il existe deux manières standard de représenter une chaîne Unicode dans TensorFlow :
-
string
scalaire - où la séquence de points de code est codé en utilisant un connue codage de caractères . -
int32
vector - où chaque position contient un seul point de code.
Par exemple, les trois valeurs suivantes représentent toute la chaîne Unicode "语言处理"
(qui signifie « le traitement du langage » en chinois):
# 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)>
Conversion entre les représentations
TensorFlow fournit des opérations de conversion entre ces différentes représentations :
-
tf.strings.unicode_decode
: convertit un scalaire de chaîne codée à un vecteur de points de code. -
tf.strings.unicode_encode
: convertit un vecteur de points de code à un scalaire de chaîne codée. -
tf.strings.unicode_transcode
: convertit un scalaire de chaîne codée à un codage différent.
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'>
Dimensions du lot
Lors du décodage de plusieurs chaînes, le nombre de caractères dans chaque chaîne peut ne pas être égal. Le résultat obtenu est un tf.RaggedTensor
, où la longueur de la dimension la plus interne varie en fonction du nombre de caractères dans chaque chaîne.
# 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]
Vous pouvez utiliser cette tf.RaggedTensor
directement, ou le convertir en un dense tf.Tensor
avec un rembourrage ou un tf.SparseTensor
en utilisant les méthodes tf.RaggedTensor.to_tensor
et 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, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]]
Lors de l' encodage des chaînes multiples avec les mêmes longueurs, utiliser une tf.Tensor
comme entrée.
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)>
Lors de l' encodage plusieurs chaînes de longueur variable, en utilisant un tf.RaggedTensor
comme entrée.
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 vous avez un tenseur avec plusieurs chaînes en format rembourré ou clairsemés, le convertir en un premier tf.RaggedTensor
avant d' appeler 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)>
Opérations Unicode
Longueur des caractères
Utilisez l' unit
paramètre de la tf.strings.length
op pour indiquer comment les longueurs de caractères doivent être calculés. l' "BYTE"
"UTF8_CHAR"
"UTF16_CHAR"
unit
par défaut "BYTE"
, mais il peut être réglé sur d' autres valeurs, telles que "UTF8_CHAR"
ou "UTF16_CHAR"
, afin de déterminer le nombre de points de code Unicode dans chaque chaîne de caractères codée.
# 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
Sous-chaînes de caractères
Le tf.strings.substr
op accepte l' unit
paramètre, et l' utilise pour déterminer quel type de compensation des pos
et len
paremeters contiennent.
# 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'
Diviser les chaînes Unicode
La tf.strings.unicode_split
opération divise les chaînes unicode en sous - chaînes de caractères individuels.
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)
Décalages d'octets pour les caractères
Pour aligner le tenseur de caractères généré par tf.strings.unicode_decode
avec la chaîne d' origine, il est utile de connaître le décalage pour où chaque personnage commence. Le procédé tf.strings.unicode_decode_with_offsets
est similaire à unicode_decode
, sauf qu'il renvoie un second tenseur contenant le décalage de début de chaque caractère.
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
Chaque point de code Unicode appartient à une collection unique de codepoints connu sous le nom d' un scénario . L'écriture d'un personnage est utile pour déterminer dans quelle langue le personnage peut être. Par exemple, savoir que « Б » est en écriture cyrillique indique que le texte moderne contenant ce caractère provient probablement d'une langue slave telle que le russe ou l'ukrainien.
Tensorflow fournit la tf.strings.unicode_script
opération pour déterminer quel script une utilisation de codepoint données. Les codes de script sont int32
des valeurs correspondant aux International Components for Unicode (ICU) UScriptCode
valeurs.
uscript = tf.strings.unicode_script([33464, 1041]) # ['芸', 'Б']
print(uscript.numpy()) # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17 8]
La tf.strings.unicode_script
opération peut également être appliquée à multidimensionnelle tf.Tensor
s ou tf.RaggedTensor
s de 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]]>
Exemple : segmentation simple
La segmentation est la tâche de diviser le texte en unités semblables à des mots. C'est souvent facile lorsque des espaces sont utilisés pour séparer les mots, mais certaines langues (comme le chinois et le japonais) n'utilisent pas d'espaces, et certaines langues (comme l'allemand) contiennent de longs composés qui doivent être séparés afin d'analyser leur sens. Dans le texte Web, différentes langues et écritures sont fréquemment mélangées, comme dans "NY株価" (New York Stock Exchange).
Nous pouvons effectuer une segmentation très grossière (sans implémenter de modèles ML) en utilisant des modifications de script pour approximer les limites des mots. Cela fonctionnera pour des chaînes comme l'exemple "NY株価" ci-dessus. Cela fonctionnera également pour la plupart des langues qui utilisent des espaces, car les caractères d'espace de divers scripts sont tous classés comme USCRIPT_COMMON, un code de script spécial qui diffère de celui de tout texte réel.
# dtype: string; shape: [num_sentences]
#
# The sentences to process. Edit this line to try out different inputs!
sentence_texts = [u'Hello, world.', u'世界こんにちは']
Tout d'abord, décodez les phrases en points de code de caractères et trouvez l'identificateur de script pour chaque caractère.
# 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]]>
Utilisez les identificateurs de script pour déterminer où les limites des mots doivent être ajoutées. Ajoutez une limite de mot au début de chaque phrase et pour chaque caractère dont l'écriture diffère du caractère précédent.
# 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)
Vous pouvez ensuite utiliser ces décalages de début pour construire une RaggedTensor
contenant la liste des mots de tous les lots.
# 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]]>
Pour finir, le segment le mot codepoints RaggedTensor
retour en phrases et encode en chaînes de caractères UTF-8 pour une meilleure lisibilité.
# 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']]