刚装上TensorFlow,还是不太会用,主要去官网还要翻墙太麻烦了。。随手翻一下教程备用
初识TensorFlow
初期准备:
- 安装好TensorFlow
- 知道如何在Python中编程
- 懂一点数组知识
- 最好了解机器学习(不必要)
TensorFLow提供多种APIs,从低级到高级,满足不同使用需求,越高级越容易学习和使用。下面的一些模型都可以用tf.contrib.learn
高级API实现。
Tensors
TensorFlow最重要的数据单元就是tensor(张量)。一个tensor包括了任意维度的数组的原始值。tensor的rank代表其维度,如:
3 # a rank 0 tensor; this is a scalar with shape []
[1. ,2., 3.] # a rank 1 tensor; this is 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]
直观感觉有几层[]
括号就有几个rank。而shape是从括号外向里,数,
的个数。
TensorFlow 核心教程
导入TensorFlow
python下基本的导入声明:
import tensorflow as tf
大多数的文档都假设已经导入了tf模块。
计算图(The Computational Graph)
你可以把TensorFlow核心程序想成两个独立的模块:
- 搭建计算图
- 运行计算图
计算图computational graph是一系列布置为节点图的TensorFlow操作。每个节点将0个或更多张量作为输入并产生一个张量作为输出。常数是其中一个节点类型,他没有输入,输出其储存的常量,建立两个浮点数张量node1
和node2
:
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)
打印为:
Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
注意其输出并不是3.0
和4.0
。相反,他们是节点,当被评价(when evaluated)时,就会输出3.0
和4.0
。为了确切评价节点,我们必须用session运行一个计算图。一个session封装了TensorFlow运行时的控件和状态。
下面是一个使用Session
运行的例子:
sess = tf.Session()
print(sess.run([node1, node2]))
得到输出:
[3.0, 4.0]
我们可以将Tensor节点与运算(运算操作也是节点)结合搭建更为复杂的计算图。比如我们可以将两个常数相加:
node3 = tf.add(node1, node2)
print("node3: ", node3)
print("sess.run(node3): ",sess.run(node3))
输出:
node3: Tensor("Add_2:0", shape=(), dtype=float32)
sess.run(node3): 7.0
TensorFlow提供了一个名为TensorBoard的工具用来显示计算图,上面的计算过程可以可视化表示为下图:
由于输入是常数,这张图的输出结果是恒定的。一个图可以参数化为接受外部输入,称为placeholders(占位符),用于为之后的数据占取空间。
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b # + provides a shortcut for tf.add(a, b)
下面三行有点像一个函数或一个lambda,其中我们定义两个输入参数(a和b),然后对它们进行操作。 我们可以通过使用feed_dict参数指定为这些占位符提供具体值的Tensors,使用多个输入来评估此图:
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
可视化之后:
在机器学习中,我们通常需要一个可以接受任意输入的模型,例如上面的模型。 为了使模型可训练,我们需要能够修改图以获得具有相同输入的新输出。 Variables(变量)允许我们向图中添加可训练的参数。 它们使用类型和初始值构造:
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], 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
是一个占位符,我们可以同时对多个值进行输入:
print(sess.run(linear_model, {x:[1,2,3,4]}))
结果为:
[ 0. 0.30000001 0.60000002 0.90000004]
我们已经创建了一个模型,但无法评价其性能。为了评估训练数据的模型,需要一个占位符y
来提供所需的值,我们需要写一个损失函数。
损失函数测量当前模型与提供的数据之间的距离。 我们将使用用于线性回归的标准损失模型,其将当前模型和提供的数据之间的增量的平方求和。 用linear_model - y
创建一个向量,其中每个元素是对应的示例的误差增量。 我们调用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
的“完美”值,但机器学习的整个要点是自动找到正确的模型参数。 我们将在下一节中说明如何完成这一任务。
tf.train API
机器学习的完整讨论超出了本教程的范围。 然而,TensorFlow提供了优化器,其缓慢地改变每个变量以便最小化损失函数。 最简单的优化器是梯度下降。 它根据相对于该变量的损失导数的大小来修改每个变量。 一般来说,人工计算符号导数是繁琐的并且容易出错。 因此,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为通用模式、结构和功能提供了更高级别的抽象。 我们将在下一节中学习如何使用这些抽象。
完整程序
import numpy as np
import tensorflow as tf
# Model parameters
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})
# evaluate training accuracy
curr_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
其可视化图:
tf.contrib.learn
tf.contrib.learn
是一个高级TensorFlow库,它简化了机器学习的机制,包括以下内容:
- 运行训练
- 运行评价
- 管理数据集
- 管理输入
tf.contrib.learn
定义了许多常见的模型。
基本用法
注意,线性回归程序用tf.contrib.learn变得更简单:
import tensorflow as tf
# NumPy is often used to load, manipulate and preprocess data.
import numpy as np
# Declare list of features. We only have one real-valued feature. There are many
# other types of columns that are more complicated and useful.
features = [tf.contrib.layers.real_valued_column("x", dimension=1)]
# An estimator is the front end to invoke training (fitting) and evaluation
# (inference). There are many predefined types like linear regression,
# logistic regression, linear classification, logistic classification, and
# many neural network classifiers and regressors. The following code
# provides an estimator that does linear regression.
estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)
# TensorFlow provides many helper methods to read and set up data sets.
# Here we use `numpy_input_fn`. We have to tell the function how many batches
# of data (num_epochs) we want and how big each batch should be.
x = np.array([1., 2., 3., 4.])
y = np.array([0., -1., -2., -3.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x}, y, batch_size=4,
num_epochs=1000)
# We can invoke 1000 training steps by invoking the `fit` method and passing the
# training data set.
estimator.fit(input_fn=input_fn, steps=1000)
# Here we evaluate how well our model did. In a real example, we would want
# to use a separate validation and testing data set to avoid overfitting.
estimator.evaluate(input_fn=input_fn)
运行后输出:
{'global_step': 1000, 'loss': 1.9650059e-11}
自定义模型
tf.contrib.learn
不会只能运行其预定义的模型。 假设我们想创建一个未内置到TensorFlow中的自定义模型。 我们仍然可以保留tf.contrib.learn
的数据集,馈送,训练等的高级抽象。 为了说明,我们将演示如何使用我们的低级TensorFlow API的知识来实现我们自己的等效模型到LinearRegressor。
要定义与tf.contrib.learn
一起使用的自定义模型,我们需要使用tf.contrib.learn.Estimator
。 tf.contrib.learn.LinearRegressor
实际上是tf.contrib.learn.Estimator
的子类。 而不是子类别Estimator
,我们只是提供Estimator
一个函数model_fn
,告诉tf.contrib.learn
如何评估预测,训练步骤和损失。 代码如下:
import numpy as np
import tensorflow as tf
# Declare list of features, we only have one real-valued feature
def model(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))
# ModelFnOps connects subgraphs we built to the
# appropriate functionality.
return tf.contrib.learn.ModelFnOps(
mode=mode, predictions=y,
loss= loss,
train_op=train)
estimator = tf.contrib.learn.Estimator(model_fn=model)
# define our data set
x=np.array([1., 2., 3., 4.])
y=np.array([0., -1., -2., -3.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x": x}, y, 4, num_epochs=1000)
# train
estimator.fit(input_fn=input_fn, steps=1000)
# evaluate our model
print(estimator.evaluate(input_fn=input_fn, steps=10))
运行后输出:
{'loss': 5.9819476e-11, 'global_step': 1000}
注意自定义model()
函数的内容与下层API的手动模型训练循环非常相似。
用惯了caffe,感觉TensorFlow有点麻烦呢