Tensors

TensorFlow,就像名字所示,是一个关于怎么定义和计算tensors的框架。一个tensor是对向量,矩阵和更高维度的数据的通用表示。实现上,TensorFlow用n维array来表示tensor这个基本数据类型。

当你写一个TensorFlow的程序,你操作和传递的主要对象就是tf.Tensor。一个tf.Tensor对象表示了一部分最终可以得到一个值的计算。TensorFlow程序首先构建一个tf.Tensor的graph,graph详细描述了每个tensor怎么根据可用的tensors来计算。然后通过图的一部分运算来得到期望的值。

一个tf.Tensor 有如下的属性
– 一个数据类型(float32,inte32,或者string,…)
– 一个shape

一个Tensor内的元素必须类型一致。并且data type是已知的。shape(表示维度的数目和每个维度的大小)可以只知道一部分。如果input的shape的形状是全部知道的,大多数的operations产生完全知道的shape。但是在一些情况下只可能在执行的时候知道一个tensor的shape。

一些tensor的类型是特殊的,它们会在文档的其他地方被介绍。主要包括:
– tf.Variable
– tf.constant
– tf.placeholder
– tf.SparseTensor
tf.Variable可能和你想的不一样,tensor的值是不能改变的,这意味着在一次执行中,一个tensor只会产生一个值。但是,对同样的tensor求值两次可以返回不同的值。比如一个tensor是从磁盘读取数据,或者产生一个随机数。

Rank


一个tf.Tensor的rank表示tensor的维度。rank还被叫做order,degree或者n-dimension。在tensorflow里的rank和数学里的矩阵的rank不一样。如下表,每一个TensorFlow里的rank都对应不同的数学实体:

Rank 数学实体
0 Scala(只有大小)
1 Vector(大小和方向)
2 Matrix(表状数据)
3 3-Tensor(数据立方体)
4 4-Tensor(自己想想吧)

Rank 0

下边的一段代码演示了如何创建一个Rank 0的变量:


mammal = tf.Variable("Elephant",tf.string)
ignition = tf.Variable(451,tf.int16)
floating = tf.Variable(3.1415926,tf.float64)
its_comlicated = tf.Variable(12.3-4.85j,tf.complex64)

注意,在TensorFlow里,一个字符串被当值一个单一的元素,并不是一个字母的序列。有可能有标量的strings,也可以有向量的strings。

Rank 1

可以通过一个list来初始化一个rank为1的tf.Tensor对象。


mystr = tf.Variable(["Hello"],tf.string)
cool_number = tf.Variable([3.14,2.11],tf.flat32)
first_primes = tf.Variable([2,3,5,7,11],tf.int32)

更高 Rank

一个rank为2的tensor,最少含有一个行和一个列:


mymat = tf.Variable([[7],[11]], tf.int16)
myxor = tf.Variable([[False, True],[True, False]], tf.bool)
linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32)
squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32)
rank_of_squares = tf.rank(squarish_squares)
mymatC = tf.Variable([[7],[11]], tf.int32)

高纬度的tensor,类似的,是一个包含了n维的数组。比如在图像处理中,很多tensor都使用了4维。4个维度分别代表:第几个样本,图像宽,图像高,颜色channel。


my_image = tf.zeros([10,299,299,3])

获取tf.Tensor对象的rank

为了获取一个tensor的rank,可以通过tf.rank方法。比如下边的代码演示了如何获取之前定义的tensor的rank:


r = tf.rank(my_image)
# 运行graph后,r的值是4

引用tensor的一部分

因为tf.Tensor是一个n维的数组。如果想获取其中一个元素,你需要指定n个坐标。
对于rank为0的tensor(一个标量),你不需要坐标。因为它已经是一个数字。
对于rank为1的tensor(一个向量),你需要传一个index去获取一个数字:


my_scalar=my_vector[2]

需要注意的是,如果你在方括号里传一个标量的tensor,这样你可以在程序里动态的获取向量tensor的值。
同样,对于一个rank为2,或者更高,你需要传更多的index。比如对一个rank为2的tensor,需要传两个index去获取一个值:


my_scalar = my_matrix[1,2]

如果值传递一个值,那么会返回矩阵的一个子向量。比如:


my_row_vector = my_matrix[2]
my_column_vector = my_matrix[:,3]

:这个符号在python的切片语法表示,保留这个维度。这个操作对高维tensor非常有用,它允许你存取它的子vector,子matrix,子tensor。

Shape

shape表示一个tensor在不同维度的元素数。TensorFlow会在graph构造时自己推断shape。推断出的shape可能有已知或者未知的rank。如果rank已知,每个dimension的大小也可能已知或者未知。

在TensorFlow的文档里用了3中标记方法来描述关于tensor的维度。rank,shape,dimension number。下边这张表展示了它们之间的关系:

Rank Shape Dimension number Example
0 [] 0-D 一个0维的tensor,标量
1 [D0] 1-D 一个shape为[5]的1维tensor
2 [D0,D2] 2-D 一个shape为[3,4]的2维tensor
n [D0,D2,…Dn] n-D 一个shape为[D0,D1,Dn-1]的tensor

Tensor的shape可以通过Python的int类型的list/tuple来改变。或者用tf.TensorShape也可以改变。

获取tf.Tensor对象的shape

有两种办法。在构建一个graph的时候,获得一个tensor的shape是很有用的。可以通过读取tensor对象的shape属性来获得。这个方法返回的是TensorShape的对象,它是一种方便的表示部分指定的shape的方法。因为在构建graph的时候,并不是所有的shape都是完全知道的。

还有一种可能是在运行时通过获取一个tf.tensor,他表示其他一个tensor的完全定义的shape。这时通过调用tf.shape操作完成的。通过这种方式,你可以构建一个graph它操作tensor的shape的方式是通过input tensor的动态shape来构建其他tensor。比如下边的例子是根据一个矩阵的列数来构建一个全是0值的向量。


zeros = tf.zeros(my_matrix.shape[1])

改变 Tensor的shape

一个tensor里所有元素的个数就是shape里各个值的乘积。一个标量的元素数是1. 因为很多不同shape的tensor会有相同的元素个数。所以如果能改变一个tensor的shape,但是元素不变是很有用的。这个可以用tf.reshape来实现:


rank_three_tensor = tf.ones([3, 4, 5])
matrix = tf.reshape(rank_three_tensor, [6, 10])  # Reshape把现有的元素放到
                                                 # 一个 6x10 的矩阵里
matrixB = tf.reshape(matrix, [3, -1])  #  Reshape把现有的元素放到 一个 3x20
                                       # 的矩阵里. -1 是让reshape自己去计算
                                       # 这个维度的大小
matrixAlt = tf.reshape(matrixB, [4, 3, -1])  # Reshape把现有的元素放到一个
                                             #4x3x5 的tensor

# 你可以通过-1,来让reshape自己计算某个维度的大小,但是有一个原则就是在保证元素数不变的前提下,
# 需要计算的维度的元素数必须是整数。下边的代码会出错,因为60除以13*2,不是一个整数
yet_another = tf.reshape(matrixAlt, [13, 2, -1])  # ERROR!

数据类型


除了维度,tensor还有类型。
一个tensor不可能有超过一种类型的元素。然而把任意类型的元素序列化为string,然后放到一个tensor里。这样你一个tensor里就存了很多“不同类型”的值了。
你可以用tf.cast把一种类型的值转化为另一种:


float_tensor = tf.cast(tf.contant([1,2,3]),dtype = tf.float32)

可以通过Tensor.dtype属性查看一个tensor的数据类型。

如果是通过一个python对象来创建的tensor,指定数据类型不是必须的。如果你不指定,tensorflow会自己选一个可以表示你的数据的数据类型。Python里的integer转为tf.int32, 浮点型转为tf.float32。除此之外,tensorflow用和numpy一样的规则来转化array。

Tensor求值


一旦建好了graph,你就可以通过计算来产生一个特定的tensor。并且获取它里边的值。这对调试有用,也是tensorflow绝大多数运行时所必须的。
最简单的方法去给一个tensor求值是通过Tensor.eval方法。比如:


constant = tf.constant([1,2,3])
tensor = constant * constant
print tensor.eval()

只有当default的session是有效的时候才可以用tensor.eval方法。
Tensor.eval返回一个和tesnor里的内容一样的 numpy 数组。
有时候tf.Tensor依赖于动态的信息,所以如果没有一个上下文,它是无法求值的。比如说它依赖于一个placeholder:


p = tf.placeholder(tf.float32)
t = p+1.0
t.eval() #失败,因为p没有值
t.eval(feed_dict={p:2.0})# 成功,因为我们已经给了p的值。

注意,我们不光可以给一个placeholder,也可以是另外一个tensor。

其他方式的tf.Tensor求值比较复杂。TensorFlow不能对定义在Funtion或者在流程控制中的tensor求值。如果一个tensor依赖于一个队列里的一个值。只有队列里有值才可以对tensor求值,不然就一直hang在哪。当你使用queues的时候,记得先调用tf.train.start_queue_runners,然后再对tensor求值。

打印Tensor


为了调试,你可能想打印一个tensor。在tfdbg提供的高级的debug功能的同时,tensorflow也可以让你直接打印一个tensor。

你很少希望用下边的方式去打印一个tensor。


t = <<一些tensorflow的操作>>
print(t) # 这时正在构件图,t里并没有值。你打印出来的只是符号t。并不是它的值。

上边的code只是打印了tf.Tensor的对象。它表示了将要执行的计算。而不是它的值。你应该使用tensorflow提供的tf.Print操作,它有两个参数,第一个是你想不做任何改动返回的tensor,第二个参数是个列表,是你想打印的tensor。

正确的做法是tf.Pring返回的tensor必须被加入到计算流中。当它的下游tensor被求值的时候。我们的print方法就起作用了。


t = <<一些tensorflow的操作>>
tf.Print(t,[t]) #这个没有什么作用
t = tf.Print(t,[t]) #返回一个tensor。
result = t+1 #将返回的tensor加入计算。当result被求值时,t会被打印。

当你对result求值,所有它依赖的tensor都被求值。因为result依赖于t,对t求值就有副作用打印出它的input(老的t),所以t被打印。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: