TF1 Hub 形式

2018 年の公開当初、TensorFlow Hub は TensorFlow 1 プログラムにインポートするための TF1 Hub 形式というアセットの種類を 1 つだけ提供していました。

このページでは、TF1(または TF2 の TF1 互換モード)で hub.Module クラスと関連する API を使用して、TF1 Hub 形式を使用する方法を説明します。(一般的な使用は、tf.Graph を構築するのが一般的な使用方法です。tf.compat.layers または tf.layers を使って TF1 Hub 形式のモデルを複数組み合わせることで、TF1 Estimator 内でグラフを構築する可能性があります。)

TensorFlow 2(TF 1 互換モードを除く)は新しい API を hub.load() または hub.KerasLayer とともに使用する必要があります。新しい API を使って新しい TF2 SavedModel アセットの種類を読み込むことができますが、TF1 Hub 形式の TF2 読み込みサポートに関する制限もあります。

TF1 Hub 形式のモデルを使用する

TF1 Hub 形式のモデルをインスタンス化する

TF1 Hub 形式のモデルは、次のように、URL とファイルシステムパスを使った文字列から hub.Module オブジェクトを作成して、TensorFlow プログラムにインポートされます。

m = hub.Module("path/to/a/module_dir")

注意: その他の有効なハンドルタイプについての詳細は、こちらをご覧ください。

これは、モジュールの変数を現在の TensorFlow グラフに追加します。イニシャライザを実行すると、ディスクからトレーニング済みの値が読み取られます。同様に、テーブルとその他の状態もグラフに追加されます。

モジュールのキャッシング

URL からモジュールを作成する場合、モジュールコンテンツはローカルシステムの一時ディレクトリにダウンロードされてキャッシュされます。モジュールがキャッシュされる場所は、TFHUB_CACHE_DIR 環境変数を使用してオーバーライドすることができます。詳細は、 キャッシングをご覧ください。

モジュールの適用

インスタンス化したら、Python 関数と同様に、テンソル入力からテンソル出力に、モジュール m をゼロ回以上呼び出すことができます。

y = m(x)

上記のような呼び出しは、現在の TensorFlow グラフに x から y を計算する演算を追加します。これにトレーニング済みの重みを使用する変数が伴う場合、すべてのアプリケーションで共有されます。

モジュールは、1 つ以上の方法で適用されるために、複数の名前付きシグネチャを定義します(Python オブジェクトにメソッドがあるとの同じです)。利用できるシグネチャはモジュールのドキュメントに説明されています。上記の呼び出しは、"default" と名付けられたシグネチャを適用します。シグネチャは、名前をオプションの signature= 引数に渡すことで選択することができます。

シグネチャに複数の入力がある場合、シグネチャが定義するキーを使って dict として渡される必要があります。同様に、シグネチャに複数の出力がある場合、しぐにちゃが定義したキーで、as_dict=True を渡して dict として取得することができます(キー "default"as_dict=False である場合に返される単一の出力に使用します)。次に、モジュールを適用する際の最も一般的な形態を示します。

outputs = m(dict(apples=x1, oranges=x2), signature="fruit_to_pet", as_dict=True)
y1 = outputs["cats"]
y2 = outputs["dogs"]

呼び出し元は、シグネチャが定義するすべての入力を提供する必要がありますが、モジュールのすべての出力を使用しなければならないという要件はありません。TensorFlow は tf.Session.run() でターゲットの依存関係となるその部分のモジュールのみを実行します。実際、モジュールのパブリッシャーは、高度な使用(中間レイヤーのアクティベーションなど)向けに、メインの出力とともにさまざまな出力を提供する場合があるため、モジュールの消費者は、体よく追加出力を処理する必要があります。

別のモジュールを試す

同じタスクに使用できるモジュールが複数存在する場合は、TensorFlow Hub は互換性のあるシグネチャ(インターフェース)でそういったモジュールを取り入れることをお勧めしています。さまざまなモジュールを試すのは、さまざまなモジュールが文字列値のハイパーパラメータとして取り扱うのと同じぐらい簡単に行えます。

この目的を達成するために、使用率の高いタスクについて推奨される一連の共通シグネチャを用意しています。

新しいモジュールを作成する

互換性に関するメモ

TF1 Hub 形式は、TensorFlow 1 に合わせて作られています。TensorFlow 2 の TF Hub では一部のみがサポートされています。新しい TF2 SavedModel 形式で公開することをぜひ検討してください。

TF1 Hub 形式は、TensorFlow 1 の SavedModel 形式と構文レベルで似ていますが(同じファイル名とプロトコルメッセージ)、セマンティックレベルでは、モジュールの再利用、合成、
および再トレーニングが可能という点で異なります(リソースイニシャライザのストレージが異なる、メタグラフのタグ規則が異なるなど)。ディスク上で区別するには、tfhub_module.pb ファイルの有無を確認することが最も簡単です。

一般的なアプローチ

新しいモジュールを定義するには、パブリッシャーは module_fn 関数を使用して hub.create_module_spec() を呼び出します。この関数は、呼び出し元が指定する入力に tf.placeholder() を使用して、モデルの内部構造を表すグラフを構築し、hub.add_signature(name, inputs, outputs) を 1 回以上呼び出してシグネチャを定義します。

次に例を示します。

def module_fn():
  inputs = tf.placeholder(dtype=tf.float32, shape=[None, 50])
  layer1 = tf.layers.dense(inputs, 200)
  layer2 = tf.layers.dense(layer1, 100)
  outputs = dict(default=layer2, hidden_activations=layer1)
  # Add default signature.
  hub.add_signature(inputs=inputs, outputs=outputs)

...
spec = hub.create_module_spec(module_fn)

特定の TensorFlow グラフ内のオブジェクトをインスタンス化するには、パスの代わりに hub.create_module_spec() の結果が使用される場合があります。この場合、チェックポイントはなく、モジュールインスタンスは変数イニシャライザを代わりに使用します。

モジュールインスタンスは、export(path, session) メソッドを使用してディスクにシリアル化されます。モジュールをエクスポートすると、その定義は session にある変数の現在の状態とともに、渡されるパスにシリアル化されます。これは、初めてモジュールをエクスポートするときだけでなく、ファインチューニングしたモジュールをエクスポートする際にも使用できます。

TensorFlow Estimator との互換性を得るため、tf.estimator.LatestExporter が最新のチェックポイントからモデル全体をエクスポートするのと同様に、hub.LatestModuleExporter も最新のチェックポイントからモジュールをエクスポートします。

モジュールのパブリッシャーは、可能な限り共通シグネチャを実装することで、消費者がモジュールを簡単に交換して、問題に最適なものを見つけ出せるようにする必要があります。

実際の例

テキスト埋め込みモジュールのエクスポーターをご覧ください。一般的なテキスト埋め込み形式からモジュールを生成する実世界の例を紹介しています。

ファインチューニング

インポートされたモジュールの変数とそれに関するモデルの変数を合わせてトレーニングすることをファインチューニング(微調整)と呼びます。ファインチューニングによって品質が高められてますが、新しい問題が生まれることがあります。品質調整を試した後に、モジュールのパブリッシャーが推奨する場合にのみ、ファインチューニングを行うことをお勧めします。

消費者対象の注意事項

ファインチューニングを有効化するには、変数をトレーニング可能にするために hub.Module(..., trainable=True) を使ってモジュールをインスタンス化し、TensorFlow のREGULARIZATION_LOSSES をインポートします。モジュールに様々なグラフバリアントが存在する場合、トレーニングに最適なものを選択するようにしてください。通常、{"train"} タグのあるものが最適なバリアントです。

ゼロからトレーニングするよりも学習速度を低めるなど、トレーニング済みの重みを台無しにしないトレーニング方法を選択します。

パブリッシャー対象の注意事項

消費者がファインチューニングを簡単に行えるように、次のことに注意してください。

  • ファインチューニングには正規化が必要です。モデルは REGULARIZATION_LOSSES コレクションとともにエクスポートされており、これによって、tf.layers.dense(..., kernel_regularizer=...) などの選択肢が、消費者が tf.losses.get_regularization_losses() から取得するものへと変化します。L1/L2 正則化損失を定義するこの方法をお勧めします。

  • パブリッシャーのモデルでは、tf.train.FtrlOptimizertf.train.ProximalGradientDescentOptimizer、およびその他の近似オプティマイザの l1_l2_regularization_strength パラメータを使って L1/L2 正規化を定義しないようにしてください。これらはモジュールとともにエクスポートされないため、正規化の強度をグローバルに設定することは消費者に適していない場合があります。ワイド(スパース線形)またはワード&ディープモデルでの L1 正規化を除き、代わりに個別の正規化損失を使用することが可能なはずです。

  • ドロップアウト、バッチ正規化、または似たようなトレーニング技法を使用する場合、ハイパーパラメータを多くの必要な使用例全体で意味がある値に設定してください。ドロップアウト率は、過学習する傾向があるターゲットの問題に対して調整する必要がある場合があります。バッチ正規化では、運動量(崩壊定数としても知られています)は小さなデータセット、大きなバッチ、またはその両方でファインチューニングできるよう小さくする必要があります。高度なコンシューマは、重要なハイパーパラメータをコントロールする署名の追加を検討してください。