目录

  1. 什么是function
  2. FunctionDef
  3. 函数相关类
  4. 关系图
  5. 涉及的文件
  6. 迭代记录

1. 什么是function

在讲解function的概念之前,我们要先回顾下op。op是规定了输入和输出的操作声明,在研究node的时候我们也看到,NodeDef是包含OpDef的,那么是不是op就只能是节点级别的操作呢?并非如此,操作是可以嵌套的,也就是说,操作A可能内部包含了操作B、C、D。从这个角度理解function就容易了,function其实就是一些大的op。函数的本质是给定输入,经过计算给出输出,这与op的定位相同。对于一些大op,我们可以定义函数与之对应,这些函数内部会包含OpDef,表示这个函数的签名(输入、输出),也会包含一系列NodeDef,用于表示函数内部的运行机制。

2. FunctionDef

有了上面的理解,我们先来看一下FunctionDef的结构:

message FunctionDef {
OpDef signature = 1;
map<string, AttrValue> attr = 5;//函数中的内部参数
repeated NodeDef node_def = 3;
map<string, string> ret = 4;//一个从signature中输出参数名称,到node_def的输出的映射
}
message GradientDef {
string function_name = 1;//原函数名称
string gradient_func = 2;//梯度函数名称
}
message FunctionDefLibrary {
repeated FunctionDef function = 1;
repeated GradientDef gradient = 2;
}

有以下几点需要说明:

  • 正如我们刚才所说的,node_def是一系列节点,这些节点组合在一起形成了函数内部的结构。OpDef中包含了输入输出的名称,在function中我们的输出是被包含在node_def中的,所以需要一个从OpDef中的输出名称到输出所在节点名称和端口号的映射,于是就有了ret;
  • TF支持梯度计算,是因为TF针对每个函数给出了它的梯度函数。梯度函数本身也是一个函数,有输入有输出,为了能将原函数和其梯度函数联系在一起,就有了GradientDef这个结构,这个结构中包含了原函数的名称,也包含了原函数所对应的梯度函数的名称;
  • TF的运行时包含了一个函数定义库,需要使用某个函数时,可以去库里找,因此这个函数定义库包含了多个普通函数,和梯度函数;

3. function related class

3.1 FunctionDefHelper

为了方便对FunctionDef的定义,设计了FunctionDefHelper类,利用它可以方便的定义函数,如下:

FunctionDef my_func = FunctionDefHelper::Create(
"my_func_name",
{"x:T", "y:T"},//每个输入参数用一个字符串表示
{"z:T"},//每个输出用一个字符串表示
{"T: {float, double}"},//每个参数一条字符串
{
{{"o"},"Mul",{"x","y"},{{"T","$T"}}}
},//每个元素对应一个节点,这里仅包含了一个节点
{{"z", "o:z"}}//函数输出到节点输出的映射
);

这个类的实现比较简单,这里我们就不再赘述了。

3.2 FuncionCallFrame

在TF图中,如果要调用一个function,仅知道函数定义是不够的,我们还要为向函数中传递数据,以及从函数中返回数据,提供结构和功能上的支持。还记得OpKernel类的Compute函数吗?每个kernel的计算函数都使用了同样一个接口,实现了不同的运算,秘密就在于函数的输入参数OpKernelContext,它相当于Compute函数调用的上下文,让同样的接口,可以为完全不同的运算提供支持。这也就是FunctionCallFrame存在的意义,它本质上是一个数据中转站,把函数输入数据填入这个结构,在函数计算结束后再把输出数据填入,让函数调用者获取需要的数据。从某种意义上讲,它很像函数调用所在的栈帧,这也就是FunctionCallFrame这个名字的由来:

class FunctionCallFrame {
//...
private:
DataTypeVector arg_types_;
DataTypeVector ret_types_;
gtl::InlinedVector<Tensor, 4> args_;
struct Retval {
bool has_val = false;
Tensor val;
};
gtl::InlinedVector<Retval, 4> rets_;
}

可以看出,这个类的私有数据成员只有输入输出类型、输入输出数值这样四类,本质上就是函数调用的一个中转站。

3.3 FunctionLibraryDefinition

刚才我们在看函数相关proto的时候看到一个结构,FunctionDefLibrary,这两个类要区分清楚。Definition类本质上是一个注册器,提供了函数注册、查找等功能,而Library本质上是一个函数定义的集合,不具备查找功能。下面我们来看一下,类的结构:

class FunctionLibraryDefinition : public OpRegistryInterface {
private:
struct FunctionDefAndOpRegistration {
FunctionDefAndOpRegistration(const FunctionDef& fdef_in);
FunctionDef fdef;
OpRegistrationData op_registration_data;
};
const OpRegistryInterface* const default_registry_;
gtl::FlatMap<string, std::unique_ptr<FunctionDefAndOpRegistration>> function_defs_;
gtl::FlatMap<string, string> func_grad_;
};

这个类给我们提供了一个方便对function进行集中管理的地方。它继承自OpRegistryInterface,因此跟OpRegistry有相似之处。

3.4 FunctionLibraryRuntime

顾名思义,是函数库的运行时类。为函数的执行提供了很多便利的接口。它单纯是包裹在FunctionLibraryDefinition这个类之上的,提供API支持,本身是没有任何数据成员的。我们简单看下它都提供了哪些API:

class FunctionLibraryRuntime {
public:
//...
virtual Status Instantiate(const string& function_name, AttrSlice attrs, Handle* handle) = 0;//用参数实例化一个函数
virtual const FunctionBody* GetFunctionBody(Handle h) = 0;//获取一个已经实例化了的函数的函数体
virtual void Run(const Option& opts, Handle handle, gtl::ArraySlice<Tensor> args, std::vector<Tensor>* rets, DoneCallback done) = 0;//异步的调用一个使用handle标识的函数
virtual Status CreateKernel(const NodeDef& ndef, OpKernel** kernel) = 0;//给定ndef,创造一个kernel
virtual bool IsStateful(const string& function_name) = 0;//该函数是否是带有状态的
virtual Device* device() = 0;//函数运行所在的设备
virtual const FunctionLibraryDefinition* GetFunctionLibraryDefinition() const = 0;
virtual Env* env() = 0;
};

4. 关系图

graph TB
FunctionDefLibrary-.包含.->FunctionDef
FunctionDefLibrary-.包含.->GradientDef
FunctionDefHelper-.辅助构造.->FunctionDef
FunctionCallFrame-.辅助调用.->FunctionDef
OpRegistryInterface-->|派生|FunctionLibraryDefinition
FunctionLibraryDefinition-.包含.->FunctionDef
OpRegistryInterface-->|派生|OpRegistry
FunctionLibraryRuntime-.包裹并管理.->FunctionLibraryDefinition

5. 涉及的文件

  • function

6. 迭代记录

  • v1.0 2018-08-28 文档创建
  • v2.0 2018-09-10 文档重构

github地址

tensorflow源码解析之framework-function的更多相关文章

  1. tensorflow源码解析之framework拾遗

    把framework中剩余的内容,按照文件名进行了简单解析.时间原因写的很仓促,算是占个坑,后面有了新的理解再来补充. allocation_description.proto 一个对单次内存分配结果 ...

  2. tensorflow源码解析系列文章索引

    文章索引 framework解析 resource allocator tensor op node kernel graph device function shape_inference 拾遗 c ...

  3. Tensorflow源码解析1 -- 内核架构和源码结构

    1 主流深度学习框架对比 当今的软件开发基本都是分层化和模块化的,应用层开发会基于框架层.比如开发Linux Driver会基于Linux kernel,开发Android app会基于Android ...

  4. tensorflow源码解析之common_runtime-executor-上

    目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...

  5. tensorflow源码解析之framework-allocator

    目录 什么是allocator 内存分配器的管理 内存分配追踪 其它结构 关系图 涉及的文件 迭代记录 1. 什么是allocator Allocator是所有内存分配器的基类,它定义了内存分配器需要 ...

  6. tensorflow源码解析之common_runtime-executor-下

    目录 核心概念 executor.h Executor NewLocalExecutor ExecutorBarrier executor.cc structs GraphView ExecutorI ...

  7. tensorflow源码解析之common_runtime拾遗

    把common_runtime中剩余的内容,按照文件名排序进行了简单的解析,时间原因写的很仓促,算是占个坑,后续有了新的理解再来补充. allocator_retry 有时候内存分配不可能一次完成,为 ...

  8. tensorflow源码解析之framework-op

    目录 什么是op op_def定义 op注册 op构建与注册辅助结构 op重写 关系图 涉及的文件 迭代记录 1. 什么是op op和kernel是TF框架中最重要的两个概念,如果一定要做一个类比的话 ...

  9. tensorflow源码解析之distributed_runtime

    本篇主要介绍TF的分布式运行时的基本概念.为了对TF的分布式运行机制有一个大致的了解,我们先结合/tensorflow/core/protobuf中的文件给出对TF分布式集群的初步理解,然后介绍/te ...

  10. Tensorflow源码解析2 -- 前后端连接的桥梁 - Session

    Session概述 1. Session是TensorFlow前后端连接的桥梁.用户利用session使得client能够与master的执行引擎建立连接,并通过session.run()来触发一次计 ...

随机推荐

  1. Entity Framework Core的坑,Select后再对导航属性进行查询或Select前进行Skip/Take

    把asp.net core的项目发布到ubuntu上了,运行的时候出现了如下警告: warn: Microsoft.EntityFrameworkCore.Query[20500] The LINQ ...

  2. HTC组件介绍及应用 HTML

    转载请注明来源:https://www.cnblogs.com/hookjc/ HTML组件封装了HTML内容,并可以插入到别的HTML文档中.在HTML组件出现以前,在HMTL文档中使用自定义控制唯 ...

  3. element ui图片上传方法

    <!--商品图片--> <template v-slot:product_cover> <el-upload list-type="picture-card&q ...

  4. 使用ajax上传文件

    1. XMLHttpRequest(原生ajax) [](javascript:void(0) <input class="file" type="file&quo ...

  5. Java--面向对象设计

    [转载自本科老师上课课件] 问题一: 在一个软件的功能模块中,需要一种图像处理的功能.该图像处理的策略(如何处理)与图像的内容是相关的.如:卫星的运行图片,使用策略A处理方式,如果是卫星内云图片,则需 ...

  6. 关于mybatis,需要掌握的基础

    目录 ❀ 总结 mybatis,需要掌握的基础如下: 1.了解ORM 思想.ORM思想的作用.映射配置的两种方式 2.MyBatis开发流程(基本使用) 3.日志框架 4.了解mybatis生命周期并 ...

  7. JDK中线程中实现同步等待闭环的一种方式

    实际Thread类自带的join方法就实现了线程同步等待,具体可以通过案例实践,如下: 本文的重点不是join,而是另一种设计的同步等待实现,涉及的关键类有:Thread.Runable.Callab ...

  8. 看SparkSql如何支撑企业数仓

    企业级数仓架构设计与选型的时候需要从开发的便利性.生态.解耦程度.性能. 安全这几个纬度思考.本文作者:惊帆 来自于数据平台 EMR 团队 前言 Apache Hive 经过多年的发展,目前基本已经成 ...

  9. Node 模块规范鏖战:难以相容的 CJS 与 ESM

    自 13.2.0 版本开始,Node.js 在保留了 CommonJS(CJS)语法的前提下,新增了对 ES Modules(ESM)语法的支持. 天下苦 CJS 久已,Node 逐渐拥抱新标准的规划 ...

  10. 02.Oracle之安装与配置

    1.Oracle简介 Oracle是世界上最早的商品化的关系型数据库管理系统,是数据库专业厂商ORACLE(中文名字叫甲骨文)公司开发的,也是当前应用最为广泛.功能最强大.具有面向对象特点.采用了客户 ...