私は Googleの例 Androidで事前に訓練されたTensorflowグラフ(モデル)を展開して使用する方法を検討しています。この例では、.pb
ファイル:
https://storage.googleapis.com/download.tensorflow.org/models/inception5h.Zip
これは、自動的にダウンロードされるファイルへのリンクです。
この例は、.pb
Tensorflowセッションにファイルし、それを使用して分類を実行しますが、そのような.pb
ファイル、グラフがトレーニングされた後(Pythonなど)。
その方法の例はありますか?
EDIT:TensorFlowリポジトリの一部である freeze_graph.py
スクリプトは、現在、既存のTensorFlow GraphDef
および保存されたチェックポイントから、「凍結された」トレーニング済みモデルを表すプロトコルバッファーを生成します。以下で説明するのと同じ手順を使用しますが、はるかに使いやすいです。
現在、このプロセスはあまり文書化されていません(そして改良の対象となっています)が、おおよその手順は次のとおりです。
tf.Graph
と呼ばれるg_1
としてモデルをビルドしてトレーニングします。Session.run()
を使用)。tf.Graph
という新しいg_2
で、変数ごとに tf.constant()
テンソルを作成します、手順2で取得した対応するnumpy配列の値を使用します。tf.import_graph_def()
を使用してノードをg_1
からg_2
にコピーし、input_map
引数を使用してg_1
の各変数を対応するtf.constant()
テンソルに置き換えます手順3. input_map
を使用して、新しい入力テンソルを指定することもできます(たとえば、 input pipeline を tf.placeholder()
)。 return_elements
引数を使用して、予測出力テンソルの名前を指定します。
g_2.as_graph_def()
を呼び出して、グラフのプロトコルバッファ表現を取得します。
(注:生成されたグラフには、トレーニング用のグラフに追加のノードがあります。パブリックAPIの一部ではありませんが、内部 graph_util.extract_sub_graph()
これらのノードをグラフから除去する関数。)
スクリプトとして呼び出す場合にのみ有効なfreeze_graph()
を使用した以前の回答の代わりに、すべての面倒な作業を行う非常に素晴らしい関数があり、通常から呼び出すのに適していますモデルトレーニングコード。
convert_variables_to_constants()
は2つのことを行います:
sess
がtf.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)
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);
}
@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つしかないため、ノードを反復処理する必要はありません。
Tensorflowコードベースに freeze_graph()
関数があり、これを行うときに役立つ可能性があります。私が理解していることから、GraphDefをシリアル化する前に変数を定数と交換するため、C++からこのグラフをロードすると、設定する必要のある変数はなく、予測に直接使用できます。
また、 test があり、 Guide にいくつかの説明があります。
これは、ここで最もクリーンなオプションのようです。
この投稿を見つけたばかりで、とても役に立ちました。私の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」を使用します