Tokenisation avec TF Text

Voir sur TensorFlow.org Exécuter dans Google Colab Voir sur GitHub Télécharger le cahier Voir les modèles TF Hub

Aperçu

La tokenisation est le processus consistant à diviser une chaîne en jetons. Généralement, ces jetons sont des mots, des nombres et/ou des signes de ponctuation. Le tensorflow_text paquet fournit un certain nombre de tokenizers disponibles pour le texte pré - traitement requis par vos modèles à base de texte. En effectuant la tokenisation dans le graphique TensorFlow, vous n'aurez pas à vous soucier des différences entre les workflows d'entraînement et d'inférence et la gestion des scripts de prétraitement.

Ce guide traite des nombreuses options de tokenisation fournies par TensorFlow Text, lorsque vous souhaiterez peut-être utiliser une option plutôt qu'une autre, et comment ces tokenizers sont appelés à partir de votre modèle.

Installer

pip install -q tensorflow-text
import requests
import tensorflow as tf
import tensorflow_text as tf_text

API de séparation

Les principales interfaces sont Splitter et SplitterWithOffsets qui ont des méthodes simples split et split_with_offsets . La SplitterWithOffsets variante (qui étend Splitter ) comprend une option pour obtenir des décalages d'octets. Cela permet à l'appelant de savoir à partir de quels octets de la chaîne d'origine le jeton créé a été créé.

Le Tokenizer et TokenizerWithOffsets sont des versions spécialisées du Splitter qui fournissent les méthodes pratiques tokenize et tokenize_with_offsets respectivement.

En règle générale, pour toute entrée à N dimensions, les jetons sont retournés dans un N + 1 dimensions RaggedTensor avec la dimension intérieure plus de jetons mappage pour les chaînes individuelles d' origine.

class Splitter {
  @abstractmethod
  def split(self, input)
}

class SplitterWithOffsets(Splitter) {
  @abstractmethod
  def split_with_offsets(self, input)
}

Il y a aussi une Detokenizer interface. Tout tokenizer implémentant cette interface peut accepter un tenseur irrégulier de jetons à N dimensions, et renvoie normalement un tenseur à N-1 dimensions ou un tenseur irrégulier qui a les jetons donnés assemblés.

class Detokenizer {
  @abstractmethod
  def detokenize(self, input)
}

Tokenizers

Vous trouverez ci-dessous la suite de tokenizers fournie par TensorFlow Text. Les entrées de chaîne sont supposées être UTF-8. S'il vous plaît examiner le Guide Unicode pour la conversion de chaînes en UTF-8.

Tokenizers de mots entiers

Ces tokenizers tentent de diviser une chaîne par des mots et constituent le moyen le plus intuitif de diviser du texte.

WhitespaceTokenizer

Le text.WhitespaceTokenizer est le plus tokenizer de base qui divise les chaînes sur les caractères blancs définis en soins intensifs (par exemple. Espace, tabulation, nouvelle ligne). C'est souvent bon pour construire rapidement des modèles prototypes.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: batch_gather (from tensorflow.python.ops.array_ops) is deprecated and will be removed after 2017-10-25.
Instructions for updating:
`tf.batch_gather` is deprecated, please use `tf.gather` with `batch_dims=-1` instead.
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Vous remarquerez peut-être qu'un défaut de ce tokenizer est que la ponctuation est incluse avec le mot pour constituer un jeton. Pour diviser les mots et la ponctuation en jetons séparés, le UnicodeScriptTokenizer doit être utilisé.

UnicodeScriptTokenizer

Le UnicodeScriptTokenizer se divise chaînes basées sur les limites de script Unicode. Les codes de script utilisés correspondent aux valeurs UScriptCode des composants internationaux pour Unicode (ICU). Voir: http://icu-project.org/apiref/icu4c/uscript_8h.html

Dans la pratique, ce qui est similaire à la WhitespaceTokenizer avec le plus être de différence apparente de fendage ponctuation (USCRIPT_COMMON) à partir de textes de langue (par exemple. USCRIPT_LATIN, USCRIPT_CYRILLIC, etc.) tout en séparant également des textes en langue de l'autre. Notez que cela divisera également les mots de contraction en jetons séparés.

tokenizer = tf_text.UnicodeScriptTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b'can', b"'", b't', b'explain', b',', b'but', b'you', b'feel', b'it', b'.']]

Tokenizers de sous-mots

Les tokenizers de sous-mots peuvent être utilisés avec un vocabulaire plus restreint et permettent au modèle d'avoir des informations sur les nouveaux mots à partir des sous-mots qui le créent.

Nous discutons brièvement les options ci - dessous tokenisation sous - mot, mais le tutoriel Tokenisation de sous - mots GOES plus en profondeur et explique également comment générer les fichiers vocab.

WordpieceTokenizer

La tokenisation WordPiece est un schéma de tokenisation piloté par les données qui génère un ensemble de sous-tokens. Ces sous-jetons peuvent correspondre à des morphèmes linguistiques, mais ce n'est souvent pas le cas.

Le WordpieceTokenizer s'attend à ce que l'entrée soit déjà divisée en jetons. En raison de cette condition, vous aurez souvent à diviser en utilisant le WhitespaceTokenizer ou UnicodeScriptTokenizer au préalable.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Après la chaîne est divisée en jetons, le WordpieceTokenizer peut être utilisé pour diviser en sous - éléments.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_wp_en_vocab.txt?raw=true"
r = requests.get(url)
filepath = "vocab.txt"
open(filepath, 'wb').write(r.content)
52382
subtokenizer = tf_text.UnicodeScriptTokenizer(filepath)
subtokens = tokenizer.tokenize(tokens)
print(subtokens.to_list())
[[[b'What'], [b'you'], [b'know'], [b'you'], [b"can't"], [b'explain,'], [b'but'], [b'you'], [b'feel'], [b'it.']]]

BertTokenizer

Le BertTokenizer reflète la mise en œuvre originale de la tokenisation du document BERT. Ceci est soutenu par WordpieceTokenizer, mais effectue également des tâches supplémentaires telles que la normalisation et la tokenisation des mots en premier.

tokenizer = tf_text.BertTokenizer(filepath, token_out_type=tf.string, lower_case=True)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[[b'what'], [b'you'], [b'know'], [b'you'], [b'can'], [b"'"], [b't'], [b'explain'], [b','], [b'but'], [b'you'], [b'feel'], [b'it'], [b'.']]]

SentencepieceTokenizer

Le SentencepieceTokenizer est un sous-tokenizer hautement configurable. Ceci est soutenu par la bibliothèque Sentencepiece. Comme le BertTokenizer, il peut inclure la normalisation et le fractionnement des jetons avant de les diviser en sous-jetons.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_oss_model.model?raw=true"
sp_model = requests.get(url).content
tokenizer = tf_text.SentencepieceTokenizer(sp_model, out_type=tf.string)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'\xe2\x96\x81What', b'\xe2\x96\x81you', b'\xe2\x96\x81know', b'\xe2\x96\x81you', b'\xe2\x96\x81can', b"'", b't', b'\xe2\x96\x81explain', b',', b'\xe2\x96\x81but', b'\xe2\x96\x81you', b'\xe2\x96\x81feel', b'\xe2\x96\x81it', b'.']]

D'autres diviseurs

UnicodeCharTokenizer

Cela divise une chaîne en caractères UTF-8. C'est utile pour les langages CJK qui n'ont pas d'espaces entre les mots.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]

La sortie est des points de code Unicode. Cela peut également être utile pour créer des ngrammes de caractères, tels que des bigrammes. Pour reconvertir en caractères UTF-8.

characters = tf.strings.unicode_encode(tf.expand_dims(tokens, -1), "UTF-8")
bigrams = tf_text.ngrams(characters, 2, reduction_type=tf_text.Reduction.STRING_JOIN, string_separator='')
print(bigrams.to_list())
[[b'Wh', b'ha', b'at', b't ', b' y', b'yo', b'ou', b'u ', b' k', b'kn', b'no', b'ow', b'w ', b' y', b'yo', b'ou', b'u ', b' c', b'ca', b'an', b"n'", b"'t", b't ', b' e', b'ex', b'xp', b'pl', b'la', b'ai', b'in', b'n,', b', ', b' b', b'bu', b'ut', b't ', b' y', b'yo', b'ou', b'u ', b' f', b'fe', b'ee', b'el', b'l ', b' i', b'it', b't.']]

HubModuleTokenizer

Il s'agit d'un wrapper autour des modèles déployés sur TF Hub pour faciliter les appels, car TF Hub ne prend actuellement pas en charge les tenseurs irréguliers. Le fait qu'un modèle effectue la tokenisation est particulièrement utile pour les langages CJK lorsque vous souhaitez diviser en mots, mais que vous ne disposez pas d'espaces pour fournir un guide heuristique. À l'heure actuelle, nous avons un modèle de segmentation unique pour le chinois.

MODEL_HANDLE = "https://tfhub.dev/google/zh_segmentation/1"
segmenter = tf_text.HubModuleTokenizer(MODEL_HANDLE)
tokens = segmenter.tokenize(["新华社北京"])
print(tokens.to_list())
[[b'\xe6\x96\xb0\xe5\x8d\x8e\xe7\xa4\xbe', b'\xe5\x8c\x97\xe4\xba\xac']]

Il peut être difficile d'afficher les résultats des chaînes d'octets codées en UTF-8. Décodez les valeurs de la liste pour faciliter la visualisation.

def decode_list(x):
  if type(x) is list:
    return list(map(decode_list, x))
  return x.decode("UTF-8")

def decode_utf8_tensor(x):
  return list(map(decode_list, x.to_list()))

print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

SplitMergeTokenizer

Le SplitMergeTokenizer et SplitMergeFromLogitsTokenizer ont un but ciblé de diviser une chaîne en fonction des valeurs fournies qui indiquent où la chaîne doit être divisée. Ceci est utile lors de la création de vos propres modèles de segmentation comme l'exemple de segmentation précédent.

Pour la SplitMergeTokenizer , une valeur de 0 est utilisé pour indiquer le début d'une nouvelle chaîne, et la valeur de 1 indique le caractère fait partie de la chaîne actuelle.

strings = ["新华社北京"]
labels = [[0, 1, 1, 0, 1]]
tokenizer = tf_text.SplitMergeTokenizer()
tokens = tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

Le SplitMergeFromLogitsTokenizer est similaire, mais il accepte au lieu des paires de valeurs logit à partir d' un réseau de neurones qui permettent de prédire si chaque caractère devrait être divisé en une nouvelle chaîne ou fusionnée dans l'actuel.

strings = [["新华社北京"]]
labels = [[[5.0, -3.2], [0.2, 12.0], [0.0, 11.0], [2.2, -1.0], [-3.0, 3.0]]]
tokenizer = tf_text.SplitMergeFromLogitsTokenizer()
tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

Séparateur d'expression régulière

Le RegexSplitter est capable de chaînes de segments à des points d' arrêt arbitraires définies par une expression régulière fournie.

splitter = tf_text.RegexSplitter("\s")
tokens = splitter.split(["What you know you can't explain, but you feel it."], )
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

Décalages

Lors de la segmentation des chaînes, il est souvent souhaitable de savoir d'où provient le jeton dans la chaîne d'origine. Pour cette raison, chaque tokenizer qui met en œuvre TokenizerWithOffsets a une méthode tokenize_with_offsets qui renverra les décalages d'octets ainsi que les jetons. start_offsets répertorie les octets de la chaîne d'origine à laquelle chaque jeton commence, et end_offsets répertorie les octets immédiatement après le point où chaque jeton se termine. Pour recadrer, les décalages de début sont inclusifs et les décalages de fin sont exclusifs.

tokenizer = tf_text.UnicodeScriptTokenizer()
(tokens, start_offsets, end_offsets) = tokenizer.tokenize_with_offsets(['Everything not saved will be lost.'])
print(tokens.to_list())
print(start_offsets.to_list())
print(end_offsets.to_list())
[[b'Everything', b'not', b'saved', b'will', b'be', b'lost', b'.']]
[[0, 11, 15, 21, 26, 29, 33]]
[[10, 14, 20, 25, 28, 33, 34]]

Détokenisation

Tokenizers qui mettent en œuvre la Detokenizer fournissent une detokenize méthode qui tente de concaténer les chaînes. Cela peut entraîner des pertes, de sorte que la chaîne détokenisée peut ne pas toujours correspondre exactement à la chaîne d'origine pré-tokenisée.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
strings = tokenizer.detokenize(tokens)
print(strings.numpy())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]
[b"What you know you can't explain, but you feel it."]

Données TF

TF Data est une API puissante pour créer un pipeline d'entrée pour les modèles d'entraînement. Les tokenizers fonctionnent comme prévu avec l'API.

docs = tf.data.Dataset.from_tensor_slices([['Never tell me the odds.'], ["It's a trap!"]])
tokenizer = tf_text.WhitespaceTokenizer()
tokenized_docs = docs.map(lambda x: tokenizer.tokenize(x))
iterator = iter(tokenized_docs)
print(next(iterator).to_list())
print(next(iterator).to_list())
[[b'Never', b'tell', b'me', b'the', b'odds.']]
[[b"It's", b'a', b'trap!']]