Tensorflowソース解析3--TensorFlowコアオブジェクト-Graph
8460 ワード
1 Graphの概要
計算図GraphはTensorFlowのコアオブジェクトであり、TensorFlowの実行プロセスは基本的にそれをめぐって行われている.図の構築、転送、枝切り、workerによる分割、デバイスによる二次分割、実行、ログアウトなどが含まれます.したがって,計算図Graphを理解することはTensorFlowの動作を把握する上で特に重要である.
2デフォルトGraph
デフォルト図の置換
前にセッションを説明したときに言ったように、1つのセッションはrunに1つのGraphしかありませんが、1つのGraphは複数のセッションで実行できます.一般的に、sessionはグローバルで唯一の暗黙的なデフォルトのGraphを実行し、operationもこのGraphに登録します.
Graphの作成を表示し、as_を呼び出すこともできます.default()はデフォルトのGraphを置き換えます.このコンテキストマネージャで作成したopは、このgraphに登録されます.コンテキストマネージャを終了すると、元のデフォルトgraphが復元されます.一般的には、Graphを明示的に作成する必要はありません.システムで作成されたデフォルトのGraphを使用します.
出力は次のとおりです.
このように、コンテキストマネージャでは、現在のスレッドのデフォルト図が置き換えられ、コンテキスト管理を終了すると、元のデフォルト図に戻ります.
デフォルトの管理
デフォルトgraphはデフォルトsessionと同様にスレッドの役割ドメインです.現在のスレッドには、graphがデフォルトマップとして1つしかありません.TensorFlowも同様にスタックによってスレッドのデフォルトgraphを管理します.
代替デフォルト図はスタックの管理方式を採用し,push pop操作により管理する.デフォルトマップを取得するには、デフォルトgraphスタックを介して次のようにします.default_graph_stackが取得します.
次に見てみましょうdefault_graph_stackの作成
_default_graph_stackの作成は上述したように,最終的にはデフォルトsessionスタックと同様に本質的にlistである.
3フロントエンドGraphデータ構造
Graphデータ構造
オブジェクトを理解するには、まずデータ構造から始めます.まずPythonフロントエンドにおけるGraphのデータ構造を見てみましょう.Graphの主なメンバー変数はOperationとTensorです.Operationは、演算演算子を表すGraphのノードです.TensorはGraphのエッジで、演算データを表しています.
次にgraphがopを追加する方法と、スレッドの安全を保証する方法を見ます.
グラフグループ
各Operationノードには特定のラベルがあり、ノードの分類を実現します.同じラベルのノードはクラスに分類され、同じCollectionに配置されます.ラベルは唯一のGraphKeyであり、GraphKeyはクラスGraphKeysに定義され、以下のように定義されています.
name_scopeノードネーミングスペース
name_の使用scopeはgraphのノードを階層化し、上下層間をスラッシュで区切る.
出力は次のとおりです.
4バックエンドGraphデータ構造
Graph
まずgraphを見てみましょうhファイルのGraphクラスの定義は、キーコードのみを参照してください.
バックエンドのGraphの主要メンバーもノードnodeとエッジedgeです.ノードnodeは計算演算子Operation,エッジは演算子に必要なデータ,あるいはノード間の依存関係を表す.これはPythonの定義と似ています.エッジEdgeのソースノードとターゲットノードのポインタを持ち、2つのノードを接続します.次に、Edgeクラスの定義を見てみましょう.
Edge
Edgeはtensorデータを担持し,ノードOperationに提供して演算することもできるし,ノード間の依存関係を表すこともできる.ノード依存を表すエッジのsrc_output_, dst_input_いずれも-1で、エッジはデータをロードしません.
ノードクラスの定義を見てみましょう.
Node
ノードノードノードに含まれるプライマリデータには、入力エッジと出力エッジのセットがあり、ノードによって関連するすべてのエッジを見つけることができます.NodeにはNodeDefとOpDefの2つのメンバーも含まれています.NodeDefはノード演算子の情報を表し、実行時に変化する可能性があり、Nodeを作成するとnewのNodeDefオブジェクトが表示されます.OpDefはノード演算子のメタ情報を表し,実行時は変化せず,Node作成時はnew OpDefを必要とせず,OpDef倉庫から取り出すだけでよい.メタ情報は確定しているので,たとえばOperationの入参個数などである.
NodeとEdge、すなわち図Graphを構成することができ、任意のノードと任意のエッジを通じて、完全な図を遍歴することができます.Graphが計算を実行する場合,トポロジー構造に従ってノードごとのop計算を順次実行し,最終的に出力結果を得ることができる.入度0のノード、すなわちデータに依存して準備されたノードは、同時に実行でき、実行効率が向上します.
システムにはデフォルトのGraphが存在し、Graphを初期化するとSourceノードとSinkノードが追加されます.SourceはGraphの開始ノードを表し,Sinkは終了ノードである.Sourceのidは0,Sinkのidは1であり,他のノードのidはいずれも1より大きい.
5 Graphランタイムライフサイクル
GraphはTensorFlowのコアオブジェクトであり、TensorFlowの実行はいずれもGraphを中心に行われます.実行時Graphはほぼ以下の段階を経ています図構築:clientエンドユーザーは作成したノードをGraphに登録します.一般的にGraphの作成を表示する必要はありません.システムで作成したデフォルトを使用すればいいです. 図送信:clientはセッションを通過する.run()実行時に構築した整図をGraphDefにシーケンス化してmaster に渡す.図剪断:masterはまず逆シーケンス化してGraphを手に入れ、sessionに従った.run()伝達fetchesとfeedsリストは,全図full graphを逆方向に遍歴し,剪断を実施し,最小依存子図を得た. 図分割:masterは最小サブ図を複数のGraph Partitionに分割し、複数のworkerに登録します.1つのワークはGraph Partitionに対応します. 図二次分裂:workerは、CPU GPUのような現在利用可能なハードウェアリソースに基づいて、Graph Partitionをop演算子デバイス制約仕様(例えばtf.device(’/cpu:0’)に従って、異なるデバイスに二次分裂させる.各コンピューティングデバイスはGraph Partitionに対応します. 図実行:各コンピューティングデバイスに対して、workerはopのkernelでの実装に従ってopの演算を完了する.デバイス間データ通信はsend/recvノードを使用することができ、worker間通信はGRPCまたはRDMAプロトコルを使用する.
これらのフェーズはTensorFlowの実行時によって異なる処理が行われます.ランタイムには、ローカルランタイムと分散ランタイムの2種類があります.したがって、Graphライフサイクルは、ローカルランタイムと分散ランタイムを後で分析するときに詳細に説明します.
著者:揚易
原文を読む
本文は雲栖コミュニティのオリジナル内容で、許可を得ずに転載してはならない.
計算図GraphはTensorFlowのコアオブジェクトであり、TensorFlowの実行プロセスは基本的にそれをめぐって行われている.図の構築、転送、枝切り、workerによる分割、デバイスによる二次分割、実行、ログアウトなどが含まれます.したがって,計算図Graphを理解することはTensorFlowの動作を把握する上で特に重要である.
2デフォルトGraph
デフォルト図の置換
前にセッションを説明したときに言ったように、1つのセッションはrunに1つのGraphしかありませんが、1つのGraphは複数のセッションで実行できます.一般的に、sessionはグローバルで唯一の暗黙的なデフォルトのGraphを実行し、operationもこのGraphに登録します.
Graphの作成を表示し、as_を呼び出すこともできます.default()はデフォルトのGraphを置き換えます.このコンテキストマネージャで作成したopは、このgraphに登録されます.コンテキストマネージャを終了すると、元のデフォルトgraphが復元されます.一般的には、Graphを明示的に作成する必要はありません.システムで作成されたデフォルトのGraphを使用します.
print tf.get_default_graph()
with tf.Graph().as_default() as g:
print tf.get_default_graph() is g
print tf.get_default_graph()
print tf.get_default_graph()
出力は次のとおりです.
True
このように、コンテキストマネージャでは、現在のスレッドのデフォルト図が置き換えられ、コンテキスト管理を終了すると、元のデフォルト図に戻ります.
デフォルトの管理
デフォルトgraphはデフォルトsessionと同様にスレッドの役割ドメインです.現在のスレッドには、graphがデフォルトマップとして1つしかありません.TensorFlowも同様にスタックによってスレッドのデフォルトgraphを管理します.
@tf_export("Graph")
class Graph(object):
#
def as_default(self):
return _default_graph_stack.get_controller(self)
# ,push pop
@tf_contextlib.contextmanager
def get_controller(self, default):
try:
context.context_stack.push(default.building_function, default.as_default)
finally:
context.context_stack.pop()
代替デフォルト図はスタックの管理方式を採用し,push pop操作により管理する.デフォルトマップを取得するには、デフォルトgraphスタックを介して次のようにします.default_graph_stackが取得します.
@tf_export("get_default_graph")
def get_default_graph():
return _default_graph_stack.get_default()
次に見てみましょうdefault_graph_stackの作成
_default_graph_stack = _DefaultGraphStack()
class _DefaultGraphStack(_DefaultStack):
def __init__(self):
#
super(_DefaultGraphStack, self).__init__()
self._global_default_graph = None
class _DefaultStack(threading.local):
def __init__(self):
super(_DefaultStack, self).__init__()
self._enforce_nesting = True
# session , list
self.stack = []
_default_graph_stackの作成は上述したように,最終的にはデフォルトsessionスタックと同様に本質的にlistである.
3フロントエンドGraphデータ構造
Graphデータ構造
オブジェクトを理解するには、まずデータ構造から始めます.まずPythonフロントエンドにおけるGraphのデータ構造を見てみましょう.Graphの主なメンバー変数はOperationとTensorです.Operationは、演算演算子を表すGraphのノードです.TensorはGraphのエッジで、演算データを表しています.
@tf_export("Graph")
class Graph(object):
def __init__(self):
# , op , op graph , graph
self._lock = threading.Lock()
# op 。
# graph op id, id op。 _nodes_by_id
self._nodes_by_id = dict() # GUARDED_BY(self._lock)
self._next_id_counter = 0 # GUARDED_BY(self._lock)
# name op, _nodes_by_name
self._nodes_by_name = dict() # GUARDED_BY(self._lock)
self._version = 0 # GUARDED_BY(self._lock)
# tensor 。
# tensor placeholder
self._handle_feeders = {}
# tensor read
self._handle_readers = {}
# tensor move
self._handle_movers = {}
# tensor delete
self._handle_deleters = {}
次にgraphがopを追加する方法と、スレッドの安全を保証する方法を見ます.
def _add_op(self, op):
# graph final , , op 。
self._check_not_finalized()
# graph
with self._lock:
# op id name , _nodes_by_id _nodes_by_name ,
self._nodes_by_id[op._id] = op
self._nodes_by_name[op.name] = op
self._version = max(self._version, op._id)
グラフグループ
各Operationノードには特定のラベルがあり、ノードの分類を実現します.同じラベルのノードはクラスに分類され、同じCollectionに配置されます.ラベルは唯一のGraphKeyであり、GraphKeyはクラスGraphKeysに定義され、以下のように定義されています.
@tf_export("GraphKeys")
class GraphKeys(object):
GLOBAL_VARIABLES = "variables"
QUEUE_RUNNERS = "queue_runners"
SAVERS = "savers"
WEIGHTS = "weights"
BIASES = "biases"
ACTIVATIONS = "activations"
UPDATE_OPS = "update_ops"
LOSSES = "losses"
TRAIN_OP = "train_op"
#
name_scopeノードネーミングスペース
name_の使用scopeはgraphのノードを階層化し、上下層間をスラッシュで区切る.
# graph
g = tf.get_default_graph()
with g.name_scope("scope1"):
c = tf.constant("hello, world", name="c")
print c.op.name
with g.name_scope("scope2"):
c = tf.constant("hello, world", name="c")
print c.op.name
出力は次のとおりです.
scope1/c
scope1/scope2/c # scope , ,
4バックエンドGraphデータ構造
Graph
まずgraphを見てみましょうhファイルのGraphクラスの定義は、キーコードのみを参照してください.
class Graph {
private:
// op
FunctionLibraryDefinition ops_;
// GraphDef
const std::unique_ptr versions_;
// node , id
std::vector nodes_;
// node
int64 num_nodes_ = 0;
// edge , id
std::vector edges_;
// graph edge
int num_edges_ = 0;
// , node edge
std::vector free_nodes_;
std::vector free_edges_;
}
バックエンドのGraphの主要メンバーもノードnodeとエッジedgeです.ノードnodeは計算演算子Operation,エッジは演算子に必要なデータ,あるいはノード間の依存関係を表す.これはPythonの定義と似ています.エッジEdgeのソースノードとターゲットノードのポインタを持ち、2つのノードを接続します.次に、Edgeクラスの定義を見てみましょう.
Edge
class Edge {
private:
Edge() {}
friend class EdgeSetTest;
friend class Graph;
// , 。
Node* src_;
// , 。
Node* dst_;
// id,
int id_;
// src_output_ 。
int src_output_;
// dst_input_ 。 。
int dst_input_;
};
Edgeはtensorデータを担持し,ノードOperationに提供して演算することもできるし,ノード間の依存関係を表すこともできる.ノード依存を表すエッジのsrc_output_, dst_input_いずれも-1で、エッジはデータをロードしません.
ノードクラスの定義を見てみましょう.
Node
class Node {
public:
// NodeDef, Operation , op ,op , 。
const NodeDef& def() const;
// OpDef, Operation , 。 Operation ,
const OpDef& op_def() const;
private:
// , 。
EdgeSet in_edges_;
// , 。
EdgeSet out_edges_;
}
ノードノードノードに含まれるプライマリデータには、入力エッジと出力エッジのセットがあり、ノードによって関連するすべてのエッジを見つけることができます.NodeにはNodeDefとOpDefの2つのメンバーも含まれています.NodeDefはノード演算子の情報を表し、実行時に変化する可能性があり、Nodeを作成するとnewのNodeDefオブジェクトが表示されます.OpDefはノード演算子のメタ情報を表し,実行時は変化せず,Node作成時はnew OpDefを必要とせず,OpDef倉庫から取り出すだけでよい.メタ情報は確定しているので,たとえばOperationの入参個数などである.
NodeとEdge、すなわち図Graphを構成することができ、任意のノードと任意のエッジを通じて、完全な図を遍歴することができます.Graphが計算を実行する場合,トポロジー構造に従ってノードごとのop計算を順次実行し,最終的に出力結果を得ることができる.入度0のノード、すなわちデータに依存して準備されたノードは、同時に実行でき、実行効率が向上します.
システムにはデフォルトのGraphが存在し、Graphを初期化するとSourceノードとSinkノードが追加されます.SourceはGraphの開始ノードを表し,Sinkは終了ノードである.Sourceのidは0,Sinkのidは1であり,他のノードのidはいずれも1より大きい.
5 Graphランタイムライフサイクル
GraphはTensorFlowのコアオブジェクトであり、TensorFlowの実行はいずれもGraphを中心に行われます.実行時Graphはほぼ以下の段階を経ています
これらのフェーズはTensorFlowの実行時によって異なる処理が行われます.ランタイムには、ローカルランタイムと分散ランタイムの2種類があります.したがって、Graphライフサイクルは、ローカルランタイムと分散ランタイムを後で分析するときに詳細に説明します.
著者:揚易
原文を読む
本文は雲栖コミュニティのオリジナル内容で、許可を得ずに転載してはならない.