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

TFテキストによるトークン化

TensorFlow.orgで表示 GoogleColabで実行 GitHubで表示 ノートブックをダウンロードするTFハブモデルを参照してください

概要

トークン化は、文字列をトークンに分割するプロセスです。通常、これらのトークンは単語、数字、句読点です。 tensorflow_textパッケージには、テキストベースのモデルで必要なテキストを前処理に利用可能なトークナイザの数を提供します。 TensorFlowグラフでトークン化を実行することにより、トレーニングワークフローと推論ワークフローの違いや前処理スクリプトの管理について心配する必要がなくなります。

このガイドでは、TensorFlow Textが提供する多くのトークン化オプション、あるオプションを別のオプションよりも使用する場合、およびこれらのトークナイザーがモデル内からどのように呼び出されるかについて説明します。

設定

pip install -q tensorflow-text
import requests
import tensorflow as tf
import tensorflow_text as tf_text
2021-08-11 18:54:22.357951: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

スプリッターAPI

主なインターフェイスは、 SplitterSplitterWithOffsets単方法有するsplitsplit_with_offsetsSplitterWithOffsets (拡張バリアントSplitter )バイトオフセットを取得するためのオプションが含まれています。これにより、呼び出し元は、作成されたトークンが元の文字列のどのバイトから作成されたかを知ることができます。

TokenizerTokenizerWithOffsets専門のバージョンであるSplitter便利な方法を提供するtokenize及びtokenize_with_offsetsそれぞれ。

一般に、任意のN次元の入力のために、返されるトークンは、N + 1次元にあるRaggedTensor元の個々のストリングにマッピングトークンの最も内側の寸法を有します。

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

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

ありDetokenizerインターフェースが。このインターフェースを実装する任意のトークナイザーは、トークンのN次元の不規則なテンソルを受け入れることができ、通常、指定されたトークンが一緒に組み立てられたN-1次元のテンソルまたは不規則なテンソルを返します。

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

トークナイザー

以下は、TensorFlowTextが提供する一連のトークナイザーです。文字列入力はUTF-8であると想定されています。確認してくださいUnicodeのガイドをUTF-8に文字列を変換します。

全単語トークナイザー

これらのトークナイザーは、文字列を単語で分割しようとし、テキストを分割する最も直感的な方法です。

WhitespaceTokenizer

text.WhitespaceTokenizer ICU定義された空白文字(例えばスペース、タブ、改行)で文字列を分割し、最も基本的なトークナイザです。これは、プロトタイプモデルをすばやく構築するのに適していることがよくあります。

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
2021-08-11 18:54:24.101492: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-11 18:54:24.764294: 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:54:24.765226: 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:54:24.765257: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:54:24.768994: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-11 18:54:24.769093: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-11 18:54:24.770272: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-11 18:54:24.770599: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-11 18:54:24.771810: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-11 18:54:24.772737: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-11 18:54:24.772912: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-11 18:54:24.773009: 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:54:24.773989: 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:54:24.774867: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:54:24.775527: 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:54:24.776134: 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:54:24.777106: 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:54:24.777201: 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:54:24.778110: 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:54:24.779002: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:54:24.779041: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:54:25.384624: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-11 18:54:25.384660: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-11 18:54:25.384668: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-11 18:54:25.384904: 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:54:25.385835: 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:54:25.386827: 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:54:25.387653: 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)
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.']]

このトークナイザーの欠点は、トークンを構成する単語に句読点が含まれていることです。別のトークンに単語や句読点を分割するには、 UnicodeScriptTokenizer使用する必要があります。

UnicodeScriptTokenizer

UnicodeScriptTokenizer Unicodeのスクリプトの境界に基づいて文字列を分割します。使用されるスクリプトコードは、International Components for Unicode(ICU)UScriptCode値に対応しています。参照: http://icu-project.org/apiref/icu4c/uscript_8h.html

実際には、これはと似てWhitespaceTokenizerもお互いに言語のテキストを分離しながら(例えば。USCRIPT_LATIN、USCRIPT_CYRILLIC、など)は、最も明白な違いは、それは言語のテキストから句読点(USCRIPT_COMMON)を分割することであることで。これにより、短縮語も個別のトークンに分割されることに注意してください。

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'.']]

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

サブワードトークナイザーは、より小さな語彙で使用でき、モデルがそれを作成するサブワードからの新しい単語に関する情報を持つことができます。

当社は、以下に簡単にサブワードトークン化オプションを議論するが、サブワードトークン化のチュートリアルでは、より深くなり、また、単語ファイルを生成する方法について説明します。

WordpieceTokenizer

WordPieceトークン化は、サブトークンのセットを生成するデータ駆動型のトークン化スキームです。これらのサブトークンは言語形態素に対応している場合がありますが、そうでない場合がよくあります。

WordpieceTokenizerは、入力がすでにトークンに分割されていることを想定しています。このための前提条件で、あなたは多くの場合、使用して分割することになるでしょうWhitespaceTokenizerまたはUnicodeScriptTokenizer事前に。

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.']]

文字列をトークンに分割された後、 WordpieceTokenizerサブトークンに分割するために使用することができます。

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

BertTokenizerは、BERTペーパーからのトークン化の元の実装を反映しています。これはWordpieceTokenizerによってサポートされていますが、最初に単語への正規化やトークン化などの追加タスクも実行します。

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

SentencepieceTokenizerは、高度に構成可能なサブトークントークナイザーです。これは、Sentencepieceライブラリによってサポートされています。 BertTokenizerと同様に、サブトークンに分割する前に、正規化とトークン分割を含めることができます。

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'.']]

その他のスプリッター

UnicodeCharTokenizer

これにより、文字列がUTF-8文字に分割されます。単語間にスペースがないCJK言語に役立ちます。

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]]

出力はUnicodeコードポイントです。これは、バイグラムなどの文字ngramの作成にも役立ちます。 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

これは、TF Hubが現在不規則なテンソルをサポートしていないため、呼び出しを容易にするためにTFHubにデプロイされたモデルのラッパーです。モデルにトークン化を実行させることは、単語に分割したいがヒューリスティックガイドを提供するスペースがない場合にCJK言語で特に役立ちます。現時点では、中国語の単一のセグメンテーションモデルがあります。

MODEL_HANDLE = "https://tfhub.dev/google/zh_segmentation/1"
segmenter = tf_text.HubModuleTokenizer(MODEL_HANDLE)
tokens = segmenter.tokenize(["新华社北京"])
print(tokens.to_list())
2021-08-11 18:54:30.368430: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-11 18:54:30.369020: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000165000 Hz
2021-08-11 18:54:31.125778: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-11 18:54:31.579674: I tensorflow/stream_executor/cuda/cuda_dnn.cc:359] Loaded cuDNN version 8100
2021-08-11 18:54:36.707170: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
[[b'\xe6\x96\xb0\xe5\x8d\x8e\xe7\xa4\xbe', b'\xe5\x8c\x97\xe4\xba\xac']]
2021-08-11 18:54:37.063024: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11

UTF-8でエンコードされたバイト文字列の結果を表示するのは難しい場合があります。リスト値をデコードして、見やすくします。

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

SplitMergeTokenizerSplitMergeFromLogitsTokenizer 、文字列を分割すべき場所を示す提供された値に基づいて文字列を分割の標的目的を持っています。これは、前のセグメンテーションの例のように独自のセグメンテーションモデルを構築するときに役立ちます。

SplitMergeTokenizer 、0の値は、新しい文字列の開始を示すために使用され、1の値は、文字が現在の文字列の一部であることを示しています。

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

SplitMergeFromLogitsTokenizer似ていますが、代わりに各文字が新しい文字列に分割したり、現在の1にマージする必要があるかどうかを予測ニューラルネットワークからロジット値のペアを受け入れます。

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))
[['新华社', '北京']]

RegexSplitter

RegexSplitter提供正規表現によって定義された任意のブレークポイントでセグメント列することができます。

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.']]

オフセット

文字列をトークン化する場合、元の文字列のどこからトークンが発生したかを知りたいことがよくあります。このため、実装の各トークナイザのためTokenizerWithOffsetsトークンと一緒にバイトオフセットを返しますtokenize_with_offsets方法があります。 start_offsetsは、各トークンが開始する元の文字列のバイトを一覧表示し、end_offsetsは、各トークンが終了するポイントの直後のバイトを一覧表示します。リフレージングするには、開始オフセットは包括的であり、終了オフセットは排他的です。

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]]

トークン化解除

実装のトークナイザDetokenizer提供detokenize文字列を結合しようとする試みの方法を。これは損失を被る可能性があるため、トークン化解除された文字列は、トークン化された元の文字列と常に正確に一致するとは限りません。

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."]

TFデータ

TF Dataは、トレーニングモデルの入力パイプラインを作成するための強力なAPIです。トークナイザーは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!']]