TENSORFLOW:GRAPH 和 SESSION

本文深入解析TensorFlow中图(Graph)与会话(Session)的概念,阐述数据流图在并行计算、分布式执行及代码优化等方面的优势。通过具体示例,介绍了tf.Graph的结构、集合,以及如何构建和执行图,同时探讨了命名、设备分配和类Tensor对象的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Graph 和 Session

TensorFlow用数据流图来表示你的计算中的不同操作的依赖关系。这也导致在使用底层API进行编程的时候,你首先定义一个数据流图,然后创建一个session在一些本地或者远程的设备上运行定义的graph的各个部分。

如果你计划使用底层API进行编程,那么这个文档很有用。像Estimator还有Keras隐藏了很多关于graph和session的细节。但是如果你想了解这些高层API是如何工作的,这篇文章对你也是有帮助的。

为什么用数据流图?


对于并行计算来说,数据流是一个通用的编程模型。在数据流图里,节点表示一些计算,边表示被一个计算消费或者生产的数据。比如,在一个tensorflow的图里,tf.matmul 操作会映射到一个节点,它有两个输入边,代表两个矩阵,一个输出的边,代表两个矩阵相乘的输出。

TensorFlow利用数据流的以下优点来执行你的程序:
– 并行化。用显式的边来代表操作之间的依赖,这样让系统很容易找到可以并行运行的操作。
– 分布式执行。通过显式的边来代表操作之间的数据流动。让tensorflow可以把你的程序分到不同机器的多个设备上执行(GPU,CPU,TPU)。TensorFlow 在设备之间加入必要的通讯和协调。
– 编译。TensorFlow的 XLA compiler 可以利用你数据流图的信息生成运行更快的代码,比如把相邻的操作合并。
– 迁移。数据流图是和语言无关的。你可以用python创建一个数据流图,然后保存它,为了低延迟,你可以用c++程序打开并执行它

tf.Graph是什么


一个tf.Graph包含两种相关的信息:
– 图的结构。 图的边和节点,表示单个的操作是怎么合并到一起。但是并没有说明怎么使用它们。图的结构就像汇编代码,它有一些信息,但是没有包含所有源代码的信息。
– 图里的集合。TensorFlow提供了一种机制可以在tf.Graph里存储元信息集合。tf.add_to_collection方法使你可以把一组对象和一个key关联起来。(tf.GraphKeys定义了一些标准的key),tf.get_collection使你可以根据一个key查询它所关联的所有对象。TensorFlow的很多地方的库都用这个功能:比如当你创建一个tf.Variable,这个变量会默认的被加入到”global variables”和”trainable variables”. 后边当你创建一个tf.train.Saver 或者一个tf.train.Optimizer,在这些集合里的变量会被当做默认参数。

构建一个 tf.Graph


大多数的TensorFlow程序是从构建一个数据流图开始的。在这个阶段,你调用tensorflow API的function来构建新的tf.Operation(节点)和tf.Tensor(边),然后把它们加入一个tf.Graph实例。TensorFlow提供了default graph,这是一个在同一个上下文下对所有API 方法的隐式参数。比如:
– 调用 tf.constant(42.0)会创建一个单一的tf.Operation,它可以产生42这个值。并把它加入到default graph,并且返回一个tf.Tensor它表示这个常量值。
– 调用 tf.matmul(x,y)会创建一个单独的tf.Operation,它会对输入的tensor x,y 相乘,把这个operation加入到default graph,并返回一个tf.Tensor来表示乘积的结果。
– 执行 v = tf.Variable(0) 会给graph上添加一个tf.Operation。这个op会存储一个可写入的tensor value。这个值可以在多次tf.Session.run之间保存。 tf.Variable对象封装了这个opration。并且可以像调用一个tensor一样调用这个variable 对象。它会读取当前存储的值。tf.Variable对象同时拥有向assign和assign_add 这样的方法来创建tf.Operation对象。当执行返回的op对象,会更新存储的值。
– 调用tf.train.Optimizer.minimize会在default graph里增加用来计算梯度的operations和tensors。并且返回一个tf.Operation。运行时,将会对一组variable应用梯度下降。

大多数的程序只依赖于default graph。然而你可以参考Dealing with multiple graphs文档来看更高级的例子。像 Estimator这样的高级接口,会自动帮你管理好default graph。并且可能创建不同的graph用来训练和验证你的模型。

注意,调用大多数的TensorFlow API里的方法只会给default graph里增加opertion和tensor。但是不会执行实际的操作。你把这些tensor和opertion组装起来,直到你有一个tensor或者operation来表示总体的计算。比如执行一步梯度下降。然后把它传给tf.Session去执行计算。

给Opertion命名


一个Graph对象给它所包含的Operation定义了一个namespace。TensorFlow给graph里的每一个opertiaon都起了一个独一无二的名字。但是指定一个有意义的名字让你的程序更易读和易于调试。TensorFlow提供了两种方式让你来给operation命名。
– 每个可以创建一个新的Operation或者tensor的方法都接受一个可选的参数 name。比如 tf.constant(42.0,name=”answer”) 会创建一个新的Operation,名字叫做answer。并且返回一个tensor,叫做answer:0 如果default graph里已经存在一个operation叫做answer,那么TensorFlow会在名字后边加上_1,_2来让名字不重复。
– tf.name_scope方法让你可以在一定的上下文内给创建的operation都加上name space的前缀。当前的name scope前缀是一个以/分隔由所有可用的tf.name_scope管理的命名。如果一个name scope在当前的上下文里已经使用了。那么tensorflow会加上_1,_2。比如:

c_0 = tf.constant(0,name="c") # 名字是c
c_1 = tf.constant(2,name="c") # 名字是c_1
with tf.name_scope("outer"):
    c_2 = tf.constant(2,name="c") # 名字是outer/c
    with tf.name_scope("inner"):
        c_3 = tf.constant(3,name="c") # 名字是outer/inner/c
    with tf.name_scope("inner"):
        c_4 = tf.constant(4,name="c") # 名字是outer/inner_1/c
    c_5 = tf.constant(5,name="c") # 名字是outer/c_1

graph的可视化用了name scope来对操作分组,减少了图形的复杂性。

注意,tensor的名字隐式的是以产生它的operation的名字命名的。一个tensor的名字是

"<OP_NAME>:<i>":
- OP_NAME 是产生这个tensor的operation的名字。
- i,是这个tensor在这个operation产生的output里的序号。

把Operation放到不同的设备


如果你想你的tensorflow程序使用多个不同的设备。tf.device方法提供了一个很方便的功能让所有创建在特定上下文里的所有operation都运行在同样的设备上(或者同类型的设备)。
一个设备的描述是下边这样的形式:

/job:<JOB_NAME>/task:<TASK_INDEX>/device:<DEVICE_TYPE>:<DEVICE_INDEX>
- JOB_NAME, 是一个以字母开头的字母数字组合的名字。
- DEVICE_TYPE 是一个注册的设备类型比如GPU或者CPU
- TASK_INDEX 是一个非负的整数,它表示在JOB里的task的index
- DEVICE_INDEX 非负整数,表示设备index,比如一个进程可以使用多个显卡。

你不需要指定设备描述的全部。如果你的程序运行在一个单独的机器上,只有一个GPU,你可能用tf.device去把operation放到CPU或者GPU上。

# 在外边的context里创建的operation会自动寻找最佳的device去执行,比如CPU和GPU都可用,
# 并且这个operation有GPU实现,就会调用GPU。
weights = tf.random_normal(...)
with tf.device("/device:CPU:0"):
    #这里创建的opertaion会放在CPU上执行。
    img = tf.decode_jpeg(tf.read_file("img.jpg"))
with tf.device("/device:GPU:0"):
    # 这里创建的operation会放在GPU上运行。
    result = tf.matmul(weights,img)

如果你的tensorflow程序是一个分布式的程序,你需要指定job name和task id,这样把variables放到parameter server job(“/job:ps”),而其他的操作放到worker job(“/job:worker”):

with tf.device("/job:ps/task:0"):
    weights_1 = tf.Variable(tf.truncated_normal([784,100]))
    biases_1 = tf.Variable(tf.zeroes([100]))
with tf.device("/job:ps/taks:1"):
    weights_2 = tf.Variable(tf.truncated_normal([100,10]))
    biases_2 = tf.Variable(tf.zeroes([10]))
with tf.device("/job:worker"):
    layer_1 = tf.matmul(train_batch,weights_1)+biases_1
    layer_2 = tf.matmul(train_batch,weights_2)+biases_2

tf.device给了你很大的灵活性去选择哪一个operation,或者图的一块,放在哪里去运行。有很多简单的已有的设置也可以很好的工作,比如tf.train.replica_device_setter 可以被tf.device用来在数据并行的分布式训练下分配operation。下边的例子显示tf.train.replica_device_setter是如何对variable和operation应用不同的分配策略的。

with tf.device(tf.train.replica_device_setter(ps_tasks=3)):
    #variable会被循环的放置在"/job:ps"的3个task上.
    w_0 = tf.Variable(...) #被分配在"/job:ps/task:0"
    b_0 = tf.Variable(...) #被分配在"/job:ps/task:1"
    w_1 = tf.Variable(...) #被分配在"/job:ps/task:2"
    b_1 = tf.Variable(...) #被分配在"/job:ps/task:0"
    input_data = tf.placeholder(tf.float32) # 分配在 "/job:worker"
    layer_0 = tf.matmul(input_data,w_0)+b_0 # 分配在 "/job:worker"
    layer_1 = tf.matmul(layer_0,w_1)+b_1 # 分配在 "/job:worker"

类Tensor对象


很多tensorflow的operation都需要一个或者多个tensor对象作为参数。tf.matmul需要两个tensor对象。tf.add_n需要n 个tensor元素的list作为参数。为了方便,这些方法接收可以接收tensor-like对象来作为参数。并隐式的将类tensor对象通过tf.convert_to_tensor方法转化为tesnor。类Tesnor对象包括:
– tf.Tensor
– tf.Variable
– numpy.ndarray
– list(和类tensor对象的列表)
– Python里的标量类型:bool,float,int,str

你也可以用tf.register_tensor_conversion_function去增加额外的类tensor类型。

注意:默认情况下,tensorflow每次会创建新的tensor对象。如果你的类tensor对象比较大,比如是numpy.ndarray的对象,它里边存着所有的训练数据。你多次调用的话,会内存溢出。所以你应该调用tf.convert_to_tensor 把类tensor对象转化为tensor对象。

在tf.Session里执行一个graph


TensorFlow用tf.Session来带便一个和client程序的连接。通常是Python程序,也可以是C++。一个tf.Session对象提供了对本地设备的访问。如果是分布式TensorFlow的运行环境,提供了对远程设备的访问。它同时缓存了关于Graph的信息,让你可以高效的多次运行同样的计算。

创建一个session

# 创建一个默认的进程内的session
with tf.Session() as sess:
    #...
# 创建一个remote session
with tf.Session("grpc://example.org:2222"):

因为session里拥有物理资源比如GPU和网络连接,所以用一个context manager(在with block里),这样当离开这个block的时候就会自动关闭session。你也可以不用with block,那你就要用Session.close自己关闭session。

高层API(Estimator)也接受以下我们说的这些session参数,可以通过直接设置或者通过类似tf.estimator.RunConfig这样的对象来设置。

tf.Session.init接受下边3个可选的参数:
– target。默认是空,表示本地资源。你可以用grpc://URL来指定一个tensorflow server。那样你就可以访问server控制的所有硬件了。你可以通过tf.train.Server去详细看如何创建一个tensorflow server。比如,通用的between-graph replication配置,tf.Session和client在同一个进程上连接到tf.tarin.Server。你可以去查看distributed TensorFlow 分布式部署文档看一下其他常用的场景。
– graph。默认情况下Session会绑定到default graph,也指定运行这个graph的operations。如果你的程序里有多个graph。你可以这明确的指定一个graph。
– config。这个参数让你可以设置一个tf.ConfigProto来控制session的行为。
allow_soft_placement:如果你错误的把一个只能在cpu下运行的操作设置成在GPU下运行,这个配置为True会将那个操作放到CPU下运行。
cluster_def:分布式环境下让你可以控制计算在哪些机器上进行。
graph_options.optimizer_options:tensorflow在运行你的graph之前会进行优化,这个参数让你可以控制这些优化。
gpu_options.allow_growth:控制显存的分配方式,True是渐进分配,False是启动一次分配。

 

 

-------------------------------------------------------------------------------------------

原文:http://www.rethink.fun/index.php/2018/03/12/tensorflow5/

我已经用1.15的tensorflow冻结生成了pb模型,同时也用1.15的tensorflow转为rknn但是报错了 W load_tensorflow: Catch exception when loading tensorflow model: ./frozen_facenet.pb! W load_tensorflow: Make sure that the tensorflow version of './frozen_facenet.pb' is consistent with the installed tensorflow version '1.15.0'! E load_tensorflow: Traceback (most recent call last): E load_tensorflow: File "/home/book/anaconda3/envs/rknn/lib/python3.7/site-packages/tensorflow_core/python/framework/importer.py", line 501, in _import_graph_def_internal E load_tensorflow: graph._c_graph, serialized, options) # pylint: disable=protected-access E load_tensorflow: tensorflow.python.framework.errors_impl.InvalidArgumentError: Input 0 of node InceptionResnetV1/Bottleneck/BatchNorm/cond_1/AssignMovingAvg_1/Switch was passed float from InceptionResnetV1/Bottleneck/BatchNorm/moving_variance:0 incompatible with expected float_ref. E load_tensorflow: During handling of the above exception, another exception occurred: E load_tensorflow: Traceback (most recent call last): E load_tensorflow: File "rknn/api/rknn_base.py", line 1271, in rknn.api.rknn_base.RKNNBase.load_tensorflow E load_tensorflow: File "rknn/base/convertor/tensorflow2onnx/tf2onnx/convert.py", line 631, in rknn.base.convertor.tensorflow2onnx.tf2onnx.convert.from_graph_def E load_tensorflow: File "rknn/base/convertor/tensorflow2onnx/tf2onnx/convert.py", line 632, in rknn.base.convertor.tensorflow2onnx.tf2onnx.convert.from_graph_def E load_tensorflow: File "rknn/base/convertor/tensorflow2onnx/tf2onnx/convert.py", line 633, in rknn.base.convertor.tensorflow2onnx.tf2onnx.convert.from_graph_def E load_tensorflow: File "rknn/base/convertor/tensorflow2onnx/tf2onnx/convert.py", line 634, in rknn.base.convertor.tensorflow2onnx.tf2onnx.convert.from_graph_def E load_tensorflow: File "/home/book/anaconda3/envs/rknn/lib/python3.7/site-packages/tensorflow_core/python/util/deprecation.py", line 507, in new_func E load_tensorflow: return func(*args, **kwargs) E load_tensorflow: File "/home/book/anaconda3/envs/rknn/lib/python3.7/site-packages/tensorflow_core/python/framework/importer.py", line 405, in import_graph_def E load_tensorflow: producer_op_list=producer_op_list) E load_tensorflow: File "/home/book/anaconda3/envs/rknn/lib/python3.7/site-packages/tensorflow_core/python/framework/importer.py", line 505, in _import_graph_def_internal E load_tensorflow: raise ValueError(str(e)) E load_tensorflow: ValueError: Input 0 of node InceptionResnetV1/Bottleneck/BatchNorm/cond_1/AssignMovingAvg_1/Switch was passed float from InceptionResnetV1/Bottleneck/BatchNorm/moving_variance:0 incompatible with expected float_ref. W If you can't handle this error, please try updating to the latest version of the toolkit2 and runtime from: https://console.zbox.filez.com/l/I00fc3 (Pwd: rknn) Path: RKNPU2_SDK / 2.X.X / develop / If the error still exists in the latest version, please collect the corresponding error logs and the model, convert script, and input data that can reproduce the problem, and then submit an issue on: https://redmine.rock-chips.com (Please consult our sales or FAE for the redmine account) E build: The model has not been loaded, please load it first! E export_rknn: RKNN model does not exist, please load & build model first!
最新发布
03-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值