一、 TensorFlow是什么?
点击打开链接
是谷歌开源的机器学习实现框架,本文从Python语言来理解学习Tensorflow以及机器学习的知识。
TensorFlow的API主要分两个层次,核心层和基于核心层的高级API。核心层面向机器学习的研究人员,以及对模型控制精细的相关人员。高级API使用和学习相对容易,简化重复性任务,使不同的用户之间保持一致性。
高级API,如tf.contrib.learn可以帮助管理数据集data set,估计量estimators,训练training,推理inference
注意,一些高级API的方法名中包含contrib,意味着这些API依然处于开发过程中,这些方法在后续的TensorFlow版本中可能改变或者不再使用
这篇教程从核心层开始,后边会提到如何使用tf.contrib.learn实现模型。了解核心层,在使用高级API的时候知道程序是如何工作的。
1. 张量Tensors
数据的核心单元,一个tensor是一个包含任意维度的数组,张量的阶Tensor' rank是数组的维度,如下:
3 # 0阶张量,一个标量scalar with shape[]
[1. ,2., 3.] # 1阶张量; 一个向量vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # 2阶 张量; 一个矩阵matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # 3阶 张量tensor with shape [2, 1, 3]
2 是指[[1., 2., 3.]] 和 [[7., 8., 9.]]
1 是指[[1., 2., 3.]] 中有1个 [1., 2., 3.]
3 是指 [1., 2., 3.]中有3个:1., 2., 3.
二、 核心层TensorFlow Core学习
1. 引入TensorFlow
典型的引入语句:import tensorflow as tf
该语句是tensorflow的所有类,方法,符号的入口
2. 计算图Computational Graph
Tensorflow编程包含两个步骤:
1.构造计算图
2.运行计算图
计算图是什么?计算图是一系列的计算操作抽象为图中的节点。
构造一个简单的计算图:每个节点将0或多个tensor作为输入,输出一个tensor。一种类型的节点是常量节点constant,就如同tensorflow中的常数,它有0个输入,输出一个值。
构建两个浮点型tensor:node1和node2
- node1 = tf.constant(3.0, tf.float32)
- node2 = tf.constant(4.0)
- print(node1, node2)
输出结果:
- Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)
需要说明,打印结果并不是我们期待的3.0 , 4.0,因为这是打印的节点(属于计算操作),当评估运行之后,才是我们期待的值。
评估一个节点,必须在一个会话Session中运行计算图,会话封装了Tensorflow运行时的状态和控制
接下来创建一个Session会话对象,调用run方法,运行计算图,去评估node1和node2
- sess=tf.Session()
- print(sess.run([node1,node2]))
输出结果:
可以使用计算操作将多个节点组合,构建更复杂的计算,例如将两个常量节点相加,产生一个新的计算图:
- node3 = tf.add(node1, node2)
- print("node3: ", node3)
- print("sess.run(node3): ",sess.run(node3))
输出结果:
- node3: Tensor("Add:0", shape=(), dtype=float32)
- sess.run(node3): 7.0
Tensorflow提供了一个名为
TensorBoard的部分,可以以图片的方式展示计算图。
Tensorboard
点击打开链接
Tensorboard是tensorflow自带的可视化模块,网上关于它的资源很少,而且基本一上来就是默认你已经有很高的水平的那种,对新手十分不友好,我摸索了好久,才搞懂怎么把官网教程里的那张图显示出来:
python中运行的代码如下:
import tensorflow as tf
node1 = tf.constant(3.0,dtype=tf.float32)
node2 = tf.constant(4.0)
node3 = tf.add(node1, node2)
writer = tf.summary.FileWriter('D:/ten',tf.get_default_graph())
writer.close()
writer = tf.summary.FileWriter(‘D:/ten’, tf.get_default_graph())这句话生成了当前计算图的日志并保存在D:/ten。
然后命令行输入
C:\WINDOWS\system32>D:
D:\>tensorboard --logdir="ten"
然后打开浏览器访问http://localhost:6006就可以看到上图界面,推荐使用谷歌浏览器查看。
这里说一下我踩的一个坑,一定要先进D盘在选目录,不能直接
C:\WINDOWS\system32>tensorboard --logdir="D:\ten"
不然你看到的画面会是这样:
计算图可以使用占位符placeholder参数化的从外部输入数据,placeholder的作用是在稍后提供一个值
-
- a=tf.placeholder(tf.float32)
- b=tf.placeholder(tf.float32)
- adder_node=a+b
-
- print("adder_node:",adder_node)
- print(sess.run(adder_node,{a:3,b:4.5}))
- print(sess.run(adder_node,{a:[1,3],b:[2,4]}))
输出结果:
- adder_node: Tensor("add:0", dtype=float32)
- 7.5
- [ 3. 7.]
我们可以添加一个操作,使计算图更加复杂:
- add_and_triple=adder_node * 3
- print("add_and_triple:",add_and_triple)
- print("sess run result:",sess.run(add_and_triple,{a:3,b:4.5}))
输出结果:
- add_and_triple: Tensor("mul:0", dtype=float32)
- sess run result: 22.5
在机器学习中,需要模型可以任意输入,为了模型具有可训练能力,需要修正计算图,使对于同样的输入得到新的输出。变量Variable允许我们为计算图添加训练参数。
构造一个变量,需要提供类型和初始值:
- 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时并不初始化,必须显性的执行如下操作:
- init = tf.global_variables_initializer()
- sess.run(init)
意识到init对象是Tensorflow子图初始化所有全局变量的句柄是重要的,在调用sess.run(init)方法之前,所有变量都是未初始化的。
因为x是一个占位符,我们可以指定几个值来评估linear_model模型(训练)
运行计算图:
- print("linear_model:",linear_model)
- print(sess.run(linear_model,{x:[1,2,3,4]}))
得到输出:
- linear_model: Tensor("add_1:0", dtype=float32)
- [ 0. 0.30000001 0.60000002 0.90000004]
我们创建了一个模型,但是不知道这个模型的效果怎么样,基于训练数据来评估模型,还需要一个placeholder y 来提供期望值,我们需要一个损失函数loss function
损失函数测量当前模型与真实数据之间的差距,对于线性模型,我们使用标准损失函数,求模型预测结果与实际数据之间差值的平方和sum the squares of the deltas
linear_model - y 构造了一个向量,对应每个元素的差值,我们调用
tf.square求平方,使用
tf.reduce_sum求和所有的平方差为一个标量scalar
- y=tf.placeholder(tf.float32)
- squared_deltas=tf.square(linear_model-y)
- loss=tf.reduce_sum(squared_deltas)
- print("loss:",loss)
- print(sess.run(loss,{x:[1,2,3,4],y:[0,-1,-2,-3]}))
输出结果:
- loss: Tensor("Sum:0", dtype=float32)
- 23.66
平方差为23.66
我们可以通过手动的方式将参数W和b置为W=-1,b=1,使模型最优,即损失函数最小。初始化后的变量可以通过tf.assign来更改,tf.assign后需要tf.run生效
- fixW=tf.assign(W,[-1.])
- fixb=tf.assign(b,[1.])
- sess.run([fixW,fixb])
- print("fix loss:",sess.run(loss,{x:[1,2,3,4],y:[0,-1,-2,-3]}))
输出结果:
我们猜想最优的W和b值,但是在机器学习中,就是自动的寻找这些最优的模型参数。下节介绍》》
三、 API tf. train
Tensorflow提供了优化器Optimizer慢慢改变每个变量来最小化损失函数。最简单的Optimizer是梯度下降gradient descent,它根据损失函数相对于该变量的导数大小来修改参数值,一般来讲,手动计算导数是乏味且易出错的,Tensorflow可以使用方法tf.gradients自动的为给定模型计算导数。优化器通常做这个工作。
- optimizer=tf.train.GradientDescentOptimizer(0.01)
- train=optimizer.minimize(loss)
- print("train:\n",trian)
- sess.run(init)
- for i in range(1000):
- sess.run(train,{x:[1,2,3,4],y:[0,-1,-2,-3]})
- print(sess.run([W,b]))
输出结果:
- train:
- name: "GradientDescent"
- op: "NoOp"
- [array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]
到此,我们实现了一次真实的机器学习,尽管我们只实现的是简单的线下回归,不需要多少Tensorflow core代码,然而复杂的模型和方法输入数据会需要更多的代码量,因此Tensorflow对于一般的模式、结构和功能提供了高级别的抽象。我们在下一章节学习。
完整的代码:
- import tensorflow as tf
-
- import numpy as np
- node1 = tf.constant(3.0,tf.float32)
- node2 = tf.constant(4.0)
- print(node1,node2)
- sess=tf.Session()
- print(sess.run([node1,node2]))
- node3=tf.add(node1,node2)
- print("node3:",node3)
- print("sess.run(node3):",sess.run(node3))
-
- a=tf.placeholder(tf.float32)
- b=tf.placeholder(tf.float32)
- adder_node=a+b
-
- print("adder_node:",adder_node)
- print(sess.run(adder_node,{a:3,b:4.5}))
- print(sess.run(adder_node,{a:[1,3],b:[2,4]}))
- add_and_triple=adder_node * 3
- print("add_and_triple:",add_and_triple)
- print("sess run result:",sess.run(add_and_triple,{a:3,b:4.5}))
-
- W=tf.Variable([.3],tf.float32)
- b=tf.Variable([-.3],tf.float32)
- x=tf.placeholder(tf.float32)
- linear_model=W*x+b
- init = tf.global_variables_initializer()
- sess.run(init)
- print("linear_model:",linear_model)
- print(sess.run(linear_model,{x:[1,2,3,4]}))
-
- y=tf.placeholder(tf.float32)
- squared_deltas=tf.square(linear_model-y)
- loss=tf.reduce_sum(squared_deltas)
- print("loss:",loss)
- print(sess.run(loss,{x:[1,2,3,4],y:[0,-1,-2,-3]}))
-
- fixW=tf.assign(W,[-1.])
- fixb=tf.assign(b,[1.])
- sess.run([fixW,fixb])
- print("fix loss:",sess.run(loss,{x:[1,2,3,4],y:[0,-1,-2,-3]}))
-
- optimizer=tf.train.GradientDescentOptimizer(0.01)
- train=optimizer.minimize(loss)
- print("train:\n",train)
- sess.run(init)
- for i in range(1000):
- sess.run(train,{x:[1,2,3,4],y:[0,-1,-2,-3]})
-
- curr_W, curr_b, curr_loss = sess.run([W, b, loss],{x:[1,2,3,4],y:[0,-1,-2,-3]})
- print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
四、 API tf. contrib. learn
tf.contrib.learn是Tensorflow高级别的库,简化机器学习:
1.运行训练循环
2.运行评估循环
3.管理数据集
4.管理feeding
tf.contrib.learn定义了许多常见的模型
1.基本使用
使用tf.contrib.learn简化线性回归:
numpy是开源的数值计算扩展,可以用来存储和处理大型矩阵。
声明特征列表list of features:我们在此使用一个real-valued列,另外还有其他复杂有用的列类型
- import tensorflow as tf
- import numpy as np
-
- features=[tf.contrib.layers.real_valued_column("x",dimension=1)]
- print("features:",features)
输出:
- features: [_RealValuedColumn(column_name='x', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)]
声明
估计器estimator,estimator负责调用训练和评估。它有很多预先定的类型,如
线性回归linear regression, 逻辑回归logistic regerssion, 线性分类linear classification, 逻辑分类logistic classification, 以及许多神经网络分类器,回归器neural network classifiers and regressors
这里做线性回归,指定特征列表:
- estimator=tf.contrib.learn.LinearRegressor(feature_columns=features)
- print("estimator:",estimator)
输出结果:
- estimator: LinearRegressor(params={'head': 0x7f6c88bcb208>, 'feature_columns': [_RealValuedColumn(column_name='x', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)], 'optimizer': None, 'gradient_clip_norm': None, 'joint_weights': False})
Tensorflow提供了很多方法读取和设置数据集,在这里我们使用
numpy_input_fn,为该方法指定数据的
批次数目num_epochs(how many batches of data ),以及每批次的大小
batch_size
- 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)
- print("input_fn:",input_fn)
输出结果:
- input_fn: .input_fn at 0x7fb572e21e18>
我们可以调用
fit方法进行1000次训练步骤,并且传递训练数据集:
- estimator.fit(input_fn=input_fn,steps=1000)
评价模型,在真实例子中,我们会将验证和测试数据分离,以免
过度拟合overftting
- print(estimator.evaluate(input_fn=input_fn))
输出结果:
- {'loss': 1.8005665e-07, 'global_step': 1000}
完整代码:
- import tensorflow as tf
- import numpy as np
-
- features=[tf.contrib.layers.real_valued_column("x",dimension=1)]
- print("features:",features)
- estimator=tf.contrib.learn.LinearRegressor(feature_columns=features)
- print("estimator:",estimator)
- 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)
- print("input_fn:",input_fn)
- estimator.fit(input_fn=input_fn,steps=1000)
- print(">>>>>>>>>>")
- res=estimator.evaluate(input_fn=input_fn)
- print(res)
2. 定制化模型
tf.contrib.learn不限定用户使用预先定义的模型,假设我们创建一个Tensorflow中不存在的新模型,我们仍然可以保留tf.contrib.learn中的数据集、feeding、训练等
接下来,我们将实现类比于线性回归器LinearRegressor的自定义的线性回归模型。
使用tf.contrib.learn定制化模型,需要使用到类tf.contrib.learn.Estimator, 线性回归器tf.contrib.learn.LinearRegerssor就是tf.contrib.learn.Estimator的子类
通过为Estimator指定model_fn方法,控制模型如何评估,训练,损失等
- import tensorflow as tf
- import numpy as np
- def model(features, labels, mode):
-
- W=tf.get_variable("W",[1],dtype=tf.float64)
- b=tf.get_variable("b",[1],dtype=tf.float64)
- y=W*features['x']+b
-
- loss=tf.reduce_sum(tf.square(y - labels))
-
- 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))
-
- return tf.contrib.learn.ModelFnOps(
- mode=mode,predictions=y,loss=loss,train_op=train)
-
- estimator=tf.contrib.learn.Estimator(model_fn=model)
-
- 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)
-
- estimator.fit(input_fn=input_fn,steps=1000)
-
- print(estimator.evaluate(input_fn=input_fn,steps=10))
说明,model方法中的内容与前边使用底层api的收到模型很相似。