TensorFlow中Graph完整的构建过程

学习TensorFlow之后,我们知道,在TensorFlow中,主要是通过构建graph的形式来构建整个框架的。
本文想要彻底高清楚的问题就是:TensorFlow中真个graph的构建流程:

  1. 用户创建的时候是怎么构建的,也就是 python api 提供了什么样的形式来构建用户看的见的 graph;
  2. python api 创建的 graph 如何转换成 c++ 底层可用于计算的graph;
  3. graph 最终执行在硬件设备上是以什么形式完成计算的;

我想,搞清楚这三个问题,基本上就掌握了深度学习框架,是怎么完成真个计算的。

(graph)TensorFlow用于表达计算任务的一个核心概念。从前端(python)描述神经网络的结构,到后端在多机和分布式系统上部署,到底层 Device(CPU、GPU、TPU)上运行,都是基于图来完成。

在tensorflow官方文档中,Graph被定义为“一些Operation和Tensor的集合”。我们使用的python代码表达的计算,就会生成一张图。

第一个阶段:Python Graph

import tensorflow as tf

bg = tf.Graph()  # 创建一个图bg 
with bg.as_default():  # 将bg 图设为当默认图
    Bdata1 = tf.placeholder(tf.float32, name="Bdata1")
    Bdata2 = tf.placeholder(tf.float32, name="Bdata2")
    Bdata3 = tf.multiply(Bdata1, Bdata2,  name="multiply")
    tf.summary.FileWriter("./test", Bgraph)

上述代码,可视化之后的图如下
在这里插入图片描述
上图中,每一个圈表示一个算子(OP),椭圆到椭圆的边为张量(tensor),箭头的指向表示了这张图的Op的输入输出的tensor的传递关系。

Python代码中所描述的 Graph(简称 Python Graph),包含各个OPtensor。构建好后,tensorflow 运行时,将session 启动,真实的数据计算会被放在多CPU、GPU、ARM等完成,并不是始终不变的东西。单纯的使用Python的是无法有效的完成计算的。所以计算过程为:tensorflow先将Python代码描绘的图进行转换,转换为Protocol Buffer(即序列化),在通过C/C++/CUDA运行Protocol Buffer所定义的图。

第二个阶段:Protocol Buffer

在第一阶段,用户使用Python构建了自己的模型的计算图,到了第二个阶段,要执行运算的时候,Python ``Graph 会被序列化为Protocol Buffer,称之为Graph Def

GraphDef由许多的叫做 NodeDefProtocol Buffer组成,NodeDefPython Graph 中的 Operation相对(也就是 tf.Operation的序列化 ProtoBufNodeDef)。

GraphDef的保存与加载的apitf.train.write_graph()/ tf.Import_graph_def()

import tensorflow as tf

bg = tf.Graph()  # 创建一个图bg
with Bgraph.as_default():  # 将bg图设为当默认图
    Bdata1 = tf.placeholder(tf.float32, name="Bdata1")
    Bdata2 = tf.placeholder(tf.float32, name="Bdata2")
    Bdata3 = tf.multiply(Bdata1, Bdata2, name="multiply")

with tf.Session(graph=bg) as sess:
    tf.train.write_graph(sess.graph_def, "./", "test.pb", False)
    print(sess.graph_def)

代码中的GraphDef 实际包含了:3个NodeDefNodeDef中,包含name, op, input, attr。其中input是不同的node之间的连接信息。

GraphDef中不会保存Variable的信息,但可以保存Constant。所以是将Python Graph中的Variable替换为constant 存储在GraphDef中(训练好的weight(variable)得以保存)。所以graphdef来恢复的图和权重,没有Variable,只能用于实际上的inference,无法用于训练

上面代码的运行结果

#上面代码运行结果
node {
  name: "Bdata1"
  op: "Placeholder"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "shape"
    value {
      shape {
        unknown_rank: true
      }
    }
  }
}
node {
  name: "Bdata2"
  op: "Placeholder"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "shape"
    value {
      shape {
        unknown_rank: true
      }
    }
  }
}
node {
  name: "multiply"
  op: "Mul"
  input: "Bdata1"
  input: "Bdata2"
  attr {
    key: "T"
    value {
      type: DT_FLOAT
    }
  }
}
versions {
  producer: 38
}

上述代码,使用api tf.train.write_graph()python graph转换成了graphdef。我么从结果来看,是没有实际的参数的。那么怎样才能将参数 Variables拿到呢?

第三阶段:Meta Graph

GraphDef中无法得到Variables,但通过MetaGraph可以得到。
MetaGraph的官方解释:一个MetaGraph由一个计算图和其相关的元数据构成。其中包含了4中主要信息:

  • MetaInfoDef:存放了一些元信息,例如版本和其他用户信息
  • GraphDefMetaGraph的核心内容之一
  • SaverDef:图的Saver信息(例:最多同时保存的checkpoint数量、需保存的tensor的名字(并不保存tensor中的实际内容))
  • CollectionDef:任何需要特殊注意的Python对象,需要特殊的标注以方便import_meta_graph后取回(例:"train_op"、"prediction"等)

集合collection是为了方便用户对图中的变量进行管理而被创建的一个概念,通过一个string类型的key来对一组python对象进行命名的集合。这个key可以是Tensorflow在内部定义的一些key,也可以是用户自定义的名字(string)

Tensorflow中内部定义的许多标准key,全部定义在了tf.GraphKeys这个类中(例:tf.graphKeys.TRAINBLE_VARIABLEtf.GraphKey.GLOBAL_VARIABLES等)。
在这里插入图片描述
以下写法存在等价关系:

tf.trainable.variables() <==> tf.get_collection(tf.GraphKey.TRAINABLE_VARIABLES)
tf.global_variables() <==> tf.get_colleciontion(tf.GraphKey.GLOBAL_VARIABLES)

用户可以自定义 key 集合:

pred = model_network(X)
loss = tf.reduce_mean(..., pred, ...)
train_op = tf.train.AdamOptimizer(learingrate).minimize(loss)

'''
对于这一段对训练过程定义的代码,用户希望特别关注pred、loss、train_op这几个操作,
那么就可以使用如下代码,将这几个变量加入collection中。并命名"training_collection":
'''
tf.add_to_collection("training_collection", pred)
tf.add_to_collection("training_collection", loss)
tf.add_to_collection("training_collection", train_op)


然后通过train_collect = tf.get_collection("training_collection")得到python list,其内容就是pred、loss、train_opTensor。 这样操作一般是为了在一个新的session中打开这张图时,方便快速获取想要的张量。

with tf.session() as sess:
	pred = model_network(X)
	loss = tf.reduce_mean(..., pred, ...)
	train_op = tf.train.AdamOptimizer(learingrate).minimize(loss)
	tf.add_to_collection("training_collection", train_op)
	tf.train.export_meta_graph(tf.get_default_graph(),  "my_graph.meta")

#通过import_meta_graph将图恢复(同时初始化本session的default图),
#并通过get_collection重新获取train_op,以及通过train_op来开始一段训练(sess.run())
with tf.Session() as sess1:
	tf.train.import_meta_graph("my_graph.meta")
	train_op = tf.get_collection("training_collection")[0]
	sess1.sun(train_op)


注意
MetaGraph中恢复构建的Graph是可以被训练的。但,MetaGraph中虽然包含Variable的信息,但是没有Variable的实际值。所以从MetaGraph中恢复的Graph,训练都是从随机初始化的值开始的,训练中的Variable的实际值都保存在checkpoint文件中,如果要从之前训练装填恢复训练,就需要从checkpointrestore

tf.export_meta_graph() / tf.import_meta_graph()用来对MetaGraph读写的API
tf.train.saver.save() 在保存checkpoint的同时也会保存MetaGraph
tf.train.saver.restore()在恢复图时,只恢复了Variable。所以需要加上tf.import_meta_graph()来从MetaGraph中恢复Graph。一般的,训练时不需要从MetaGraph中恢复图Graph,而是在python中构建的网络的Graph,并对其恢复Variable

checkpoint中全面保存了训练某时间截断的信息,包括参数、超参数、梯度等。
tf.train.saver.save() / tf.train.saver.restore():则能够完整的保存和恢复神经网络的训练。
checkpoint分为两个文件保存Variable的二进制信息:ckpt文件保存了Variable的二进制信息,index文件用于保存了ckpt文件中对应Variable的偏移量信息。

总结

Tensorflow三种API所保存和恢复的Graph是不一样的

  • GraphDef的保存与加载的api: tf.train.write_graph() / tf.Import_graph_def()
  • MetaGraph读写的api :tf.export_meta_graph() / tf.import_meta_graph()
  • checkpoint读写的api :tf.train.saver.save() / tf.train.saver.restore()

总觉的还是没有分析的很清楚,等继续深入研究学习,之后争取将来龙去脉讲清楚。

声明
本博客是个人学习时的一些笔记摘录和感想,不保证是为原创,内容汇集了网上相关资料和书记内容,在这之中也必有疏漏未加标注者,如有侵权请与博主联系。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值