采用其他编程语言的 TensorFlow

背景
本文档旨在为那些想要用其他编程语言创建或开发 TensorFlow 功能的人员提供指导。本文介绍了 TensorFlow 的功能以及在其他编程语言中实现相同功能应采取的推荐步骤。海风教育退费

海风教育在线辅导0元一对一试听课等你来领取,领取课程方法:
1、私信留下您的手机号和姓名,需要补习的科目。
2、也可以在海风教育官网留下您的手机号领取 https://www.hfjy.com

Python 是 TensorFlow 支持的第一种客户端语言,目前支持的功能最多。该功能正逐步移植到 TensorFlow 的核心(用 C++ 实现)并通过 C API 公开。客户端语言应使用该语言的外部函数接口 (FFI) 调用此 C API 以提供 TensorFlow 功能。

概述
通过某个编程语言提供 TensorFlow 功能可分为几大类别:
运行预定义图:给定 GraphDef(或 MetaGraphDef)协议消息,能够创建会话,运行查询并获得张量结果。这对于想要在预训练模型上进行推断的移动应用或服务器来说已足够
图构造:每个定义的 TensorFlow 操作至少有一个向图添加操作的函数。理想情况下,这些函数会自动生成,因此在修改操作定义时它们会保持同步
梯度(亦称为自动微分):给定图及输入和输出操作列表,向图添加计算输入相对于输出的偏导数(梯度)的操作。允许针对图中的特定操作自定义梯度函数
函数:定义可在主 GraphDef 中的多个位置调用的子图。在包含在 GraphDef 中的 FunctionDefLibrary 内定义 FunctionDef
控制流:使用用户指定的子图构造 If 和 While 循环。理想情况下,这些控制流支持梯度(参见上文)
神经网络库:一些组件,它们共同支持神经网络模型的创建和训练(可能在分布式设置中)。虽然以其他语言提供此功能会很方便,但目前还没有用除 Python 以外的语言支持此功能的计划。这些库通常是上述功能的封装容器

语言绑定至少应支持运行预定义的图,但大多数还应支持图构造。TensorFlow Python API 提供了所有这些功能。

当前状态
应该在 C API 基础之上构建新的语言支持。但是,正如您在下表中所看到的,部分功能尚未在 C 中提供。在 C API 中提供更多功能是一个长期项目。

推荐方法
运行预定义的图
语言绑定应该定义以下类:
Graph:表示 TensorFlow 计算的图。包含操作(在客户端语言中由 Operation 表示)并且对应于 C API 中的 TF_Graph。主要在创建新的 Operation 对象和启动 Session 时用作参数。还支持遍历图中的操作 (TF_GraphNextOperation),按名称查询操作 (TF_GraphOperationByName),以及转换为 GraphDef 协议消息或对其进行转换(C API 中的 TF_GraphToGraphDef 和 TF_GraphImportGraphDef)
Operation:表示图中的计算节点。对应于 C API 中的 TF_Operation
Output:表示图中某个操作的一个输出。具有 DataType(最终是一个形状)。可以作为输入参数传递给函数以向图添加操作,或传递给 Session 的 Run() 方法以将该输出提取为张量。对应于 C API 中的 TF_Output
Session:表示 TensorFlow 运行时的特定实例的客户端。它的主要任务是使用 Graph 和一些选项进行构建,然后进行字段调用以对图执行 Run() 操作。对应于 C API 中的 TF_Session
Tensor:表示所有元素都具有相同 DataType 的 N 维(矩形)数组。向 Session 的 Run() 调用提供数据或从中获取数据。对应于 C API 中的 TF_Tensor
DataType:包含受 TensorFlow 支持的所有可能张量类型的枚举。对应于 C API 中的 TF_DataType,在 Python API 中通常称为 dtype

图构造
TensorFlow 有很多操作,并且操作列表不是静态的,因此我们建议生成用于将操作添加到图中的函数,而不是手动逐个编写这些函数(但手动编写一些函数不失为确定生成器应该生成哪些内容的不错方法)。生成函数所需的信息包含在 OpDef 协议消息中。

您可以通过以下几种方法获取已注册操作的 OpDef 列表:
C API 中的 TF_GetAllOpList 会检索所有已注册的 OpDef 协议消息。它可以用于以客户端语言编写生成器。这就要求客户端语言支持协议缓冲区,以便解释 OpDef 消息
C++ 函数 OpRegistry::Global()->GetRegisteredOps() 会返回所有已注册 OpDef(在 tensorflow/core/framework/op.h 中定义)的相同列表。它可以用于以 C++ 编写生成器(对于不支持协议缓冲区的语言特别有用)
该列表的 ASCII 序列化版本通过自动化流程定期检入 tensorflow/core/ops/ops.pbtxt

OpDef 会指定以下内容:
驼峰命名法 (CamelCase) 的操作名称。对于生成的函数,请遵循相应语言的惯例。例如,如果该语言使用蛇形命名法 (snake_case),则使用该命名法而不是驼峰命名法表示此操作的函数名称
输入和输出列表。这些内容的类型可以通过引用属性实现多态,如添加操作的输入和输出部分所述
属性列表及其默认值(如果有)。请注意,系统会推断其中一些属性(如果它们由输入确定),一些属性是可选属性(如果它们具有默认值),另一些属性是必需属性(无默认值)
一般操作以及输入、输出和非推断属性的文档
运行时使用的其他一些字段,可以被代码生成器忽略

可以将 OpDef 转换为函数的文本,该函数使用 TF_OperationDescription C API(封装在相应语言的 FFI 中)将该操作添加到图中:
从 TF_NewOperation() 开始创建 TF_OperationDescription*。
每个输入调用 TF_AddInput() 或 TF_AddInputList() 一次(取决于输入是否具有列表类型)。
调用 TF_SetAttr*() 函数来设置非推断属性。如果您不想替换默认值,可以跳过具有默认值的属性。
根据需要设置选填字段:
TF_SetDevice():强制在特定设备上执行操作。
TF_AddControlInput():添加要求以说明在此操作开始运行之前完成另一个操作
TF_SetAttrString("_kernel"):设置内核标签(很少使用)
TF_ColocateWith():将一个操作与另一个操作共置到一起
完成后调用 TF_FinishOperation()。这样会将操作添加到图中,之后便无法修改。

现有示例会在编译流程中运行代码生成器(使用 Bazel genrule)。或者,代码生成器可以由自动化 cron 进程运行,并可能检入结果中。这可能会导致生成的代码与检入代码库中的 OpDef 之间产生分歧,但对于会提前生成代码的语言(例如适用于 Go 的 go get 和适用于 Rust 的 cargo ops)很有用。另一方面,对于某些语言,代码可以从 tensorflow/core/ops/ops.pbtxt 动态生成。

处理常量
如果用户可以为输入参数提供常量,则调用代码将更加简洁。生成的代码应该将这些常量转换为以下操作:添加到图中并用作正在实例化的操作的输入。

可选参数
如果语言允许函数具有可选参数(如 Python 中具有默认值的关键字参数),则将它们用于可选属性、操作名称、设备、控制输入等。在某些语言中,可以使用动态作用域设置这些可选参数(如 Python 中的 “with” 块)。如果没有这些功能,库可能会依靠 “编译器模式”,就像在 TensorFlow API 的 C++ 版本中所做的那样。

名称作用域
建议您使用某种作用域层次结构支持命名图操作,特别是考虑到 TensorBoard 依靠它以合理的方式显示大图这一事实。现有的 Python 和 C++ API 采用不同的方法:在 Python 中,名称的 “目录” 部分(最后一个 “/” 前面的所有内容)来自 with块。实际上,有一个线程局部堆栈,其中的作用域定义了名称层次结构。名称的最后一个组成部分由用户显式提供(使用可选的 name 关键字参数),或者默认为要添加的操作的类型名称。在 C++ 中,名称的 “目录” 部分存储在显式 Scope 对象中。NewSubScope() 方法附加到名称的该部分并返回一个新的 Scope。名称的最后一个组成部分是使用 WithOpName() 方法设置的,并且像 Python 一样默认为要添加的操作的类型名称。系统会显式传递 Scope 对象以指定上下文的名称。

封装容器
可以保留生成的函数专用于某些操作,这样可以改用执行一些额外工作的封装容器函数。这也为支持所生成代码作用域之外的功能提供了一个应急路径。

封装容器的一个用途是支持 SparseTensor 输入和输出。SparseTensor 是一个包含 3 个密集张量(索引、值和形状)的元组。值是向量大小 [n],形状是向量大小 [秩],索引是矩阵大小 [n, 秩]。有一些稀疏操作使用此三元组表示单个稀疏张量。

使用封装容器的另一个原因是用于保留状态的操作。有一些此类操作(例如变量)有几个用于处理该状态的伴随操作。Python API 为这些操作提供了类,其中构造函数会创建操作,并且此类上的方法会向处理该状态的图添加操作。

其他注意事项
最好添加一个关键字列表,用于重命名与语言关键字冲突的操作函数和参数(或其他会导致问题的符号,如生成的代码中引用的库函数或变量的名称)。

用于向图添加 Const 操作的函数通常是封装容器,因为生成的函数通常具有冗余的 DataType 输入。

梯度、函数和控制流
目前,对梯度、函数和控制流操作(“if” 和 “while”)的支持并未在除 Python 以外的其他语言中提供。当 C API 提供必要的支持时,将会更新这方面的功能。

猜你喜欢

转载自blog.csdn.net/weixin_43932460/article/details/89448995