Tensorflow核心代碼解析之計算圖篇其二:節點與邊

介紹

計算圖本身是一個有向無環圖,它主要由一組節點(Node,抽象表示一個Op函數執行)與表示節點之間相互依賴的邊(Edge,表示Nodes之間的輸入、輸出或次序控制依賴關系)組成。

本節當中我們將詳細分析Tensorflow里面節點與邊的一些實現。

節點

以下為Node class在Tensorflow里面的定義。詳見:tensorflow/core/graph/graph.h。我們將在其代碼中逐個分析其不同的屬性與方法。

  • 基本方法與屬性
class Node {
 public:
  string DebugString() const;
  int id() const { return id_; } // 每個節點都會分配這么一個固定的id,同一副圖里面的不同Node有著其唯一的標識id
  int cost_id() const { return cost_id_; } // 此處主要標明Node 內存分配相關的id,有些Node為ref類型Node,可能其實現當中并不實際分配內存而只是引用其它Node節點里面分配的內存;這樣它們將擁有相同的cost_id,它在對圖的內存分配優化及優先級策略上有指導、幫助的意義
  const string& name() const;
  const string& type_string() const; // 顯示不同的type,如有的為Conv,有的為Multmul,還有則為Send或Recv等

  const NodeDef& def() const; //輸出Node的protocol buffer definition
  const OpDef& op_def() const; //輸出此Node相關聯的Op的protocol buffer definition
  
  /* 以下主要為Node的輸入、輸出Tensor類型、數量及其引用等,容易理解 */
  // input and output types
  int32 num_inputs() const;
  DataType input_type(int32 i) const;
  const DataTypeVector& input_types() const;

  int32 num_outputs() const;
  DataType output_type(int32 o) const;
  const DataTypeVector& output_types() const;

  /* 用戶可指定或查詢某Node節點執行所用的device,但其在真正執行時,executor只是參考此建議,最終真正所用的device還是由executor綜合考慮后決定 */
  // The device requested by the user.  For the actual assigned device,
  // use assigned_device_name() below.
  const string& requested_device() const;
  // This changes the user requested device but not necessarily the device that
  // on which the operation will run.
  void set_requested_device(const string& device);

  // 以下一組函數可用來查詢/添加/刪除此Node所具有的屬性
  // Read only access to attributes
  AttrSlice attrs() const;
  template <typename T>
  void AddAttr(const string& name, const T& val) {
    SetAttrValue(val, AddAttrHelper(name));
  }
  void ClearAttr(const string& name);
  // Inputs requested by the NodeDef.  For the actual inputs, use in_edges.
  const protobuf::RepeatedPtrField<string>& requested_inputs() const;

  //以下為一組功能函數,具體來查詢輸入、輸出的Edges/Nodes,并使用不同的數據結構返回,因為此類操作在Tensorflow中使用非常頻繁,因此需要考慮數據結構的效率、內存使用等特點
  // Get the neighboring nodes via edges either in or out of this node.  This
  // includes control edges.
  gtl::iterator_range<NeighborIter> in_nodes() const;
  gtl::iterator_range<NeighborIter> out_nodes() const;
  const EdgeSet& in_edges() const { return in_edges_; }
  const EdgeSet& out_edges() const { return out_edges_; }
  // Returns into '*n' the node that has an output connected to the
  // 'idx' input of this Node.
  Status input_node(int idx, const Node** n) const;
  Status input_node(int idx, Node** n) const;

private:
  friend class Graph; //Graph與Node經常會相互調用彼此函數,這里設為友類
  Node();

  NodeProperties* properties() const { return props_.get(); }

  void Initialize(int id, int cost_id, std::shared_ptr<NodeProperties> props);

  // Releases memory from props_, in addition to restoring *this to its
  // uninitialized state.
  void Clear();
};
  • 節點類型

Tensorflow的程序設計當中,一切計算、控制操作都會由節點來表示。因此不只像傳統意義上大家認為構成主要模型的Conv/Relu/Matmul/FC等計算操作被表示為節點,其它像變量初始化(VARIABLE),常量賦值等操作都會有相應的Node節點存在在圖中。然后由Session統一驅動執行。這就是靜態圖構建與執行的基本原理。

以下為所有的節點類型。我們平時說的Conv/Relu/Matmul/BN等計算節點都被歸于NC_OTHER里面。。而其它在這里有名有姓的則為圖上的控制節點,也稱為特殊節點。

  // A set of mutually exclusive classes for different kinds of nodes,
  // class_ is initialized in the Node::Initialize routine based on the
  // node's type_string().
  enum NodeClass {
    NC_UNINITIALIZED,
    NC_SWITCH,
    NC_MERGE,
    NC_ENTER,
    NC_EXIT,
    NC_NEXT_ITERATION,
    NC_LOOP_COND,
    NC_CONTROL_TRIGGER,
    NC_SEND,
    NC_HOST_SEND,
    NC_RECV,
    NC_HOST_RECV,
    NC_CONSTANT,
    NC_VARIABLE,
    NC_IDENTITY,
    NC_GET_SESSION_HANDLE,
    NC_GET_SESSION_TENSOR,
    NC_DELETE_SESSION_TENSOR,
    NC_METADATA,
    NC_SCOPED_ALLOCATOR,
    NC_COLLECTIVE,
    NC_OTHER  // Not a special kind of node
  };

  • 節點輸入/輸出

以下兩個結構分別抽象表示Node的輸入、輸出張量(Tensor),本質上Tensorflow圖上流動的正是如此一個個Input/Output tensors。

// Represents an input of a node, i.e., the `index`-th input to `node`.
struct InputTensor {
  const Node* node;
  int index;
  InputTensor(const Node* n, int i) : node(n), index(i) {}
  InputTensor() : node(nullptr), index(0) {}
};

// Represents an output of a node, i.e., the `index`-th output of `node`. Note
// that a single `OutputTensor` can correspond to multiple `Edge`s if the output
// is consumed by multiple destination nodes.
struct OutputTensor {
  const Node* node;
  int index;
  OutputTensor(const Node* n, int i) : node(n), index(i) {}
  OutputTensor() : node(nullptr), index(0) {}
};
  • 節點屬性

tf中每個節點的屬性包含其輸入、輸出Tensors的類型以及此節點的protocol定義NodeDef及其所關聯的Op的定義OpDef。

class NodeProperties {
 public:
  NodeProperties(const OpDef* op_def, const NodeDef& node_def,
                 const DataTypeSlice inputs, const DataTypeSlice outputs)
      : op_def(op_def),
        node_def(node_def),
        input_types(inputs.begin(), inputs.end()),
        output_types(outputs.begin(), outputs.end()) {}

  const OpDef* op_def;  // not owned
  NodeDef node_def;
  const DataTypeVector input_types;
  const DataTypeVector output_types;
};

在下面我們從class Edge的代碼里來分析下TF中邊的實現。詳細可見:tensorflow/core/graph/graph.h

class Edge {
 public:
 //我們介紹過邊表示Nodes之間的依賴關系,此處即為dst節點執行所需的某個輸入依賴于來自src節點的某個輸出或者作為控制邊要求src節點的執行先于節點dst完成
  Node* src() const { return src_; }
  Node* dst() const { return dst_; }
  int id() const { return id_; } //TF Graph當中與Node一樣,每個邊也有其唯一的標識id

  // Return the index of the source output that produces the data
  // carried by this edge.  The special value kControlSlot is used
  // for control dependencies.
  int src_output() const { return src_output_; }

  // Return the index of the destination input that consumes the data
  // carried by this edge.  The special value kControlSlot is used
  // for control dependencies.
  int dst_input() const { return dst_input_; }

  // Return true iff this is an edge that indicates a control-flow
  // (as opposed to a data-flow) dependency.
  bool IsControlEdge() const;

  string DebugString() const;
private:
  Edge() {}

  friend class EdgeSetTest;
  friend class Graph;
  Node* src_;
  Node* dst_;
  int id_;
  int src_output_;
  int dst_input_;
};

參考文獻

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容