TensorFlow架构与设计:会话生命周期

uvtn3309as2 8年前
   <p>TensorFlow的系统结构以C API为界,将整个系统分为「前端」和「后端」两个子系统:</p>    <ul>     <li>前端系统:提供编程模型,负责构造计算图;</li>     <li>后端系统:提供运行时环境,负责执行计算图。</li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1c435f8ab11b82ec76caf3c8f8073210.png"></p>    <p>系统架构</p>    <p>前端系统主要扮演Client的角色,主要负责计算图的构造,并管理Session生命周期过程。</p>    <p>前端系统是一个支持多语言的编程环境,并提供统一的编程模型支撑用户构造计算图。Client通过Session,连接TensorFlow后端的「运行时」,启动计算图的执行过程。</p>    <p>后端系统是TensorFlow的运行时系统,主要负责计算图的执行过程,包括计算图的剪枝,设备分配,子图计算等过程。</p>    <p>本文首先以Session创建为例,揭示前端Python与后端C/C++系统实现的通道,阐述TensorFlow多语言编程的奥秘。随后,以Python前端,C API桥梁,C++后端为生命线,阐述Session的生命周期过程。</p>    <p><strong>Swig: 幕后英雄</strong></p>    <p>前端多语言编程环境与后端C/C++实现系统的通道归功于Swig的包装器。TensorFlow使用Bazel的构建工具,在编译之前启动Swig的代码生成过程,通过tf_session.i自动生成了两个适配(Wrapper)文件:</p>    <ul>     <li>pywrap_tensorflow.py: 负责对接上层Python调用;</li>     <li>pywrap_tensorflow.cpp: 负责对接下层C实现。</li>    </ul>    <p>此外,pywrap_tensorflow.py模块首次被加载时,自动地加载_pywrap_tensorflow.so的动态链接库。从而实现了pywrap_tensorflow.py到pywrap_tensorflow.cpp的函数调用关系。</p>    <p>在pywrap_tensorflow.cpp的实现中,静态注册了一个函数符号表。在运行时,按照Python的函数名称,匹配找到对应的C函数实现,最终转调到c_api.c的具体实现。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/009c0bc15f625c602c2f9b99fee0f542.png"></p>    <p>Swig代码生成器</p>    <p><strong>编程接口:Python</strong></p>    <p>当Client要启动计算图的执行过程时,先创建了一个Session实例,进而调用父类BaseSession的构造函数。</p>    <pre>  <code class="language-python"># tensorflow/python/client/session.py  class Session(BaseSession):    def __init__(self, target='', graph=None, config=None):      super(Session, self).__init__(target, graph, config=config)      # ignoring others</code></pre>    <p>在BaseSession的构造函数中,将调用pywrap_tensorflow模块中的函数。其中,pywrap_tensorflow模块自动由Swig生成。</p>    <pre>  <code class="language-python"># tensorflow/python/client/session.py  from tensorflow.python import pywrap_tensorflow as tf_session    class BaseSession(SessionInterface):    def __init__(self, target='', graph=None, config=None):      self._session = None      opts = tf_session.TF_NewSessionOptions(target=self._target, config=config)      try:        with errors.raise_exception_on_not_ok_status() as status:          self._session = tf_session.TF_NewDeprecatedSession(opts, status)      finally:        tf_session.TF_DeleteSessionOptions(opts)      # ignoring others</code></pre>    <p><strong>生成代码:Swig</strong></p>    <pre>  <code class="language-python">pywrap_tensorflow.py</code></pre>    <p>在pywrap_tensorflow模块中,通过_pywrap_tensorflow将在_pywrap_tensorflow.so中调用对应的C++函数实现。</p>    <pre>  <code class="language-python"># tensorflow/bazel-bin/tensorflow/python/pywrap_tensorflow.py  def TF_NewDeprecatedSession(arg1, status):      return _pywrap_tensorflow.TF_NewDeprecatedSession(arg1, status)</code></pre>    <pre>  <code class="language-python">pywrap_tensorflow.cpp</code></pre>    <p>在pywrap_tensorflow.cpp的具体实现中,它静态注册了函数调用的符号表,实现Python的函数名称到C++实现函数的具体映射。</p>    <pre>  <code class="language-python"># tensorflow/bazel-bin/tensorflow/python/pywrap_tensorflow.cpp  static PyMethodDef SwigMethods[] = {      ...       {"TF_NewDeprecatedSession", _wrap_TF_NewDeprecatedSession, METH_VARARGS, NULL},  }    PyObject *_wrap_TF_NewDeprecatedSession(    PyObject *self, PyObject *args) {    TF_SessionOptions* arg1 = ...     TF_Status* arg2 = ...      TF_DeprecatedSession* result = TF_NewDeprecatedSession(arg1, arg2);    // ignoring others implements  }</code></pre>    <p>最终,自动生成的pywrap_tensorflow.cpp仅仅负责函数调用的转发,最终将调用底层C系统向上提供的API接口。</p>    <p><strong>C API:桥梁</strong></p>    <p>c_api.h是TensorFlow的后端执行系统面向前端开放的公共API接口之一,自此将进入TensorFlow后端系统的浩瀚天空。</p>    <pre>  <code class="language-python">// tensorflow/c/c_api.c  TF_DeprecatedSession* TF_NewDeprecatedSession(    const TF_SessionOptions*, TF_Status* status) {    Session* session;    status->status = NewSession(opt->options, &session);    if (status->status.ok()) {      return new TF_DeprecatedSession({session});    } else {      return NULL;    }  }</code></pre>    <p><strong>后端系统:C++</strong></p>    <p>NewSession将根据前端传递的Session.target,使用SessionFactory多态创建不同类型的Session(C++)对象。</p>    <pre>  <code class="language-python">Status NewSession(const SessionOptions& options, Session** out_session) {    SessionFactory* factory;    Status s = SessionFactory::GetFactory(options, &factory);    if (!s.ok()) {      *out_session = nullptr;      LOG(ERROR) << s;      return s;    }    *out_session = factory->NewSession(options);    if (!*out_session) {      return errors::Internal("Failed to create session.");    }    return Status::OK();  }</code></pre>    <p><strong>会话生命周期</strong></p>    <p>下文以前端Python,桥梁C API,后端C++为生命线,理顺三者之间的调用关系,阐述Session的生命周期过程。</p>    <p>在Python前端,Session的生命周期主要体现在:</p>    <p>Session._extend_graph(graph)</p>    <ul>     <li>创建Session(target)</li>     <li>迭代执行Session.run(fetches, feed_dict)</li>     <li>Session.TF_Run(feeds, fetches, targets)</li>     <li>关闭Session</li>     <li>销毁Session</li>    </ul>    <pre>  <code class="language-python">sess = Session(target)  for _ in range(1000):    batch_xs, batch_ys = mnist.train.next_batch(100)    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})  sess.close()</code></pre>    <p>相应地,C++后端,Session的生命周期主要体现在:</p>    <ul>     <li>根据target多态创建Session</li>     <li>Session.Create(graph):有且仅有一次</li>     <li>Session.Extend(graph):零次或多次</li>     <li>迭代执行Session.Run(inputs, outputs, targets)</li>     <li>关闭Session.Close</li>     <li>销毁Session对象</li>    </ul>    <pre>  <code class="language-python">// create/load graph ...  tensorflow::GraphDef graph;    // local runtime, target is ""  tensorflow::SessionOptions options;    // create Session  std::unique_ptr<tensorflow::Session>   sess(tensorflow::NewSession(options));    // create graph at initialization.  tensorflow::Status s = sess->Create(graph);  if (!s.ok()) { ... }    // run step  std::vector<tensorflow::Tensor> outputs;  s = session->Run(    {},               // inputs is empty     {"output:0"},     // outputs names    {"update_state"}, // target names    &outputs);        // output tensors  if (!s.ok()) { ... }    // close  session->Close();</code></pre>    <p><strong>创建会话</strong></p>    <p>上文介绍了Session创建的详细过程,从Python前端为起点,通过Swig自动生成的Python-C++的包装器为媒介,实现了Python到TensorFlow的C API的调用。</p>    <p>其中,C API是前端系统与后端系统的分水岭。后端C++系统根据前端传递的Session.target,使用SessionFactory多态创建Session(C++)对象。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c5f53338c0b4a11967b6a3b5cd42023a.png"></p>    <p>创建会话</p>    <p>从严格的角色意义上划分,GrpcSession依然扮演了Client的角色。它使用target,通过RPC协议与Master建立通信连接,因此,GrpcSession同时扮演了RPC Client的角色。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5697bd3aab3ecbcae1f4a05cf21d641b.png"></p>    <p>Session多态创建</p>    <p><strong>创建/扩展图</strong></p>    <p>随后,Python前端将调用Session.run接口,将构造好的计算图,以GraphDef的形式发送给C++后端。</p>    <p>其中,前端每次调用Session.run接口时,都会试图将新增节点的计算图发送给后端系统,以便后端系统将新增节点的计算图Extend到原来的计算图中。特殊地,在首次调用Session.run时,将发送整个计算图给后端系统。</p>    <p>后端系统首次调用Session.Extend时,转调(或等价)Session.Create;以后,后端系统每次调用Session.Extend时将真正执行Extend的语义,将新增的计算图的节点追加至原来的计算图中。</p>    <p>随后,后端将启动计算图执行的准备工作。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d31b5e226b569537ef1064275da369c4.png"></p>    <p>创建/扩展图</p>    <p><strong>迭代运行</strong></p>    <p>接着,Python前端Session.run实现将Feed, Fetch列表准备好,传递给后端系统。后端系统调用Session.Run接口。</p>    <p>后端系统的一次Session.Run执行常常被称为一次Step,Step的执行过程是TensorFlow运行时的核心。</p>    <p>每次Step,计算图将正向计算网络的输出,反向传递梯度,并完成一次训练参数的更新。首先,后端系统根据Feed, Fetch,对计算图(常称为Full Graph)进行剪枝,得到一个最小依赖的计算子图(常称为Client Graph)。</p>    <p>然后,运行时启动设备分配算法,如果节点之间的边横跨设备,则将该边分裂,插入相应的Send与Recv节点,实现跨设备节点的通信机制。</p>    <p>随后,将分裂出来的子图片段(常称为Partition Graph)注册到相应的设备上,并在本地设备上启动子图片段的执行过程。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d326dee10286cd3a711cbc57740f71d8.png"></p>    <p>Run Step</p>    <p><strong>关闭会话</strong></p>    <p>当计算图执行完毕后,需要关闭Session,以便释放后端的系统资源,包括队列,IO等。会话关闭流程较为简单,如下图所示。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/896856f6e64f4c803bc3a6baf478bb83.png"></p>    <p>关闭会话</p>    <p><strong>销毁会话</strong></p>    <p>最后,会话关闭之后,Python前端系统启动GC,当Session.del被调用后,启动后台C++的Session对象销毁过程。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/6a1d9a6a12d96c576218d413dfc39172.png"></p>    <p>销毁会话</p>    <p> </p>    <p>来自:http://www.iteye.com/news/32241</p>    <p> </p>