读书笔记,垃圾内容
We designed TensorFlow for large-scale distributed training and inference, but it is also flexible enough to support experimentation with new machine learning models and system-level optimizations. TensorFlow Architecture
上面源自TensorFlow官方doc,下面三点需要特别关注:
Tensorflow的整体架构图如下所示:
注:上图源于TensorFlow Architecture
文档写的很详尽,我直接摘抄过来。
官方文档中指明Client是通过写TensorFlow program来创建计算图,用户可以通过独立的operations或者使用提供的API去构造特定的神经网络。TensorFlow比较倾向于python和C++,但骨子里还是倾向于C++的,用户使用python,但库的作者期望C++的高性能。
Client创建session,这个session会将graph definition通过tf.GraphDel
协议buffer传递给distributed master。当Client evaluate图中的节点时,这个evaluation会触发distributed master来启动整个计算。
官方文档描述如下:
由于master能够看到整个computation,所以能够应用一些标准的优化,例如common subexpression elimination或者constant folding,然后优化这些子图的执行。
由于Master会将这些计算图进行分割,从而将它们放到不同的device上进行运行,一种可能的分割如下所示。既然是分割,肯定需要考虑到这些部分之间的通信。
一种信息的传递方式如下:
注: “PS” stands for “parameter server”: a task responsible for storing and updating the model’s parameters.
最终distributed master将这些分割后的子图传递给不同的分布式任务。
官方文档 The worker service in each task:
我们可以看到worker是真正干活儿的人,目的是把这些computation干完而且干好。如果distributed master比较重视较顶层逻辑层面的分派调度,而worker则较为重视实现层面的优化,直接与CPU和GPU打交道。
The worker service dispatches kernels to local devices and runs kernels in parallel when possible, for example by using multiple CPU and GPU streams.
注:The implementation of an op is known as a kernel, and it is the concrete implementation of the specification
一个整体的computation,拆分成不同的子部分,丢给不同的device进行处理,中间肯定需要进行通信,CPU与CPU之间的,CPU和GPU之间的,例如通过DMA。
For transfers between tasks, TensorFlow uses multiple protocols, including:
注:什么是gRPC?与json-RPC有什么异同?什么是RDMA?
另外TensorFlow还支持NVIDIA的NCCL来进行多GPU之间的通信。一种可能的方式如下:
The implementation of an op is known as a kernel, and it is the concrete implementation of the specification you registered in Step 1. Create an op
就像上面说的那样,一个operation的implementation称之为kernel。据官方文档指出的那样,TensorFlow的runtime已经包含了超过200个标准的实现,数学,数组处理,控制流和状态管理等operations。这些operations有多种针对不同device进行优化后的kernel implementation。
许多operation kernels通过Eigen::Tensor
实现,它使用C++模板生成高效的能够在多核GPU和CPU上并行处理的代码。但是TensorFlow推荐使用更加高效的cuDNN
。
注:什么是Eigen::Tensor
?什么是cuDNN
?
注:cuDNN
是用于深度神经网络的GPU加速库
通过官方的介绍,对TensorFlow有了一个比较顶层的认识,但是细节还有些不清楚,未来进行填充。下面罗列本章的一些初识TensorFlow的代码片段。
tf.Graph
TensorFlow中的数据流图由一组算子tf.Operation
和一组张量tf.Tensor
构成,其中tf.Operation
代表了一组运算,张量tf.Tensor
表示了算子之间流动的数据。
按照官方文档,Tensor
表示了Operation
的输出, Tensor
可以作为输入传递给Operation
。
一个Oepration
是Graph
中的一个节点,接收0个或者多个Tensor
作为输入,然后产生0个或多个Tensor
对象。Operation
可以通过一个算子构造器tf.matmul
或者tf.Graph.create_op
来创建。
例如c = tf.matmul
创建了一个Operation
,接收a
和b
作为输入,c
作为输出。
A class for running TensorFlow operations.
A Session
object encapsulates the environment in which Operation
objects are executed, and Tensor
objects are evaluated.
Graph
仅仅定义了所有op
与Tensor
流向,但是并没有进行任何计算。一个Session
拥有一些资源,例如tf.Variable
或者tf.queue.QueueBase
等等,最好在使用完成之后释放这些资源。例如通过sess.close()
等方法。
g = tf.Graph()
with g.as_default():
# Build a graph.
a = tf.constant(5.0)
b = tf.constant(6.0)
c = a * b
# Launch the graph in a session.
sess = tf.compat.v1.Session()
# Evaluate the tensor `c`.
print(sess.run(c))
Session
的构造参数包含下面三个:
__init__(
target='',
graph=None.
config=None
)
target
,可选。需要连接的执行引擎graph
,可选。加载的Graph
对象config
,可选。例如CPU,GPU或者图中的优化参数,日志等选项。Session
中一个比较重要的方法是run
,
run(
fetches,
feed_dict = None,
options = None,
run_metadata = None
)
This method runs one “step” of TensorFlow computation, by running the necessary graph fragment to execute every
Operation
and evaluate everyTensor
infetches
, substituting the values infeed_dict
for the corresponding input values.
fetches
接收的是一个single graph element,一个single graph element可以是tf.Operation
,可以是tf.Tensor
,或者string
等等。
创建一个常量Tensor
,
tf.compat.v1.constant(
value,
dtype=None,
shape=None,
name='Const',
verify_shape=False
)
与常量对应的就是变量Variable
,变量是TensorFlow中非常重要的概念,一个变量维护了TensorFlow graph的状态。Variable()
构造函数可以接收任何类型和形状的Tensor
作为输入。不像常量,变量的值是可以被修改的。
像张量一样,变量也可以作为算子的输入。但是当你装在graph时,变量必须被显示初始化过了。
The most common initialization pattern is to use the convenience function global_variables_initializer() to add an Op to the graph that initializes all the variables.
然后你就可以地运行这个算子。
我们可以在张量中插入一个占位符,未来再填充。
tf.compat.v1.placeholder(
dtype,
shape=None,
name=None
)
上面的tensor在evaluate的时候一定会产生error,我们必须使用feed_dict
喂给它数据,例如在Session.run()
,Tensor.eval()
或者Operation.run()
。
TensorFlow一个比较酷功能是可视化数据流图,对于普通的程序,也有CallGraph的功能,但是这个功能对于TensorFlow更为紧要。
graph = tf.Graph()
with graph.as_default():
in_1 = tf.compat.v1.placeholder(tf.float32, shape=[], name="input_a")
in_2 = tf.compat.v1.placeholder(tf.float32, shape=[], name="input_b")
const = tf.constant(3, dtype=tf.float32, name="static_value")
with tf.name_scope("Transformation"):
with tf.name_scope("A"):
A_mul = tf.multiply(in_1, const)
A_out = tf.subtract(A_mul, in_1)
with tf.name_scope("B"):
B_mul = tf.multiply(in_2, const)
B_out = tf.subtract(B_mul, in_2)
with tf.name_scope("C"):
C_div = tf.math.divide(A_out, B_out)
C_out = tf.add(C_div, const)
with tf.name_scope("D"):
D_div = tf.math.divide(B_out, A_out)
D_out = tf.add(D_div, const)
out = tf.maximum(C_out, D_out)
sess = tf.compat.v1.Session()
print(sess.run(D_out, feed_dict={in_1: 4.0, in_2: 3.0}))
writer = tf.compat.v1.summary.FileWriter('/Users/henrywong/test/tmp', sess.graph)
writer.close()
对于上面的代码来说,整个graph如下图所示,我们可以有选择性的展开某个子部分。TensorFlow能够这样做的目的在于,TensorFlow的代码是基于数据流图的模型,逻辑性更强。
none