Visualizza su TensorFlow.org | Esegui in Google Colab | Visualizza la fonte su GitHub | Scarica taccuino |
introduzione
I modelli NLP spesso gestiscono lingue diverse con set di caratteri diversi. Unicode è un sistema di codifica standard utilizzata per rappresentare i caratteri da quasi tutte le lingue. Tipiche ogni Unicode è codificato utilizzando un unico intero punto di codice tra 0
e 0x10FFFF
. Una stringa Unicode è una sequenza di zero o più punti di codice.
Questo tutorial mostra come rappresentare stringhe Unicode in TensorFlow e manipolarle usando gli equivalenti Unicode delle operazioni di stringa standard. Separa le stringhe Unicode in token in base al rilevamento degli script.
import tensorflow as tf
import numpy as np
Il tf.string
tipo di dati
La base tensorflow tf.string
dtype
consente di costruire tensori di stringhe di byte. Stringhe Unicode sono UTF-8 codificati per impostazione predefinita.
tf.constant(u"Thanks 😊")
<tf.Tensor: shape=(), dtype=string, numpy=b'Thanks \xf0\x9f\x98\x8a'>
A tf.string
tensore tratta Byte di stringhe come unità atomiche. Ciò consente di memorizzare stringhe di byte di lunghezza variabile. La lunghezza della corda non è inclusa nelle dimensioni del tensore.
tf.constant([u"You're", u"welcome!"]).shape
TensorShape([2])
Se si utilizza Python per le stringhe di costruire, nota che stringhe letterali sono con codifica Unicode per impostazione predefinita.
Rappresentare Unicode
Esistono due modi standard per rappresentare una stringa Unicode in TensorFlow:
-
string
scalare - dove la sequenza di punti di codice è codificato utilizzando un noto codifica dei caratteri . -
int32
vettore - dove ogni posizione contiene un singolo punto di codice.
Ad esempio, i seguenti tre valori rappresentano tutti la stringa Unicode "语言处理"
(che significa "l'elaborazione del linguaggio" in cinese):
# 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)>
Conversione tra rappresentazioni
TensorFlow fornisce operazioni per la conversione tra queste diverse rappresentazioni:
-
tf.strings.unicode_decode
: Converte una stringa codificata scalari ad un vettore di punti di codice. -
tf.strings.unicode_encode
: Converte un vettore di punti di codice ad uno scalare stringa codificata. -
tf.strings.unicode_transcode
: Converte una stringa codificata scalari di una codifica diversa.
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'>
Dimensioni del lotto
Quando si decodificano più stringhe, il numero di caratteri in ciascuna stringa potrebbe non essere uguale. Il risultato è un ritorno tf.RaggedTensor
, in cui la lunghezza dimensione più interna varia a seconda del numero di caratteri in ogni stringa.
# 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]
È possibile utilizzare questo tf.RaggedTensor
direttamente, o convertirlo in un denso tf.Tensor
con imbottitura o un tf.SparseTensor
utilizzando i metodi 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()
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, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]]
Durante la codifica più stringhe con le stesse lunghezze, utilizzare un tf.Tensor
come input.
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)>
Quando si codifica più stringhe di lunghezza variabile, utilizzare un tf.RaggedTensor
come input.
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 si dispone di un tensore con più stringhe in formato imbottito o sparse, convertirlo prima in un tf.RaggedTensor
prima di chiamare 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)>
Operazioni Unicode
Lunghezza del carattere
Utilizzare l' unit
parametro del tf.strings.length
op per indicare come lunghezza di caratteri devono essere calcolate. unit
default "BYTE"
, ma può essere impostato ad altri valori, come "UTF8_CHAR"
o "UTF16_CHAR"
, per determinare il numero di codepoints Unicode in ogni stringa codificata.
# 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
Sottostringhe di caratteri
Il tf.strings.substr
op accetta l' unit
dei parametri, e lo utilizza per determinare quale tipo di offset i pos
e len
paremeters contengono.
# 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'
Dividi le stringhe Unicode
Il tf.strings.unicode_split
operazione divide stringhe Unicode in sottostringhe dei singoli personaggi.
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)
Offset di byte per i caratteri
Per allineare il tensore di carattere generato dal tf.strings.unicode_decode
con la stringa originale, è utile conoscere l'offset per cui inizia ogni personaggio. Il metodo tf.strings.unicode_decode_with_offsets
è simile a unicode_decode
, eccetto che restituisce un secondo tensore contenente l'offset iniziale di ciascun carattere.
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
Script Unicode
Ciascun punto codice Unicode appartiene ad un unico insieme di Codepoints conosciuti come scritto . La scrittura di un carattere è utile per determinare in quale lingua potrebbe trovarsi il carattere. Ad esempio, sapere che "Б" è in caratteri cirillici indica che il testo moderno che contiene quel carattere è probabilmente di una lingua slava come il russo o l'ucraino.
Tensorflow fornisce il tf.strings.unicode_script
operazione per determinare quale script un dato usi codepoint. I codici di script sono int32
valori corrispondenti a International Components for Unicode (ICU) UScriptCode
valori.
uscript = tf.strings.unicode_script([33464, 1041]) # ['芸', 'Б']
print(uscript.numpy()) # [17, 8] == [USCRIPT_HAN, USCRIPT_CYRILLIC]
[17 8]
Il tf.strings.unicode_script
operazione può anche essere applicata a multidimensionale tf.Tensor
s o tf.RaggedTensor
s di 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]]>
Esempio: segmentazione semplice
La segmentazione è il compito di suddividere il testo in unità simili a parole. Questo è spesso facile quando vengono utilizzati caratteri di spazio per separare le parole, ma alcune lingue (come il cinese e il giapponese) non utilizzano gli spazi e alcune lingue (come il tedesco) contengono composti lunghi che devono essere divisi per analizzarne il significato. Nel testo web, lingue e scritture diverse sono spesso mescolate insieme, come in "NY株価" (New York Stock Exchange).
Possiamo eseguire una segmentazione molto approssimativa (senza implementare alcun modello ML) utilizzando modifiche nello script per approssimare i confini delle parole. Funzionerà per stringhe come l'esempio "NY株価" sopra. Funzionerà anche per la maggior parte delle lingue che utilizzano spazi, poiché i caratteri spazio di vari script sono tutti classificati come USCRIPT_COMMON, un codice di script speciale che differisce da quello di qualsiasi testo effettivo.
# dtype: string; shape: [num_sentences]
#
# The sentences to process. Edit this line to try out different inputs!
sentence_texts = [u'Hello, world.', u'世界こんにちは']
Innanzitutto, decodifica le frasi in codepoint dei caratteri e trova l'identificativo dello script per ciascun carattere.
# 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]]>
Usa gli identificatori di script per determinare dove devono essere aggiunti i confini delle parole. Aggiungi un limite di parola all'inizio di ogni frase e per ogni carattere il cui script è diverso dal carattere precedente.
# 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)
È quindi possibile utilizzare questi offset di inizio per costruire una RaggedTensor
contenente l'elenco delle parole di tutti i lotti.
# 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]]>
Per finire, il segmento della parola Codepoints RaggedTensor
di nuovo in frasi e codificare in stringhe UTF-8 per migliorare la leggibilità.
# 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']]