页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

大多数用户不需要关心 TensorFlow 如何将数据存储在磁盘上的内部细节,但如果您是工具开发人员,您可能需要。例如,您可能需要分析模型,或者在 TensorFlow 和其他格式之间来回转换。本指南尝试解释一些关于如何使用持有模型数据的主文件的细节,以便更容易地开发这些工具。

协议缓冲区

TensorFlow 的所有文件格式都基于 协议缓冲区,所以开始它值得熟悉它们的工作原理。总结是您在文本文件中定义数据结构,protobuf 工具生成 CPython 和其他语言的类,可以以友好的方式加载,保存和访问数据。我们经常将协议缓冲区称为 protobuf ,我将在本指南中使用该约定。

 

GraphDef

TensorFlow 计算的基础是 Graph 对象。这包含节点网络,每个节点代表一个操作,彼此连接成输入和输出。创建 Graph 对象后,可以通过调用保存它 as_graph_def() ,返回一个 GraphDef 对象。

GraphDef 类是由 protoorflow / core / framework / graph.proto 中的定义ProtoBuf 库创建的对象 。protobuf 工具解析此文本文件,并生成加载,存储和操作图形定义的代码。如果您看到一个代表模型的独立 TensorFlow 文件,则可能包含 GraphDefprotobuf 代码保存的其中一个对象的序列化版本。

此生成的代码用于从 Disk 存储并加载 GraphDef 文件。实际加载模型的代码如下所示:

graph_def = graph_pb2.GraphDef()

该行创建一个空 GraphDef 对象,该对象是从 graph.proto 中的文本定义创建的类。这是我们将使用我们的文件中的数据填充的对象。

with open(FLAGS.graph, "rb") as f:
这里我们得到一个文件句柄,用于我们传递给脚本的路径
  if FLAGS.input_binary:
    graph_def.ParseFromString(f.read())
  else:
    text_format.Merge(f.read(), graph_def)

文本还是二进制?

实际上有两种不同的格式可以保存 ProtoBufTextFormat 是一种人性化的形式,它可以很好地进行调试和编辑,但是如果存在数字数据,比如存储权重则可以变大。你可以在 graph_run_run2.pbtxt 中看到一个小例子 。

二进制格式文件比它们的文本等同物小很多,尽管它们对我们来说不是可读的。在这个脚本中,我们要求用户提供一个指示输入文件是二进制还是文本的标志,所以我们知道正确的调用函数。您可以在 inception_v3存档 中找到一个大型二进制文件的 示例inception_v3_2016_08_28_frozen.pb

API 本身可能有点混乱 - 二进制调用实际上是 ParseFromString() ,而您使用 text_format 模块中的实用程序函数来加载文本文件。

 

节点

一旦将文件加载到 graph_def 变量中后,就可以访问其中的数据。对于大多数实际目的,重要部分是存储在节点成员中的节点列表。这是循环遍历的代码:

for node in graph_def.node

每个节点是一个 NodeDef 对象,在  tensorflow/core/framework/node_def.proto 中定义。这些是 TensorFlow 图的基本构建块,每个图形都与其输入连接一起定义单个操作。这是NodeDef的成员,和它们表示什么意思。

name

每个节点都应该有唯一的标识符,该标识符不被图中任何其他节点使用。如果您在使用 Python API 构建图形时没有指定一个,则会为您选择一个反映操作名称的操作,例如 “MatMul” ,连接到单调增加的数字(例如“5”) 。在定义节点之间的连接时以及在整个图形运行时设置输入和输出时使用该名称。

op

这定义执行何种操作,例如 "Add" ,"MatMul" 或  "Conv2D" 。运行图表时,在注册表中查找此 op 名称以查找实现。注册表通过调用 REGISTER_OP() 宏来填充,就像在  tensorflow/core/ops/nn_ops.cc 中那样。

input

字符串列表,其中每一个都是另一个节点的名称,可选地后跟冒号和输出端口号。例如,具有两个输入的节点可以具有 ["some_node_name", "another_node_name"] 等同于的列表 ["some_node_name:0", "another_node_name:0"] ,并将节点的第一个输入定义为第一个 从节点输出名称为 “some_node_name” ,第二个输入从 “another_node_name” 的第一个输出

device

在大多数情况下,您可以忽略这一点,因为它定义了在分布式环境中运行节点的位置,或者当您要将操作强制到 CPUGPU 时。

attr

这是一个保存节点所有属性的键/值存储。这些是节点的永久属性,在运行时不改变的事物,例如卷积的过滤器的大小或常量运算的值。因为可以有很多不同类型的属性值,从字符串到 int ,到张量值的数组,在 tensorflow/core/framework/attr_value.proto 中有一个独立的 protobuf 文件来定义保存它们的数据 结构

每个属性都有唯一的名称字符串,并且在定义操作时列出预期的属性。如果节点中不存在属性,但在操作定义中列出了默认属性,则在创建图时使用默认值。

你可以通过在 Python 中调用 node.namenode.op 等访问所有这些成员。存储在 GraphDef 中的节点列表是模型体系结构的完整定义。

 

冷冻

关于这个的一个令人困惑的部分是,在训练期间,权重通常不会存储在文件格式内。相反,它们被保存在单独的检查点文件 Variable 中,,并且图中有变量操作,它们在初始化时加载最新值。在部署到生产环境时,单独的文件通常不是很方便,所以有一个 freeze_graph.py 脚本可以采用图形定义和一组检查点,并将它们冻结成一个文件。

这样做是加载 GraphDef ,从最新检查点文件中拉取所有变量的值,然后将每个变量 op 替换为具有存储在其属性中的权重的数值数据的 Const 。然后将所有无关的 不用于正向推理的节点,并将生成的 GraphDef 保存到输出文件中。

 

Weight Formats ( 权重格式 )

如果您正在处理代表神经网络的 TensorFlow 模型,最常见的问题之一是提取和解释权重值。存储它们的常见方法,例如在由 freeze_graph 脚本创建的图中,是包含权重作为 TensorsConst 操作。这些定义在 tensorflow/core/framework/tensor.proto 中,并包含有关数据的大小和类型以及值本身的信息。在 Python 中,您可以通过调用像 some_node_def.attr ['value'] 张量来获取一个表示 Const opNodeDef 中的 TensorProto 对象。

这将给你一个表示权重数据的对象。数据本身将存储在其中一个列表中,后缀为 _val ,如对象的类型所示,例如 float_val 对于 32 位浮点数据类型。

在不同框架之间转换时,卷积权重值的排序通常很棘手。在 TensorFlow 中,Conv2D 操作的滤波器权重存储在第二个输入端,并且预期为 [filter_height,filter_width,input_depth,output_depth] 的顺序,其中 filter_count 增加一个意味着移动到内存中的相邻值。

希望这个下载可以让您更好地了解 TensorFlow 模型文件中的内容,如果您需要操作它们将帮助您。

 

  • 无标签