すべての関数に@ tf.functionを使用する必要がありますか?


8

公式チュートリアル@tf.function言う:

最高のパフォーマンスを得て、モデルをどこにでもデプロイできるようにするには、tf.functionを使用してプログラムからグラフを作成します。AutoGraphのおかげで、驚くほどの量のPythonコードがtf.functionで機能するだけですが、まだ注意すべき落とし穴があります。

主な要点と推奨事項は次のとおりです。

  • オブジェクトの変更やリストの追加などのPythonの副作用に依存しないでください。
  • tf.functionは、NumPy演算やPythonプリミティブではなく、TensorFlow演算で最適に機能します。
  • 疑問がある場合は、for x in yイディオムを使用してください。

それだけ言及どのように実装する@tf.function注釈付きの機能はありませんしたときにそれを使用します。

少なくとも関数に注釈を付ける必要があるかどうかを判断する方法にヒューリスティックはありtf.functionますか?副作用を削除したり、range()->のようなものを変更するのが面倒だったりしない限り、そうしない理由はないようですtf.range()。しかし、私がこれをやる気があるなら...

@tf.functionすべての機能を使用しない理由はありますか?


1
なぜこれらのタグを追加するのですか?我々としても追加することができtensorflow0.1tensorflow0.2tensorflow0.3tensorflow0.4tensorflow0.5など、などのそれぞれのためのタグこれらtfのモジュールやクラス、その後。また、Pythonの標準モジュールとその関数およびクラスのそれぞれにタグを追加してみませんか?
ForceBru

tensorflow2.0だけでなくtensorflow2.xタグに関連する質問があるため、私がtensorflow2.xタグを導入したのはそのためです。ただし、ライブラリのすべてのバージョンにタグを追加することは不適切であり、実行不可能です。Pythonの例を見てみましょう。python3.4.6 ..... python.3.8.2はありませんが、python3.x
Timbus Calin

一方で、tf.functionガイドはそれが「ローカル関数やメソッドを飾る飾るモジュールレベルの関数、モジュールレベルのクラスのメソッド、および回避を」と言います。「すべての関数を装飾するのではなくtf.function、トレーニングループのような高レベルの関数で使用する」など、より明確な表現を覚えているようですが、覚えていない可能性があります(または削除されている可能性があります)。OTOH、この議論には開発者からの興味深い意見があります。最終的には、テンソル/変数のあらゆる関数でそれを使用しても問題ないようです。
jdehesa

@jdehesa AFAIK @tf.function注釈付き関数は、それらがグラフに呼び出す関数もコンパイルします。そのため、記述した内容と整合性のあるモジュールへのエントリポイントに注釈を付けるだけで済みます。しかし、コールスタックの下位にある関数に手動で注釈を付けることも害にはなりません。
problemofficer

@problemofficerはい、私がリンクしたGitHubの問題では、複数の中間関数を作成するとパフォーマンスにわずかな影響があるかどうかについての議論がありますが、グラフオプティマイザー(グラップラー)は必要に応じて関数を「インライン化」できますが、もう1つは別のnon- tf.functionが複数回呼び出された場合、グラフ内の「コードの重複」を防ぐことはできません。そのため、広範な使用が推奨されるようです。
jdehesa

回答:


4

TLDR:それはあなたの機能と、あなたが本番か開発かによって異なります。tf.function関数を簡単にデバッグしたい場合、またはオートグラフまたはtf.v1コードの互換性の制限に該当する場合は使用しないでください。Inside TensorFlowがセッションではなくオートグラフ関数について語るのを見ることを強くお勧めします。

以下では、理由を詳しく説明します。理由はすべて、Googleがオンラインで提供する情報から取得されます。

一般に、tf.functionデコレータは、TensorFlowグラフを実行する呼び出し可能関数として関数をコンパイルします。これは以下を伴います:

  • 必要に応じて、オートグラフによるコードの変換(注釈付き関数から呼び出された関数を含む)
  • 生成されたグラフコードのトレースと実行

この背後にある設計アイデアに関する詳細な情報があります。

関数を装飾することの利点 tf.function

一般的なメリット

  • より高速な実行、特に関数が多数の小さな演算で構成される場合(ソース)

Pythonコードを含む関数の場合/ tf.functionデコレーションによるオートグラフの使用

AutoGraphを使用する場合tf.functionは、AutoGraphを直接呼び出すよりもを使用することを強くお勧めします。これには、自動制御の依存関係、一部のAPI、より多くのキャッシュ、および例外ヘルパー(ソース)に必要な理由が含まれます。

関数を装飾することの欠点 tf.function

一般的な欠点

  • 関数がいくつかの高価な演算のみで構成されている場合、速度はそれほど向上しません(ソース)

Pythonコードを含む関数の場合/ tf.functionデコレーションによるオートグラフの使用

  • 例外のキャッチなし(意欲的なモードで実行する必要があります。装飾された関数の外で)(ソース)
  • デバッグはずっと難しい
  • 隠れた副作用とTF制御フローによる制限

AutoGraphの制限に関する詳細情報が利用可能です。

tf.v1コードを持つ関数の場合

  • で変数を複数回作成することは許可されていませんがtf.function、tf.v1コードが段階的に廃止されるため、これは変更される可能性があります(ソース)

tf.v2コードを持つ関数の場合

  • 特定の欠点はありません

制限の例

変数を複数回作成する

v次の例のように、変数を複数回作成することはできません。

@tf.function
def f(x):
    v = tf.Variable(1)
    return tf.add(x, v)

f(tf.constant(2))

# => ValueError: tf.function-decorated function tried to create variables on non-first call.

次のコードでは、これがself.v1度だけ作成されるようにすることでこれを軽減しています。

class C(object):
    def __init__(self):
        self.v = None
    @tf.function
    def f(self, x):
        if self.v is None:
            self.v = tf.Variable(1)
        return tf.add(x, self.v)

c = C()
print(c.f(tf.constant(2)))

# => tf.Tensor(3, shape=(), dtype=int32)

AutoGraphによってキャプチャされない隠れた副作用

self.aこの例ののような変更を非表示にすることはできません。クロスファンクション分析がまだ行われていないため、エラーが発生します(まだ)(ソース)

class C(object):
    def change_state(self):
        self.a += 1

    @tf.function
    def f(self):
        self.a = tf.constant(0)
        if tf.constant(True):
            self.change_state() # Mutation of self.a is hidden
        tf.print(self.a)

x = C()
x.f()

# => InaccessibleTensorError: The tensor 'Tensor("add:0", shape=(), dtype=int32)' cannot be accessed here: it is defined in another function or code block. Use return values, explicit Python locals or TensorFlow collections to access it. Defined in: FuncGraph(name=cond_true_5, id=5477800528); accessed from: FuncGraph(name=f, id=5476093776).

明白な視界の変化は問題ありません:

class C(object):
    @tf.function
    def f(self):
        self.a = tf.constant(0)
        if tf.constant(True):
            self.a += 1 # Mutation of self.a is in plain sight
        tf.print(self.a)

x = C()
x.f()

# => 1

TF制御フローによる制限の例

elseの値をTF制御フローに定義する必要があるため、このifステートメントはエラーになります。

@tf.function
def f(a, b):
    if tf.greater(a, b):
        return tf.constant(1)

# If a <= b would return None
x = f(tf.constant(3), tf.constant(2))   

# => ValueError: A value must also be returned from the else branch. If a value is returned from one branch of a conditional a value must be returned from all branches.

1
これは良い要約です。また、eagerモードから呼び出された場合、tf.functionには、最初の呼び出し後に約200 us(giveまたはtake)のオーバーヘッドがあることにも注意してください。ただし、別のtf.functionからtf.functionを呼び出すことは問題ありません。したがって、できるだけ多くの計算をラップしたいとします。制限がなければ、プログラム全体をラップする必要があります。
Dan Moldovan

この答えはtl; dr IMHOであり、実際には私の質問には答えませんが、私が見つけた同じ断片化された情報を提供します。また、私は@tf.functionプロダクションに使用するのではなく、開発にのみ使用するべきであると言うことは現実的な解決策ではありません。まず、機械学習(少なくとも研究)では、開発段階でのトレーニングによって最終的な製品(トレーニング済みモデル)も作成されます。第二に、デコレータは大きな変化です。それらを「開発後」に配置して、コードが同じように動作することを確認することはできません。これは、私がすでに開発し、テストしなければならないことを意味します。
problemofficer

@problemofficer混乱してすみません。私の回答で生産について話しているとき、私はその一環としてトレーニング(大規模なデータセット)を検討していました。私自身の研究では、eagerモードのおもちゃのデータセットで関数を開発/デバッグし、tf.function必要に応じて追加しています。
prouast

2

tf.functionは、計算グラフの作成と使用に役立ちます。それらはトレーニングとデプロイメントで使用する必要がありますが、ほとんどの関数では必要ありません。

より大きなモデルの一部となる特別なレイヤーを構築しているとしましょう。tf.functionデコレーターは、そのレイヤーを構築する関数の上には配置しないでください。これは、レイヤーがどのように見えるかの定義にすぎないためです。

一方、予測を行うか、関数を使用してトレーニングを継続するとします。実際に計算グラフを使用して値を取得しているため、デコレータtf.functionが必要になります。

良い例は、エンコーダー/デコーダーモデルの構築です。エンコーダー、デコーダー、または任意のレイヤーを作成する関数の周りにデコレーターを配置しないでください。これは、それが何をするかの定義にすぎません。「train」または「predict」メソッドの周りにデコレータを配置してください。これらは実際に計算に計算グラフを使用するためです。


1
しかし、副作用などについてはどうですかtf.range()。AFAIKこれらは自動的に変換できません。したがって、最初から自動グラフを念頭に置いてカスタムレイヤーを作成する必要があります。したがって、呼び出し(予測)関数を装飾することはできません。
problemofficer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.