TensorFlow之二 ----- 开始使用TensorFlow

来源:互联网 发布:淘宝客一单佣金多少 编辑:程序博客网 时间:2024/06/04 19:56

这份指南让你开始在TensorFlow里面编程。在使用这份指南之前,请先安装TensorFlow。为了更好的理解这份指南,你需要了解以下知识:

  • 如何在python中编程
  • 至少了解一点关于数组
  • 理想的,一些关于机器学习的知识。然而,如果你只了解一点点或者完全不了解机器学习,那么这仍然是你应该阅读的第一份指南。
TensorFlow提供了大量的API。最底层的API ---- TensorFlow Core ---为你提供了完全的编程控制。我们为机器学习研究人员和其他要求对他们的模型有良好控制的人员推荐TensorFlow Core。高层API建立在TensorFlow Core上面。这些高层API相对要容易学习和使用。额外的,高层API使重复性的任务在不同用户之间更加容易和一致。一个高层API,如tf.estimator帮助你管理数据集合,估计,训练和推断。

这份指南在TensorFlow Core上开始一个教程。稍后,我们展示如何在tf.estimator里实现相同的模型。了解TensorFlow Core原理将会在思想上有一个很好的帮助,关于当你使用更紧凑的高层API时一些事物在内部是如何工作的。

1. 张量

TensorFlow里数据的中心单元就是张量。一个张量是由一系列原始值组成的,这些值被形成一个任意数量维度的数组。一个张量的维度shape是它的维度的数字。下面是一些张量的例子:
3 # a rank 0 tensor; a scalar with shape [][1., 2., 3.] # a rank 1 tensor; a vector with shape [3][[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3][[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]


1.1 TensorFlow Core教程

导入TensorFlow
典型的TensorFlow程序的导入语法如下:
import tensorflow as tf
这让python可以访问所有TensorFlow的类,方法和符号。大部分的文档都会假定你已经做了这个。

计算图形
你可以将TensorFlow核心程序看成是2个分离的部分:
  1. 建立计算图形
  2. 运行计算图形
一个计算图形是指TensorFlow操作的序列被安排进了一个节点的图形里。让我们来建立一个简单的计算图形。每个节点需要0个或者更多的张量作为输入并且产生一个张量作为输出。节点的一个类型是常量。就像所有TensorFlow常量一样,它没有输入,并且输出一个它在内部存储的值。我们可以按如下所述创建2个浮点张量node1和node2:
node1 = tf.constant(3.0, dtype=tf.float32)node2 = tf.constant(4.0) # also tf.float32 implicitlyprint(node1, node2)
最后的打印语句生成
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
注意,打印节点并没有如你所期望的那样输出值3.0和4.0.相反的,当被评估后,他们将会各自产生3.0和4.0的节点。
为了确实的评估这些节点,我们必须在会话里运行这个计算图形。一个会话封装了TensorFlow运行时的控制和状态。

下列代码创建一个Session对象,然后调用他的run方法来完全运行计算图形来评估node1和node2。按如下的方式在一个会话里运行计算图形:
sess = tf.Session()print(sess.run([node1, node2]))
我们将会看到期望的值3.0和4.0:
[3.0, 4.0]

我们可以通过使用操作组合Tensor来建立更复杂的计算(操作也是节点)。例如,我们可以添加我们的2个常量节点并且产生一个下列的新的图表:
from __future__ import print_functionnode3 = tf.add(node1, node2)print("node3:", node3)print("sess.run(node3):", sess.run(node3))
最后的2个print语句产生
node3: Tensor("Add:0", shape=(), dtype=float32)sess.run(node3): 7.0
TensorFlow提供了一种称为TensorBoard的功能,能显示一个计算图表的一个图片。下面是一个屏幕截图表明TensorBoard如何形象化图表:

如它所展示的那样,这个图表并不是特别的有趣,因为它总是产生一个常量的结果。一个图表能够被参数化来接受外部的输出,既是placeholders。一个placeholders是一个提供一个后续值的承诺。
a = tf.placeholder(tf.float32)b = tf.placeholder(tf.float32)adder_node = a + b  # + provides a shortcut for tf.add(a, b)
在这3行之前是一个有点像一个函数或一个lambda,在其中我们定义2个输入参数(a和b),和他们之间的一个操作。我们可以使用多个输出来评估这个图表,通过使用run方法的feed_dict参数来为placeholders提供固定的值:
print(sess.run(adder_node, {a: 3, b: 4.5}))print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))
输出的结果是:
7.5[ 3.  7.]
在TensorBoard里,图表看起来像这样:

我们可以通过添加另一个操作来使计算图形更负责。例如:
add_and_triple = adder_node * 3.print(sess.run(add_and_triple, {a: 3, b: 4.5}))
产生输出
22.5
上面的计算图形在TensorBoard中将会看起来如下:
在机器学习里我们将会特别需要一个能包含任意输出,例如上面的那个的模型。为了让模型可训练,我们需要能修改图形使用相同的输入来得到一些新的输出。变量允许我们添加可训练的参数到一个图形。他们是由一个类型和初始值组成:
W = tf.Variable([.3], dtype=tf.float32)b = tf.Variable([-.3], dtype=tf.float32)x = tf.placeholder(tf.float32)linear_model = W*x + b
当你调用tf.constant的时候,常量会被初始化,并且他们的值将不能被改变。相比之下,当你调用tf.Variable的时候变量将不会被初始化。为了在一个TensorFlow项目中初始化所有的变量,你必须明确的调用下列的一个特殊操作:
init = tf.global_variables_initializer()sess.run(init)
认识到init对于TensorFlow的子图形来说是一个用于初始化所有全局变量的一个句柄是很重要的。直到我们调用sess.run,变量才会被初始化。

因为x是一个占位符,我们能对x的几个值同时地评估linear_model,如下:
print(sess.run(linear_model, {x: [1, 2, 3, 4]}))
来产生输出:
[ 0.          0.30000001  0.60000002  0.90000004]
我们创建了一个模型,但是我们不知道它有多好。为了在训练数据上评估这个模型,我们需要一个y占位符来提供想要的值,并且我们需要编写一个损失函数。

一个损失函数测量出当前的模型离提供的数据到底相隔有多远。我们将为线性回归使用一个标准的损失模型,它汇总当前模型和提供的数据之间的deltas的平方值。linear_model_y创建了一个矢量,其中每个元素都是对应例子的错误delta。我们调用tf.square来平方误差。然后,我们汇总所有的平方误差来使用tf.reduce_sum创建一个单独的标量来抽象所有例子中的误差:
y = tf.placeholder(tf.float32)squared_deltas = tf.square(linear_model - y)loss = tf.reduce_sum(squared_deltas)print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))
产生的损失值:
23.66
我们将通过重新复制W和b的值来手工提升这个到完美的-1和1的值。变量被初始化给由tf.Variable提供的值,但是能通过使用操作例如tf.assign修改。例如,W=-1和b=1都是我们模型的可选参数。我们因此可以修改W和b:
fixW = tf.assign(W, [-1.])fixb = tf.assign(b, [1.])sess.run([fixW, fixb])print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))
最后的打印显示损失现在是0.
0.0
我们猜测W和b的‘完美’的值,但是机器学习的意义是自动的找到正确的模型参数。我们将在下一节展示如何完成这个。

1.2 tf.train api

机器学习的完整讨论并不在这个教程的范畴。然而,TensorFlow提供优化器optimizers来缓慢改变每一个变量为了最小化损失函数。最简单的优化器是梯度下降gradient descent.它根据损失函数的导数的大小来修改每个变量。通常,手工计算导数符号是乏味并且易出错的。因此,TensorFlow能自动的产生导数,只需要提供一个使用tf.gradients函数的模型的声明。简单的说,优化器将会为你做这些事。例如:
optimizer = tf.train.GradientDescentOptimizer(0.01)train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.for i in range(1000):  sess.run(train, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]})print(sess.run([W, b]))
最后模型参数的结果是:
[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]
现在我们做的就是真正的机器学习!尽管这个简单的线性回归模型并不需要太多TensorFlow核心代码,更多负责的模型和方法用于将数据送入你的模型需要更多的代码。因此,TensorFlow为普通的模式,结构,功能提供更高层次的抽象。我们将在下一节学会如何使用这些抽象的一部分。

1.2.1 完整程序
完整的可训练线性回归模型如下所示:
import tensorflow as tf# Model parametersW = tf.Variable([.3], dtype=tf.float32)b = tf.Variable([-.3], dtype=tf.float32)# Model input and outputx = tf.placeholder(tf.float32)linear_model = W*x + by = tf.placeholder(tf.float32)# lossloss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares# optimizeroptimizer = tf.train.GradientDescentOptimizer(0.01)train = optimizer.minimize(loss)# training datax_train = [1, 2, 3, 4]y_train = [0, -1, -2, -3]# training loopinit = tf.global_variables_initializer()sess = tf.Session()sess.run(init) # reset values to wrongfor i in range(1000):  sess.run(train, {x: x_train, y: y_train})# evaluate training accuracycurr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
当运行是,它产生
W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
注意,损失是一个非常小的数字(非常接近于0)。如果你运行这个程序,你的损失值也许不一定和上述提及的损失值完全一样因为模型是由伪随机值初始化的。

这个更复杂的程序仍然能在TensorBoard中可视化



1.3 tf.estimator

tf.estimator是一个高层次的TensorFlow库,它简化了机器学习的结构,包括如下:
  • 运行训练循环
  • 运行评估循环
  • 管理数据集
tf.estimator定义了许多通用的模型。


1.3.1 基础用法

注意当使用tf.estimator时线性回归程序将会变的有多么简单:
# NumPy is often used to load, manipulate and preprocess data.import numpy as npimport tensorflow as tf# Declare list of features. We only have one numeric feature. There are many# other types of columns that are more complicated and useful.feature_columns = [tf.feature_column.numeric_column("x", shape=[1])]# An estimator is the front end to invoke training (fitting) and evaluation# (inference). There are many predefined types like linear regression,# linear classification, and many neural network classifiers and regressors.# The following code provides an estimator that does linear regression.estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)# TensorFlow provides many helper methods to read and set up data sets.# Here we use two data sets: one for training and one for evaluation# We have to tell the function how many batches# of data (num_epochs) we want and how big each batch should be.x_train = np.array([1., 2., 3., 4.])y_train = np.array([0., -1., -2., -3.])x_eval = np.array([2., 5., 8., 1.])y_eval = np.array([-1.01, -4.1, -7, 0.])input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)train_input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)eval_input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)# We can invoke 1000 training steps by invoking the  method and passing the# training data set.estimator.train(input_fn=input_fn, steps=1000)# Here we evaluate how well our model did.train_metrics = estimator.evaluate(input_fn=train_input_fn)eval_metrics = estimator.evaluate(input_fn=eval_input_fn)print("train metrics: %r"% train_metrics)print("eval metrics: %r"% eval_metrics)
当运行的时候,它将会产生下列的事情:
train metrics: {'average_loss': 1.4833182e-08, 'global_step': 1000, 'loss': 5.9332727e-08}eval metrics: {'average_loss': 0.0025353201, 'global_step': 1000, 'loss': 0.01014128}
注意我们的eval数据如何有一个更高的损失,但是它仍然接近于0.那意味着我们正在正确的学习。

1.3.2 一个自定义模型

tf.estimator 不会将你锁在它定义的模型里。假设我们想要创建一个没有内置在TensorFlow中的自定义模型。我们仍然能保留tf.estimator的数据集,传送,训练等等的高层次抽象。为了说明,我们将展示如何使用我们的TensorFlow API的知识来实现等价于线性回归LinearRegressor的我们自己的模型。

为了使用tf.estimator定义个自定义模型,我们需要使用tf.estimator.Estimator。tf.estimator.LinearRegressor实际是tf.estimator.Estimator的一个子类。我们仅仅提供Estimator一个函数model_fn来告诉tf.estimator它如何能评估预测,训练不走,和损失,而不是子类Estimator。代码如下:
import numpy as npimport tensorflow as tf# Declare list of features, we only have one real-valued featuredef model_fn(features, labels, mode):  # Build a linear model and predict values  W = tf.get_variable("W", [1], dtype=tf.float64)  b = tf.get_variable("b", [1], dtype=tf.float64)  y = W*features['x'] + b  # Loss sub-graph  loss = tf.reduce_sum(tf.square(y - labels))  # Training sub-graph  global_step = tf.train.get_global_step()  optimizer = tf.train.GradientDescentOptimizer(0.01)  train = tf.group(optimizer.minimize(loss),                   tf.assign_add(global_step, 1))  # EstimatorSpec connects subgraphs we built to the  # appropriate functionality.  return tf.estimator.EstimatorSpec(      mode=mode,      predictions=y,      loss=loss,      train_op=train)estimator = tf.estimator.Estimator(model_fn=model_fn)# define our data setsx_train = np.array([1., 2., 3., 4.])y_train = np.array([0., -1., -2., -3.])x_eval = np.array([2., 5., 8., 1.])y_eval = np.array([-1.01, -4.1, -7., 0.])input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)train_input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)eval_input_fn = tf.estimator.inputs.numpy_input_fn(    {"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)# trainestimator.train(input_fn=input_fn, steps=1000)# Here we evaluate how well our model did.train_metrics = estimator.evaluate(input_fn=train_input_fn)eval_metrics = estimator.evaluate(input_fn=eval_input_fn)print("train metrics: %r"% train_metrics)print("eval metrics: %r"% eval_metrics)
当运行时,它产生
train metrics: {'loss': 1.227995e-11, 'global_step': 1000}eval metrics: {'loss': 0.01010036, 'global_step': 1000}
注意,自定义函数model_fn()与来自低层API的我们的手工模型训练循环是如何的相似。