阅读更多
引用
作者:刘光聪 ,中兴通讯高级系统架构师,专注机器学习算法,分布式系统架构与优化。
原文:TensorFlow架构与设计:会话生命周期
责编:王艺 CSDN AI记者,投稿、寻求报道、深入交流请邮件wangyi@csdn.net
相关文章:
图解TensorFlow架构与设计
TensorFlow架构与设计:图模块

TensorFlow的系统结构以C API为界,将整个系统分为「前端」和「后端」两个子系统:
  • 前端系统:提供编程模型,负责构造计算图;
  • 后端系统:提供运行时环境,负责执行计算图。

系统架构

前端系统主要扮演Client的角色,主要负责计算图的构造,并管理Session生命周期过程。

前端系统是一个支持多语言的编程环境,并提供统一的编程模型支撑用户构造计算图。Client通过Session,连接TensorFlow后端的「运行时」,启动计算图的执行过程。

后端系统是TensorFlow的运行时系统,主要负责计算图的执行过程,包括计算图的剪枝,设备分配,子图计算等过程。

本文首先以Session创建为例,揭示前端Python与后端C/C++系统实现的通道,阐述TensorFlow多语言编程的奥秘。随后,以Python前端,C API桥梁,C++后端为生命线,阐述Session的生命周期过程。

Swig: 幕后英雄
前端多语言编程环境与后端C/C++实现系统的通道归功于Swig的包装器。TensorFlow使用Bazel的构建工具,在编译之前启动Swig的代码生成过程,通过tf_session.i自动生成了两个适配(Wrapper)文件:
  • pywrap_tensorflow.py: 负责对接上层Python调用;
  • pywrap_tensorflow.cpp: 负责对接下层C实现。
此外,pywrap_tensorflow.py模块首次被加载时,自动地加载_pywrap_tensorflow.so的动态链接库。从而实现了pywrap_tensorflow.py到pywrap_tensorflow.cpp的函数调用关系。

在pywrap_tensorflow.cpp的实现中,静态注册了一个函数符号表。在运行时,按照Python的函数名称,匹配找到对应的C函数实现,最终转调到c_api.c的具体实现。

Swig代码生成器

编程接口:Python
当Client要启动计算图的执行过程时,先创建了一个Session实例,进而调用父类BaseSession的构造函数。
# 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

在BaseSession的构造函数中,将调用pywrap_tensorflow模块中的函数。其中,pywrap_tensorflow模块自动由Swig生成。
# 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

生成代码:Swig
pywrap_tensorflow.py

在pywrap_tensorflow模块中,通过_pywrap_tensorflow将在_pywrap_tensorflow.so中调用对应的C++函数实现。
# tensorflow/bazel-bin/tensorflow/python/pywrap_tensorflow.py
def TF_NewDeprecatedSession(arg1, status):
    return _pywrap_tensorflow.TF_NewDeprecatedSession(arg1, status)

pywrap_tensorflow.cpp

在pywrap_tensorflow.cpp的具体实现中,它静态注册了函数调用的符号表,实现Python的函数名称到C++实现函数的具体映射。
# 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
}

最终,自动生成的pywrap_tensorflow.cpp仅仅负责函数调用的转发,最终将调用底层C系统向上提供的API接口。

C API:桥梁
c_api.h是TensorFlow的后端执行系统面向前端开放的公共API接口之一,自此将进入TensorFlow后端系统的浩瀚天空。
// 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;
  }
}

后端系统:C++
NewSession将根据前端传递的Session.target,使用SessionFactory多态创建不同类型的Session(C++)对象。
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();
}

会话生命周期
下文以前端Python,桥梁C API,后端C++为生命线,理顺三者之间的调用关系,阐述Session的生命周期过程。

在Python前端,Session的生命周期主要体现在:
  • 创建Session(target)
  • 迭代执行Session.run(fetches, feed_dict)
  • Session._extend_graph(graph)
    Session.TF_Run(feeds, fetches, targets)
  • 关闭Session
  • 销毁Session
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()

相应地,C++后端,Session的生命周期主要体现在:
  • 根据target多态创建Session
  • Session.Create(graph):有且仅有一次
  • Session.Extend(graph):零次或多次
  • 迭代执行Session.Run(inputs, outputs, targets)
  • 关闭Session.Close
  • 销毁Session对象
// 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();

创建会话
上文介绍了Session创建的详细过程,从Python前端为起点,通过Swig自动生成的Python-C++的包装器为媒介,实现了Python到TensorFlow的C API的调用。

其中,C API是前端系统与后端系统的分水岭。后端C++系统根据前端传递的Session.target,使用SessionFactory多态创建Session(C++)对象。

创建会话

从严格的角色意义上划分,GrpcSession依然扮演了Client的角色。它使用target,通过RPC协议与Master建立通信连接,因此,GrpcSession同时扮演了RPC Client的角色。

Session多态创建

创建/扩展图
随后,Python前端将调用Session.run接口,将构造好的计算图,以GraphDef的形式发送给C++后端。

其中,前端每次调用Session.run接口时,都会试图将新增节点的计算图发送给后端系统,以便后端系统将新增节点的计算图Extend到原来的计算图中。特殊地,在首次调用Session.run时,将发送整个计算图给后端系统。

后端系统首次调用Session.Extend时,转调(或等价)Session.Create;以后,后端系统每次调用Session.Extend时将真正执行Extend的语义,将新增的计算图的节点追加至原来的计算图中。

随后,后端将启动计算图执行的准备工作。

创建/扩展图

迭代运行
接着,Python前端Session.run实现将Feed, Fetch列表准备好,传递给后端系统。后端系统调用Session.Run接口。

后端系统的一次Session.Run执行常常被称为一次Step,Step的执行过程是TensorFlow运行时的核心。

每次Step,计算图将正向计算网络的输出,反向传递梯度,并完成一次训练参数的更新。首先,后端系统根据Feed, Fetch,对计算图(常称为Full Graph)进行剪枝,得到一个最小依赖的计算子图(常称为Client Graph)。

然后,运行时启动设备分配算法,如果节点之间的边横跨设备,则将该边分裂,插入相应的Send与Recv节点,实现跨设备节点的通信机制。

随后,将分裂出来的子图片段(常称为Partition Graph)注册到相应的设备上,并在本地设备上启动子图片段的执行过程。

Run Step

关闭会话
当计算图执行完毕后,需要关闭Session,以便释放后端的系统资源,包括队列,IO等。会话关闭流程较为简单,如下图所示。

关闭会话

销毁会话
最后,会话关闭之后,Python前端系统启动GC,当Session.del被调用后,启动后台C++的Session对象销毁过程。

销毁会话

  • 大小: 19.4 KB
  • 大小: 34.2 KB
  • 大小: 21.7 KB
  • 大小: 26.2 KB
  • 大小: 16.3 KB
  • 大小: 24.3 KB
  • 大小: 11.6 KB
  • 大小: 11.5 KB
0
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • [原创]可以让table支持手动调节列宽的js(ie only)

    自动支持手动调节列宽的功能。完全基于DHTML前台技术,与服务器端无关。   通常情况下 你真正要做的工作只是(当然还有更高级的用法,说明晚些时候奉上) 1 引入脚本 2 在页面初始化方法里执行: 新建 ...

  • jQuery拖动table列宽组件colResizable

    jQuery拖动table列宽组件colResizable 表格内容过多比较挤的情况下会有可拖动列的需求 以下是列拖动的组件 可以 兼容dataTable 组件 官网:http://www.bacubacu.com/colresizable/ 基本使用 // ---------引入cdn--...

  • jQuery插件来调整表列的大小colResizable.js 拖动改变单元格宽度

    jQuery插件来调整表列的大小官网地址:http://www.bacubacu.com/colresizable/github项目地址:https://github.com/ybx13579/resize-table-th效果图: colResizablecolResizable是一个免费的jQuery插件,旨在通过...

  • SheetJS中文文档-js导出Excel脚本库

    转载自 GITHUB用户...SheetJS强调解析和编写的稳健,其跨格式的特点和统一的JS规范兼容,并且ES3/ES5浏览器向后兼容IE6。 目前这个是社区版,我们也提供了性能增强的专业版,专业版提供样式和专业支持的附加功能。

  • Ext JS的4.1.0的RC 1的发行说明

    JS的 - JavaScript框架 Ext JS的4.1.0的RC 1的发行说明 发行日期:2012年3月13, 版本号:4.1.0的RC 1 修正的错误 钮 EXTJSIV-5129 按钮不坚持宽度设置在IE9 图表 EXTJSIV-4416 ...

  • 2021年Bootstrap实用手册和最强总结以及工具

    Responsive Web Page,响应式网页/自适应网页,即一个页面既可以在 PC 浏览器中浏览,也可以在手机、平板中浏览,并且配合不同设备有不同的响应结果,响应式网页的特点: (1). 页面上的图片和文字要随着屏幕尺寸...

  • Element-ui源码分析

    虽然说基本需求看文档就可以了,但是文档中提供的方法和业务需求相比肯定是有一定差距的,这时候就需要自己封装组件了;并且,在写了一些代码后感觉,其实在不同的项目中写过功能差不多相同的代码,那为什么不封装...

  • Web开发系列 - EcSide

    可手动调整列宽 11 增加了“ 列表内部滚动条 ”(实现列表头固定,列表体滚动的功能) 12 为ec:row和ec:column 添加更多的html事件支持,现支持:onmouserover onmouserout onclick ondbclick 13 为ec:table ec:...

  • ecside

    这几天偶尔会碰到ecside的问题,零零碎碎碰下来也对他有了一点了解,有些问题...下面是从网上转载的资料,可以用来参考:http://blog.sina.com.cn/s/blog_4b22130e010009b5.html~type=v5_one&amp;amp;label=rela_pre...

  • w3c h5 + css + js笔记

    公司电脑:/Users/yangyangzi/Desktop/YangZi2/2019前端/h5+css+js 「 1. js基础/css基础/html基础 w3school https://www.w3school.com.cn/js/index.asp 菜鸟 https://www.runoob.com/js/js-tutorial.html ...

  • iview学习帮助文档

    在main.js中导入iview import ViewUI from 'view-design'; import 'view-design/dist/styles/iview.css'; Vue.use(ViewUI); 按需引用iview组件 a.安装babel npm install babel-plugin-import --save-dev b.在 ....

  • springboot+vue实现导出excel(解决乱码)兼纯vue前端实现导出

    springboot+vue实现导出excel(解决乱码)兼纯vue前端实现导出springboot+vue导出纯前端vue导出文件Blob.jsExport2Excel.js 刚开始实现excel导出是用的springboot+vue的方式实现,后来发现可能是系统的原因(同一套...

  • [2007-01-21 18时发布]ecside 1.0rc1: 列表组件eXtremeComponents全面增强版

    10) 一些细节的修改(例如对滚轮的支持更自然,filterable="true"手动调节列宽功能屏蔽等) 还有对一些小bug的修正,但是由于变化较大,肯定还会带来一些新bug。 11) 整个示例的应用 逐步发展为 ecside 的最佳实践 ...

  • 流氓软件的完全卸载的一些方法

     网络猪与划词搜索虽然可以通过添加/删除软件进行卸载,但是总卸载不干净,必须要手动删除安装目录。为此你首先需要卸载掉网络猪与划词搜索;然后单击“开始”-“运行”,输入“MSConfig”,回车后在弹出的窗口中...

  • 批量导出并设置样式/批量导入

    order to support older browsers that only have BlobBuilder var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || ( function ( view ) { var get_class =...

  • java ec 分页_【转】EC Side分页组件

    有一点要主义,它是在DAO层导出,这样可以支持较大的数据量。 6 完善了ECSideFilter过滤器 修正了导出下载时的一个小bug(bug虽小,耗时不少)。 7 一些代码调整和bug修改。 =============================== 2007-...

  • ecside二次笔摘

    这几天偶尔会碰到ecside的问题,零零碎碎碰下来也对他有了一点了解,有些问题从网上搜到...下面是从网上转载的资料,可以用来参考: http://blog.sina.com.cn/s/blog_4b22130e010009b5.html~type=v5_one&label=rela_pre

  • 前端基础面试题答案

    答:都是标记语言,都用标签来标记元素,都符合w3c标准 ...答:标准css盒模型:margin,content,padding,border ie盒模型:margin和content,content包含了padding和border 当页面需要适配不同的屏幕大...

  • setting.xml文件,修改Maven仓库指向至阿里仓

    setting.xml文件,修改Maven仓库指向至阿里仓

Global site tag (gtag.js) - Google Analytics