Tensorflow学习笔记——常见概念的整理

2021年09月15日 阅读数:4
这篇文章主要向大家介绍Tensorflow学习笔记——常见概念的整理,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

  TensorFlow的名字中已经说明了它最重要的两个概念——Tensor和Flow。Tensor就是张量,张量这个概念在数学或者物理学中能够有不一样的解释,可是这里咱们不强调它自己的含义。在TensorFlow中,张量能够被简单地理解为多维数组,Flow翻译成中文就是“流”,它直观的表述计算的编程系统。TensorFlow中的每个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。javascript

TensorFlow计算模型——计算图的概念

  计算图是TensorFlow中最基本的一个概念,TensorFlow中全部计算都会被转化为计算图上的一个节点。java

  在TensorFlow程序中,全部的数据都经过张量的形式来表示。从功能的角度上看,张量能够被简单理解为多为数组。其中零阶张量表示标量(scalar),也便是一个数(张量的类型也能够是字符串)。第一阶张量为向量(vector),也就是一个一维数组;第 n 阶张量能够理解为一个 n 维数组。可是张量在TensorFlow中的实现并非直接采用数组的形式,它只是对TensorFlow中运算结果的引用。在张量中并无真正保存数字,它保存的是如何获得这些数字的计算过程。以向量加法为例,当运行以下代码的时候,获得的不是加法的结果,而是对结果的一个引用。node

?
1
2
3
4
5
6
7
8
9
#_*_coding:utf-8_*_
import tensorflow as tf
 
# tf.constant 是一个计算,这个计算的结果为一个张量,保存在变量a中
a = tf.constant([1.0, 2.0], name= 'a' )
b = tf.constant([2.0, 3.0], name= 'b' )
 
result = a + b
# print(result)   # Tensor("add:0", shape=(2,), dtype=float32)

  从上面的结果来看,TensorFlow的张量和Numpy的数组不一样,他计算的结果不是一个具体的数字,而是一个张量的结构。从上面结果来看,一个张量主要保存了三个属性,名字(name),维度(shape)和类型(type)。python

  张量的第一个属性名字不只是一个张量的惟一标识符,它一样也给出了这个张量是如何计算的,TensorFlow的计算均可以经过计算图的模型来创建,而计算图上的每个节点表明一个计算,计算的结果就保存在张量之中。因此张量和计算图上节点所表明的计算结果是对应的。因此张量的命名就能够经过“node : src_output”的形式来给出。其中node为节点的名称,src_output 表示当前张量来自节点的第几个输出。好比上面的“add:0” 就说明了result这个张量是计算节点“add” 输出的第一个结果(编号从0 开始)。算法

  张量的第二个属性是张量的维度。这个属性描述了一个张量的维度信息,好比上面样例中 shape = (2, ) 说明了张量 result 是一个一维数组,这个数组的长度为2。维度是张量一个很重要的属性,围绕张量的维度TensorFlow也给出了不少有用的运算。编程

  张量的第三个属性就是类型(type),每个张量会有一个惟一的类型。TensorFlow 会对参与运算的全部张量进行类型的检查,当发现类型不匹配的时候会报错,好比下面的代码就会获得类型不匹配的错误:数组

?
1
2
3
4
5
6
7
8
#_*_coding:utf-8_*_
import tensorflow as tf
 
# tf.constant 是一个计算,这个计算的结果为一个张量,保存在变量a中
a = tf.constant([1, 2], name= 'a' )
b = tf.constant([2.0, 3.0], name= 'b' )
 
result = a + b

  这段代码和上面例子基本如出一辙,惟一不一样就是把其中一个加数的小数点去掉了。这会使得加数 a 的类型为整数而加数 b 的类型为实数,这样程序就会报类型不匹配的错误:浏览器

?
1
2
ValueError: Tensor conversion requested dtype int32 for Tensor with
dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)'

  若是将第一个加数指定成实数类型 ,以下:网络

?
1
a = tf.constant([1, 2], name= 'a' , dtype=tf.float32)

  那么两个加数的类型相同,就不会报错了。若是不指定类型,则会默认为 int32,而带小数的则会默认为float32.因此通常建议经过指定dtype来明确指出变量或者常量的类型。session

tensorflow中的数据类型列表

Tensorflow学习笔记——常见概念的整理_tensorflow

张量的使用

  和TensorFlow的计算模型相比,TensorFlow的数据模型相比较简单。张量使用主要能够总结为两大类。

  第一类用途是对中间计算结果的引用。当一个计算包含不少中间结果时,使用张量能够大大提升代码的可读性。好比上面的例子。

  第二类是当计算图构造完成以后,张量能够用来得到计算结果,也就是获得真实的数字,虽然张量自己没有存储具体的数字,可是经过下面的Session就能够获得具体的数字。

  

会话(TensorFlow运行模型——Session)

  下面学习如何使用会话(session)来执行定义好的运算,会话拥有并管理TensorFlow程序运行时的全部资源。当全部计算完成以后须要关闭会话来帮助系统回收资源,不然就可能出现资源泄露的问题。TensorFlow中使用会话的模式通常有两种,第一种模式须要明确调用会话生成函数和关闭会话函数,这么模式以下:

?
1
2
3
4
5
6
7
# 建立一个会话
sess  =  tf.Session()
# 使用这个建立好的会话来获得关心的运算结果
# 好比能够调用 sess.run(result) 来获得张量计算的结果
sess.run(...)
# 关闭会话使得本次运算中使用到的资源能够被释放
sess.close()

  使用这种模式的时候,在全部计算完成以后,须要明确调用Session.close 函数来关闭会话并释放资源。然而,当程序由于异常而退出时,关闭会话的函数可能就不会被执行而致使资源泄露。为了解决异常退出时资源释放的问题,TensorFlow能够经过Python的上下文管理器来使用会话,也就是能够利用 with 代码块生成Session,限制做用域,代码以下:

?
1
2
3
4
5
6
# 建立一个会话,并经过python中的上下文管理器来管理这个会话
with  tf.Session()  as sess:
     # 使用这建立好的会话来计算关心的结果
  sess.run(...)
# 不须要再调用“Session.close()” 函数来关闭会话
# 当上下文退出时会话关闭和资源释放也自动完成了。

  经过Python上下文管理器的机制,只要将全部的计算放在'with' 的内部就能够。当上下文管理器退出时候会自动释放全部资源。这样即解决了由于异常退出时资源释放的问题,同时也解决了忘记调用Session.close 函数而产生的资源泄露问题。

  Session 函数中没有传入参数,代表该代码将会依附于(若是尚未建立会话,则会建立新的会话)默认的本地会话。生成会话以后,全部的 tf.Variable 实例都会经过调用各自初始化操做的 sess.run() 函数进行初始化。

?
1
2
init = tf.initialize_all_variables()
sess.run(init)

  在经过initializer给变量赋值当然可行,可是当变量的数据增多后,或者变量之间存在依赖关系时,单个调用的方案就比较麻烦了。因此使用上述代码更加便捷。

  sess.run() 方法将会运行图表中与做为参数传入的操做相对应的完整子集。在初始调用时, init操做只包含了变量初始化程序 tf.group。图标的其余部分不会再这里,而是在下面的训练训练运行。

  在交互式环境中(好比Python脚本或者Jupyter的编译器下),经过设置默认会话的方式来得到张量的取值更加方便。全部TensorFlow提供了一种在交互式环境下直接构建默认会话的函数,这和函数就是tf.InteractiveSession.使用这个函数会自动将生成的会话注册为默认会话。下面代码展现了tf.InteractiveSession 函数的用法:

?
1
2
3
4
sess = tf.InteractiveSession()
print(result.eval())
# 其实 sess.run(result) 和 result.eval(session=sess)) 功能相同
sess.close()

  经过tf.InteractiveSession 函数能够省去将产生的会话注册为默认会话的过程。

  变量在被使用前,须要经过会话(session)运行其初始化方法完成初始化赋值。

?
1
sess.run(tf.global_variables_initializer)

  注意:在新版本的tensorflow中,使用下面代码替换上面代码,否则会报 Warning。

?
1
sess.run(tf.global_variables_initializer)

 

神经网络参数与TensorFlow变量tf.Variable()

  神经网络中的参数是神经网络实现分类或者回归问题中重要的部分。在TensorFlow中变量(tf.Variable()的做用就是保存和更新神经网络中的参数),下面学习一下变量的定义:

?
1
2
3
4
5
6
7
8
9
10
11
def __init__(self,
                initial_value=None,
                trainable=True,
                collections=None,
                validate_shape=True,
                caching_device=None,
                name=None,
                variable_def=None,
                dtype=None,
                expected_shape=None,
                import_scope=None):
  • initial_value:初始化的值,能够是随机数,常数或者经过其余变量的初始值获得的。
  • trainable:标记是否加入GraphKeys.TRAINABLE_VARIABLES集合
  • validate_shape:若是为False则能够更改shape
  • dtype:变量的类型,不可改变

  下面代码给出了一种在TensorFlow中声明一个2*3的矩阵变量的方法:

?
1
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))

  首先它调用了TensorFlow变量的声明函数tf.Variable。在变量声明函数中给出了初始化这个变量的方法。TensorFlow中变量的初始值能够设置成随机数,常数或者是同其余变量的初始值计算获得。在上面的样例中,tf.reandom_normal([2, 3], stddev=2)会产生一个2*3的矩阵,矩阵的元素均值为0,标准差为2 的随机数。tf.random_normal函数能够经过参数mean来指定平均值,在没有指定时默认为0,经过知足正态分布的随机数来初始化神经网络中的参数是一个很是有用的方法,除了正态分布的随机数,TensorFlow还提供了一些其余的随机数生成器,下图列出了TensorFlow目前支持的全部随机数生成器

Tensorflow学习笔记——常见概念的整理_取值_02

  TensorFlow也支持经过常数来初始化一个变量,下图给出了TensorFlow中经常使用的常量声明方法:

Tensorflow学习笔记——常见概念的整理_取值_03

  在神经网络中,偏置项(bias)一般会使用常数来设置初始值,下面代码给出了一个例子:

?
1
2
# 下面会产生一个初始值为0且长度为3 的变量
biases = tf.Variable(tf.zeros([3]))

  固然,TensorFlow也支持经过其余变量的初始值来初始化新的变量,下面给出了具体的方法:

?
1
2
3
# 声明w1 w2两个变量
w1 = tf.Variable(weights.initialized_value())
w2 = tf.Variable(weights.initialized_value() * 2.0)

  以上代码中,w1的初始值被设置成了与weights变量相同,w2的初始值则是weights初始值的两倍。在TensorFlow中,一个变量的初始化过程须要被明确的调用。

   相似于张量,维度(shape)和类型(type)也是变量最重用的两个属性,和大部分程序语言相似,变量的类型是不可改变的。一个变量在被构建以后,它的类型就不能再改变量。

  以下代码会报出类型不匹配的错误:

?
1
2
3
4
5
6
7
8
9
10
11
# 声明w1 w2两个变量
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w1' )
w2 = tf.Variable(tf.random_normal([2, 3], dtype=tf.float64, stddev=1), name= 'w2' )
 
w1.assign(w2)
'' '
程序会报错:
TypeError: Input 'value' of 'Assign' Op has type float64 that does not
match type float32 of argument 'ref' .
'' '

  维度是另一个重要的属性,和类型不大同样的是,维度在程序运行中是有可能改变的,可是须要设置参数,以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
# 声明w1 w2两个变量
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w1' )
w2 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w2' )
 
w1.assign(w1, w2)
'' '
程序会报错(维度不匹配的错误):
TypeError: Expected bool for argument 'use_locking' not
<tf.Variable 'w2:0' shape=(2, 3) dtype=float32_ref>.
'' '
# 下面代码能够被成功执行
tf.assign(w1, w2, validate_shape=False)

  虽然TensorFlow支持更改变量的维度,可是这种作法比较罕见。

经过TensorFlow游乐场了解神经网络

   首先咱们经过TensorFlow游乐场来快速了解神经网络的主要功能。TensorFlow游乐场是一个经过网页浏览器就能够训练的简单神经网络并实现了可视化训练过程的工具。

  TensorFlow游乐场的地址:http://playground.tensorflow.org/

Tensorflow学习笔记——常见概念的整理_神经网络_04

  从上图中能够看出,TensorFlow的左侧提供四个不一样的数据集来测试神经网络。默认的数据为左上角被框出来的那个。被选中的数据也会显示在上面最右边的“OUTPUT”栏目下。在这个数据中,能够看到一个二维平面上有蓝色或者橙色的点,每个小点都表明了一个样例,而点的颜色表明了样例的标签。由于点的颜色只有两种,全部这是一个二分类问题。在这里举这么一个例子来讲明这个数据能够表明的实际问题。假设须要判断某工厂生产的零件是否合格,那么蓝色的点能够表示全部合格的零件,而橙色表明不合格的零件。这样判断一个零件是否合格就变成了区分点的颜色。

  为了将一个实际问题对应到屏幕上不一样颜色点的划分,还须要将实际问题中的实体,好比上述例子中的零件,变成屏幕上的一个点。这就是特征提取解决的问题。仍是以零件为例,能够用零件的长度和质量来大体描述一个零件。这样一个物理意义上的零件就能够被转化成长度和质量这两个数字。在机器学习中,全部用于描述实体的数字的组合就是一个实体的特征向量(feature vector)。而特征向量的提取对机器学习的效果相当重要,经过特征提取就能够将实际问题中的实体转化为空间中的点。假设使用长度和质量做为一个零件的特征向量,那么每一个零件就是二维平面上的一个点。TensorFlow游乐园中Features一栏对应了特征向量。

  特征向量是神经网络的输入,神经网络的主体结构显示了在上图的中间位置。目前主流的神经网络都是分层的结构,第一层是输入层,表明特征向量中每个特征的取值。好比若是一个零件的长度是0.5,那么x1的值就是0.5。同一层的节点不会相互链接,并且每一层只和下一层链接,直到最后一层做为输出层获得计算的结果。在二分类问题中,好比判断零件是否合格,神经网络的输出层每每只包含一个节点。在二分类问题中,好比判断零件是否合格,神经网络的输出层每每只包含一个节点,而这个节点会输出一个实数值。经过这个输出值和一个事先设定的阈值,就能够判断结果是零件合格,反之则零件不合格,通常能够认为当输出值离阈值越远获得的答案越可靠。

   在输入和输出层之间的神经网络叫作隐藏层,通常一个神经网络的隐藏层越多,这个神经网络就越“深”。而所谓深度学习中的这个“深度”和神经网络的层数也是密切相关的。在TensorFlow游乐场中能够经过点击加或者减来增长或者减小神经网络隐藏层的数量。处理能够选择深入网络的深度,TensorFlow游乐场也支持选择神经网络每一层的节点数以及学习率(learning rate),激活函数(activation),正则化(regularization)。

  因此经过神经网络解决分类问题主要能够分为如下四个步骤:

  1,提取问题中实体的特征向量做为神经网络的输入。不一样的实体能够提取不一样的特征向量。

  2,定义神经网络的结构,并定义如何从神经网络的输入获得输出。这个过程能够是神经网络的前向传播算法

  3,经过训练数据来调整神经网络中参数的取值,这就是训练神经网络的过程。

  4,使用训练好的神经网络来预测未知的数据。

前向传播算法

  下面学习一下最简单的全链接网络结构的前向传播算法,而且将展现如何经过TensorFlow来实现这个算法。

  下面首先了解神经元的结构,神经元是一个神经网络的最小单位,下面显示一个最简单的神经元结构:

Tensorflow学习笔记——常见概念的整理_数据_05

  从上图能够看出,一个神经元有多个输入和一个输出。每一个神经元的输入既能够是其余神经元的输出,也能够是整个神经网络的输入。所谓神经网络的结构就是指的不一样神经元之间的链接结构。一个最简单的神经元结构的输出就是全部输入的加权和,而不一样输入的权重就是神经元的参数。神经网络的优化过程就是优化神经元中的参数取值的过程。

   下图给出了一个简单的判断零件是否合格的三层全链接神经网络,之因此称为全链接神经网络是由于相邻两层之间任意两个节点之间都有链接。

  下图展现一个判断零件是否合格的三层神经网络结构图:

Tensorflow学习笔记——常见概念的整理_tensorflow_06

  计算神经网络的前向传播结构须要三部分信息。第一个部分是神经网络的输入,这个输入就是从实体中提取的特征向量。好比上面有两个输入,一个是零件的长度x1,一个是零件的质量 x2,第二个部分为神经网络的链接结构。神经网络是由神经元构成的,神经网络的结构给出不一样神经元之间输入输出的链接关系。神经网络中的神经元也能够称为节点。在上图中 a11节点有两个输入 ,分别是x1 和 x2的输出。而 a11 的输出则是节点 y 的输入。最后一个部分是每一个神经元中的采纳数。咱们用W来表示神经元中的参数。W的上标表名了神经网络的层数,好比W(1) 表示第一层节点的参数,而W(2) 表示第二层节点的参数。W的下标代表了链接节点编号,好比W(1) 1,2 表示链接 x1 和 a12节点的边上的权重。这里咱们假设权重是已知的。

  当咱们给定神经网络的输入,神经网络的结构以及边上权重,就能够经过前向传播算法来计算出神经网络的输出,下图展现了这个神经网络前向传播的过程:

Tensorflow学习笔记——常见概念的整理_tensorflow_07

  上图给出来输入层的取值,从输入层开始一层一层地使用前向传播算法,首先隐藏层中有三个节点,每个节点的取值都是输入层取值的加权和。当求出输出值的阈值,判断是否大于0,这样就能够判断是否合格。上面整个过程就是前向传播的算法。

  固然前向传播的算法能够表示为矩阵乘法,将输入 x1  x2 组织成一个1*2 的矩阵x = [x1, x2],而W(1) 组织成一个2*3 的矩阵:

Tensorflow学习笔记——常见概念的整理_取值_08

  这样经过矩阵乘法能够获得隐藏层三个节点所组成的向量取值:

Tensorflow学习笔记——常见概念的整理_初始化_09

  相似的输出层能够表示为:

Tensorflow学习笔记——常见概念的整理_取值_10

  这样就能够将前向传播算法经过矩阵乘法的方式表达出来了。在TensorFlow中矩阵政法是很是容易实现的。如下代码实现了神经网络的前向传播过程:

?
1
2
3
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

  其中 tf.matmul 实现了矩阵乘法的功能。

  如下样例介绍了如何经过遍历实现神经网络的参数并实现前向传播的过程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#_*_coding:utf-8_*_
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 暂时将输出的特征向量定义为一个常量,注意这里x是一个1*2的矩阵
x = tf.constant([[0.7, 0.9]])
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
sess.run(w1.initializer)
sess.run(w2.initializer)
print(sess.run(y))   # 输出[[3.957578]]
sess.close()

  从代码中能够看出,当声明了变量w1 w2以后,能够经过w1  w2来定义神经网络的前向传播过程并获得中间结果 a 和最后答案 y 。可是这些被定义的计算在这一步中并非真正的运算,当须要运行这些计算并获得具体的数字的时候,须要进入TensorFlow程序第二步。

  在第二步,咱们会声明一个会话(session),而后经过会话计算结果。

 经过TensorFlow训练神经网络模型

  使用监督学习的方式设置神经网络参数须要有一个标注好的训练数据集。以判断零件是否合格为例,这个标注好的训练数据集就是手机的一批合格零件和一批不合格零件。监督学习最重要的思想就是在已知答案的标注数据集上,模型给出的预测结果要尽可能接近真实的答案。经过调整神经网络中的参数对训练数据进行拟合,可使得模型对未知的样本提供预测的能力。

  在神经网络优化算法中,最经常使用的方法是反向传播算法(backpropagation),下图展现了使用反向传播算法训练神经网络的流程图:

Tensorflow学习笔记——常见概念的整理_初始化_11

  从上图能够看出,反向传播算法实现了一个迭代的过程。在每次迭代的开始,首先须要选取一小部分训练数据,这一小部分数据叫作一个batch。而后这个batch的样例会经过前向传播算法获得神经网络模型的额预测结果。由于训练数据都是由正确答案标注的,因此能够计算出当前神经网络模型的预测答案与正确答案之间的差距。最后,基于这预测值和真实值之间的差距,反向传播算法会相应的更新神经参数的取值,使得在这个batch上神经网络模型的预测结果和真实答案更加接近。

   经过TensorFlow实现反向传播算法的第一步是使用TensorFlow表达一个batch的数据,在以前咱们使用常量来表达,可是若是每轮迭代中选取的数据都要经过常量来表示,那么TensorFlow都会在计算图中增长一个节点。通常来讲,一个神经网络的训练过程会须要通过几百万轮甚至几亿轮的迭代,这样计算图就会很是大,并且利用率很低。为了不这个问题,TensorFlow提供了placeholder机制用于提供输入数据。placeholder至关于定义了一个位置,这个位置中的数据在程序运行时再指定。这样在程序中就不须要生成大量常量来提供输入数据,而只须要将数据经过placeholder传入TensorFlow计算图。在placeholder定义时,这个位置上的数据类型是须要指定的。和其余张量同样,placeholder的类型也是不能够改变的。placeholder中数据的维度信息是能够根据提供的数据推导出来,因此不必定给出。

  下面给出了经过placeholder实现前向传播算法:

?
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
29
30
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 定义placeholder做为存放输入数据的地方,这里维度也不必定要定义
# 可是若是维度是肯定的,那么给出维度能够下降出错的几率
x = tf.placeholder(tf.float32, shape=(1, 2), name= 'input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
'' '
下面一行将报错:
InvalidArgumentError (see above for traceback): You must feed a value
  for placeholder tensor 'input' with dtype float and shape [1,2]
'' '
# print(sess.run(y))
 
# 下面一行将会获得以前同样的输出结果
print(sess.run(y, feed_dict={x: [[0.7, 0.9]]}))   # [[3.957578]]
sess.close()

  在这段程序中替换了原来经过常量定义的输入 x ,在新的程序中计算前向传播结果时,须要提供一个feed_dict 来指定 x 的取值。 feed_dict 是一个字典(map),在字典中须要给出每一个用到的placeholder的取值,若是某个须要的placeholder没有被指定取值,那么在程序运行时候会报错。

  在上面的样例程序中,若是将输入的1*2 矩阵改成 n*2 的矩阵,那么就能够获得 n 个样例的前向传播结果了。其中 n*2 的矩阵的每一行为一个样例数据。这样前向传播的结果为 n*1 的矩阵,这个矩阵的每一行就表明了一个样例的前向传播结果,下面程序给出一个实例:

?
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
29
30
31
32
33
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 定义placeholder做为存放输入数据的地方,这里维度也不必定要定义
# 可是若是维度是肯定的,那么给出维度能够下降出错的几率
# x = tf.placeholder(tf.float32, shape=(1, 2), name='input')
x =tf.placeholder(tf.float32, shape=(3, 2), name= 'input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
 
 
# 由于x 在定义时指定了 n 为3,因此在运行前向传播过程时须要提供三个样例数据
 
print(sess.run(y, feed_dict={x: [[0.7, 0.9], [0.1, 0.4], [0.5, 0.8]]}))
'' '
输出结果为:
[[3.957578 ]
  [1.1537654]
  [3.1674924]]
'' '
sess.close()

  上面的样例中展现了一次性计算多个样例的前向传播结果。在运行时,须要将3个样例组成一个3*2的矩阵传入placeholder。计算获得的结果为3*1 的矩阵。

  在获得一个batch的前向传播结果以后,须要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。而后经过反向传播算法来调整神经网络参数的取值使得差距能够被缩小。下面定义一个简单的额损失函数,并经过TensorFlow定义反向传播的算法。

?
1
2
3
4
5
6
7
8
# 定义损失函数来刻画预测值与真实值的差距
cross_entropy = -tf.reduce_mean(
     y_ * tf.log(tf.clip_by_value(y, le-10, 1.0))
)
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的采纳数
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)

  在上面代码中,cross_entropy 定义了真实值和预测值之间的交叉熵(cross entropy),这是分类问题中一个经常使用的损失函数,第二行 train_step 定义了反向传播的优化方法。目前TensorFlow支持7种不一样的优化器,比较经常使用的优化方法有三种:

?
1
2
3
tf.train.GradientDescentOptimizer
tf.train.AdamOptimizer
tf.train.MomentumOptimizer

  

完整神经网络样例程序

  代码以下:

?
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# _*_coding:utf-8_*_
import tensorflow as tf
 
# Numpy 是一个科学计算的工具包,这里经过Numpy工具包生成模拟数据集
from numpy.random import RandomState
 
# 定义训练数据batch的大小
batch_size = 8
 
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 在shape的一个维度上使用None能够方便的表示使用不大的batch大小,
# 在训练时须要把数据分红比较小的batch,在测试的时候,能够一次性的使用所有的数据
# 可是数据集比较大的是,将大量数据放入一个batch可能会致使内存溢出。
x = tf.placeholder(tf.float32, shape=(None, 2), name= 'x-input' )
y_ = tf.placeholder(tf.float32, shape=(None, 1), name= 'y-input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
# 定义损失函数来刻画预测值与真实值的差距
cross_entropy = -tf.reduce_mean(
     y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
)
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的采纳数
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
 
# 经过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
 
# 定义规则来给出样本的标签,在这里全部x1+x2<1 的样例都被认为是正样本(好比零件合格)
# 而其余为负样本(好比零件不合格)和TensorFlow游乐场中的表示法不大同样的地方式
#  这里使用0表示负样本,1来表示正样本,大部分解决分类问题的神经网络都会采用0和1的表示方法
Y = [[ int (x1 + x2 < 1)] for (x1, x2) in X]
 
# 建立一个会话来运行TensorFlow
with tf.Session() as sess:
     init_op = tf.global_variables_initializer()
     # 初始化变量
     sess.run(init_op)
     print(sess.run(w1))
     print(sess.run(w2))
     '' '
     在训练以前神经网络参数的值
     w1   =  [[-0.8113182   1.4845988   0.06532937]
      [-2.4427042   0.0992484   0.5912243 ]]
      
     w2 = [[-0.8113182 ]
      [ 1.4845988 ]
      [ 0.06532937]]
     '' '
     # 设定训练的轮数
     STEPS = 5000
     for i in range(STEPS):
         # 每次选取batch_size 个样本进行训练
         start = (i * batch_size) % dataset_size
         end = min(start + batch_size, dataset_size)
 
         # 经过选取的样本训练神经网络并更新参数
         sess.run(train_step,
                  feed_dict={x: X[start:end], y_: Y[start: end]})
         if i % 1000 == 0:
             # 每隔一段时间计算在全部数据上的交叉熵并输出
             total_cross_entropy = sess.run(cross_entropy,
                                            feed_dict={x: X, y_: Y})
             print( "After %d training step(s), cross_entropy on all data is %g" % (i, total_cross_entropy))
             '' '
             输出结果:
             After 0 training step(s), cross_entropy on all data is 0.0674925
             After 1000 training step(s), cross_entropy on all data is 0.0163385
             After 2000 training step(s), cross_entropy on all data is 0.00907547
             After 3000 training step(s), cross_entropy on all data is 0.00714436
             After 4000 training step(s), cross_entropy on all data is 0.00578471
 
             经过这个结果能够发现随着训练的进行,交叉熵是逐渐变小的
             交叉熵越小说明预测的结果和真实的结果差距越小
             '' '
     print(sess.run(w1))
     print(sess.run(w2))
     '' '
     在训练以后神经网络参数的值
     w1 = [[-1.9618275  2.582354   1.6820377]
      [-3.4681718  1.0698231  2.11789  ]]
     w2 = [[-1.824715 ]
      [ 2.6854665]
      [ 1.418195 ]]
      
      从和开始的神经网络参数值对比,咱们发现这两个参数的取值是已经发生变化
       这个变化就是训练的结果,它使得这个神经网络能更好的拟合提供的训练数据集
     '' '

  上面的程序实现了训练神经网络的所有过从,从这段程序中能够总结出训练神经网络的过程分为如下三个步骤:

  • 1,定义神经网络的结构和前向传播的输出结果
  • 2,定义损失函数以及选择反向传播优化的算法
  • 3,生成会话(tf.Session)而且在训练数据上反复进行反向传播优化算法

不管神经网络的结构如何变化,这三个步骤是不变的。

 

tf.Variable()  & tf.get_variable()

  tf.Variable() 和 tf.get_variable() 均可以用来建立变量,可是前者会自动保证惟一性,然后者不能保证惟一性。

  咱们能够对比两个函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 新建一个变量,变量值是 initial_value
Variable(initial_value=None, trainable=True,
          collections=None, validate_shape=True,
          caching_device=None,name=None,
          expected_shape=None, import_scope=None,
          constraint=None)
 
 
# 获取具备这些参数的现有变量或者建立一个新变量。(能够建立共享变量)
# 若是该name的变量还未定义,则新建立一个,若是依据定义了,则直接获取该变量
get_variable(name, shape=None, dtype=None,
              initializer=None, regularizer=None,
              trainable=True, collections=None,
              caching_device=None, partitioner=None,
              validate_shape=True, use_resouce=None,
              constraint=None)

  下面举个例子来讲明两者的不一样之处:

?
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
#_*_coding:utf-8_*_
'' '
下面例子来讲明 tf.Variable()  和 tf.get_variable() 的不一样之处
'' '
 
import tensorflow as tf
 
with tf.variable_scope( 'scope1' ):
     w1 = tf.Variable(1, name= 'w1' )
     w2 = tf.get_variable(name= 'w2' , initializer=2.)
 
with tf.variable_scope( 'scope1' , reuse=True):
     w1_p = tf.Variable(1, name= 'w1' )
     w2_p = tf.get_variable(name= 'w2' , initializer=3.)
 
print( 'w1' , w1)
print( 'w1_p' , w1_p)
# w1          <tf.Variable 'scope1/w1:0' shape=() dtype=int32_ref>
# w1_p        <tf.Variable 'scope1_1/w1:0' shape=() dtype=int32_ref>
 
print( 'w2' , w2)
print( 'w2_p' , w2_p)
# w2          <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>
# w2_p        <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>
 
print(w1 is w1_p, w2 is w2_p)
# False True

  咱们能够看出, tf.Variable()会自动处理冲突问题,如上面代码所示。而tf.get_variable()会判断是否已经存在该name的变量,若是有,且该变量空间的reuse=True,那么就能够直接共享以前的值,若是没有,则从新建立。(注意:若是没有将reuse设置为True,则会提示冲突发生)。错误以下:

?
1
2
ValueError: Variable scope1/w2 already exists, disallowed. Did you
mean to set reuse=True in VarScope? Originally defined at:

  由于代码的最后一句语句是是判断上述变量是否相等,能够看出,经过get_variable()定义的变量是彻底等价的,即便后一句 get_variable 是将 initializer 设为3,可是因为 name='w2' 的变量已经存在,而且 reuse=True,则直接引用以前定义的,这样就能够用 get_variable() 来定义共享变量。

  在生成上下文管理器时,若设置reuse=True,tf.variable_scope将只能获取已经建立过的变量,若是空间中没有变量则会报错。若是reuse=False 或者 reuse=None,tf.get_variable将建立新的变量。并且同名变量已经存在,会报错。

tf.get_variable  & tf.variable_scope

  tf.get_variable 函数能够用来建立或者获取变量,当建立变量时,与 tf.Variable是同样的。

  tf.variable_scope 函数生成一个上下文管理器,用于控制 tf.get_variable。

  这里,咱们会发现, tf.get_variable() 在使用时,通常会和 tf.varibale_scope() 配套使用,须要指定它的做用域空间,这样在引用的使用的使用就能够经过设置指定的scope的 reuse=True进行引用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#_*_coding:utf-8_*_
'' '
变量生成之  tf.get_variable 与 tf.variable_scope   reuse参数
'' '
import tensorflow as tf
 
with tf.variable_scope( 'a' ):
     v1 = tf.get_variable( "v" , [1], initializer=tf.constant_initializer(1.0))
 
# with tf.variable_scope("a"):
     # 报错 ValueError: Variable a/v already exists,
#     v2 = tf.get_variable("v", [1])  
 
with tf.variable_scope( "a" , reuse=True):
     v3 = tf.get_variable( "v" , [1])
     print(v3 == v1)  # True
 
with tf.variable_scope( "b" , reuse=True):
     # 报错 ValueError: Variable b/v does not exist, or was not created with tf.get_variable().
     v4 = tf.get_variable( "v" ,[1]) 

  最后咱们看一下 tf.variable_scope()  和 tf.name_scope()。 其中name_scope() 是给 op_name加前缀,指定op的做用域空间 ,op是指操做。而variable_scope() 是给get_variable() 建立的变量的名字加前缀,代表做用域空间,也能够用于处理命名冲突。

 

tf.cast() 数据类型转换

  tf.cast() 函数的做用是执行 tensorflow中张量数据类型转换,好比读入的图片若是是 int8 类型的,通常在训练前把图像的数据格式转换为float32。

  cast()定义:

?
1
cast(x, dtype, name=None)

  第一个参数 x:待转换的数据(张量)

  第二个参数dtype:目标数据类型

  第三个参数name:可选参数,定义操做的名称

int32转换为float32 代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#_*_coding:utf-8_*_
import tensorflow as tf
 
t1 = tf.Variable([1, 2, 3, 4, 5])
t2 = tf.cast(t1, dtype=tf.float32)
 
print( 't1: {}' .format(t1))
print( 't2:{}' .format(t2))
 
with tf.Session() as sess:
     sess.run(tf.global_variables_initializer())
     sess.run(t2)
     print(t2.eval())
     print(sess.run(t2))

  输出以下:

?
1
2
3
4
t1: <tf.Variable 'Variable:0' shape=(5,) dtype=int32_ref>
t2:Tensor( "Cast:0" , shape=(5,), dtype=float32)
[1. 2. 3. 4. 5.]
[1. 2. 3. 4. 5.]

 

 batch

  深度学习的优化算法,说白了就是梯度降低。每次的参数更新有两种方式。

1,遍历所有数据集算一次损失函数,而后算函数对各个参数的梯度,更新梯度。这种方法每更新一次参数都要把数据集里的全部样本都看一遍,计算量开销大,计算速度慢,不支持在线学习,这种称谓Batch gradient descent,批梯度降低。

2,每看一个数据就算一下损失函数,而后求梯度更新参数,这个称为随机梯度降低,stochastic gradient descent。这个方法速度比较快,可是收敛性能不太好,可能在最优势附近晃来晃去,hit不到最优势。两次参数的更新也有可能互相抵消掉,形成目标函数震荡的比较剧烈。

  为了克服两种方法的缺点,如今通常采用的是一种折中手段,mini-batch gradient decent ,小批的梯度降低,这种方法把数据分红若干个批,按批来更新参数,这样一个批中的一组数据共同决定了本次梯度的方向,降低起来就不容易跑偏,减小了随机性,另外一方面由于批的样本数与整个数据集相比小了不少,计算量也不是很大。

  基本上如今的梯度降低都是基于mini-batch的,因此深度学习框架的函数中常常会出现batch_size,就指的是这个。

 tf.argmax的使用

   tf.argmax(vector, 1):返回的是vector中的最大值的索引号,若是vector是一个向量,那就返回一个值,若是是一个矩阵,那就返回一个向量,这个向量的每个维度都是相对应矩阵行的最大值元素的索引号。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import tensorflow as tf
import numpy as np
  
A = [[1,3,4,5,6]]
B = [[1,3,4], [2,4,1]]
  
with tf.Session() as sess:
     print(sess.run(tf.argmax(A, 1)))
     print(sess.run(tf.argmax(B, 1)))
 
---------------------
 
输出:
[4]
[2 1]

  

 

参考文献:

javascript:void(0)

  此文是本身的学习笔记总结,学习于《TensorFlow深度学习框架》,俗话说,好记性不如烂笔头,写写老是好的,因此若侵权,请联系我,谢谢。

 

出处:javascript:void(0)

  TensorFlow的名字中已经说明了它最重要的两个概念——Tensor和Flow。Tensor就是张量,张量这个概念在数学或者物理学中能够有不一样的解释,可是这里咱们不强调它自己的含义。在TensorFlow中,张量能够被简单地理解为多维数组,Flow翻译成中文就是“流”,它直观的表述计算的编程系统。TensorFlow中的每个计算都是计算图上的一个节点,而节点之间的边描述了计算之间的依赖关系。

TensorFlow计算模型——计算图的概念

  计算图是TensorFlow中最基本的一个概念,TensorFlow中全部计算都会被转化为计算图上的一个节点。

  在TensorFlow程序中,全部的数据都经过张量的形式来表示。从功能的角度上看,张量能够被简单理解为多为数组。其中零阶张量表示标量(scalar),也便是一个数(张量的类型也能够是字符串)。第一阶张量为向量(vector),也就是一个一维数组;第 n 阶张量能够理解为一个 n 维数组。可是张量在TensorFlow中的实现并非直接采用数组的形式,它只是对TensorFlow中运算结果的引用。在张量中并无真正保存数字,它保存的是如何获得这些数字的计算过程。以向量加法为例,当运行以下代码的时候,获得的不是加法的结果,而是对结果的一个引用。

?
1
2
3
4
5
6
7
8
9
#_*_coding:utf-8_*_
import tensorflow as tf
 
# tf.constant 是一个计算,这个计算的结果为一个张量,保存在变量a中
a = tf.constant([1.0, 2.0], name= 'a' )
b = tf.constant([2.0, 3.0], name= 'b' )
 
result = a + b
# print(result)   # Tensor("add:0", shape=(2,), dtype=float32)

  从上面的结果来看,TensorFlow的张量和Numpy的数组不一样,他计算的结果不是一个具体的数字,而是一个张量的结构。从上面结果来看,一个张量主要保存了三个属性,名字(name),维度(shape)和类型(type)。

  张量的第一个属性名字不只是一个张量的惟一标识符,它一样也给出了这个张量是如何计算的,TensorFlow的计算均可以经过计算图的模型来创建,而计算图上的每个节点表明一个计算,计算的结果就保存在张量之中。因此张量和计算图上节点所表明的计算结果是对应的。因此张量的命名就能够经过“node : src_output”的形式来给出。其中node为节点的名称,src_output 表示当前张量来自节点的第几个输出。好比上面的“add:0” 就说明了result这个张量是计算节点“add” 输出的第一个结果(编号从0 开始)。

  张量的第二个属性是张量的维度。这个属性描述了一个张量的维度信息,好比上面样例中 shape = (2, ) 说明了张量 result 是一个一维数组,这个数组的长度为2。维度是张量一个很重要的属性,围绕张量的维度TensorFlow也给出了不少有用的运算。

  张量的第三个属性就是类型(type),每个张量会有一个惟一的类型。TensorFlow 会对参与运算的全部张量进行类型的检查,当发现类型不匹配的时候会报错,好比下面的代码就会获得类型不匹配的错误:

?
1
2
3
4
5
6
7
8
#_*_coding:utf-8_*_
import tensorflow as tf
 
# tf.constant 是一个计算,这个计算的结果为一个张量,保存在变量a中
a = tf.constant([1, 2], name= 'a' )
b = tf.constant([2.0, 3.0], name= 'b' )
 
result = a + b

  这段代码和上面例子基本如出一辙,惟一不一样就是把其中一个加数的小数点去掉了。这会使得加数 a 的类型为整数而加数 b 的类型为实数,这样程序就会报类型不匹配的错误:

?
1
2
ValueError: Tensor conversion requested dtype int32 for Tensor with
dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)'

  若是将第一个加数指定成实数类型 ,以下:

?
1
a = tf.constant([1, 2], name= 'a' , dtype=tf.float32)

  那么两个加数的类型相同,就不会报错了。若是不指定类型,则会默认为 int32,而带小数的则会默认为float32.因此通常建议经过指定dtype来明确指出变量或者常量的类型。

tensorflow中的数据类型列表

Tensorflow学习笔记——常见概念的整理_tensorflow

张量的使用

  和TensorFlow的计算模型相比,TensorFlow的数据模型相比较简单。张量使用主要能够总结为两大类。

  第一类用途是对中间计算结果的引用。当一个计算包含不少中间结果时,使用张量能够大大提升代码的可读性。好比上面的例子。

  第二类是当计算图构造完成以后,张量能够用来得到计算结果,也就是获得真实的数字,虽然张量自己没有存储具体的数字,可是经过下面的Session就能够获得具体的数字。

  

会话(TensorFlow运行模型——Session)

  下面学习如何使用会话(session)来执行定义好的运算,会话拥有并管理TensorFlow程序运行时的全部资源。当全部计算完成以后须要关闭会话来帮助系统回收资源,不然就可能出现资源泄露的问题。TensorFlow中使用会话的模式通常有两种,第一种模式须要明确调用会话生成函数和关闭会话函数,这么模式以下:

?
1
2
3
4
5
6
7
# 建立一个会话
sess  =  tf.Session()
# 使用这个建立好的会话来获得关心的运算结果
# 好比能够调用 sess.run(result) 来获得张量计算的结果
sess.run(...)
# 关闭会话使得本次运算中使用到的资源能够被释放
sess.close()

  使用这种模式的时候,在全部计算完成以后,须要明确调用Session.close 函数来关闭会话并释放资源。然而,当程序由于异常而退出时,关闭会话的函数可能就不会被执行而致使资源泄露。为了解决异常退出时资源释放的问题,TensorFlow能够经过Python的上下文管理器来使用会话,也就是能够利用 with 代码块生成Session,限制做用域,代码以下:

?
1
2
3
4
5
6
# 建立一个会话,并经过python中的上下文管理器来管理这个会话
with  tf.Session()  as sess:
     # 使用这建立好的会话来计算关心的结果
  sess.run(...)
# 不须要再调用“Session.close()” 函数来关闭会话
# 当上下文退出时会话关闭和资源释放也自动完成了。

  经过Python上下文管理器的机制,只要将全部的计算放在'with' 的内部就能够。当上下文管理器退出时候会自动释放全部资源。这样即解决了由于异常退出时资源释放的问题,同时也解决了忘记调用Session.close 函数而产生的资源泄露问题。

  Session 函数中没有传入参数,代表该代码将会依附于(若是尚未建立会话,则会建立新的会话)默认的本地会话。生成会话以后,全部的 tf.Variable 实例都会经过调用各自初始化操做的 sess.run() 函数进行初始化。

?
1
2
init = tf.initialize_all_variables()
sess.run(init)

  在经过initializer给变量赋值当然可行,可是当变量的数据增多后,或者变量之间存在依赖关系时,单个调用的方案就比较麻烦了。因此使用上述代码更加便捷。

  sess.run() 方法将会运行图表中与做为参数传入的操做相对应的完整子集。在初始调用时, init操做只包含了变量初始化程序 tf.group。图标的其余部分不会再这里,而是在下面的训练训练运行。

  在交互式环境中(好比Python脚本或者Jupyter的编译器下),经过设置默认会话的方式来得到张量的取值更加方便。全部TensorFlow提供了一种在交互式环境下直接构建默认会话的函数,这和函数就是tf.InteractiveSession.使用这个函数会自动将生成的会话注册为默认会话。下面代码展现了tf.InteractiveSession 函数的用法:

?
1
2
3
4
sess = tf.InteractiveSession()
print(result.eval())
# 其实 sess.run(result) 和 result.eval(session=sess)) 功能相同
sess.close()

  经过tf.InteractiveSession 函数能够省去将产生的会话注册为默认会话的过程。

  变量在被使用前,须要经过会话(session)运行其初始化方法完成初始化赋值。

?
1
sess.run(tf.global_variables_initializer)

  注意:在新版本的tensorflow中,使用下面代码替换上面代码,否则会报 Warning。

?
1
sess.run(tf.global_variables_initializer)

 

神经网络参数与TensorFlow变量tf.Variable()

  神经网络中的参数是神经网络实现分类或者回归问题中重要的部分。在TensorFlow中变量(tf.Variable()的做用就是保存和更新神经网络中的参数),下面学习一下变量的定义:

?
1
2
3
4
5
6
7
8
9
10
11
def __init__(self,
                initial_value=None,
                trainable=True,
                collections=None,
                validate_shape=True,
                caching_device=None,
                name=None,
                variable_def=None,
                dtype=None,
                expected_shape=None,
                import_scope=None):
  • initial_value:初始化的值,能够是随机数,常数或者经过其余变量的初始值获得的。
  • trainable:标记是否加入GraphKeys.TRAINABLE_VARIABLES集合
  • validate_shape:若是为False则能够更改shape
  • dtype:变量的类型,不可改变

  下面代码给出了一种在TensorFlow中声明一个2*3的矩阵变量的方法:

?
1
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))

  首先它调用了TensorFlow变量的声明函数tf.Variable。在变量声明函数中给出了初始化这个变量的方法。TensorFlow中变量的初始值能够设置成随机数,常数或者是同其余变量的初始值计算获得。在上面的样例中,tf.reandom_normal([2, 3], stddev=2)会产生一个2*3的矩阵,矩阵的元素均值为0,标准差为2 的随机数。tf.random_normal函数能够经过参数mean来指定平均值,在没有指定时默认为0,经过知足正态分布的随机数来初始化神经网络中的参数是一个很是有用的方法,除了正态分布的随机数,TensorFlow还提供了一些其余的随机数生成器,下图列出了TensorFlow目前支持的全部随机数生成器

Tensorflow学习笔记——常见概念的整理_取值_02

  TensorFlow也支持经过常数来初始化一个变量,下图给出了TensorFlow中经常使用的常量声明方法:

Tensorflow学习笔记——常见概念的整理_取值_03

  在神经网络中,偏置项(bias)一般会使用常数来设置初始值,下面代码给出了一个例子:

?
1
2
# 下面会产生一个初始值为0且长度为3 的变量
biases = tf.Variable(tf.zeros([3]))

  固然,TensorFlow也支持经过其余变量的初始值来初始化新的变量,下面给出了具体的方法:

?
1
2
3
# 声明w1 w2两个变量
w1 = tf.Variable(weights.initialized_value())
w2 = tf.Variable(weights.initialized_value() * 2.0)

  以上代码中,w1的初始值被设置成了与weights变量相同,w2的初始值则是weights初始值的两倍。在TensorFlow中,一个变量的初始化过程须要被明确的调用。

   相似于张量,维度(shape)和类型(type)也是变量最重用的两个属性,和大部分程序语言相似,变量的类型是不可改变的。一个变量在被构建以后,它的类型就不能再改变量。

  以下代码会报出类型不匹配的错误:

?
1
2
3
4
5
6
7
8
9
10
11
# 声明w1 w2两个变量
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w1' )
w2 = tf.Variable(tf.random_normal([2, 3], dtype=tf.float64, stddev=1), name= 'w2' )
 
w1.assign(w2)
'' '
程序会报错:
TypeError: Input 'value' of 'Assign' Op has type float64 that does not
match type float32 of argument 'ref' .
'' '

  维度是另一个重要的属性,和类型不大同样的是,维度在程序运行中是有可能改变的,可是须要设置参数,以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
# 声明w1 w2两个变量
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w1' )
w2 = tf.Variable(tf.random_normal([2, 3], stddev=1), name= 'w2' )
 
w1.assign(w1, w2)
'' '
程序会报错(维度不匹配的错误):
TypeError: Expected bool for argument 'use_locking' not
<tf.Variable 'w2:0' shape=(2, 3) dtype=float32_ref>.
'' '
# 下面代码能够被成功执行
tf.assign(w1, w2, validate_shape=False)

  虽然TensorFlow支持更改变量的维度,可是这种作法比较罕见。

经过TensorFlow游乐场了解神经网络

   首先咱们经过TensorFlow游乐场来快速了解神经网络的主要功能。TensorFlow游乐场是一个经过网页浏览器就能够训练的简单神经网络并实现了可视化训练过程的工具。

  TensorFlow游乐场的地址:http://playground.tensorflow.org/

Tensorflow学习笔记——常见概念的整理_神经网络_04

  从上图中能够看出,TensorFlow的左侧提供四个不一样的数据集来测试神经网络。默认的数据为左上角被框出来的那个。被选中的数据也会显示在上面最右边的“OUTPUT”栏目下。在这个数据中,能够看到一个二维平面上有蓝色或者橙色的点,每个小点都表明了一个样例,而点的颜色表明了样例的标签。由于点的颜色只有两种,全部这是一个二分类问题。在这里举这么一个例子来讲明这个数据能够表明的实际问题。假设须要判断某工厂生产的零件是否合格,那么蓝色的点能够表示全部合格的零件,而橙色表明不合格的零件。这样判断一个零件是否合格就变成了区分点的颜色。

  为了将一个实际问题对应到屏幕上不一样颜色点的划分,还须要将实际问题中的实体,好比上述例子中的零件,变成屏幕上的一个点。这就是特征提取解决的问题。仍是以零件为例,能够用零件的长度和质量来大体描述一个零件。这样一个物理意义上的零件就能够被转化成长度和质量这两个数字。在机器学习中,全部用于描述实体的数字的组合就是一个实体的特征向量(feature vector)。而特征向量的提取对机器学习的效果相当重要,经过特征提取就能够将实际问题中的实体转化为空间中的点。假设使用长度和质量做为一个零件的特征向量,那么每一个零件就是二维平面上的一个点。TensorFlow游乐园中Features一栏对应了特征向量。

  特征向量是神经网络的输入,神经网络的主体结构显示了在上图的中间位置。目前主流的神经网络都是分层的结构,第一层是输入层,表明特征向量中每个特征的取值。好比若是一个零件的长度是0.5,那么x1的值就是0.5。同一层的节点不会相互链接,并且每一层只和下一层链接,直到最后一层做为输出层获得计算的结果。在二分类问题中,好比判断零件是否合格,神经网络的输出层每每只包含一个节点。在二分类问题中,好比判断零件是否合格,神经网络的输出层每每只包含一个节点,而这个节点会输出一个实数值。经过这个输出值和一个事先设定的阈值,就能够判断结果是零件合格,反之则零件不合格,通常能够认为当输出值离阈值越远获得的答案越可靠。

   在输入和输出层之间的神经网络叫作隐藏层,通常一个神经网络的隐藏层越多,这个神经网络就越“深”。而所谓深度学习中的这个“深度”和神经网络的层数也是密切相关的。在TensorFlow游乐场中能够经过点击加或者减来增长或者减小神经网络隐藏层的数量。处理能够选择深入网络的深度,TensorFlow游乐场也支持选择神经网络每一层的节点数以及学习率(learning rate),激活函数(activation),正则化(regularization)。

  因此经过神经网络解决分类问题主要能够分为如下四个步骤:

  1,提取问题中实体的特征向量做为神经网络的输入。不一样的实体能够提取不一样的特征向量。

  2,定义神经网络的结构,并定义如何从神经网络的输入获得输出。这个过程能够是神经网络的前向传播算法

  3,经过训练数据来调整神经网络中参数的取值,这就是训练神经网络的过程。

  4,使用训练好的神经网络来预测未知的数据。

前向传播算法

  下面学习一下最简单的全链接网络结构的前向传播算法,而且将展现如何经过TensorFlow来实现这个算法。

  下面首先了解神经元的结构,神经元是一个神经网络的最小单位,下面显示一个最简单的神经元结构:

Tensorflow学习笔记——常见概念的整理_数据_05

  从上图能够看出,一个神经元有多个输入和一个输出。每一个神经元的输入既能够是其余神经元的输出,也能够是整个神经网络的输入。所谓神经网络的结构就是指的不一样神经元之间的链接结构。一个最简单的神经元结构的输出就是全部输入的加权和,而不一样输入的权重就是神经元的参数。神经网络的优化过程就是优化神经元中的参数取值的过程。

   下图给出了一个简单的判断零件是否合格的三层全链接神经网络,之因此称为全链接神经网络是由于相邻两层之间任意两个节点之间都有链接。

  下图展现一个判断零件是否合格的三层神经网络结构图:

Tensorflow学习笔记——常见概念的整理_tensorflow_06

  计算神经网络的前向传播结构须要三部分信息。第一个部分是神经网络的输入,这个输入就是从实体中提取的特征向量。好比上面有两个输入,一个是零件的长度x1,一个是零件的质量 x2,第二个部分为神经网络的链接结构。神经网络是由神经元构成的,神经网络的结构给出不一样神经元之间输入输出的链接关系。神经网络中的神经元也能够称为节点。在上图中 a11节点有两个输入 ,分别是x1 和 x2的输出。而 a11 的输出则是节点 y 的输入。最后一个部分是每一个神经元中的采纳数。咱们用W来表示神经元中的参数。W的上标表名了神经网络的层数,好比W(1) 表示第一层节点的参数,而W(2) 表示第二层节点的参数。W的下标代表了链接节点编号,好比W(1) 1,2 表示链接 x1 和 a12节点的边上的权重。这里咱们假设权重是已知的。

  当咱们给定神经网络的输入,神经网络的结构以及边上权重,就能够经过前向传播算法来计算出神经网络的输出,下图展现了这个神经网络前向传播的过程:

Tensorflow学习笔记——常见概念的整理_tensorflow_07

  上图给出来输入层的取值,从输入层开始一层一层地使用前向传播算法,首先隐藏层中有三个节点,每个节点的取值都是输入层取值的加权和。当求出输出值的阈值,判断是否大于0,这样就能够判断是否合格。上面整个过程就是前向传播的算法。

  固然前向传播的算法能够表示为矩阵乘法,将输入 x1  x2 组织成一个1*2 的矩阵x = [x1, x2],而W(1) 组织成一个2*3 的矩阵:

Tensorflow学习笔记——常见概念的整理_取值_08

  这样经过矩阵乘法能够获得隐藏层三个节点所组成的向量取值:

Tensorflow学习笔记——常见概念的整理_初始化_09

  相似的输出层能够表示为:

Tensorflow学习笔记——常见概念的整理_取值_10

  这样就能够将前向传播算法经过矩阵乘法的方式表达出来了。在TensorFlow中矩阵政法是很是容易实现的。如下代码实现了神经网络的前向传播过程:

?
1
2
3
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

  其中 tf.matmul 实现了矩阵乘法的功能。

  如下样例介绍了如何经过遍历实现神经网络的参数并实现前向传播的过程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#_*_coding:utf-8_*_
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 暂时将输出的特征向量定义为一个常量,注意这里x是一个1*2的矩阵
x = tf.constant([[0.7, 0.9]])
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
sess.run(w1.initializer)
sess.run(w2.initializer)
print(sess.run(y))   # 输出[[3.957578]]
sess.close()

  从代码中能够看出,当声明了变量w1 w2以后,能够经过w1  w2来定义神经网络的前向传播过程并获得中间结果 a 和最后答案 y 。可是这些被定义的计算在这一步中并非真正的运算,当须要运行这些计算并获得具体的数字的时候,须要进入TensorFlow程序第二步。

  在第二步,咱们会声明一个会话(session),而后经过会话计算结果。

 经过TensorFlow训练神经网络模型

  使用监督学习的方式设置神经网络参数须要有一个标注好的训练数据集。以判断零件是否合格为例,这个标注好的训练数据集就是手机的一批合格零件和一批不合格零件。监督学习最重要的思想就是在已知答案的标注数据集上,模型给出的预测结果要尽可能接近真实的答案。经过调整神经网络中的参数对训练数据进行拟合,可使得模型对未知的样本提供预测的能力。

  在神经网络优化算法中,最经常使用的方法是反向传播算法(backpropagation),下图展现了使用反向传播算法训练神经网络的流程图:

Tensorflow学习笔记——常见概念的整理_初始化_11

  从上图能够看出,反向传播算法实现了一个迭代的过程。在每次迭代的开始,首先须要选取一小部分训练数据,这一小部分数据叫作一个batch。而后这个batch的样例会经过前向传播算法获得神经网络模型的额预测结果。由于训练数据都是由正确答案标注的,因此能够计算出当前神经网络模型的预测答案与正确答案之间的差距。最后,基于这预测值和真实值之间的差距,反向传播算法会相应的更新神经参数的取值,使得在这个batch上神经网络模型的预测结果和真实答案更加接近。

   经过TensorFlow实现反向传播算法的第一步是使用TensorFlow表达一个batch的数据,在以前咱们使用常量来表达,可是若是每轮迭代中选取的数据都要经过常量来表示,那么TensorFlow都会在计算图中增长一个节点。通常来讲,一个神经网络的训练过程会须要通过几百万轮甚至几亿轮的迭代,这样计算图就会很是大,并且利用率很低。为了不这个问题,TensorFlow提供了placeholder机制用于提供输入数据。placeholder至关于定义了一个位置,这个位置中的数据在程序运行时再指定。这样在程序中就不须要生成大量常量来提供输入数据,而只须要将数据经过placeholder传入TensorFlow计算图。在placeholder定义时,这个位置上的数据类型是须要指定的。和其余张量同样,placeholder的类型也是不能够改变的。placeholder中数据的维度信息是能够根据提供的数据推导出来,因此不必定给出。

  下面给出了经过placeholder实现前向传播算法:

?
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
29
30
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 定义placeholder做为存放输入数据的地方,这里维度也不必定要定义
# 可是若是维度是肯定的,那么给出维度能够下降出错的几率
x = tf.placeholder(tf.float32, shape=(1, 2), name= 'input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
'' '
下面一行将报错:
InvalidArgumentError (see above for traceback): You must feed a value
  for placeholder tensor 'input' with dtype float and shape [1,2]
'' '
# print(sess.run(y))
 
# 下面一行将会获得以前同样的输出结果
print(sess.run(y, feed_dict={x: [[0.7, 0.9]]}))   # [[3.957578]]
sess.close()

  在这段程序中替换了原来经过常量定义的输入 x ,在新的程序中计算前向传播结果时,须要提供一个feed_dict 来指定 x 的取值。 feed_dict 是一个字典(map),在字典中须要给出每一个用到的placeholder的取值,若是某个须要的placeholder没有被指定取值,那么在程序运行时候会报错。

  在上面的样例程序中,若是将输入的1*2 矩阵改成 n*2 的矩阵,那么就能够获得 n 个样例的前向传播结果了。其中 n*2 的矩阵的每一行为一个样例数据。这样前向传播的结果为 n*1 的矩阵,这个矩阵的每一行就表明了一个样例的前向传播结果,下面程序给出一个实例:

?
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
29
30
31
32
33
import tensorflow as tf
 
# 定义神经网络的参数
# 声明w1 w2两个变量,这里还经过seed设定了随机种子,这样能够保证运行结果同样
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 定义placeholder做为存放输入数据的地方,这里维度也不必定要定义
# 可是若是维度是肯定的,那么给出维度能够下降出错的几率
# x = tf.placeholder(tf.float32, shape=(1, 2), name='input')
x =tf.placeholder(tf.float32, shape=(3, 2), name= 'input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
sess = tf.Session()
# 这里不能直接经过sess.run(y)来获取y的取值
# 由于w1和w2 都尚未运行初始化过程,下面分别初始化两个变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
 
 
# 由于x 在定义时指定了 n 为3,因此在运行前向传播过程时须要提供三个样例数据
 
print(sess.run(y, feed_dict={x: [[0.7, 0.9], [0.1, 0.4], [0.5, 0.8]]}))
'' '
输出结果为:
[[3.957578 ]
  [1.1537654]
  [3.1674924]]
'' '
sess.close()

  上面的样例中展现了一次性计算多个样例的前向传播结果。在运行时,须要将3个样例组成一个3*2的矩阵传入placeholder。计算获得的结果为3*1 的矩阵。

  在获得一个batch的前向传播结果以后,须要定义一个损失函数来刻画当前的预测值和真实答案之间的差距。而后经过反向传播算法来调整神经网络参数的取值使得差距能够被缩小。下面定义一个简单的额损失函数,并经过TensorFlow定义反向传播的算法。

?
1
2
3
4
5
6
7
8
# 定义损失函数来刻画预测值与真实值的差距
cross_entropy = -tf.reduce_mean(
     y_ * tf.log(tf.clip_by_value(y, le-10, 1.0))
)
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的采纳数
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)

  在上面代码中,cross_entropy 定义了真实值和预测值之间的交叉熵(cross entropy),这是分类问题中一个经常使用的损失函数,第二行 train_step 定义了反向传播的优化方法。目前TensorFlow支持7种不一样的优化器,比较经常使用的优化方法有三种:

?
1
2
3
tf.train.GradientDescentOptimizer
tf.train.AdamOptimizer
tf.train.MomentumOptimizer

  

完整神经网络样例程序

  代码以下:

?
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# _*_coding:utf-8_*_
import tensorflow as tf
 
# Numpy 是一个科学计算的工具包,这里经过Numpy工具包生成模拟数据集
from numpy.random import RandomState
 
# 定义训练数据batch的大小
batch_size = 8
 
# 定义神经网络的参数
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))
 
# 在shape的一个维度上使用None能够方便的表示使用不大的batch大小,
# 在训练时须要把数据分红比较小的batch,在测试的时候,能够一次性的使用所有的数据
# 可是数据集比较大的是,将大量数据放入一个batch可能会致使内存溢出。
x = tf.placeholder(tf.float32, shape=(None, 2), name= 'x-input' )
y_ = tf.placeholder(tf.float32, shape=(None, 1), name= 'y-input' )
 
# 定义神经网络前向传播的过程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
 
# 定义损失函数来刻画预测值与真实值的差距
cross_entropy = -tf.reduce_mean(
     y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
)
# 定义学习率
learning_rate = 0.001
# 定义反向传播算法来优化神经网络中的采纳数
train_step = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
 
# 经过随机数生成一个模拟数据集
rdm = RandomState(1)
dataset_size = 128
X = rdm.rand(dataset_size, 2)
 
# 定义规则来给出样本的标签,在这里全部x1+x2<1 的样例都被认为是正样本(好比零件合格)
# 而其余为负样本(好比零件不合格)和TensorFlow游乐场中的表示法不大同样的地方式
#  这里使用0表示负样本,1来表示正样本,大部分解决分类问题的神经网络都会采用0和1的表示方法
Y = [[ int (x1 + x2 < 1)] for (x1, x2) in X]
 
# 建立一个会话来运行TensorFlow
with tf.Session() as sess:
     init_op = tf.global_variables_initializer()
     # 初始化变量
     sess.run(init_op)
     print(sess.run(w1))
     print(sess.run(w2))
     '' '
     在训练以前神经网络参数的值
     w1   =  [[-0.8113182   1.4845988   0.06532937]
      [-2.4427042   0.0992484   0.5912243 ]]
      
     w2 = [[-0.8113182 ]
      [ 1.4845988 ]
      [ 0.06532937]]
     '' '
     # 设定训练的轮数
     STEPS = 5000
     for i in range(STEPS):
         # 每次选取batch_size 个样本进行训练
         start = (i * batch_size) % dataset_size
         end = min(start + batch_size, dataset_size)
 
         # 经过选取的样本训练神经网络并更新参数
         sess.run(train_step,
                  feed_dict={x: X[start:end], y_: Y[start: end]})
         if i % 1000 == 0:
             # 每隔一段时间计算在全部数据上的交叉熵并输出
             total_cross_entropy = sess.run(cross_entropy,
                                            feed_dict={x: X, y_: Y})
             print( "After %d training step(s), cross_entropy on all data is %g" % (i, total_cross_entropy))
             '' '
             输出结果:
             After 0 training step(s), cross_entropy on all data is 0.0674925
             After 1000 training step(s), cross_entropy on all data is 0.0163385
             After 2000 training step(s), cross_entropy on all data is 0.00907547
             After 3000 training step(s), cross_entropy on all data is 0.00714436
             After 4000 training step(s), cross_entropy on all data is 0.00578471
 
             经过这个结果能够发现随着训练的进行,交叉熵是逐渐变小的
             交叉熵越小说明预测的结果和真实的结果差距越小
             '' '
     print(sess.run(w1))
     print(sess.run(w2))
     '' '
     在训练以后神经网络参数的值
     w1 = [[-1.9618275  2.582354   1.6820377]
      [-3.4681718  1.0698231  2.11789  ]]
     w2 = [[-1.824715 ]
      [ 2.6854665]
      [ 1.418195 ]]
      
      从和开始的神经网络参数值对比,咱们发现这两个参数的取值是已经发生变化
       这个变化就是训练的结果,它使得这个神经网络能更好的拟合提供的训练数据集
     '' '

  上面的程序实现了训练神经网络的所有过从,从这段程序中能够总结出训练神经网络的过程分为如下三个步骤:

  • 1,定义神经网络的结构和前向传播的输出结果
  • 2,定义损失函数以及选择反向传播优化的算法
  • 3,生成会话(tf.Session)而且在训练数据上反复进行反向传播优化算法

不管神经网络的结构如何变化,这三个步骤是不变的。

 

tf.Variable()  & tf.get_variable()

  tf.Variable() 和 tf.get_variable() 均可以用来建立变量,可是前者会自动保证惟一性,然后者不能保证惟一性。

  咱们能够对比两个函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 新建一个变量,变量值是 initial_value
Variable(initial_value=None, trainable=True,
          collections=None, validate_shape=True,
          caching_device=None,name=None,
          expected_shape=None, import_scope=None,
          constraint=None)
 
 
# 获取具备这些参数的现有变量或者建立一个新变量。(能够建立共享变量)
# 若是该name的变量还未定义,则新建立一个,若是依据定义了,则直接获取该变量
get_variable(name, shape=None, dtype=None,
              initializer=None, regularizer=None,
              trainable=True, collections=None,
              caching_device=None, partitioner=None,
              validate_shape=True, use_resouce=None,
              constraint=None)

  下面举个例子来讲明两者的不一样之处:

?
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
#_*_coding:utf-8_*_
'' '
下面例子来讲明 tf.Variable()  和 tf.get_variable() 的不一样之处
'' '
 
import tensorflow as tf
 
with tf.variable_scope( 'scope1' ):
     w1 = tf.Variable(1, name= 'w1' )
     w2 = tf.get_variable(name= 'w2' , initializer=2.)
 
with tf.variable_scope( 'scope1' , reuse=True):
     w1_p = tf.Variable(1, name= 'w1' )
     w2_p = tf.get_variable(name= 'w2' , initializer=3.)
 
print( 'w1' , w1)
print( 'w1_p' , w1_p)
# w1          <tf.Variable 'scope1/w1:0' shape=() dtype=int32_ref>
# w1_p        <tf.Variable 'scope1_1/w1:0' shape=() dtype=int32_ref>
 
print( 'w2' , w2)
print( 'w2_p' , w2_p)
# w2          <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>
# w2_p        <tf.Variable 'scope1/w2:0' shape=() dtype=float32_ref>
 
print(w1 is w1_p, w2 is w2_p)
# False True

  咱们能够看出, tf.Variable()会自动处理冲突问题,如上面代码所示。而tf.get_variable()会判断是否已经存在该name的变量,若是有,且该变量空间的reuse=True,那么就能够直接共享以前的值,若是没有,则从新建立。(注意:若是没有将reuse设置为True,则会提示冲突发生)。错误以下:

?
1
2
ValueError: Variable scope1/w2 already exists, disallowed. Did you
mean to set reuse=True in VarScope? Originally defined at:

  由于代码的最后一句语句是是判断上述变量是否相等,能够看出,经过get_variable()定义的变量是彻底等价的,即便后一句 get_variable 是将 initializer 设为3,可是因为 name='w2' 的变量已经存在,而且 reuse=True,则直接引用以前定义的,这样就能够用 get_variable() 来定义共享变量。

  在生成上下文管理器时,若设置reuse=True,tf.variable_scope将只能获取已经建立过的变量,若是空间中没有变量则会报错。若是reuse=False 或者 reuse=None,tf.get_variable将建立新的变量。并且同名变量已经存在,会报错。

tf.get_variable  & tf.variable_scope

  tf.get_variable 函数能够用来建立或者获取变量,当建立变量时,与 tf.Variable是同样的。

  tf.variable_scope 函数生成一个上下文管理器,用于控制 tf.get_variable。

  这里,咱们会发现, tf.get_variable() 在使用时,通常会和 tf.varibale_scope() 配套使用,须要指定它的做用域空间,这样在引用的使用的使用就能够经过设置指定的scope的 reuse=True进行引用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#_*_coding:utf-8_*_
'' '
变量生成之  tf.get_variable 与 tf.variable_scope   reuse参数
'' '
import tensorflow as tf
 
with tf.variable_scope( 'a' ):
     v1 = tf.get_variable( "v" , [1], initializer=tf.constant_initializer(1.0))
 
# with tf.variable_scope("a"):
     # 报错 ValueError: Variable a/v already exists,
#     v2 = tf.get_variable("v", [1])  
 
with tf.variable_scope( "a" , reuse=True):
     v3 = tf.get_variable( "v" , [1])
     print(v3 == v1)  # True
 
with tf.variable_scope( "b" , reuse=True):
     # 报错 ValueError: Variable b/v does not exist, or was not created with tf.get_variable().
     v4 = tf.get_variable( "v" ,[1]) 

  最后咱们看一下 tf.variable_scope()  和 tf.name_scope()。 其中name_scope() 是给 op_name加前缀,指定op的做用域空间 ,op是指操做。而variable_scope() 是给get_variable() 建立的变量的名字加前缀,代表做用域空间,也能够用于处理命名冲突。

 

tf.cast() 数据类型转换

  tf.cast() 函数的做用是执行 tensorflow中张量数据类型转换,好比读入的图片若是是 int8 类型的,通常在训练前把图像的数据格式转换为float32。

  cast()定义:

?
1
cast(x, dtype, name=None)

  第一个参数 x:待转换的数据(张量)

  第二个参数dtype:目标数据类型

  第三个参数name:可选参数,定义操做的名称

int32转换为float32 代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#_*_coding:utf-8_*_
import tensorflow as tf
 
t1 = tf.Variable([1, 2, 3, 4, 5])
t2 = tf.cast(t1, dtype=tf.float32)
 
print( 't1: {}' .format(t1))
print( 't2:{}' .format(t2))
 
with tf.Session() as sess:
     sess.run(tf.global_variables_initializer())
     sess.run(t2)
     print(t2.eval())
     print(sess.run(t2))

  输出以下:

?
1
2
3
4
t1: <tf.Variable 'Variable:0' shape=(5,) dtype=int32_ref>
t2:Tensor( "Cast:0" , shape=(5,), dtype=float32)
[1. 2. 3. 4. 5.]
[1. 2. 3. 4. 5.]

 

 batch

  深度学习的优化算法,说白了就是梯度降低。每次的参数更新有两种方式。

1,遍历所有数据集算一次损失函数,而后算函数对各个参数的梯度,更新梯度。这种方法每更新一次参数都要把数据集里的全部样本都看一遍,计算量开销大,计算速度慢,不支持在线学习,这种称谓Batch gradient descent,批梯度降低。

2,每看一个数据就算一下损失函数,而后求梯度更新参数,这个称为随机梯度降低,stochastic gradient descent。这个方法速度比较快,可是收敛性能不太好,可能在最优势附近晃来晃去,hit不到最优势。两次参数的更新也有可能互相抵消掉,形成目标函数震荡的比较剧烈。

  为了克服两种方法的缺点,如今通常采用的是一种折中手段,mini-batch gradient decent ,小批的梯度降低,这种方法把数据分红若干个批,按批来更新参数,这样一个批中的一组数据共同决定了本次梯度的方向,降低起来就不容易跑偏,减小了随机性,另外一方面由于批的样本数与整个数据集相比小了不少,计算量也不是很大。

  基本上如今的梯度降低都是基于mini-batch的,因此深度学习框架的函数中常常会出现batch_size,就指的是这个。

 tf.argmax的使用

   tf.argmax(vector, 1):返回的是vector中的最大值的索引号,若是vector是一个向量,那就返回一个值,若是是一个矩阵,那就返回一个向量,这个向量的每个维度都是相对应矩阵行的最大值元素的索引号。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import tensorflow as tf
import numpy as np
  
A = [[1,3,4,5,6]]
B = [[1,3,4], [2,4,1]]
  
with tf.Session() as sess:
     print(sess.run(tf.argmax(A, 1)))
     print(sess.run(tf.argmax(B, 1)))
 
---------------------
 
输出:
[4]
[2 1]

  

 

参考文献:

  此文是本身的学习笔记总结,学习于《TensorFlow深度学习框架》,俗话说,好记性不如烂笔头,写写老是好的,因此若侵权,请联系我,谢谢。

上一篇: Chromium 命名规范
下一篇: