MLコミュニティデーは11月9日です! TensorFlow、JAXからの更新のために私たちに参加し、より多くの詳細をご覧ください

サブワードトークナイザー

TensorFlow.orgで表示 GoogleColabで実行 GitHubでソースを表示 ノートブックをダウンロードする

このチュートリアルでは、データセットからサブワード語彙を生成し、構築するためにそれを使用する方法を示しtext.BertTokenizer語彙から。

サブワードトークナイザーの主な利点は、ワードベースのトークン化と文字ベースのトークン化の間で補間することです。一般的な単語は語彙にスロットを取得しますが、トークナイザーは未知の単語の単語の断片や個々の文字にフォールバックできます。

概要

tensorflow_textパッケージには、多くの一般的なトークナイザのTensorFlow実装が含まれています。これには、3つのサブワードスタイルのトークナイザーが含まれます。

  • text.BertTokenizer - BertTokenizerクラスは、より高いレベルのインタフェースです。これは、BERTのトークン分割アルゴリズムと含まWordPieceTokenizer 。これは、入力として文章を取り、トークンIDを返します。
  • text.WordpeiceTokenizer - WordPieceTokenizerクラスは、下位レベルのインターフェースです。それだけで実装WordPieceアルゴリズムを。呼び出す前に、テキストを標準化して単語に分割する必要があります。これは、入力として単語を取り、トークンIDを返します。
  • text.SentencepieceTokenizer - SentencepieceTokenizerより複雑な設定が必要です。その初期化子には、事前にトレーニングされたセンテンスピースモデルが必要です。参照してくださいグーグル/ sentencepieceリポジトリこれらのモデルのいずれかを構築する方法については、を。トークン化するときには、入力として文章を受け入れることができます。

このチュートリアルでは、既存の単語から始めて、トップダウン方式でWordpieceの語彙を構築します。日本語、中国語、韓国語には明確な複数文字の単位がないため、このプロセスは機能しません。 conside使用してこれらの言語トークン化text.SentencepieceTokenizertext.UnicodeCharTokenizerまたはこのアプローチを

設定

pip install -q -U tensorflow-text
pip install -q tensorflow_datasets
import collections
import os
import pathlib
import re
import string
import sys
import tempfile
import time

import numpy as np
import matplotlib.pyplot as plt

import tensorflow_datasets as tfds
import tensorflow_text as text
import tensorflow as tf
2021-08-11 18:46:18.414452: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
tf.get_logger().setLevel('ERROR')
pwd = pathlib.Path.cwd()

データセットをダウンロードする

ポルトガル語/英語の翻訳データセットを取得するのTFD

examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
                               as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']
2021-08-11 18:46:23.603290: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-11 18:46:24.267575: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.268580: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-11 18:46:24.268617: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:46:24.271953: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-11 18:46:24.272053: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-11 18:46:24.273192: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-11 18:46:24.273527: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-11 18:46:24.274580: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-11 18:46:24.275559: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-11 18:46:24.275779: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-11 18:46:24.275884: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.276973: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.277878: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:46:24.278525: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-11 18:46:24.279136: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.280033: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-11 18:46:24.280116: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.281009: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.281856: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:46:24.281894: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:46:24.912068: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-11 18:46:24.912106: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-11 18:46:24.912115: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-11 18:46:24.912363: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.913392: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.914368: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.915336: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)

このデータセットは、ポルトガル語と英語の文のペアを生成します。

for pt, en in train_examples.take(1):
  print("Portuguese: ", pt.numpy().decode('utf-8'))
  print("English:   ", en.numpy().decode('utf-8'))
2021-08-11 18:46:25.028577: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-11 18:46:25.029197: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000165000 Hz
Portuguese:  e quando melhoramos a procura , tiramos a única vantagem da impressão , que é a serendipidade .
English:    and when you improve searchability , you actually take away the one advantage of print , which is serendipity .

上記の例文についていくつか注意してください。

  • それらは小文字です。
  • 句読点の周りにスペースがあります。
  • ユニコード正規化が使用されているかどうか、またはどのユニコード正規化が使用されているかは明確ではありません。
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)

語彙を生成する

このセクションでは、データセットからワードピースの語彙を生成します。あなたは既に語彙ファイルを持っているだけで構築する方法を確認したい場合はtext.BertTokenizerまたはtext.Wordpieceそれは、あなたが先にスキップすることができるとのトークナイザをビルドトークナイザのセクション。

語彙生成コードが含まれtensorflow_textピップパッケージ。デフォルトではインポートされません。手動でインポートする必要があります。

from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab

bert_vocab.bert_vocab_from_dataset機能は、語彙を生成します。

その動作を調整するために設定できる多くの引数があります。このチュートリアルでは、主にデフォルトを使用します。あなたはより多くのオプションについて知りたい場合は、最初に読んアルゴリズム、その後、見ていたコードを

これには約2分かかります。

bert_tokenizer_params=dict(lower_case=True)
reserved_tokens=["[PAD]", "[UNK]", "[START]", "[END]"]

bert_vocab_args = dict(
    # The target vocabulary size
    vocab_size = 8000,
    # Reserved tokens that must be included in the vocabulary
    reserved_tokens=reserved_tokens,
    # Arguments for `text.BertTokenizer`
    bert_tokenizer_params=bert_tokenizer_params,
    # Arguments for `wordpiece_vocab.wordpiece_tokenizer_learner_lib.learn`
    learn_params={},
)
%%time
pt_vocab = bert_vocab.bert_vocab_from_dataset(
    train_pt.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 36s, sys: 2.48 s, total: 1min 39s
Wall time: 1min 33s

結果の語彙の一部を次に示します。

print(pt_vocab[:10])
print(pt_vocab[100:110])
print(pt_vocab[1000:1010])
print(pt_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['no', 'por', 'mais', 'na', 'eu', 'esta', 'muito', 'isso', 'isto', 'sao']
['90', 'desse', 'efeito', 'malaria', 'normalmente', 'palestra', 'recentemente', '##nca', 'bons', 'chave']
['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']

語彙ファイルを書く:

def write_vocab_file(filepath, vocab):
  with open(filepath, 'w') as f:
    for token in vocab:
      print(token, file=f)
write_vocab_file('pt_vocab.txt', pt_vocab)

その関数を使用して、英語のデータから語彙を生成します。

%%time
en_vocab = bert_vocab.bert_vocab_from_dataset(
    train_en.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 8s, sys: 1.98 s, total: 1min 10s
Wall time: 1min 5s
print(en_vocab[:10])
print(en_vocab[100:110])
print(en_vocab[1000:1010])
print(en_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['as', 'all', 'at', 'one', 'people', 're', 'like', 'if', 'our', 'from']
['choose', 'consider', 'extraordinary', 'focus', 'generation', 'killed', 'patterns', 'putting', 'scientific', 'wait']
['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']

2つの語彙ファイルは次のとおりです。

write_vocab_file('en_vocab.txt', en_vocab)
ls *.txt
en_vocab.txt  pt_vocab.txt

トークナイザーを構築する

text.BertTokenizer (上のセクションを参照の最初の引数として語彙ファイルのパスを渡すことで初期化することができtf.lookup他のオプションのために):

pt_tokenizer = text.BertTokenizer('pt_vocab.txt', **bert_tokenizer_params)
en_tokenizer = text.BertTokenizer('en_vocab.txt', **bert_tokenizer_params)

これで、これを使用してテキストをエンコードできます。英語のデータから3つの例のバッチを取ります:

for pt_examples, en_examples in train_examples.batch(3).take(1):
  for ex in en_examples:
    print(ex.numpy())
b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .'
b'but what if it were active ?'
b"but they did n't test for curiosity ."

それを実行しBertTokenizer.tokenize方法。最初に、この戻りtf.RaggedTensor軸を有する(batch, word, word-piece)

# Tokenize the examples -> (batch, word, word-piece)
token_batch = en_tokenizer.tokenize(en_examples)
# Merge the word and word-piece axes -> (batch, tokens)
token_batch = token_batch.merge_dims(-2,-1)

for ex in token_batch.to_list():
  print(ex)
[72, 117, 79, 1259, 1491, 2362, 13, 79, 150, 184, 311, 71, 103, 2308, 74, 2679, 13, 148, 80, 55, 4840, 1434, 2423, 540, 15]
[87, 90, 107, 76, 129, 1852, 30]
[87, 83, 149, 50, 9, 56, 664, 85, 2512, 15]

あなたはそれらのテキスト表現でトークンIDを交換した場合(使用tf.gather )あなたは言葉最初の例ではそれを見ることができる"searchability""serendipity"に分解されている"search ##ability""s ##ere ##nd ##ip ##ity"

# Lookup each token id in the vocabulary.
txt_tokens = tf.gather(en_vocab, token_batch)
# Join with spaces.
tf.strings.reduce_join(txt_tokens, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve search ##ability , you actually take away the one advantage of print , which is s ##ere ##nd ##ip ##ity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

抽出されたトークンから単語を再度組み立てるには、使用BertTokenizer.detokenize方法を:

words = en_tokenizer.detokenize(token_batch)
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

カスタマイズとエクスポート

このチュートリアルでは、で使用されるテキストのトークナイザとdetokenizer構築する変圧器のチュートリアルを。このセクションでは、使用方法とそのチュートリアルを簡素化するための処理工程、および輸出トークナイザを追加tf.saved_model彼らは他のチュートリアルでインポートすることができるようにします。

カスタムトークン化

下流のチュートリアルでは、トークン化されたテキストが含まれるように期待して、両方の[START][END]トークンを。

reserved_tokensそう、語彙の先頭にスペースを確保[START][END]両方の言語で同じインデックスを持っています

START = tf.argmax(tf.constant(reserved_tokens) == "[START]")
END = tf.argmax(tf.constant(reserved_tokens) == "[END]")

def add_start_end(ragged):
  count = ragged.bounding_shape()[0]
  starts = tf.fill([count,1], START)
  ends = tf.fill([count,1], END)
  return tf.concat([starts, ragged, ends], axis=1)
words = en_tokenizer.detokenize(add_start_end(token_batch))
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'[START] and when you improve searchability , you actually take away the one advantage of print , which is serendipity . [END]',
       b'[START] but what if it were active ? [END]',
       b"[START] but they did n ' t test for curiosity . [END]"],
      dtype=object)>

カスタムトークン化解除

トークナイザーをエクスポートする前に、ダウンストリームチュートリアル用にクリーンアップできることがいくつかあります。

  1. 彼らはとても、きれいなテキスト出力を生成するように予約されたトークンをドロップしたい[START] [END][PAD]
  2. 彼らは完全な文字列に興味を持っているので、文字列を適用するに沿って参加words結果の軸。
def cleanup_text(reserved_tokens, token_txt):
  # Drop the reserved tokens, except for "[UNK]".
  bad_tokens = [re.escape(tok) for tok in reserved_tokens if tok != "[UNK]"]
  bad_token_re = "|".join(bad_tokens)

  bad_cells = tf.strings.regex_full_match(token_txt, bad_token_re)
  result = tf.ragged.boolean_mask(token_txt, ~bad_cells)

  # Join them into strings.
  result = tf.strings.reduce_join(result, separator=' ', axis=-1)

  return result
en_examples.numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n't test for curiosity ."], dtype=object)
token_batch = en_tokenizer.tokenize(en_examples).merge_dims(-2,-1)
words = en_tokenizer.detokenize(token_batch)
words
<tf.RaggedTensor [[b'and', b'when', b'you', b'improve', b'searchability', b',', b'you', b'actually', b'take', b'away', b'the', b'one', b'advantage', b'of', b'print', b',', b'which', b'is', b'serendipity', b'.'], [b'but', b'what', b'if', b'it', b'were', b'active', b'?'], [b'but', b'they', b'did', b'n', b"'", b't', b'test', b'for', b'curiosity', b'.']]>
cleanup_text(reserved_tokens, words).numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)

書き出す

次のコードブロックビルドCustomTokenizer格納するクラスをtext.BertTokenizerインスタンス、カスタム・ロジック、および@tf.function輸出に必要なラッパーを。

class CustomTokenizer(tf.Module):
  def __init__(self, reserved_tokens, vocab_path):
    self.tokenizer = text.BertTokenizer(vocab_path, lower_case=True)
    self._reserved_tokens = reserved_tokens
    self._vocab_path = tf.saved_model.Asset(vocab_path)

    vocab = pathlib.Path(vocab_path).read_text().splitlines()
    self.vocab = tf.Variable(vocab)

    ## Create the signatures for export:   

    # Include a tokenize signature for a batch of strings. 
    self.tokenize.get_concrete_function(
        tf.TensorSpec(shape=[None], dtype=tf.string))

    # Include `detokenize` and `lookup` signatures for:
    #   * `Tensors` with shapes [tokens] and [batch, tokens]
    #   * `RaggedTensors` with shape [batch, tokens]
    self.detokenize.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.detokenize.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    self.lookup.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.lookup.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    # These `get_*` methods take no arguments
    self.get_vocab_size.get_concrete_function()
    self.get_vocab_path.get_concrete_function()
    self.get_reserved_tokens.get_concrete_function()

  @tf.function
  def tokenize(self, strings):
    enc = self.tokenizer.tokenize(strings)
    # Merge the `word` and `word-piece` axes.
    enc = enc.merge_dims(-2,-1)
    enc = add_start_end(enc)
    return enc

  @tf.function
  def detokenize(self, tokenized):
    words = self.tokenizer.detokenize(tokenized)
    return cleanup_text(self._reserved_tokens, words)

  @tf.function
  def lookup(self, token_ids):
    return tf.gather(self.vocab, token_ids)

  @tf.function
  def get_vocab_size(self):
    return tf.shape(self.vocab)[0]

  @tf.function
  def get_vocab_path(self):
    return self._vocab_path

  @tf.function
  def get_reserved_tokens(self):
    return tf.constant(self._reserved_tokens)

ビルドしCustomTokenizer言語ごとに:

tokenizers = tf.Module()
tokenizers.pt = CustomTokenizer(reserved_tokens, 'pt_vocab.txt')
tokenizers.en = CustomTokenizer(reserved_tokens, 'en_vocab.txt')

トークナイザをエクスポートsaved_model

model_name = 'ted_hrlr_translate_pt_en_converter'
tf.saved_model.save(tokenizers, model_name)
2021-08-11 18:49:08.917974: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.

リロードsaved_modelとメソッドをテストします。

reloaded_tokenizers = tf.saved_model.load(model_name)
reloaded_tokenizers.en.get_vocab_size().numpy()
7010
tokens = reloaded_tokenizers.en.tokenize(['Hello TensorFlow!'])
tokens.numpy()
array([[   2, 4006, 2358,  687, 1192, 2365,    4,    3]])
text_tokens = reloaded_tokenizers.en.lookup(tokens)
text_tokens
<tf.RaggedTensor [[b'[START]', b'hello', b'tens', b'##or', b'##f', b'##low', b'!', b'[END]']]>
round_trip = reloaded_tokenizers.en.detokenize(tokens)

print(round_trip.numpy()[0].decode('utf-8'))
hello tensorflow !

以下のためにそれをアーカイブ翻訳チュートリアル

zip -r {model_name}.zip {model_name}
adding: ted_hrlr_translate_pt_en_converter/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/variables/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.data-00000-of-00001 (deflated 51%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.index (deflated 33%)
  adding: ted_hrlr_translate_pt_en_converter/assets/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/assets/pt_vocab.txt (deflated 57%)
  adding: ted_hrlr_translate_pt_en_converter/assets/en_vocab.txt (deflated 54%)
  adding: ted_hrlr_translate_pt_en_converter/saved_model.pb (deflated 91%)
du -h *.zip
184K    ted_hrlr_translate_pt_en_converter.zip

オプション:アルゴリズム

ここで、WordPieceアルゴリズムにはボトムアップとトップダウンの2つのバージョンがあることに注意してください。どちらの場合も、目標は同じです。「トレーニングコーパスと必要なトークンDの数が与えられた場合、最適化問題は、選択したワードピースモデルに従ってセグメント化したときに、結果のコーパスがワードピースの数で最小になるようにDワードピースを選択することです。 「」

オリジナルボトムアップWordPieceアルゴリズムは、に基づいて、バイトペア符号化。 BPEのように、それはアルファベットで始まり、一般的なバイグラムを繰り返し組み合わせて単語の断片と単語を形成します。

TensorFlowテキストの語彙・ジェネレータは、からのトップダウンの実装は次のBERTを。単語から始めて、頻度のしきい値に達するまで、またはそれ以上分解できないまで、単語を小さなコンポーネントに分解します。次のセクションでは、これについて詳しく説明します。日本語、中国語、韓国語の場合、最初に明示的な単語単位がないため、このトップダウンアプローチは機能しません。それらのためにあなたが必要とする別のアプローチを

語彙の選択

WordPieceトップダウン生成アルゴリズムは、(ワードカウント)対閾値のセットを取り込みT 、戻る語彙V

アルゴリズムは反復的です。それはのために実行されたk通常の反復、 k = 4が、最初の2つだけは本当に重要です。 3番目と4番目(およびそれ以降)は2番目とまったく同じです。バイナリ探索の各ステップは、のためにゼロからアルゴリズムを実行することに留意されたいk反復。

以下に説明する反復:

最初の反復

  1. すべての単語を反復として示される、入力に対をカウント(w, c)
  2. 各単語についてw 、と表記すべての部分文字列、発生するs 。例えば、単語のためのhuman 、我々が生成する{h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
  3. サブ・ツー・カウントハッシュマップを維持し、それぞれのカウント増分sすることによりc 。我々が持っている場合などは、 (human, 113)及び(humas, 3)私たちの入力中に、カウントs = humaなります113+3=116
  4. 我々は、すべての部分文字列の数を集めたら、反復は、上(s, c)のペア、最長で始まるs最初の
  5. 任意のキープs持っているc > T 。例えば、もしT = 100 、我々は持っている(pers, 231); (dogs, 259); (##rint; 76)その後、我々は続けるだろうpersdogs
  6. ときs保持され、そのプレフィックスのすべてからそのカウントを差し引きます。これは、すべての並べ替えの理由であるsそれ以外の言葉が二重にカウントされるため、これはアルゴリズムの重要な一部であり、ステップ4で長さ。例えば、我々が守ってきたとしましょうhuman 、私たちはを取得(huma, 116)我々はことを知っている113人の116から来たhuman 、そして3から来たhumas 。しかし、今というhuman私たちの語彙であり、我々は決してセグメント知っているhumanhuma ##n 。一度だから、 human保たれてきた、そしてhuma唯一の効果的な数がある3

このアルゴリズムは、単語ピースのセットが生成されますs (単語全体になりますが、多くのw我々はWordPiece語彙として使用することができ、)。

ただし、問題があります。このアルゴリズムは、単語の断片を大幅に過剰生成します。その理由は、プレフィックストークンのカウントを差し引くだけだからです。我々はワード続ける場合はそのため、 human 、私たちはのカウントをオフに差し引きますh, hu, hu, humaではなく、ために##u, ##um, ##uma, ##umanなど。我々は両方生成する場合がありますのでhuman##umanにもかかわらず、単語片として、 ##uman適用されることはありませんが。

なぜ、すべての部分文字列だけでなく、すべての接頭辞のためのカウントを差し引くありませんか?その場合、カウントを複数回減算することになりかねないためです。その私たちがしている処理しましょうと言うs長さ5の、我々は両方の維持(##denia, 129)(##eniab, 137) 65それらのカウントの単語から来undeniable 。我々は、すべての部分文字列から差し引く場合、我々は引くだろう65のサブストリングから##enia我々は一度だけ差し引く必要があるにもかかわらず、二回。ただし、プレフィックスから減算するだけの場合、正しく減算されるのは1回だけです。

2番目(および3番目...)の反復

上記の過剰生成の問題を解決するために、アルゴリズムを複数回繰り返します。

後続の反復は、一つの重要な違いで、最初と同じです:ステップ2では、代わりにすべての部分文字列を考慮すると、我々は以前の反復からの語彙を使用してWordPieceのトークン化アルゴリズムを適用し、唯一の分岐点に開始するサブストリングを検討します。

例えば、我々は、アルゴリズムのステップ2を実行し、単語に遭遇しているとしましょうundeniable 。最初の反復では、我々はすべての部分文字列を、検討するなど、 {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...}

ここで、2回目の反復では、これらのサブセットのみを検討します。最初の反復の後、関連する単語の断片は次のようになります。

un, ##deni, ##able, ##ndeni, ##iable

WordPieceアルゴリズム意志セグメントこのun ##deni ##able (セクション参照適用WordPiece詳細については)。この場合、我々は唯一の分割点から開始するサブストリングを検討します。我々はまだ、すべての可能な終了位置を検討します。だから、2回目の反復の間に、一連のsためundeniable次のとおりです。

{u, un, und, unden, undeni, undenia, undeniab, undeniabl, undeniable, ##d, ##de, ##den, ##deni, ##denia, ##deniab, ##deniabl , ##deniable, ##a, ##ab, ##abl, ##able}

それ以外のアルゴリズムは同じです。この例では、最初の反復では、アルゴリズムはsupriousトークン生成##ndeni及び##iable 。現在、これらのトークンは考慮されないため、2回目の反復では生成されません。結果が収束することを確認するために、いくつかの反復を実行します(ただし、文字通りの収束保証はありません)。

WordPieceの適用

WordPieceの語彙が生成されたら、それを新しいデータに適用できるようにする必要があります。アルゴリズムは、単純な貪欲な最長一致優先アプリケーションです。

例えば、単語を分割考慮undeniable

私たちは最初のルックアップundeniable当社WordPiece辞書で、それの存在する場合、我々は完了です。そうでない場合、私たちは、一つの文字、と繰り返しにより、例えばエンドポイントをデクリメントundeniabl

最終的には、語彙にサブトークンを見つけるか、1文字のサブトークンになります。 (一般的には、我々は、これはまれなUnicode文字の場合ではないかもしれないが。我々は単に単語全体をマップ語彙にない珍しいUnicode文字が発生した場合、すべての文字が、私たちの語彙であると仮定<unk>を。

この場合、我々は見つけるun私たちの語彙に。これが私たちの最初の言葉です。その後、我々はの終わりにジャンプしunと探してみてください、例えば、処理を繰り返す##deniable 、その後、 ##deniabl我々は単語全体をセグメント化するまでこれが繰り返されるなど、。

直感

直感的に、WordPieceトークン化は2つの異なる目的を達成しようとしています。

  1. できるだけ片の最小数にデータをトークン化。 WordPieceアルゴリズムは単語を分割することを「望んでいない」ことを覚えておくことが重要です。そうでなければ、それだけで、その文字にすべての単語を分割することになる、例えば、 human -> {h, ##u, ##m, ##a, #n}これも一般的な単語のための言語的形態素に分割されます、形態素スプリッタからWordPieceの異なるを作る1つの重要な事である(例えば、 unwanted -> {un, want, ed} )。

  2. 単語を分割する必要がある場合は、トレーニングデータで最大数の単語に分割します。例えば、ワード理由undeniableに分割されるだろう{un, ##deni, ##able}というなどの代替よりも{unde, ##niab, ##le}であるのカウントun##ableでこれらは一般的な接頭辞と接尾辞であるため、特に非常に高くなります。カウントにもかかわらず##leよりも高くなければならない##able 、低カウントunde##niab 、このアルゴリズムにあまり「望ましい」トークン化になります。

オプション:tf.lookup

あなたがへのアクセス、または語彙をより詳細に制御する必要がある場合はそれの価値があるあなたが、ルックアップテーブルを自分で構築しにそれを渡すことができることを指摘しBertTokenizer

あなたは、文字列を渡すと、 BertTokenizer次の処理を行います。

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.TextFileInitializer(
        filename='pt_vocab.txt',
        key_dtype=tf.string,
        key_index = tf.lookup.TextFileIndex.WHOLE_LINE,
        value_dtype = tf.int64,
        value_index=tf.lookup.TextFileIndex.LINE_NUMBER)) 
pt_tokenizer = text.BertTokenizer(pt_lookup)

これで、トークナイザーで使用されるルックアップテーブルに直接アクセスできます。

pt_lookup.lookup(tf.constant(['é', 'um', 'uma', 'para', 'não']))
<tf.Tensor: shape=(5,), dtype=int64, numpy=array([7765,   85,   86,   87, 7765])>

あなたは語彙ファイル、使用する必要はありませんtf.lookup他の初期化オプションがあります。あなたはメモリ内の語彙を持っている場合は、使用することができlookup.KeyValueTensorInitializer

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.KeyValueTensorInitializer(
        keys=pt_vocab,
        values=tf.range(len(pt_vocab), dtype=tf.int64))) 
pt_tokenizer = text.BertTokenizer(pt_lookup)