关于Tensorflow计算模型
tensorflow的编程和我以往接触的编程方式有很大差异。以前的编程,无论是编译类型的语言还是脚本语言,都是一步一步的,变量计算后,就会得到结果,比如c=a+b,当执行完语句后,就会得到c的值。但tensorflow不是,它首先要通过编程,构建一个计算图出来,然后启用一个会话来把数据作为输入,通过这个图规定的计算步骤计算,最后得到结果。
普通的命令式编程容易理解和调试,命令语句基本没有优化,按原有逻辑执行。tensorflow这种符号式编程有较多的嵌入和优化,不容易理解和调试,但运行速度会有一定的提升。
Tensor张量
Tensor是Tensorflow中一个很重要的概念,它定义了计算的规则而不保存计算的数据,是构建计算图不可或缺的重要组成部分。首先看一下官方文档的介绍:
A Tensor is a symbolic handle to one of the outputs of an Operation. It does not hold the values of that operation’s output, but instead provides a means of computing those values in a TensorFlow tf.Session.
This class has two primary purposes:
A Tensor can be passed as an input to another Operation. This builds a dataflow connection between operations, which enables TensorFlow to execute an entire Graph that represents a large, multi-step computation.
After the graph has been launched in a session, the value of the Tensor can be computed by passing it to tf.Session.run. t.eval() is a shortcut for calling tf.get_default_session().run(t).
上面简单来说,Tensor是一个代表计算操作的句柄,它不保存数据,只是提供一个计算数据的方法。使用Tensor主要有两个目的,一个是Tensor可以把不同的操作连接起来,从而去构建一个计算图,支持Tensorflow去进行大规模多步的计算,另一个是当Tensor被一个会话Session启用的时候,就可以计算出该操作对应产出的数据。
可以说,Tensorflow计算的过程就是利用的Tensor来建立一个计算图,然后使用Session会话来启动计算,最后得到结果的过程。
简单的验证代码
写了一点代码来验证一下上面的说法。
import tensorflow as tf
def prn_obj(obj):
print '\n'.join(['%s:%s' % item for item in obj.__dict__.items()])
def run():
g1 = tf.Graph()
with g1.as_default():
a = tf.Variable(tf.random_normal([2,3],stddev=0.35,name='a'))
b = tf.Variable(tf.random_normal([2,3],stddev=0.35,name='b'))
c = a + b
d = tf.reduce_max([b,c],0)
e = tf.reduce_max([a,c],0)
f = tf.reduce_max([d,e],0)
print 'a:----------------------------------------'
prn_obj(a)
print 'b:----------------------------------------'
prn_obj(b)
print 'c:----------------------------------------'
print prn_obj(c)
print 'd:----------------------------------------'
print prn_obj(d)
print 'e:----------------------------------------'
print prn_obj(e)
print 'f:----------------------------------------'
print prn_obj(f)
run()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
下面是结果,可以看到,Tensor中并没有实际的数据,a和b是变量,先不看。以c为例说一下,c是一个Tensor,有几个需要注意的属性:
- op: “Add” 规定了操作的类型
- _shape:(2, 3) 规定了需求数据的shape
- _dtype:dtype: ‘float32’ 规定了需求数据的类型
- _consumers:[tf.Operation ‘Max/input’ type=Pack, tf.Operation ‘Max_1/input’ type=Pack] consumers,字面意思也就是消费者,记录了以该Tensor的计算结果作为输入的下一步计算的Tensor
了解Tensor是一个什么东西对于tensorflow的编程还是很有好处很有必要的,由于tensorflow首先要建立一个计算图,一定要对每个操作的输入输出的概念有一个清晰的认识才能在编程中不迷糊。
a:----------------------------------------
_variable:Tensor("Variable:0", shape=(2, 3), dtype=float32_ref)
_initial_value:Tensor("a:0", shape=(2, 3), dtype=float32)
_save_slice_info:None
_caching_device:None
_snapshot:Tensor("Variable/read:0", shape=(2, 3), dtype=float32)
_initializer_op:name: "Variable/Assign"
op: "Assign"
input: "Variable"
input: "a"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
attr {
key: "_class"
value {
list {
s: "loc:@Variable"
}
}
}
attr {
key: "use_locking"
value {
b: true
}
}
attr {
key: "validate_shape"
value {
b: true
}
}
b:----------------------------------------
_variable:Tensor("Variable_1:0", shape=(2, 3), dtype=float32_ref)
_initial_value:Tensor("b:0", shape=(2, 3), dtype=float32)
_save_slice_info:None
_caching_device:None
_snapshot:Tensor("Variable_1/read:0", shape=(2, 3), dtype=float32)
_initializer_op:name: "Variable_1/Assign"
op: "Assign"
input: "Variable_1"
input: "b"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
attr {
key: "_class"
value {
list {
s: "loc:@Variable_1"
}
}
}
attr {
key: "use_locking"
value {
b: true
}
}
attr {
key: "validate_shape"
value {
b: true
}
}
c:----------------------------------------
_op:name: "add"
op: "Add"
input: "Variable/read"
input: "Variable_1/read"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
_shape:(2, 3)
_value_index:0
_handle_shape:unknown_rank: true
_dtype:<dtype: 'float32'>
_consumers:[<tf.Operation 'Max/input' type=Pack>, <tf.Operation 'Max_1/input' type=Pack>]
_handle_dtype:0
None
d:----------------------------------------
_op:name: "Max"
op: "Max"
input: "Max/input"
input: "Max/reduction_indices"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
attr {
key: "Tidx"
value {
type: DT_INT32
}
}
attr {
key: "keep_dims"
value {
b: false
}
}
_shape:(2, 3)
_value_index:0
_handle_shape:unknown_rank: true
_dtype:<dtype: 'float32'>
_consumers:[<tf.Operation 'Max_2/input' type=Pack>]
_handle_dtype:0
None
e:----------------------------------------
_op:name: "Max_1"
op: "Max"
input: "Max_1/input"
input: "Max_1/reduction_indices"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
attr {
key: "Tidx"
value {
type: DT_INT32
}
}
attr {
key: "keep_dims"
value {
b: false
}
}
_shape:(2, 3)
_value_index:0
_handle_shape:unknown_rank: true
_dtype:<dtype: 'float32'>
_consumers:[<tf.Operation 'Max_2/input' type=Pack>]
_handle_dtype:0
None
f:----------------------------------------
_op:name: "Max_2"
op: "Max"
input: "Max_2/input"
input: "Max_2/reduction_indices"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
attr {
key: "Tidx"
value {
type: DT_INT32
}
}
attr {
key: "keep_dims"
value {
b: false
}
}
_shape:(2, 3)
_value_index:0
_handle_shape:unknown_rank: true
_dtype:<dtype: 'float32'>
_consumers:[]
_handle_dtype:0
None