質問があります? TensorFlowフォーラム訪問フォーラムでコミュニティとつながる

テキスト タスクの一般的な SavedModel API

このページでは、テキスト関連のタスク用のTF2 SavedModelsReusable SavedModel API を実装する方法について説明します。 (これは、現在非推奨となっているTF1 Hub 形式のテキスト共通署名を置き換えて拡張します。)

概要

テキスト埋め込みテキストの密な表現、またはテキスト特徴ベクトルとも呼ばれます)を計算するためのAPIがいくつかあります。

  • テキスト入力からのテキスト埋め込み用の API は、文字列のバッチを埋め込みベクトルのバッチにマップする SavedModel によって実装されます。これは非常に使いやすく、TF Hub の多くのモデルで実装されています。ただし、これでは TPU でモデルを微調整することはできません。

  • 前処理された入力を使用したテキスト埋め込み用の API は同じタスクを解決しますが、2 つの別個の SavedModel によって実装されます。

    • tf.data入力パイプライン内で実行でき、文字列やその他の可変長データを数値テンソルに変換できるプリプロセッサ
    • プリプロセッサの結果を受け入れ、埋め込み計算のトレーニング可能な部分を実行するエンコーダ

    この分割により、トレーニング ループに入力される前に、入力を非同期的に前処理できます。特に、 TPUで実行および微調整できるエンコーダーを構築できます。

  • Transformer エンコーダーを使用したテキスト埋め込み用の API は、前処理された入力から BERT およびその他の Transformer エンコーダーの特定のケースにテキスト埋め込み用の API を拡張します。

    • プリプロセッサは、入力テキストの複数のセグメントからエンコーダ入力を構築するように拡張されています。

    • Transformerエンコーダーは、個々のトークンのコンテキスト認識埋め込みを公開します。

いずれの場合も、モデルのドキュメントで別段の定めがない限り、テキスト入力は UTF-8 でエンコードされた文字列であり、通常はプレーン テキストです。

API に関係なく、さまざまなモデルがさまざまな言語とドメインのテキストで事前にトレーニングされており、さまざまなタスクを念頭に置いています。したがって、すべてのテキスト埋め込みモデルがすべての問題に適しているわけではありません。

テキスト入力からのテキスト埋め込み

テキスト入力からのテキスト埋め込み用の SavedModel は、形状[batch_size]文字列 Tensor 内の入力のバッチを受け入れ、入力の高密度表現 (特徴ベクトル) を持つ形状[batch_size, dim] float32 Tensor にマップします。

利用概要

obj = hub.load("path/to/model")
text_input = ["A long sentence.",
              "single-word",
              "http://example.com"]
embeddings = obj(text_input)

Reusable SavedModel APIから、モデルをトレーニングモード(ドロップアウトなど)で実行するにはキーワード引数obj(..., training=True)が必要な場合があり、 obj.variables.trainable_variables属性.variables.trainable_variables 、および.regularization_lossesを提供することを.variables 。 。

Kerasでは、これはすべてによって処理されます

embeddings = hub.KerasLayer("path/to/model", trainable=...)(text_input)

分散型トレーニング

テキスト埋め込みが配布戦略でトレーニングされるモデルの一部として使用される場合、 hub.load("path/to/model")またはhub.KerasLayer("path/to/model", ...) 、それぞれ、分散された方法でモデルの変数を作成するために、DistributionStrategyスコープ内で発生する必要があります。例えば

  with strategy.scope():
    ...
    model = hub.load("path/to/model")
    ...

前処理された入力を使用したテキスト埋め込み

前処理された入力を含むテキスト埋め込みは、2つの別々のSavedModelsによって実装されます。

  • 形状[batch_size]文字列テンソルを数値テンソルの辞書にマップするプリプロセッサ
  • プリプロセッサによって返される、実行embbedding計算の訓練可能な部分をテンソルの辞書を受け付け、出力の辞書を返しエンコーダ。キー"default"下の出力は、形状[batch_size, dim] float32テンソルです。

これにより、入力パイプラインでプリプロセッサを実行できますが、より大きなモデルの一部としてエンコーダーによって計算された埋め込みを微調整できます。特に、 TPUで実行および微調整できるエンコーダーを構築できます。

これは、プリプロセッサの出力に含まれるテンソルと、エンコーダの出力に含まれる"default"以外の追加のテンソル(存在する場合)の実装の詳細"default"

エンコーダのドキュメントでは、エンコーダで使用するプリプロセッサを指定する必要があります。通常、正しい選択は1つだけです。

使用法の概要

text_input = tf.constant(["A long sentence.",
                          "single-word",
                          "http://example.com"])
preprocessor = hub.load("path/to/preprocessor")  # Must match `encoder`.
encoder_inputs = preprocessor(text_input)

encoder = hub.load("path/to/encoder")
enocder_outputs = encoder(encoder_inputs)
embeddings = enocder_outputs["default"]

リコール再利用可能なSavedModelのAPI (ドロップアウト用など、)トレーニングモードでエンコーダを実行すると、キーワード引数必要とするかもしれないことにencoder(..., training=True) 、そのencoder 、属性の提供.variables.trainable_variables.regularization_losses適用可能な。

preprocessorモデルには.variablesが含まれている場合があり.variablesが、これ以上トレーニングすることを意図したものではありません。プリプロセスはモードに依存しません: preprocessor()training=...引数がまったく含まれていても、効果はありません。

Kerasでは、これはすべてによって処理されます

encoder_inputs = hub.KerasLayer("path/to/preprocessor")(text_input)
encoder_outputs = hub.KerasLayer("path/to/encoder", trainable=True)(encoder_inputs)
embeddings = encoder_outputs["default"]

分散トレーニング

エンコーダーが配布戦略でトレーニングされるモデルの一部として使用される場合、 hub.load("path/to/encoder") hub.KerasLayer("path/to/encoder", ...) hub.load("path/to/encoder")またはhub.KerasLayer("path/to/encoder", ...) 、それぞれ、内部で発生する必要があります

  with strategy.scope():
    ...

エンコーダ変数を分散して再作成するため。

同様に、プリプロセッサがトレーニング済みモデルの一部である場合(上記の簡単な例のように)、配布戦略スコープの下でロードする必要もあります。しかし、プリプロセッサが入力パイプラインに使用されている場合(例えば、に渡された呼び出し可能にtf.data.Dataset.map()そのロードは(もしあればその変数を配置するために、流通戦略の範囲外で行われる必要があります) ホスト CPU 上。

トランスフォーマーエンコーダーによるテキスト埋め込み

テキストのトランスエンコーダはNに結合したいくつかのモデル固有の内、各シーケンスはトークン化テキストのN≥1つのセグメントを含む、入力シーケンスのバッチ上で動作します。 BERTとその拡張機能の多くでは、その境界は2であるため、単一のセグメントとセグメントのペアを受け入れます。

Transformer エンコーダーを使用したテキスト埋め込みの API は、前処理された入力を含むテキスト埋め込みの API をこの設定に拡張します。

プリプロセッサ

Transformer エンコーダーを使用したテキスト埋め込み用のプリプロセッサ SavedModel は、前処理された入力 (上記を参照) を使用したテキスト埋め込み用のプリプロセッサ SavedModel の API を実装します。これにより、単一セグメントのテキスト入力をエンコーダー入力に直接マップする方法が提供されます。

また、プリプロセッサSavedModelは、呼び出し可能なサブオブジェクトが提供するtokenize (別々のセグメントごとに)トークン化とのためbert_pack_inputsエンコーダのための1つの入力シーケンスにNトークン化セグメントを梱包します。各サブオブジェクトは、 Reusable SavedModelAPIに従います

使用法の概要

テキストの 2 つのセグメントの具体例として、前提 (最初のセグメント) が仮説を示唆するかどうか (2 番目のセグメント) を問う文含意タスクを見てみましょう。

preprocessor = hub.load("path/to/preprocessor")

# Tokenize batches of both text inputs.
text_premises = tf.constant(["The quick brown fox jumped over the lazy dog.",
                             "Good day."])
tokenized_premises = preprocessor.tokenize(text_premises)
text_hypotheses = tf.constant(["The dog was lazy.",  # Implied.
                               "Axe handle!"])       # Not implied.
tokenized_hypotheses = preprocessor.tokenize(text_hypotheses)

# Pack input sequences for the Transformer encoder.
seq_length = 128
encoder_inputs = preprocessor.bert_pack_inputs(
    [tokenized_premises, tokenized_hypotheses],
    seq_length=seq_length)  # Optional argument.

Keras では、この計算は次のように表現できます。

tokenize = hub.KerasLayer(preprocessor.tokenize)
tokenized_hypotheses = tokenize(text_hypotheses)
tokenized_premises = tokenize(text_premises)

bert_pack_inputs = hub.KerasLayer(
    preprocessor.bert_pack_inputs,
    arguments=dict(seq_length=seq_length))  # Optional argument.
encoder_inputs = bert_pack_inputs([tokenized_premises, tokenized_hypotheses])

tokenize詳細

preprocessor.tokenize()呼び出しは、形状[batch_size]文字列 Tensor を受け入れ、入力文字列を表す int32 トークン ID を値とする形状[batch_size, ...] RaggedTensorを返します。 R≥1つの後にボロボロの次元が存在することができbatch_sizeが、ありません、他の均一な寸法。

  • r = 1の場合、形状は[batch_size, (tokens)]であり、各入力は単純にトークンのフラットシーケンスにトークン化されます。
  • r >1 の場合、グループ化にはr -1 の追加レベルがあります。たとえば、 tensorflow_text.BertTokenizerr = 2を使用してトークンを単語でグループ化し[batch_size, (words), (tokens_per_word)]形状[batch_size, (words), (tokens_per_word)]ます。これらの追加レベルがいくつ存在するか、およびそれらがどのグループを表すかは、手元のモデル次第です。

ユーザーは、トークン化された入力を変更できます(ただし、変更する必要はありません)。たとえば、エンコーダ入力のパッキングで適用されるseq_length制限に対応するためです。トークナイザー出力の余分な次元は、ここでは (たとえば、単語の境界を尊重するために) 役に立ちますが、次のステップでは無意味になります。

Reusable SavedModel APIに関しては、 preprocessor.tokenizeオブジェクトに.variablesがある場合があり.variablesが、さらにトレーニングすることは意図されていません。トークン化はモードに依存しません。preprocessor.tokenize preprocessor.tokenize()training=...引数がある場合、効果はありません。

bert_pack_inputs詳細

preprocessor.bert_pack_inputs()を呼び出すと、トークン化された入力のPythonリスト(入力セグメントごとに個別にバッチ処理されます)が受け入れられ、Transformerエンコーダーモデルの固定長入力シーケンスのバッチを表すTensorsのdictが返されます。

トークン化された各入力は、形状[batch_size, ...] int32 RaggedTensor [batch_size, ...]ここで、batch_size の後の不規則な次元の数rは 1 であるか、またはpreprocessor.tokenize().出力と同じpreprocessor.tokenize(). (後者は便宜上のものです。余分な寸法は梱包前に平らにされます。)

パッキングは、エンコーダーが期待するように、入力セグメントの周りに特別なトークンを追加します。 bert_pack_inputs()呼び出しは、元の BERT モデルとその拡張機能の多くで使用されているパッキング スキームを正確に実装します。パックされたシーケンスは、1 つのシーケンス開始トークンで始まり、その後にトークン化されたセグメントが続きます。トークン。 seq_length までの残りの位置があれば、パディング トークンで埋められます。

パックされたシーケンスがseq_lengthを超える場合、 bert_pack_inputs()は、パックされたシーケンスがseq_length内に正確に収まるように、セグメントをほぼ同じサイズのプレフィックスに切り捨てます。

パッキングはモードに依存しません: preprocessor.bert_pack_inputs()training=...引数がまったく含まれていても、効果はありません。また、 preprocessor.bert_pack_inputsは変数が含まれていないか、微調整がサポートされている必要はありません。

エンコーダー

エンコーダーは、再利用可能な SavedModel APIからのプロビジョニングを含む、前処理された入力 (上記を参照) を使用したテキスト埋め込みの API と同じ方法で、 encoder_inputsの dict でencoder_inputsます

使用法の概要

enocder = hub.load("path/to/encoder")
enocder_outputs = encoder(encoder_inputs)

または同等にKerasで:

encoder = hub.KerasLayer("path/to/encoder", trainable=True)
encoder_outputs = encoder(encoder_inputs)

詳細

encoder_outputsは、次のキーを持つ Tensor の dict です。

  • "sequence_output" : すべてのパックされた入力シーケンスの各トークンのコンテキスト認識埋め込みを[batch_size, seq_length, dim]形状[batch_size, seq_length, dim] float32 Tensor。
  • "pooled_output" :float32形状[batch_size, dim]テンソルで、各入力シーケンス全体が埋め込まれており、いくつかのトレーニング可能な方法でsequence_outputから導出されます。
  • 前処理された入力を使用したテキスト埋め込みのAPIで必要とされる"default" :各入力シーケンスの埋め込みを伴う形状[batch_size, dim] float32テンソル。 (これは、pooled_output の単なるエイリアスかもしれません。)

このAPI定義では、 encoder_inputsの内容は厳密には必要ありません。ただし、BERTスタイルの入力を使用するエンコーダーの場合は、エンコーダーの交換やプリプロセッサーモデルの再利用における摩擦を最小限に抑えるために、(TensorFlow Model GardenのNLPモデリングツールキットから)次の名前を使用することをお勧めします。

  • "input_word_ids" :パックされた入力シーケンスのトークンID(つまり、シーケンスの開始トークン、セグメントの終了トークン、およびパディングを含む)を持つ形状[batch_size, seq_length] int32テンソル。
  • "input_mask" : パディングの前に存在するすべての入力トークンの位置に値 1 があり、パディング トークンの値が 0 の形状[batch_size, seq_length] int32 テンソル。
  • "input_type_ids" :それぞれの位置に入力トークンを生成した入力セグメントのインデックスを持つ形状[batch_size, seq_length] int32テンソル。最初の入力セグメント (インデックス 0) には、シーケンス開始トークンとそのセグメント終了トークンが含まれます。 2 番目以降のセグメント (存在する場合) には、繰り返しのセグメント終了トークンが含まれます。パディング トークンは再びインデックス 0 を取得します。

分散型トレーニング

配布戦略スコープの内部または外部でプリプロセッサおよびエンコーダオブジェクトをロードする場合、前処理された入力を使用したテキスト埋め込みのAPIと同じルールが適用されます(上記を参照)。