TENSORFLOW:底层概念介绍

介绍


这个帮助文档让你可以使用TensorFlow的底层API开始编程,让你知道:
– 如何管理你自己的TensorFlow程序(用tf.Graph)和一个TensorFlow的runtime(用tf.Session)。这样你就不需要依赖Estimators去帮你管理了。
– 利用tf.Session去运行一个TensorFlow的Operations。
– 在底层API里使用高层的component,比如datasets,layers和feature_columns.
– 定义你自己的training loop,而不依赖于Estimators提供的。

我们推荐尽量使用高层的API去建模。但是知道TensorFlow的底层API也是有价值的。
– 用底层API实验和调试让你更加知道细节和明白。
– 它让你在使用高层API建模时知道内部是怎么运行的。

配置


首先你需要安装一个TensorFlow。
要看明白这个文档,你需要有以下知识:
– 如何用Python编程
– 知道一些线性代数
– 最好有一些机器学习背景
启动你的python,运行下边几行来配置你的运行环境:

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
import tensorflow as tf

Tensor Values


在TensorFlow里最核心的数据单元是tensor。一个tensor是由任意维度的数组构成,里边的元素是基本数据类型。一个tensor的rank指的就是它的维度。它的shape是一个由整数构成的tuple,里边每个int表示tensor一个维度的长度。

3. # 一个 rank 0 的tensor; 一个标量的shape是[],
[1., 2., 3.] # 一个rank为1的tensor; 这个向量的shape是[3]
[[1., 2., 3.], [4., 5., 6.]] # 一个rank为2的tensor; 这个矩阵的shape是[2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 一个rank为3的 tensor,它的shape是[2, 1, 3]

TensorFlow使用numpy array来表示tensor的值。

TensorFlow 核心概念


你可以认为TensorFlow的核心程序包含两个独立的块:
– 构建一个计算流图(一个tf.Graph)
– 运行一个计算流图(用一个tf.Session)

Graph

一个计算流图是一系列TensorFlow的operations被编排到一个graph里。这个graph由两种类型的Object构成。
– Operations(ops):是Graph的节点。Ops用tensor作为输入来计算,并产生新的tensor。
– Tensors:它构成graph的边。tensors是在graph上流动的数据。大多数的TensorFlow function返回 tf.Tensors.

注意:tf.Tensors 并不拥有值,他们只是持有计算流图中元素的句柄。

让我们建一个简单的计算流图。最简单的operation是一个constant。这个Python function建了一个operation,它用一个tensor value作为input,最终产生结果的operation不需要input。运行时,它输出传入构造函数的值。我们可以创建两个浮点数常数a和b:

a = tf.constant(3.0,dtype=tf.float32)
b = tf.constant(4.0) # 默认为tf.float32
total = a+b
print(a)
print(b)
print(total)

print 语句生成的输出:

Tensor("Const:0",shape=(),dtype=float32)
Tensor("Const_1:0",shape=(),dtype=float32)
Tensor("add:0",shape=(),dtype=float32)

注意print这些tensors并没有像你可能期望的输出3.0,4.0,和7.0。上边的这些语句只是在构建计算流图。tf.Tensor 只是表示这些opterations运行后会产生的result。

每一个Graph中的operation都有一个唯一的名字。它和你在Python中起的变量名是没有关系的。Tensors是以产生他们的operation的名字后边加上index构成的。比如”add:0″。

TensorBoard

TensorFlow提供了一个工具叫做TensorBoard. TensorBoard众多功能之一是可视化一个计算流图。你可以通过几行代码就可以容易的看到TensorBoard。

首先你需要把计算流图存入一个TensorBoard summary文件:

writer = tf.summary.FileWriter('.')
writer.add_graph(tf.get_default_graph())

上边的代码会生在当前文件夹成一个event文件。文件名是下边的格式:

events.out.tfevents.{timestamp}.{hostname}

现在,在一个新的termianl里,用下边的命令:

tensorboard --logdir .

这时你打开下边网址就可以看到计算流图了:
http://localhost:6006/#graphs

关于TensorBoard的更多细节,可以看 TensorBoard:Graph Visualizaiton

Session

为了给tensors求值,实例化一个tf.Session对象。通常叫做session。一个session包含了TensorFlow的runtime的状态信息。并且运行tensorflow的operations。如果你把tf.Graph比作一个.py文件。那么tf.Session就是python的可执行程序。
下边的代码创建一个tf.session的实例,用它的run方法来给total这个我们之前定义的tensor求值。

sess = tf.Session()
print(sess.run(total))

当你用Session.run去请求一个node的output,TensorFlow就会在图里向前一直求值,运行所有给output节点提供input的节点。所以我们得到输出7.0:

7.0

你可以一次给session传多个tensors去运行。run方法会自动处理任意类型的tuple或者dictionaries的组合。比如:

print(sess.run({'ab':(a,b),'total':total}}))

运行后可以得同样的结构:

{'total': 7.0, 'ab': (3.0, 4.0)}

在对tf.Session.run的一次调用中,任何tf.Tensor只会有一个值。比如,下边的代码调用tf.random_uniform去产生一个tf.Tensor,它生成一个3个元素的随机值构成的向量。每个值的区间在0和1之间。

vec = tf.random_uniform(shape=(3,))
out1 = vec+1
out2 = vec+2
print(sess.run(vec))
print(sess.run(vec))
print(sess.run((vec,out1,out2))

结果显示前两次调用,每次调用生成一个新的随机值。但是最后一个sess.run因为是一次调用,所以vec的值不变。out1和out2应用同样的vec。

[0.4134481  0.14323366 0.9671421 ]
[0.05151916 0.4826808  0.8643962 ]
(array([0.01238942, 0.10127807, 0.33896255], dtype=float32), array([1.0123894, 1.1012781, 1.3389626], dtype=float32), array([2.0123894, 2.101278 , 2.3389626], dtype=float32))

一些TensorFlow方法返回tf.Operations而不是tf.Tensors。在Operation上调用run方法返回的结果是None。你运行一个operation,是为了使用它的副作用,而不是为了它的返回值。后边我们演示的初始化和training的ops都是这样。

Feeding

如果一个计算流图里只有常数,那也没啥意思。一个graph可以通过参数来接收外部的input。这被叫做 placeholders, 一个placeholder是一个保证稍后运行时会提供一个实际值。就像函数的参数一样。

x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
z = x +y

上边这三行代码就像一个函数定义了2个float32的参数x,y。然后执行对x和y的加法。我们可以对这个graph求值,前提是定义x,y的值通过tf.Session.run方法的feed_dict参数传入。

print(sess.run(z,feed_dict={x:3,y:4.5}))
print(sess.run(z,feed_dict={x:[1,3],y:[2,4]}))

结果显示为:

7.5
[ 3.  7.]

同时需要注意的是feed_dict可以用来覆盖任何图上的tensor。即使之前定义的constant的tensor,你也可以在后用的feed_dict里覆盖它的值。palceholder和tensor唯一的不同就是如果没有给placeholder赋值,会出异常。

Datasets


Placeholders是用来单次执行的。而Dataset是用来流式读入数据到模型的。
为了从Dataset得到一个可运行的tf.Tensor,你首先必须把它转成一个tf.data.Iterator。然后调用iterator的get_next方法。

最简单的创建一个Iterator是用make_one_shot_iterator 方法。比如,下边的代码,next_item 这个tensor每次run调用会返回my_data数据的一行。

my_data =[
[0,1,],
[2,3,],
[4,5,],
[6,7,],
]
slices = tf.data.Dataset.from_tensor_slices(my_data)
next_item = slices.make_one_shot_iterator().get_next()

当达到数据流的结尾时,会触发Dataset抛出一个OutOfRangeError的异常。比如,下边的代码读next_item直到没有数据可读。

while True:
    try:
        print(sess.run(next_item))
    except tf.errors.OutOfRangeError:
        break

如需了解更多细节关于dataset和Iterators,可以看:Importing Data。

Layers


一个可以训练的模型必须修改graph里的值,这样图才可以不断改进,同样的输入可以输出不同的output。Layers是推荐的给graph里添加可以训练的parameter的方法。
Layers 把变量和变量上操作封装到一起。比如一个 densely-connected layer就是对所有的input执行一个带权重的求和,然后应用一个可选的activation function。得到输出。这个链接里的weight值和bias的值是由layer这个对象管理的。

创建一个Layer

下边的代码创建一个Dense层,它的输入是一组input向量,输出是对每一个向量生成一个单一的值。给layer传入一个input,那么就像调用一个函数一样的调用layer,比如:

x = tf.placeholder(tf.float32,shape=[None,3])
linear_model = tf.layers.Dense(units=1)
y = linear_model(x)

Layer需要知道向量的大小来初始化内部的变量,所以这里我们必须传入x这个placeholder的shape来让Layer内部可以创建一个有着正确大小的weight矩阵。

现在我们已经定义了计算output的计算,也就是y,在执行前,我们还有一件事需要注意。

初始化Layers

Layers里的变量是必须先初始化才能使用的。当然你可以一个个初始化,TensorFlow提供了一种简单的方法来统一初始化graph里所有的变量:

init = tf.global_variables_initializer()
sess.run(init)

注意:调用tf.global_variables_initializer只创建和返回了一个对TensorFlow operation的句柄。只有用tf.Session.run去调用这个operation,它才会对所有全局的变量进行初始化。

同样,global_variables_initializer只会初始化比它先存在graph里的那些变量,所以它应该最后创建。

执行Layers

现在Layer已经已经被初始化了,我们可以对linear_model的output进行求值,就像对其他tensor一样。

print(sess.run(y,{x:[[1,2,3],[4,5,6]]}))

会生成一个2个元素的输出向量:

[[-3.41378999]
 [-9.14999008]]

Layer Function的快捷方式

对于每一个layer class(比如 tf.layers.Dense) TensorFlow 也提供一个快捷方法(比如tf.layers.dense)。他们的区别是快捷方法的创建和调用时一起的。比如,之前例子的等价方式:

x = tf.placehodler(tf.float32,shape=[None,3])
y = tf.layers.dense(x,units=1)

init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(y,{x:[[1,2,3],[4,5,6]}))

这样调用比较省事,x,y都是tensor,没有了对tf.laysers.Layer的对象。但是它因为绑定了变量x,就不能重用了。调试也麻烦了。

Feature Columns


最简单的体验feature columns的方法是使用tf.feature_column.input_layer方法。这个方法只接受dense columns作为输入。所以为了看一个离散类型的列,你必须把它封装到一个tf.feature_column.indicator_column里。比如:

features = {
    'sales':[[5],[10],[8],[9]],
    'department':['sports','sports','gardening','gardening']
}

department_column = tf.feature_column.categorical_column_with_vocabulary_list('department',['sports','gardening'])
department_column = tf.feature_column.indicator_column(department_column)
columns = [
    tf.feature_column.numeric_column('sales'),
    department_column
]
inputs = tf.feature_column.input_layer(features,columns)

运行inputs tensor 会自动从features解析出一组vectors。
Feature columns可以有内部状态,比如layers。所以他们经常需要被初始化。离散型的column需要用lookup tables,它需要一个独立的初始化操作,tf.tables_initializer

var_init = tf.global_variables_initializer()
table_init = tf.tables_initializer()
sess = tf.Session()
sess.run((var_init,table_init))

一旦你初始化完成,你就可以像运行其他tensor一样的运行inputs

print(sess.run(inputs))

输出显示了feature columns是如何对input向量用one-hot encoding对department列编码的,前两列是对department进行编码,第三列是sales。

[[  1.   0.   5.]
 [  1.   0.  10.]
 [  0.   1.   8.]
 [  0.   1.   9.]]

Training


现在你已经对TensorFlow一些基本的概念有所了解,我们开始自己动手训练一个小的regession模型吧。

定义数据

首先让我们定义一些inputs,x,和每个input的期望输出,y_true:

x = tf.contant([[1],[2],[3],[4]],dtype=tf.float32)
y_true = tf.constant([[0],[-1],[-2],[-3]],dtype=tf.float32)

定义模型

下来让我们建一个简单的只有一个输出的线性模型:

linear_model = tf.layers.Dense(units=1)
y_pred = linear_model(x)

我们可以这样求得预测值:

sess = tf.Session()
init = tf.gloable_variables_initializer()
sess.run(init)
print(sess.run(y_pred))

因为模型还没有被训练,所以预测的值并不好,它和你期望的output肯定有差距:

[[ 0.02631879]
 [ 0.05263758]
 [ 0.07895637]
 [ 0.10527515]]

Loss

为了优化一个模型,你首先需要定义它的损失loss。我们使用均方差,它是回归问题的一个标准的loss。

你可以自己写均方差的数学公式来计算,同时tf.losses模块提供了一组常用的loss function。你可以像下面这样调用它来求得均方差:

loss = tf.losses.mean_squard_error(lables=y_true,predictions = y_pred)
print(sess.run(loss))

通过上边的调用,你就可以产生一个loss值。比如下边这样:

2.23962

Training

TensorFlow 提供了optimizers,它实现了标准的优化算法。他们都是tf.train.Optimizer的子类。他们渐进的修改每个参数来达到最小化loss的目的。最简单的优化算法时梯度下降(gradient descent),由tf.train.GradientDescentOptimizer实现。它是根据loss对每个变量导数的大小来改变参数值的。比如:

optimizer = tf.train.GradentDescentOptimizer(0.01)
train = optimizer.minimize(loss)

这一段代码会构建图里和这个优化相关的所有的component。并返回一个traning的operation。运行这个operation的时候。它会更新图里的参数。你可以这样调用它:

for i in range(100):
    _,loss_value = sess.run((train,loss))
    print(loss_value)

因为 train 是一个operation,不是一个tensor。所以它运行时没有返回值。为了看到loss function值的变化,每次迭代,我们也运行一个loss tensor。这样我们就看到如下的输出:

1.35659
1.00412
0.759167
0.588829
0.470264
0.387626
0.329918
0.289511
0.261112
0.241046
...

完整的程序

x = tf.contant([[1],[2],[3],[4]], dtype = tf.float32)
y_true = tf.constant([[0],[-1],[-2],[-3]], dtype = tf.float32)

linear_model = tf.layers.Dense(units=1)
y_pred = linear_model(x)
loss = tf.losses.mean_squared_error(labels=y_true,predictions=y_pred)

optimizer = tf.train.GradentDescentOptimizer(0.01)
train = optimizer.minimize(loss)

init = tf.global_variables_initializer()

sess = tf.Session()
sess.run(init)

for i in range(100):
    _,loss_value = sess.run((train,loss))
    print(loss_value)
print(sess.run(y_pred))

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

原文:http://www.rethink.fun/index.php/2018/03/09/tensorflow2/

猜你喜欢

转载自blog.csdn.net/lcczzu/article/details/91445215