TensorFlow 基本使用
一、 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
输出结果:
需要说明,打印结果并不是我们期待的3.0 , 4.0,因为这是打印的节点(属于计算操作),当评估运行之后,才是我们期待的值。
评估一个节点,必须在一个会话Session中运行计算图,会话封装了Tensorflow运行时的状态和控制
接下来创建一个Session会话对象,调用run方法,运行计算图,去评估node1和node2
输出结果:
可以使用计算操作将多个节点组合,构建更复杂的计算,例如将两个常量节点相加,产生一个新的计算图:
输出结果:
Tensorflow提供了一个名为TensorBoard的部分,可以以图片的方式展示计算图。
这个计算图不是特别有趣,因为它产生的是一个常量结果。
计算图可以使用占位符placeholder参数化的从外部输入数据,placeholder的作用是在稍后提供一个值
输出结果:
我们可以添加一个操作,使计算图更加复杂:
输出结果:
在机器学习中,需要模型可以任意输入,为了模型具有可训练能力,需要修正计算图,使对于同样的输入得到新的输出。变量Variable允许我们为计算图添加训练参数。
构造一个变量,需要提供类型和初始值:
常量节点在调用tf.constant时就被初始化,而变量在调用tf.Variable时并不初始化,必须显性的执行如下操作:
意识到init对象是Tensorflow子图初始化所有全局变量的句柄是重要的,在调用sess.run(init)方法之前,所有变量都是未初始化的。
因为x是一个占位符,我们可以指定几个值来评估linear_model模型(训练)
运行计算图:
得到输出:
我们创建了一个模型,但是不知道这个模型的效果怎么样,基于训练数据来评估模型,还需要一个placeholder y 来提供期望值,我们需要一个损失函数loss function
损失函数测量当前模型与真实数据之间的差距,对于线性模型,我们使用标准损失函数,求模型预测结果与实际数据之间差值的平方和sum the squares of the deltas
linear_model - y 构造了一个向量,对应每个元素的差值,我们调用tf.square求平方,使用tf.reduce_sum求和所有的平方差为一个标量scalar输出结果:
平方差为23.66
我们可以通过手动的方式将参数W和b置为W=-1,b=1,使模型最优,即损失函数最小。初始化后的变量可以通过tf.assign来更改,tf.assign后需要tf.run生效
输出结果:
我们猜想最优的W和b值,但是在机器学习中,就是自动的寻找这些最优的模型参数。下节介绍》》
三、 API tf. train
Tensorflow提供了优化器Optimizer慢慢改变每个变量来最小化损失函数。最简单的Optimizer是梯度下降gradient descent,它根据损失函数相对于该变量的导数大小来修改参数值,一般来讲,手动计算导数是乏味且易出错的,Tensorflow可以使用方法tf.gradients自动的为给定模型计算导数。优化器通常做这个工作。
输出结果:
到此,我们实现了一次真实的机器学习,尽管我们只实现的是简单的线下回归,不需要多少Tensorflow core代码,然而复杂的模型和方法输入数据会需要更多的代码量,因此Tensorflow对于一般的模式、结构和功能提供了高级别的抽象。我们在下一章节学习。
完整的代码:
四、 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列,另外还有其他复杂有用的列类型
输出:
声明估计器estimator,estimator负责调用训练和评估。它有很多预先定的类型,如线性回归linear regression, 逻辑回归logistic regerssion, 线性分类linear classification, 逻辑分类logistic classification, 以及许多神经网络分类器,回归器neural network classifiers and regressors
这里做线性回归,指定特征列表:
输出结果:
Tensorflow提供了很多方法读取和设置数据集,在这里我们使用numpy_input_fn,为该方法指定数据的批次数目num_epochs(how many batches of data ),以及每批次的大小batch_size
输出结果:
我们可以调用fit方法进行1000次训练步骤,并且传递训练数据集:
评价模型,在真实例子中,我们会将验证和测试数据分离,以免过度拟合overftting
输出结果:
完整代码:
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方法,控制模型如何评估,训练,损失等
说明,model方法中的内容与前边使用底层api的收到模型很相似
使用TensorFlow
,你必须明白TensorFlow
:
- 使用图
(graph)
来表示任务 - 被称之为会话
(Session)
的上下文(context)
中执行图 - 使用
tensor
表示数据 - 通过变量
(Variable)
维护状态 - 使用
feed
和fetch
可以为任意操作(arbitrary operation)
赋值或者从其中获取数据
综述
TensorFlow
是一个编程系统,使用图来表示计算任务,图中的节点被称之为op
(operation
的缩写),一个op
获得0个或者多个tensor
,执行计算,产生0个或多个tensor
。每个tensor
是一个类型化的多维数组。例如,你可以将一组图像素集表示为一个四维浮点数数组,这四个维度分别是[batch,
height, width, channels]
。
一个TensorFlow
图描述了计算的过程,为了进行计算,图必须在会话
里被启动,会话
将图的op
分发到诸如CPU或GPU之类的设备上,同时提供执行op
的方法,这些方法执行后,将产生的tensor
返回。在python语言中,返回的tensor
是numpy
ndarry
对象;在C/C++语言中,返回的是tensor
是tensorflow::Tensor
实例。
计算图
Tensorflow
程序通常被组织成一个构建阶段和一个执行阶段,在构建阶段,op
的执行步骤被描述成为一个图,在执行阶段,使用会话执行图中的op
。
例如,通常在构建阶段创建一个图来表示和训练神经网络,然后在执行阶段反复执行图中的训练op
。
Tensorflow
支持C/C++,python编程语言。目前,TensorFlow
的python库更易使用,它提供了大量的辅助函数来简化构建图的工作,这些函数尚未被C/C++库支持。
三种语言的会话库(session libraries)
是一致的。
构建图
构件图的第一步是创建源op (source op)
。源op
不需要任何输入。源op
的输出被传递给其它op
做运算。
python库中,op
构造器的返回值代表被构造出的op
输出,这些返回值可以传递给其它op
作为输入。
TensorFlow
Python库中有一个默认图(default
graph)
,op
构造器可以为其增加节点。这个默认图对许多程序来说已经足够用了,可以阅读Graph
类文档,来了解如何管理多个视图。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
默认图现在有三个节点,两个constant() op
和matmul()
op
。为了真正进行矩阵乘法的结果,你必须在会话里启动这个图。
在一个会话中启动图
构造阶段完成后,才能启动图。启动图的第一步是创建一个Session
对象,如果无任何创建参数,会话构造器将无法启动默认图。
欲了解完整的会话API,请阅读Session
类。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Session
对象在使用完成后需要关闭以释放资源,除了显式调用close
外,也可以使用with
代码来自动完成关闭动作:
- 1
- 2
- 3
在实现上,Tensorflow
将图形定义转换成分布式执行的操作,以充分利用可以利用的计算资源(如CPU或GPU)。一般你不需要显式指定使用CPU还是GPU,Tensorflow
能自动检测。如果检测到GPU,Tensorflow
会尽可能地使用找到的第一个GPU来执行操作。
如果机器上有超过一个可用的GPU,除了第一个外的其他GPU是不参与计算的。为了让Tensorflow
使用这些GPU,你必须将op
明确地指派给它们执行。with...Device
语句用来指派特定的CPU或GPU操作:
- 1
- 2
- 3
- 4
- 5
设备用字符串进行标识,目前支持的设备包括:
/cpu:0
:机器的CPU/gpu:0
:机器的第一个GPU,如果有的话/gpu:1
:机器的的第二个GPU,以此类推
交互式使用
文档中的python示例使用一个会话Session
来启动图,并调用Session.run()
方法执行操作。
为了便于使用诸如IPython之类的python交互环境,可以使用InteractiveSession
代替Session
类,使用Tensor.eval()
和Operation.run()
方法代替Session.run()
。这样可以避免使用一个变量来持有会话:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
Tensor
Tensorflow
程序使用tensor
数据结构来代表所有的数据,计算图中,操作间传递数据都是tensor
。你可以把Tensorflow
的tensor
看做是一个n
维的数组或列表。一个tensor
包含一个静态类型rank
和一个shape
。
阶
在Tensorflow
系统中,张量的维数被描述为阶。但是张量的阶和矩阵的阶并不是同一个概念。张量的阶是张量维数的一个数量描述,下面的张量(使用python中list
定义的)就是2阶:
- 1
你可以认为一个二阶张量就是我们平常所说的矩阵,一阶张量可以认为是一个向量。对于一个二阶张量,你可以使用语句t[i, j]
来访问其中的任何元素。而对于三阶张量你可以通过t[i,
j, k]
来访问任何元素:
阶 | 数学实例 | python例子 |
---|---|---|
0 |
纯量(只有大小) | s=483 |
1 |
向量(大小和方向) | v=[1.1, 2.2, 3.3] |
2 |
矩阵(数据表) | m=[[1, 2, 3], [4, 5, 6], [7, 8, 9]] |
3 |
3 阶张量 |
t=[[[2], [4], [6]], [[8], [9], [10]], [[11], [12], [13]]] |
n |
n 阶 |
形状
Tensorflow
文档中使用了三种记号来方便地描述张量的维度:阶,形状以及维数。以下展示了它们之间的关系:
阶 | 形状 | 维数 | 实例 |
---|---|---|---|
0 |
[] |
0-D | 一个0维张量,一个纯量 |
1 |
[D0] |
1-D | 一个1维张量的形式[5] |
2 |
[D0, D1] |
2-D | 一个2维张量的形式[3, 4] |
3 |
[D0, D1, D2] |
3-D | 一个3维张量的形式[1, 4, 3] |
n |
[D0, D1, ... Dn] |
n-D | 一个n维张量的形式[D0, D1, ..., Dn] |
数据类型
除了维度,tensor
有一个数据类型属性。你可以为一个张量指定下列数据类型中的任意一个类型:
数据类型 | python类型 | 描述 |
---|---|---|
DT_FLOAT |
tf.float32 |
32位浮点数 |
DT_DOUBLE |
tf.float64 |
64位浮点数 |
DT_INT64 |
tf.int64 |
64位有符号整型 |
DT_INT32 |
tf.int32 |
32位有符号整型 |
DF_INT16 |
tf.int16 |
16位有符号整型 |
DT_INT8 |
tf.int8 |
8位有符号整型 |
DT_UINT8 |
tf.uint8 |
8位无符号整型 |
DT_STRING |
tf.string |
可变长度的字节数组,每一个张量元素都是一个字节数组 |
DT_BOOL |
tf.bool |
布尔型 |
DT_COMPLEX64 |
tf.complex64 |
由32位浮点数组成的负数:实数和虚数 |
DT_QINT32 |
tf.qint32 |
用于量化Ops的32位有符号整型 |
DT_QINT8 |
tf.qint8 |
用于量化Ops的8位有符号整型 |
DT_QUINT8 |
tf.quint8 |
用于量化Ops的8位无符号整型 |
变量
在Variables 中查看更多细节。变量维护图执行过程中的状态信息。下面的例子演示了如何使用变量实现一个简单的计数器:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
代码中assign()
操作是图所描述的表达式的一部分,正如add()
操作一样,所以在调用run()
执行表达式之前,它并不会真正执行赋值操作。
通常会将一个统计模型中的参数表示为一组变量。例如,你可以将一个神经网络的权重作为某个变量存储在一个tensor
中。在训练过程中,通过反复训练图,更新这个tensor
。
Fetch
为了取回操作的输出内容,可以在使用Session
对象的run()
调用执行图时,传入一些tensor
,这些tensor
会帮助你取回结果。在之前的例子里,我们只取回了单个节点state
,但是你也可以取回多个tensor
:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
需要获得更多个tensor
值,在op
的依次运行中获得(而不是逐个去获得tenter
)。
Feed
上述示例在计算图中引入tensor
,以常量或变量的形式存储。Tensorflow
还提供了feed
机制,该机制可以临时替代图中的任意操作中的tensor
可以对图中任何操作提交补丁,直接插入一个tensor
。
feed
使用一个tensor
值临时替换一个操作的输出结果,你可以提供feed
数据作为run()
调用的参数。feed
只在调用它的方法内有效,方法结束,feed
就会消失。最常见的用例是将某些特殊的操作指定为feed
操作,标记的方法是使用tf.placeholder()
为这些操作创建占位符。