web-dev-qa-db-ja.com

訓練されたTensorFlowグラフを保持するprotobufファイルを生成する方法の例はありますか

私は Googleの例 Androidで事前に訓練されたTensorflowグラフ(モデル)を展開して使用する方法を検討しています。この例では、.pbファイル:

https://storage.googleapis.com/download.tensorflow.org/models/inception5h.Zip

これは、自動的にダウンロードされるファイルへのリンクです

この例は、.pb Tensorflowセッションにファイルし、それを使用して分類を実行しますが、そのような.pbファイル、グラフがトレーニングされた後(Pythonなど)。

その方法の例はありますか?

44
scai

EDIT:TensorFlowリポジトリの一部である freeze_graph.py スクリプトは、現在、既存のTensorFlow GraphDefおよび保存されたチェックポイントから、「凍結された」トレーニング済みモデルを表すプロトコルバッファーを生成します。以下で説明するのと同じ手順を使用しますが、はるかに使いやすいです。


現在、このプロセスはあまり文書化されていません(そして改良の対象となっています)が、おおよその手順は次のとおりです。

  1. tf.Graphと呼ばれるg_1としてモデルをビルドしてトレーニングします。
  2. 各変数の最終値を取得し、numpy配列として保存します( Session.run() を使用)。
  3. tf.Graphという新しいg_2で、変数ごとに tf.constant() テンソルを作成します、手順2で取得した対応するnumpy配列の値を使用します。
  4. tf.import_graph_def() を使用してノードをg_1からg_2にコピーし、input_map引数を使用してg_1の各変数を対応するtf.constant()テンソルに置き換えます手順3. input_mapを使用して、新しい入力テンソルを指定することもできます(たとえば、 input pipelinetf.placeholder() )。 return_elements引数を使用して、予測出力テンソルの名前を指定します。

  5. g_2.as_graph_def()を呼び出して、グラフのプロトコルバッファ表現を取得します。

注:生成されたグラフには、トレーニング用のグラフに追加のノードがあります。パブリックAPIの一部ではありませんが、内部 graph_util.extract_sub_graph() これらのノードをグラフから除去する関数。)

35
mrry

スクリプトとして呼び出す場合にのみ有効なfreeze_graph()を使用した以前の回答の代わりに、すべての面倒な作業を行う非常に素晴らしい関数があり、通常から呼び出すのに適していますモデルトレーニングコード。

convert_variables_to_constants() は2つのことを行います:

  • 変数を定数に置き換えることで重みをフリーズします
  • フィードフォワード予測に関連しないノードを削除します

sesstf.Session()および"output"は予測ノードの名前です。次のコードは、最小のグラフをテキストとバイナリの両方のprotobufにシリアル化します。


from tensorflow.python.framework.graph_util import convert_variables_to_constants

minimal_graph = convert_variables_to_constants(sess, sess.graph_def, ["output"])

tf.train.write_graph(minimal_graph, '.', 'minimal_graph.proto', as_text=False)
tf.train.write_graph(minimal_graph, '.', 'minimal_graph.txt', as_text=True)
16
mirosval

Mrryによって記述されたメソッドの実装方法がわかりませんでした。しかし、ここで私はそれをどのように解決しました。それが問題を解決する最良の方法であるかどうかはわかりませんが、少なくともそれは解決します。

Write_graphは定数の値も保存できるため、write_graph関数を使用してグラフを書き込む直前にpythonに次のコードを追加しました。

for v in tf.trainable_variables():
    vc = tf.constant(v.eval())
    tf.assign(v, vc, name="assign_variables")

これにより、トレーニング後に変数の値を保存する定数が作成され、テンソル「assign_variables」が作成されて変数に割り当てられます。これで、write_graphを呼び出すと、変数の値が定数の形式でファイルに保存されます。

残りの部分は、これらのテンソルをcコードで「assign_variables」と呼び、変数がファイルに保存されている定数値。これを行う1つの方法を次に示します。

      Status status = NewSession(SessionOptions(), &session);
      std::vector<tensorflow::Tensor> outputs;
      char name[100];
      for(int i = 0;status.ok(); i++) {
        if (i==0)
            sprintf(name, "assign_variables");
        else
            sprintf(name, "assign_variables_%d", i);

        status = session->Run({}, {name}, {}, &outputs);
      }
4
Mostafa Hassan

@Mostafaの回答の別の見解を次に示します。 tf.assign opsを実行する多少クリーンな方法は、tf.groupに保存することです。ここに私のPythonコード:

  ops = []
  for v in tf.trainable_variables():
    vc = tf.constant(v.eval())
    ops.append(tf.assign(v, vc));
  tf.group(*ops, name="assign_trained_variables")

C++の場合:

  std::vector<tensorflow::Tensor> tmp;
  status = session.Run({}, {}, { "assign_trained_variables" }, &tmp);
  if (!status.ok()) {
    // Handle error
  }

この方法では、C++側で実行するopという名前の名前が1つしかないため、ノードを反復処理する必要はありません。

4
Kris Giesing

Tensorflowコードベースに freeze_graph() 関数があり、これを行うときに役立つ可能性があります。私が理解していることから、GraphDefをシリアル化する前に変数を定数と交換するため、C++からこのグラフをロードすると、設定する必要のある変数はなく、予測に直接使用できます。

また、 test があり、 Guide にいくつかの説明があります。

これは、ここで最もクリーンなオプションのようです。

1
mirosval

この投稿を見つけたばかりで、とても役に立ちました。私のC++コードは少し異なりますが、@ Mostafaのメソッドも使用します。

    std::vector<string> names;
    int node_count = graph.node_size();
    cout << node_count << " nodes in graph" << endl;

    // iterate all nodes
    for(int i=0; i<node_count; i++) {
        auto n = graph.node(i);
        cout << i << ":" << n.name() << endl;

        // if name contains "var_hack", add to vector
        if(n.name().find("var_hack") != std::string::npos) {
            names.Push_back(n.name());
            cout << "......bang" << endl;
        }
    }
    session.Run({}, names, {}, &outputs);

注意:Pythonで変数名として「var_hack」を使用します

1
memo