TensorFlow的使用1

1 TensorFlow 计算模型一一计算图node

1.1 计算图的概念python

  • TensorFlow 是一个经过计算图的形式来表述计算的编程系统 。计算图是 TensorFlow 中最基本的一个概念, TensorFlow 中的全部计算都会被转化为计算图上的节点 ,而节点之间的边描述了计算之间的依赖关系。TensorFlow 的基本计算模型以下图所示:git

    • 图 3 -1 中的每个节点都是一个运算,而每一条边表明了计算之间的依赖关系 。若是一个运算的输入依赖于另 一个运算的输出,那么这两个运算有依赖关系。
    • 每个节点表明着一个操做(operation,Op),通常用来表示施加的的数学运算,也能够表示数据输入的起点以及输出的终点,或者是读取/写入持久变量(persistent veriable)的终点,Op也可用于状态初始化的任务.
  • TensorFlow 的名字中己经说明了它最重要的两个概念一一Tensor 和 Flow 。 Tensor 就是张量。 Flow 翻译成中文就是“流” 。
    • 在 TensorFlow 中,张量能够被简单地理解为多维数组。
    • Flow直观地表达了张量之间经过计算相互转化的过程 。

1.2 计算图的使用github

  • TensorFlow 程序通常能够分为两个阶段。在第一个阶段须要定义计算图中全部的计算 。 第二个阶段为执行计算 。
  • 在 TensorFlow 程序中,系统会自动维护一个默认的计算图,经过 tf.get_default_graph 函数能够获取当前默认的计算图。 若是想查看一个张量a所属的计算图,可使用a.graph。
  • 除了使用默认的计算图, TensorFlow 支持经过tf.Graph函数来生成新的计算图。不一样计算图上的张量和运算都不会共享。
  • TensorFlow 中的计算图不只仅能够用来隔离张量和计算,它还提供了管理张量和计算的机制。计算图能够经过 tf.Graph.device 函数来指定运行计算的设备。这为 TensorFlow 使用 GPU 提供了机制。 若是要指定计算运行的设备,以下所示:
    a = tf.Graph()
    # 指定计算运行的设备
    with g.device('/gpu:0'):
    result = a + b
  • 有效地整理 TensorFlow 程序中的资源也是计算图的一个重要功能。在一个计算图中,能够经过集合 (collection )来管理不一样类别的资源。
    • 经过 tf.add_to_collection 函数能够将资源加入一个或多个集合中。
    • 经过 tf.get_collection 能够获取一个集合中的全部资源。
    • 这里的资源能够是张量、变量或者运行TensorFlow程序所需的队列资源。
    • 为了方便使用, TenorsFlow 自动管理了一些也最经常使用的集合以下表总结了最经常使用的几个 自动维护的集合。
      集合名称 集合内容 使用场景
      tf.GraphKeys.VARIABLES 全部变量 持久化 TensorFlow 模型
      tf.GraphKeys.TRAINABLE_VARIABLES 可学习的变量(通常指神经网络中的参数) 模型训练、生成模型可视化内容
      tf.GraphKeys.SUMMARIES 日志生成相关的张量 TensorFlow计算可视化
      tf.GraphKeys.QUEUE_RUNNERS 处理输入的 QueueRunner 输入处理
      tf.GraphKeys.MOVING_AVERAGE_VARIABLES 全部计算了滑动平均值的变量 计算变量的滑动平均值

2 TensorFlow 数据模型一一张量正则表达式

2.1 张量的概念算法

  • 在 TensorFlow程序中,全部的数据都经过张量的形式来表示。从功能的角度上看,张量能够简单的理解为多维数组。其中:
    • 零阶张量表示标量( scalar ),也就是一个数;
    • 第一阶张量为向量 (vector),也就是一个一维数组;
    • 第 n 阶张量能够理解为一个 n 维数组 。
  • 张量在TensorFlow中的实现并非直接采用数组的形式,它只是对TensorFlow中运算结果的引用。在张量中并无真正保存数字,它保存的是如何获得这些数字的计算过程。
    • 以向量的加法为例,当运行以下代码时,并不会获得加法的结果,而会获得对结果的一个引用。
      import tensorflow as tf
      # tf.constant 是一个计算,这个计算的结果为一个张量 , 保存在变量 a 中。
      a= tf .constant([l . O, 2 . 0] , name=” a”)
      b = tf . constant ( [2 . 0 , 3 . 0] , name=”b ”)
      result = tf . add (a , b , name=” add”)
      print(result)
      """
      输出 :
      Tensor ( ” add :0”, shape= ( 2 ,), dtype=float32)
      """
    • 从以上代码能够看出 TensorFlow 中的张量和 NumPy 中 的数组不一样, TensorFlow 计算的结果不是一个具体的数字 , 而是一个张量的结构。
    • 从上面代码的运行结果能够看出, 一个张量中主要保存了三个属性 : 名字( name )、维度( shape )和类型( type )。
      • 张量的第一个属性name不只是一个张量的惟一标识符 , 它一样也给出了这个张量是如何计算出来的。 由于TensorFlow 的计算均可以经过计算图 的模型来创建,而计算图上的每个节点表明了 一个计算,计算的结果就保存在张量之中。因此张量和计算图上节点所表明的计算结果是对应的。 这样张量的命名就能够经过 “ node:src_output”的形式来给出。其中node为节点的名称,src一output 表示当前张量来自节点的第几个输出。
      • 张量的第二个属性是张量的维度(shape)。这个属性描述了 一个张量的维度信息。好比上面样例中 shape=(2,)说明了张量 result 是一个一维数组 , 这个数组的长度为 2。
      • 张量的第三个属性是类型(type ) ,每个张量会有一个惟一的类型。TensorFlow 会对参与运算的全部张量进行类型的检查,当发现类型不匹配时会报错。
        • 若是不指定类型, TensorFlow 会给出默认的类型,好比不带小数点的数会被默认为 int32,带小数点的会默认为 float32 。
        • 由于使用默认类型有可能会致使潜在的类型不匹配问题,因此通常建议经过指定 dtype 来明确指出变量或者常量的类型 。
        • TensorFlow 支持 14 种不一样的类型, 主要包括了实数( tf.float32 、 tf.float64 )、整数( tf.int8 、 tf.intl 6 、 tf.int32 、 tf.int64 、 tf.uint8 )、布尔型(tf.bool) 和复数( tf.complex64 、tf.complex128 )。

2.2 张量的使用编程

  • 张量使用主要能够总结为两大类 :
    • 第一类用途是对中间计算结果的引用。当一个计算包含不少中间结果时,使用张量能够大大提升代码的可读性。
      • 当计算的复杂度增长时(好比在构建深层神经网络时〉经过张量来引用计算的中间结果可使代码的可阅读性大大提高 。
      • 同时,经过张量来存储中间结果能够方便获取中间结果 。 好比在卷积神经网络中,卷积层或者池化层有可能改变张量的维度,经过 result.get_shape 函数来获取结果张量的维度信息能够免去人工计算的麻烦。
    • 第二类用途是当计算图构造完成以后,张量能够用来得到计算结果,也就是获得真实的数字。 虽然张量自己没有存储具体的数字 ,可是经过会话,就能够获得具体的数字。好比可使用tf.Session().run()语句获得计算结果。

3 TensorFlow 运行模型一一会话后端

  • TensorFlow中的会话(session)用来执行定义好的运算。会话拥有并管理TensorFlow程序运行时全部的资源。
  • 在会话中,全部计算完成以后须要关闭会话来帮助系统回收资源,不然就可能出现资源泄露的问题。
  • TensorFlow 中使用会话的模式通常有两种:
    • 第一种模式须要明确调用会话生成函数和关闭会话函数,这种模式的代码流程以下。
      # 建立一个会话。
      sess = tf.Session()
      # 使用这个建立好的会话来获得关心的运算的结果。好比能够调用 sess. run (result) ,
      # 来获得张量 result 的取值。
      sess.run (... )
      # 关闭会话使得本次运行巾使用到的资源能够被释放。
      sess.close ()
      • 使用这种模式时,在全部计算完成以后,须要明确调用 Session.close 函数来关闭会话并释放资源。 然而,当程序由于异常而退出时,关闭会话的函数可能就不会被执行从而致使资源泄漏 。
    • 为了解决异常退出时资源释放的问题, TensorFlow 能够经过 Python 的上下文管理器来使用会话。如下代码展现了如何使用这种模式。
      # 建立一个会话,并经过 Python 中的上下文管理器来管理这个会话。
      with tf. Session() as sess :
      #使用建立好的会话来计算关心的结果。
      sess.run()
      # 不须要再调用“ Session.close()”函数来关闭会话,
      # 当上下文退出时会话关闭和资源释放也自动完成了。
      • 经过 Python 上下文管理器的机制,只要将全部的计算放在 “ with”的内部就能够 。 当上下文管理器退出时候会自动释放全部资源。这样既解决了由于异常退出时资源释放的问题,同时也解决了忘记调用 Session.close 函数而产生的资源泄漏。
  • TensorFlow 不会自动生成默认的会话,而是须要手动指定 。 当默认的会话被指定以后能够经过 tf.Tensor.eval 函数来计算一个张量的取值 。 实例代码以下:
    # 建立一个会话
    sess = tf.Session()
    # 手动指定会话为默认会话
    with sess.as_default():
    print(result.eval())
  • TensorFlow 提供了 一种在交互式环境下直接构建默认会话的函数。这个函数就是 tf.lnteractiveSession。使用这个函数会自动将生成的会话注册为默认会话。 实列以下:
    # 在交互式环境下直接构建默认会话
    sess = tf.InteractiveSession()
    print(result.eval())
    # 关闭会话
    sess.close()
  • 经过 tf.InteractiveSession 函数能够省去将产生的会话注册为默认会话的过程。不管使用哪一种方法均可以经过 ConfigProto来配置须要生成的会话 。下面给出了经过ConfigProto配置会话的方法 :
    # 定义配置
    config = tf.ConfigProto(allow_soft_placement=True,
    log_device_placement=True)
    # 在交互式环境下直接构建默认会话,配置生成的会话
    sessl = tf.InteractiveSession(config=config)
    sess2 = tf.Session(config=config)
    • 经过 ConfigProto 能够配置相似并行的线程数、 GPU 分配策略、运算超时时间等参数。在这些参数中,最常使用的有两个 :
      • 第一个是 allow_soft_placement,这是一个布尔型的参数,当它为 True 时, 在如下任意一个条件成立时, GPU 上的运算能够放到 CPU 上进行 :
        • 运算没法在 GPU 上执行 。
        • 没有 GPU 资源(好比运算被指定在第 二个 GPU 上运行 ,可是机器只有一个 GPU ) 。
        • 运算输入包含对 CPU 计算结果的引用 。
      • 这个参数的默认值为 False,可是为了使得代码的可移植性更强,在有 GPU 的环境下
        这个参数通常会被设置为 True。 经过将 allow_soft_placement 参数设为 True , 当某些运算没法被当前 GPU 支持时,可 以自动调整到 CPU 上,而不是报错。相似地,经过将这个参数设置为 True,可让程序在拥有不一样数量的 GPU 机器上顺利运行。
      • 第二个使用得比较多的配置参数是 log_device_placement 这也是一个布尔型的参数, 当它为 True 时日志中将会记录每一个节点被安排在哪一个设备上以方便调试。而在生产环境中将这个参数设置为 False 能够减小日志量。

4 TensorFlow变量api

4.1 TensorFlow中变量的概念数组

  • 变量维护图执行过程当中的状态信息。变量经过tf.Variable 类以及tf.get_variable()函数进行操做。变量的特色有:
    • 存储持久化
    • 可修改值
    • 可指定被训练

4.2 建立和使用变量

  • tf.Variable(initial_value=None,trainable=True,collections=None,name=None)
    • initial_value:初始化的值
    • trainable:是否被训练
    • collections:新变量将添加到列出的图的集合中
  • 在 TensorFlow 中,全部的变量都会被自动地加入到 GraphKeys .VARIABLES这个集合中。
  • 经过tf.global_variables()函数能够拿到当前计算图上全部的变量。拿到计算图上全部的变量有助于持久化整个计算图的运行状态 。
  • 当构建机器学习模型时,好比神经网络,能够经过变量声明函数中的 trainable参数来区分须要优化的参数(好比神经网络中的参数)和其余参数(好比选代的轮数)。若是声明变量时参数 trainable 为 True ,那么这个变量将会被加入到GraphKeys.TRAINABLE_VARiABLES 集合。
  • 在TensorFlow中能够经过tf.trainable_variables函数获得全部须要优化的参数 。 TensorF!ow 中提供的神经网络优化算法会将GraphKeys.TRAINABLE_VARIABLES集合中的变量做为默认的优化对象 。
  • 在 TensorFlow 中,类 (tf.Variable )的做用就是保存和更新神经网络中的参数 。和其余编程语言相似,TensorFlow中的变量也须要指定初始值 。 由于在神经网络中, 给参数赋予随机初始值最为 常见,因此通常也使用随机数给 TensorFlow 中的变量初始化 。
    • 下面一段代码给出了 一种在 TensorFlow中声明一个2X3的矩阵变量的方法 :
      weights = tf.Variable(tf.random_normal([2,3],mean=0,stddev=2))
    • tf.random_normal([2, 3],mean=0, stddev=2)会产生一个2X3的矩阵,矩阵中的元素是均值为0,标准差为2的随机数。
  • 除了使用随机数或者常数,TensorFlow 也支持经过其余变量 的初始值来初始化新的变量。 如下代码给出了具体的方法 。
    w2 = tf.Variable(weights.initialized_value())
    w3 = tf.Variable(weights.initialized_value()*2.0)

    • 以上代码中, w2 的初始值被设置成了与 weights 变量相同。 w3 的初始值则是 weights初始值的两倍。
  • 在 TensorFlow 中,一个变量的值在被使用以前,这个变量的初始化过程须要被明确地调用 ,即变量在使用以前须要初始化。 TensorFlow 提供了一种便捷的方式来完成变量初始化过程。如下程序展现了经过 tf.global_variables_initializer 函数实现初始化全部变量的过程 。
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    • 经过tf.global_variables_initializer 函数,就不须要将变量一个一个初始化了 。 这个函数也会自动处理变量之间的依赖关系。

4.3 TensorFlow目前支持的全部随机数生成器

函数名称                            随机数分布                                 主要参数

tf.random_normal 正太分布 平均值、标准差、取值类型·
tf.truncated_normal 正太分布,但若是随机出来的值偏离平均值超过 2个标准差,那么这个数将会被从新随机 平均值、标准差、取值类型
tf.random.uniform 均匀分布 最小、最大取值、取值类型
tf.random_gamma Gamma 分布 形状参数 alpha、尺度参数 beta 、 取值类型

4.4 TensorFlow中经常使用的常量声明方法

函数名称             功能                           样例

tf.zeros 产生全0的数组 tf.zeros([2, 3), int32) > ([0, 0, 0], [0, 0, 0]]
tf.ones 产生全1的数组 tf.ones([2, 3], int32) -> [[1,1,1],[1,1,1]]
tf.fill 产生一个所有为给定数字的数组 tf.fill([2, 3), 9) ->[ [9, 9, 9],[9, 9, 9]]
tf.constant 产生一个给定值的常量 tf.constant([1,2,3])—>[1,2,3]

4.5 案例:使用变量实现一个简单的计数器.

# 建立一个变量为计数器, 初始化为标量 0.
state = tf.Variable(0, name="counter")
# 建立一个变量, 其做用是使state增长1
one = tf.constant(1)
new_value = tf.add(state, one)
# 更新计数器
update = tf.assign(state, new_value)

# 启动图后, 变量必须先通过变量初始化,
init_op = tf.global_variables_initializer()
# 启动图, 运行会话
with tf.Session() as sess:
  # 运行变量初始化
  sess.run(init_op)
  # 打印 'state' 的初始值
  print sess.run(state)
  # 运行更新 'state', 并打印 'state'
  for _ in range(3):
    sess.run(update)
    print sess.run(state)
  • 经常使用的变量操做
    • tf.assign(ref, value, validate_shape=None, use_locking=None, name=None)
      • 将value 赋值给ref,并输出 ref
    • tf.assign_add(ref,value,use_locking=None,name=None)
      • 更新ref的值,经过增长value

4.6 命名空间与共享变量

  • 共享变量的主要用途在一些网络当中的参数共享, 因为在TensorFlow当中,只要咱们定义的OP,name参数指定同样,其实并非同一个变量。若是想要达到重复利用变量的效果,咱们就要使用tf.variable_scope()结合tf.get_variable()一块儿使用.
  • 定义一个相同名字的变量

    var = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)
    var_double = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)

    <tf.Variable 'var:0' shape=() dtype=float32_ref>
    <tf.Variable 'var_1:0' shape=() dtype=float32_ref>

  • 使用tf.variable_scope()修改OP命名空间

    with tf.variable_scope("name"):
    var = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)
    var_double = tf.Variable(name='var', initial_value=[4], dtype=tf.float32)

    <tf.Variable 'name/var:0' shape=() dtype=float32_ref>
    <tf.Variable 'name/var_1:0' shape=() dtype=float32_ref>

  • tf.get_variable共享变量

经过tf.get_variable的初始化与Variable参数同样,可是要是实现共享须要打开tf.variable_scope("name")中的reuse=tf.AUTO_REUSE参数。

# 打开共享参数
# 或者
#  with tf.variable_scope("name") as scope:
#  在须要使用共享变量的前面定义: scope.reuse_variables()
with tf.variable_scope("name", reuse=tf.AUTO_REUSE):
    var = tf.Variable(initial_value=4.0, name="var", dtype=tf.float32)
    var_double = tf.Variable(initial_value=4.0, name="var", dtype=tf.float32)

    var1 = tf.get_variable(initializer=tf.random_normal([2, 2], mean=0.0, stddev=1.0),
                           name="var1",
                           dtype=tf.float32)
    var1_double = tf.get_variable(initializer=tf.random_normal([2, 2], mean=0.0, stddev=1.0),
                           name="var1",
                           dtype=tf.float32)
    
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(var1)
    print(var1_double)

5 TensorFlow模型的保存和恢复

5.1 什么是Tensorflow模型?

  • 训练神经网络后,须要将它保存以备未来使用和部署到生产环境。Tensorflow模型主要包含咱们神经网络的网络图和已经训练好的变量参数。所以,Tensorflow模型主要有两个文件:
    • 元数据图(meta graph):
      • 它保存了tensorflow完整的网络图结构。这个文件以.meta为扩展名。
    • 检查点文件(checkpoint file):
      • *.ckpt文件:这是一个二进制文件,它包含的权重变量,biases变量和其余变量。
      • *.data文件:包含咱们的训练的变量的文件
      • 名为checkpoint的文件:它不断的保存最新的检查点文件的记录。

5.2 为何要保存Tensorflow模型:

  • 好比对图像分类训练卷积神经网络。做为一种标准的作法,在训练模型的时候,须要一直关注着模型损失值和模型准确度。一旦咱们发现,网络已经收敛,就能够手动中止训练。训练完成后,咱们但愿将全部的变量和网络模型保存下来,供之后使用。
  • 例如,假设您能够访问通过训练的 DNN,将图片分为 100 个不一样的类别,包括动物,植物,车辆和平常物品。 您如今想要训练一个 DNN 来对特定类型的车辆进行分类。 这些任务很是类似,所以您应该尝试从新使用第一个网络的一部分(请参见图 11-4)。

    • 若是新任务的输入图像与原始任务中使用的输入图像的大小不一致,则必须添加预处理步骤以将其大小调整为原始模型的预期大小。

5.3 如何保存Tensorflow模型:

  • Tensorflow中要保存训练好的网络模型和全部的变量和网络结构图,使用tf.train.Saver()
  • Tensorflow变量的做用范围是在一个session里面。在保存模型的时候,应该在session里面经过save方法保存。
    saver.save(sess,'路径+名称'):sess是session对象,第二个参数是要保存的模型的路径与名称
  • 若是咱们但愿在迭代i次以后保存模型,能够把当前的迭代步数传进去:

    # 每隔一百步保存一次模型
    if i % 100 == 0:
    saver.save(sess, 'my_test_model',global_step=i)
    # 利用global_step参数能够把迭代的步数追加到文件名中

  • 在屡次保存模型时,若保存的文件中变化的只是变量,而网络结构没有变化,则没有必要重复保存.meta文件,可使用以下方式,让网络结构只保存一次。
    saver.save(sess,'my_model',global_step=step,write_meta_graph=False)
    • write_meta_graph 参数设置成False:可让网络结构只保存一次,不重复保存.meta 文件。
  • 若是只想保存最新的m个模型,并但愿每n小时保存一次,可使用
    saver = tf.train.Saver(max_to_keep=m, keep_checkpoint_every_n_hour= n)
    • max_to_keep参数:设置保留最新的模型的个数
    • keep_checkpoint_every_n_hours参数:设置间隔多少小时保存一次模型。
  • 若是咱们没有在tf.train.Saver()指定任何参数,这样就表示要保存全部的变量。若是咱们不但愿保存全部的变量,只是其中的一部分。咱们能够指定要保存的变量或者集合。
    • 在建立tf.train.Saver()的时候,咱们把一个列表或者要保存变量的字典做为参数传进去。代码以下:
      import tensorflow as tf
      # 定义变量OP
      w1 = tf.Variable(tf.random_normal(shape=[2]),name='w1')
      w2 = tf.Variable(tf.random_normal([5]),name='w2')
      # 建立模型保存对象,指定要保存的变量
      saver = tf.train.Saver([w1,w2])
      with tf.Session() as sess:
      # 显示初始化变量OP
      sess.run(tf.global_variables_initializer())
      ........

5.4 如何导入已经保存的模型

若是想用别人的预先训练好的模型做为咱们的模型的一部分,那么须要作两件事情:

  • 建立网络结构图:
    • 咱们能够经过编写Python代码手动建立每一层和原始模型同样的网络结构图。可是,不要作重复的工做。
    • 由于在.meta文件中,原始的网络结构已经保存在其中,咱们只须要导入便可,无需重写一遍。
    • 导入原始网络结构图的方式以下:
      saver = tf.train.import_meta_graph('模型名.meta')
  • 加载的(变量)参数
    • 使用restore()方法恢复模型的变量参数
      with tf.Session() as sess:
      new_saver = tf.train.import_meta_graph('my_test_model-1000.meta')
      new_saver.restore(sess, tf.train.latest_checkpoint('./'))

5.5 复用TensorFlow模型

  • 若是原始模型使用 TensorFlow 进行训练,则能够简单地将其恢复并在新任务上进行训练:
    [...] # 构建原始模型

    with tf.Session() as sess:
        # 还原模型
        saver.restore(sess, "./my_model_final.ckpt")
        # 在新任务中继续训练模型...
  • 然而,一般咱们只想要重用原有模型的一部分(咱们立刻就会讨论这种情况),一种简单的解决方案就是配置Saver使之在还原原模型时只还原全部参数的一个子集。举例来讲,下面的代码就只还原了隐藏层一、隐藏层2和隐藏层3:
    """
    首先咱们创建新的模型,确保复制原始模型的隐藏层 1 到 3
    """
    n_inputs = 28 * 28 # MNIST
    n_hidden1 = 300 # reused
    n_hidden2 = 50 # reused
    n_hidden3 = 50 # reused
    n_hidden4 = 20 # new!
    n_outputs = 10 # new!

    X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
    y = tf.placeholder(tf.int64, shape=(None), name="y")
    
    with tf.name_scope("dnn"):
        hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")       # reused
        hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # reused
        hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # reused
        hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # new!
        logits = tf.layers.dense(hidden4, n_outputs, name="outputs")                         # new!
    
    with tf.name_scope("loss"):
        xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
        loss = tf.reduce_mean(xentropy, name="loss")
    
    with tf.name_scope("eval"):
        correct = tf.nn.in_top_k(logits, y, 1)
        accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
    
    with tf.name_scope("train"):
        optimizer = tf.train.GradientDescentOptimizer(learning_rate)
        training_op = optimizer.minimize(loss)
    
    # 获取保存的模型中用trainable = True(这是默认值)建立的全部变量的列表
    reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                   scope="hidden[123]") # 咱们只保留那些范围与正则表达式hidden [123]相匹配的变量(即,咱们获得全部可训练的隐藏层 1 到 3 中的变量)。
    # 咱们建立一个字典,将原始模型中每一个变量的名称映射到新模型中的名称中(一般须要保持彻底相同的名称)
    reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
    # 建立一个SaverOP用来还原模型中的参数
    restore_saver = tf.train.Saver(reuse_vars_dict) # to restore layers 1-3
    
    init = tf.global_variables_initializer()
    # 建立一个新的SaverOP用来保存新模型
    new_saver = tf.train.Saver()
    
    with tf.Session() as sess:
        init.run()
        # 从原始模型的层 1 到 3中恢复变量值
        restore_saver.restore(sess, "./my_model_final.ckpt")
    
        for epoch in range(n_epochs):                                      
            for iteration in range(mnist.train.num_examples // batch_size): 
                X_batch, y_batch = mnist.train.next_batch(batch_size)      
                sess.run(training_op, feed_dict={X: X_batch, y: y_batch})  
            accuracy_val = accuracy.eval(feed_dict={X: mnist.test.images,  
                                                    y: mnist.test.labels})
            print(epoch, "Test accuracy:", accuracy_val)                  
      # 保存新模型
        save_path = new_saver.save(sess, "./my_new_model_final.ckpt")
  • 首先咱们创建新的模型,确保复制原始模型的隐藏层 1 到 3。咱们还建立一个节点来初始化全部变量。 而后咱们获得刚刚用trainable = True(这是默认值)建立的全部变量的列表,咱们只保留那些范围与正则表达式hidden [123]相匹配的变量(即,咱们获得全部可训练的隐藏层 1 到 3 中的变量)。 接下来,咱们建立一个字典,将原始模型中每一个变量的名称映射到新模型中的名称(一般须要保持彻底相同的名称)。 而后,咱们建立一个Saver,它将只恢复这些变量,而且建立另外一个Saver来保存整个新模型,而不只仅是第 1 层到第 3 层。而后,咱们开始一个会话并初始化模型中的全部变量,而后从原始模型的层 1 到 3中恢复变量值。最后,咱们在新任务上训练模型并保存。
  • 任务越类似,您能够重复使用的层越多(从较低层开始)。 对于很是类似的任务,您能够尝试保留全部隐藏的层,只替换输出层。
  • 案例:识别手势数据集

    """
    一天下午,咱们和一些朋友决定教咱们的电脑破译手语。咱们花了几个小时在白色的墙壁前拍照,因而就有了了如下数据集。如今,你的任务是创建一个算法,使有语音障碍的人与不懂手语的人交流。

    训练集:有从0到5的数字的1080张图片(64x64像素),每一个数字拥有180张图片。
    测试集:有从0到5的数字的120张图片(64x64像素),每一个数字拥有5张图片。
    须要注意的是这是完整数据集的一个子集,完整的数据集包含更多的符号。

    下面是每一个数字的样本,以及咱们如何表示标签的解释。这些都是原始图片,咱们实际上用的是64 * 64像素的图片。
    """
    import numpy as np
    import h5py
    import matplotlib.pyplot as plt
    import tensorflow as tf
    from tensorflow.python.framework import ops
    import time
    import math
    from functools import partial

    # %matplotlib inline #若是你使用的是jupyter notebook取消注释
    np.random.seed(1)

    class GestureSymbolRecognition(object):
    def init(self):
    self.learning_rate = 0.0001
    self.num_epochs = 1000
    self.minibatch_size = 32
    self.regularazion_rate = 0.001

    self.n_inputs = 12288
          self.n_hidden1 = 200
          self.n_hidden2 = 100
          self.n_hidden3 = 100
          self.n_outputs = 6
    
          self.batch_norm_momentum = 0.9
    
      def load_data(self):
          """
          加载数据集
          :return:
          """
          # 从文件中读取训练集数据
          train_dataset = h5py.File('datasets/train_signs.h5', "r")
          # 从训练集数据中提取特征值与标签值数据
          train_set_x_orig = np.array(train_dataset["train_set_x"][:])
          train_set_y_orig = np.array(train_dataset["train_set_y"][:])
    
          # 从文件中读取测试集数据
          test_dataset = h5py.File('datasets/test_signs.h5', "r")
          # 从测试集数据中提取特征值与标签值数据
          test_set_x_orig = np.array(test_dataset["test_set_x"][:])
          test_set_y_orig = np.array(test_dataset["test_set_y"][:])
          classes = np.array(test_dataset["list_classes"][:])  # 类别列表
          classes_num = len(classes)  # 数据集标签分为几类
    
          train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
          test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
          X_train_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1)  # 每一列就是一个样本
          X_test_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1)
    
          # 归一化数据
          X_train = X_train_flatten / 255
          X_test = X_test_flatten / 255
    
          # 转换为one-hot编码矩阵
          Y_train = self.convert_to_one_hot(train_set_y_orig, len(classes)).T
          Y_test = self.convert_to_one_hot(test_set_y_orig, len(classes)).T
    
          return X_train, Y_train, X_test, Y_test, classes_num
    
      def convert_to_one_hot(self, Y, C):
          """
          实现one-hot编码
          :param Y:标签矩阵
          :param C:分类数量
          :return:one_hot:one-hot矩阵
          """
          one_hot = np.eye(C)[Y.reshape(-1)].T
          return one_hot
    
      def random_mini_batches(self, X, Y, mini_batch_size, seed=0):
          """
          从数据集中建立一个随机的mini-batch列表
          :param X: 输入数据,维度为(输入节点数量,样本数量)
          :param Y: 对应的目标值,维度为(1,样本数量)
          :param mini_batch_size: 每一个mini-batch的样本数量
          :param seed: 随机数种子
          :return: mini_batches:一个同步列表,维度为(mini_batch_X, mini_batch_Y)
          """
          # 指定随机数种子
          np.random.seed(seed)
          # 获取样本数
          m = X.shape[0]
          # print('样本数量',m)
          # 建立一个同步列表
          mini_batches = []
    
          # 第一步:打乱样本的顺序
          permutation = list(np.random.permutation(m))  # 返回一个长度为m的随机数组,且里面的数是0到m-1
          shuffled_X = X[permutation, :]  # 将每一列特征值数据按permutation的顺序来从新排列
          shuffled_Y = Y[permutation, :]  # .reshape((Y.shape[0], m))  # 将每一列的目标值数据按permutation的顺序来从新排列
    
          # 第二步:分割训练集数据
          num_complete_minibatches = math.floor(m / mini_batch_size)  # 迭代完成整个训练集的次数
          for k in range(0, num_complete_minibatches):
              mini_batch_X = shuffled_X[k * mini_batch_size:(k + 1) * mini_batch_size, :]
              mini_batch_Y = shuffled_Y[k * mini_batch_size:(k + 1) * mini_batch_size, :]
              mini_batch = (mini_batch_X, mini_batch_Y)
              mini_batches.append(mini_batch)
    
          # 若是训练集的大小不是mini_batch_size的整数倍,那么最后确定会剩下一些,取剩余的数据
          if m % mini_batch_size != 0:
              # 获取剩余部分的数据
              mini_batch_X = shuffled_X[mini_batch_size * num_complete_minibatches:, :]
              mini_batch_Y = shuffled_Y[mini_batch_size * num_complete_minibatches:, :]
              mini_batch = (mini_batch_X, mini_batch_Y)
              mini_batches.append(mini_batch)
    
          # 返回同步列表
          return mini_batches
    
      def train(self, print_cost=True, is_plot=True):
          """
          实现一个三层的TensorFlow神经网络训练逻辑:LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX
          参数:
              print_cost - 是否打印成本,每100代打印一次
              is_plot - 是否绘制曲线图
    
          """
          # 加载数据集
          x_train, y_train, x_test, y_test, label_classes_num = self.load_data()
          print("训练集样本数 = " + str(x_train.shape[0]))
          print("测试集样本数 = " + str(x_test.shape[0]))
          print("X_train.shape: " + str(x_train.shape))
          print("Y_train.shape: " + str(y_train.shape))
          print("X_test.shape: " + str(x_test.shape))
          print("Y_test.shape: " + str(y_test.shape))
          # 设置图级别的随机数种子
          tf.set_random_seed(1)
          # 设置操做级别的随机数种子
          seed = 3
          # 获取输入节点数量和样本数
          (m, n_x) = x_train.shape
          # 成本函数值列表
          costs = []
    
          # 给X和Y建立占位符节点
          X = tf.placeholder(tf.float32, shape=(None, self.n_inputs), name="X")
          Y = tf.placeholder(tf.int32, shape=(None, label_classes_num), name="y")
    
          # 定义神经网络全链接层
          with tf.name_scope("dnn"):
    
              # 使用He权重初始化方法初始化权重
              he_init = tf.contrib.layers.variance_scaling_initializer(mode='FAN_AVG')
              # 定义全链接层,使用elu激活函数
              hidden1 = tf.layers.dense(X, self.n_hidden1, name="hidden1",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              hidden2 = tf.layers.dense(hidden1, self.n_hidden2, name="hidden2",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              hidden3 = tf.layers.dense(hidden2, self.n_hidden3, name="hidden3",
                                        activation=tf.nn.elu, kernel_initializer=he_init)
              outputs = tf.layers.dense(hidden3, self.n_outputs, name="outputs")
              # 输出预测的类别
              y_proba = tf.nn.softmax(outputs)
    
          # 定义损失函数计算损失
          with tf.name_scope('loss'):
              # 前向传播要在Z3处中止,由于在TensorFlow中最后的线性输出层的输出做为计算损失函数的输入,因此不须要A3.
              xentropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y,
                                                                 logits=outputs)
              loss = tf.reduce_mean(xentropy, name="loss")
    
          # 定义优化器
          with tf.name_scope("train_optimizer"):
              optimizer = tf.train.AdamOptimizer(self.learning_rate)
              training_op = optimizer.minimize(loss)
    
          # 评估模型,使用准确性做为咱们的绩效指标
          with tf.name_scope("eval"):
              # 计算当前的预测结果
              # 检测使用了滑动平静模型的神经网络前向传播是否正确。tf.argmax(average_y,1)
              # 计算每个样例的预测答案。其中average_y是一个batch_size*10的二维数组,每
              # 一行表示案例向前传播的结果。tf.argmax的第二个参数为1,表示选取最大值的
              # 操做只在第一个维度上进行(x轴上),也就是说只在每一行选取最大值对应的下标
              # 因而获得的结果是一个长度为batch的一维数组,这个一维数组中的值就表示了每
              # 一个数字对应的样例识别的结果.tf.equal()判断每一个Tensor的每一维度是否相同
              # 若是相等返回True,不然返回False.
              correct_prediction = tf.equal(tf.argmax(y_proba, 1), tf.argmax(Y, 1))
    
              # 计算准确率
              # 这个运算首先将一个布尔型的值转换为实数型,而后计算平均值。这一个平均值
              # 就表明模型在这一组数据上的正确率
              accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
          """
          首先咱们创建新的模型,确保复制原始模型的隐藏层 1 到 2
          """
          # 获取保存的模型中用trainable = True(这是默认值)建立的全部变量的列表
          # 咱们只保留那些范围与正则表达式hidden [123]相匹配的变量(即,咱们获得全部可训练的隐藏层 1 到 3 中的变量)。
          reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
                                         scope='hidden[12]')
          # 咱们建立一个字典,将原始模型中每一个变量的名称映射到新模型中的名称中(一般须要保持彻底相同的名称)
          reuse_vars_dict = dict([(var.op.name, var) for var in reuse_vars])
          # 建立一个SaverOP用来还原模型中的参数
          restore_saver = tf.train.Saver(reuse_vars_dict)
    
          # 建立初始化全部变量的节点
          init = tf.global_variables_initializer()
    
          # 建立一个新的SaverOP用来保存新模型
          new_saver = tf.train.Saver(max_to_keep=1)
    
          # 开始会话并计算
          with tf.Session() as sess:
              # 初始化全部变量
              sess.run(init)
              # 加载模型,从模型中找出与当前训练的模型代码当中(名字同样的OP操做),覆盖原来的值
              checkpoin = tf.train.latest_checkpoint('./ckpt/')
              # 若是模型已经保存则加载模型
              if checkpoin:
                  # 从原始模型恢复变量值
                  restore_saver.restore(sess, './ckpt/my_test1_model.ckpt')
    
              # 正常训练模型
              for epoch in range(self.num_epochs):
    
                  epoch_cost = 0  # 每轮的成本函数值
                  num_minibatches = math.floor(m / self.minibatch_size)  # 分红多少个mini-batch组
                  seed = seed + 1
                  minibatches = self.random_mini_batches(x_train, y_train, self.minibatch_size, seed)
    
                  for minibatch in minibatches:
                      # 选择一个minibatch
                      (minibatch_X, minibatch_Y) = minibatch
    
                      # 数据已经准备好了,开始运行session
                      _, minibatch_cost = sess.run([training_op, loss], feed_dict={X: minibatch_X, Y: minibatch_Y})
                      # 计算这个minibatch在这一代中所占的偏差
                      epoch_cost = epoch_cost + minibatch_cost / num_minibatches
    
                  if epoch % 5 == 0:
                      # 将每轮迭代的代价函数值存入列表
                      costs.append(epoch_cost)
                      # 打印每轮迭代的代价函数值:
                      if print_cost and epoch % 100 == 0:
                          print("epoch = " + str(epoch) + "    epoch_cost = " + str(epoch_cost))
              # 保存学习后的参数
              new_saver.save(sess, './ckpt/my_test1_model.ckpt')
    
              # 绘制代价函数值的学习曲线
              if is_plot:
                  plt.plot(np.squeeze(costs))
                  plt.ylabel('cost')
                  plt.xlabel('iterations (per tens)')
                  plt.title("Learning rate =" + str(self.learning_rate))
                  plt.show()
    
              # 计算模型学习完成后的训练集及测试集准确率
    
              train_acc = accuracy.eval({X: x_train, Y: y_train})
              test_acc = accuracy.eval({X: x_test, Y: y_test})
              print("训练集的准确率:", train_acc)
              print("测试集的准确率:", test_acc)

    if name == 'main':
    # 开始时间
    start_time = time.clock()
    # 开始训练
    nn = GestureSymbolRecognition()
    nn.train()
    # 结束时间
    end_time = time.clock()
    # 计算时差
    print("CPU的执行时间 = " + str(end_time - start_time) + " 秒")

5.6 冻结底层

  • 若是一个深度神经网络的的低层已经学会了检测图像中的低级特征,这对于另外一个图像分类任务来讲是有用的。所以能够重用这些图层。训练新的DNN时,冻结图层的权重是一个比较好的方法。
  • 为了在训练中冻结低层,最简单的方法就是给优化器列出要训练的变量列表,列表以外的变量即被冻结,不会被训练。代码以下:
    # 获取全部的须要训练的隐藏层的权重变量
    train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,scope='hidden[34]|outputs')
    # 把可训练变量的列表传给优化器的minimize()函数,未出如今列表中的图层即被冻结了。
    training_op = optimizer.minimize(loss,var_list=train_vars)

5.7 缓存冻结层

  • 由于冻结层不会变化,因此就有可能将每个训练实例的最高冻结层的输出缓存起来。因为训练会轮询整个数据集不少次,因此你将得到巨大的速度提高,由于你只须要在一个训练实例中遍历一次冻结层(而不是每一个全数据集一次)。举个例子,你能够第一次在低层跑完整个训练集(假设你有足够的RAM):
    hidden2_outputs = sess.run(hidden2, feed_dict={X: X_train})

  • 而后在训练中,你要作的不是构建批量的训练实例,而是批量构建隐藏层2的输出,而且将它们喂给训练操做:
    import numpy as np
    n_epochs = 100
    n_batches = 500
    for epoch in range(n_epochs):
    shuffled_idx = rnd.permutation(len(hidden2_outputs))
    hidden2_batches = np.array_split(hidden2_outputs[shuffled_idx], n_batches)
    y_batches = np.array_split(y_train[shuffled_idx], n_batches)
    for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):
    sess.run(training_op, feed_dict={hidden2: hidden2_batch, y: y_batch})

    • 最后一行运行的是以前定义好的训练操做(冻结层1和层2),而后把隐藏层2(对应批量目标)的批量输出传给它。由于咱们把隐藏层2的输出传给TensorFlow,因此它不用尝试去评估它。

5.8 调整、丢弃或替换层

  • 模型复用时,原始模型的输出层常常会被替换,由于对于新的任务,它基本没有用,甚至没有办法提供正确的输出个数。
  • 一样,模型复用时,原始模型的高隐藏层没有低隐藏层的用处多,由于对于新的任务来讲,最有效的高级特性可能和原始模型中的最有效特性差异很大。因此,模型复用时,咱们应该找到正确的层数来重用。
  • 首先应尝试冻结全部的复制的层,而后训练模型观察效果。接着尝试解冻一到两个顶部的隐藏层, 用反向传播进行调整来观察是否有改善。 训练数据越多, 越能解冻更多的层。
  • 若是你一直不能得到好的效果, 并且训练数据不多, 那就尝试丢弃最高的一层或多层, 而后从新冻结剩下的隐藏层。 你能够一直迭代直到找到正确的重用层数。 若是你有不少训练数据, 你能够尝试替换顶部的隐藏层而不是丢弃它们, 甚至能够添加一些隐藏层

5.9 模型动物园

  • 在哪里能够找到一个训练好的相似神经网络来完成一个你想要解决的任务呢? 首选固然是你本身已有的模型目录。 这是一个很好的方式, 能够保存你全部的模型, 方便你整理, 也方便你往后随时调用。另外一个选择就是在模型动物园(Model Zoo) 里搜索。 不少人针对各类任务训练了不少机器学习的模型, 而且很慷慨地公开这些预训练的模型。
    • TensorFlow在https://github.com/tensorflow/models上公开了本身的
      模型动物园。 特别须要指出的是, 这个模型动物园包含了先进图片识
      别的网络, 好比VGG、 Inception和ResNet(见第13章, 同时查看
      models/slim目录) , 包括代码、 预训练模型, 以及下载流行图片数据集的工具 。
    • 另外一个流行的模型动物园是Caffe模型动物园(https://goo.gl/XI02X3) 。 其中也包括不少计算机视觉模型(好比:LeNet、 AlexNet、 ZFNet、 GoogLeNet、 VGGNet和inception) , 也都在各类数据集(好比ImageNet、 Places Database、 CIFAR10等) 上通过训练。
    • Dasgupta写了一个转换器, 能够在https://github.com/ethereon/caffe-tensorflow找到。

5.10 无监督的预训练

  • 假设你要完成一个没有太多标记训练数据的复杂任务, 而且没有找到在近似任务上训练过的模型。
    • 首先, 你确定须要努力去收集更多的标记过的训练数据, 可是若是这个代价很高或者很难, 你仍然能够运行无监督的预训练 ,也就是说, 若是 你有一堆未标记的训练数据,那你能够逐层训练它们,从最低层开
      始,而后向上,利用一种非监督特性检测算法好比受限玻尔兹曼机 或者自动编码器 。
    • 每一层都是基于提早训练好的图层(除去被冻结的训练层)的输出进行训练。一旦全部层都用这个方式训练过以后,你就能够用监督学习的方式(即反向传播)来微调网络。
  • 在面对复杂任务处理、没有类似模型可重用、有极少标记过的训练数据但
    是却有不少未标记的训练数据的状况下,无监督的预处理(如今一般使用自动编码器而不是RBM)是一个很是不错的选择。

5.11 辅助任务中的预训练

  • 最后一个选择是在辅助任务中训练第一个神经网络, 你能够轻松得到或者生成标记过的训练数据, 而后重用该网络的低层来实现你的实际任务。 第一个神经网络的低层会学习可能被第二个神经网络重用的特征检测器。
    • 举个例子, 若是你想构建一我的脸识别系统, 可是你只有每一个人的几张照片, 很明显没办法训练一个好的分类器。 收集每一个人成百上千张照片根本不可行。 可是, 你能够在网上收集不少随机的人像照片, 用它们能够训练第一个神经网络来检测两张不一样的照片是否属于相同的人。 这个网络将学习优质的人脸特征检测器, 而后重用这个网络的低层,这样你就能够用不多的训练数据训练出一个优质的人脸分类器。
  • 一般状况下,收集未标记训练示例会廉价不少,可是标记它们却很贵。针对这种状况,经常使用的技术是将全部示例都标记为“好”,而后经过破坏好的训练示例来生成新的训练实例,并把那些被破坏的实例标记为“坏”。接着你就能够训练第一个神经网络来区分好的实例和坏的实例。
    • 举个例子,你能够下载数百万条句子,标记它们为“好”,而后随机修改每一句中的一个单词,并标记这些修改后的句子为“坏”。若是一个神经网络能够识别出“The dog sleeps”是好, “The dog they”是坏,那么它可能已经学会很多语言了。重用这个网络的低层可能有助于不少语言处理任务。
  • 另外一种方法是训练第一个神经网络,让它输出每个训练实例的得分,而后利用成本函数,确保每个好的实例的得分都比坏的实例的得分至少高一些。咱们称其为最大边界学习。

6 TensorBoard:可视化学习

  • TensorBoard简介
    • 为了更方便 TensorFlow 程序的理解、调试与优化,有了TensorBoard 的可视化工具。
      • TensorBoard 是TensorFlow 的可视化工具,它能够经过TensorFlow 程序运行过程当中输出的日志文件可视化Te nsorFlow 程序的运行状态。
      • TensorBoard 和TensorFlow 程序跑在不一样的进程中。
      • Tensor Board 会自动读取最新的TensorFlow 日志文件,并呈现当前TensorFlow程序运行的最新状态。
  • 使用可视化学习的流程
    • 数据序列化-events文件
      • 返回filewriter,写入事件文件到指定目录(最好用绝对路径),以提供给tensorboard使用
      • TensorBoard 经过读取 TensorFlow 的事件文件来运行,须要将数据生成一个序列化的 Summary protobuf 对象。
      • 注意:路径不能出现中文,不然会打不开!!!
        # 实现加法
        con1 = tf.constant(11.0, name='input1')
        con2 = tf.constant(12.0, name='input2')
        sum_ = tf.add(con1, con2, name='add')
        with tf.Session() as sess:
        # 在会话中序列化events文件
        file_writer = tf.summary.FileWriter('f:logs/', tf.get_default_graph())
        file_writer.close()

      • 将在指定目录中生成一个envent文件,格式为:
        events.out.tfevents.{timestamp}.{hostname}
    • 启动TensorBoard
  • 变量Tensorboard显示
    • 目的:在TensorBoard当中观察模型的参数、损失值等变量值的变化
    • 一、收集变量
      • tf.summary.scalar(name='',tensor):收集如损失函数和准确率等单值变量。name为变量名称,tensor为值。
      • tf.summary.histogram(name='',tensor):收集高维度的变量参数,如线性回归的偏置,权重等。
      • tf.summary.image(name='',tensor):收集输入的图片张量,能显示图片。
    • 二、合并变量
      • merged = tf.summary.merge_all()
    • 三、生成事件文件,观察图结构
      • file_writer = tf.summary.FileWriter('路径',graph=sess.graph)
        • 注意路径中不要出现中文
    • 四、运行合并:summary=sess.run(merged)
      • 每次迭代都须要运行
    • 五、把summary,张量的值写入到events文件当中
      • file_writer.add_summary(summary, i)
        • i表示第几回的值

7 Tensorflow案例—实现线性回归

7.1 线性回归原理复习

  • 根据数据创建回归模型,w_1x_1+w_2x_2+...+w_nx_n+b = y经过真实值与预测值之间创建损失偏差,使用梯度降低优化获得损失最小对应的权重和偏置。最终肯定模型的权重和偏置参数。最后用这些参数进行预测。

7.2 案例肯定

  • 假设随机指定100个点,只有一个特征
  • 数据自己的分布为 y = 0.7 * x + 0.8

    这里将数据分布的规律肯定,是为了使咱们训练出的参数跟真实的参数(即0.7和0.8)比较是否训练准确。

7.3 使用的API

  • 运算
    • 矩阵运算:tf.matmul(x,w) 注:x与w的维数要相同
    • 平方:tf.square(error)
    • 求均值:tf.reduce_mean(error)
  • 梯度降低优化
    • tf.train.GradientDescentOptimizer(learning_rate)
      • 梯度降低优化
      • learning_rate:学习率,通常为0~1之间较小的值
      • method:minimize(loss)
      • return:梯度降低OP
  • 定义变量以及初始化
    • 定义变量:
      tf.Variable(initial_value=None,trainable=True,collection=None,name=None)
      • 模型的要优化参数必须选择tf.Variable定义,而且能够指定trainable参数指定是否被训练
      • inital_value:初始化的值
      • trainable:是否被训练,默认为True
      • collections:新变量将添加到列出的图的集合collections中
    • 变量初始化
      • 变量op只有通过显式初始化才能被运行
      • 显式初始化:sess.run(tf.global_variables_initializer())
  • 获取默认的计算图并在默认计算图中操做
    • 获取默认计算图:g = tf.get_default_graph()
    • 使用默认的计算图:with g.as_default():
  • 增长命名空间
    • 做用:使代码结构更加清晰,Tensorboard图结构清楚,有助于代码的模块化。
    • with tf.variable_scope('lr_model'):
  • 变量Tensorboard显示
    • 目的:在TensorBoard当中观察模型的参数、损失值等变量值的变化
    • 一、收集变量
      • tf.summary.scalar(name='',tensor):收集如损失函数和准确率等单值变量。name为变量名称,tensor为值。
      • tf.summary.histogram(name='',tensor):收集高维度的变量参数,如线性回归的偏置,权重等。
      • tf.summary.image(name='',tensor):收集输入的图片张量,能显示图片。
    • 二、合并变量
      • merged = tf.summary.merge_all()
    • 三、生成事件文件,观察图结构
      • TensorFlow1.x中的API:file_writer = tf.summary.FileWriter('路径',graph=sess.graph)
        • 注意路径中不要出现中文
    • 四、运行合并:summary=sess.run(merged)
      • 每次迭代都须要运行
    • 五、把summary,张量的值写入到events文件当中
      • file_writer.add_summary(summary, i)
        • i表示第几回的值
  • 模型的保存与加载
    • 建立一个保存文件的saveOP: saver = tf.train.Saver(var_list=None,max_to_keep=5)
      • 保存和加载模型(保存文件格式:checkpoint文件)
      • var_list:指定将要保存和还原的变量。它能够做为一个dict或一个列表传递.
      • max_to_keep:设置要保留的最近检查点文件的最大数量。建立新文件时,会删除较旧的文件。默认为5(即保留最新的5个检查点文件。)
    • 保存训练好的模型:saver.save(sess,'ckpt/myregression.ckpt',global_step=step)
      • sess:当前会话名称
      • 第二个参数是设定保存的路径+名字
      • 第三个参数将训练的次数做为后缀加入到模型名字中
    • 加载模型:checkpoint = tf.train.latest_checkpoint("./ckpt/")
      • 从模型当中找出与当前训练的模型代码当中(名字同样的op操做),覆盖原来的值。
      • 判读模型是否存在,若存在则恢复模型:
        if ckpt:
        saver.restore(sses,ckpt)
  • 命令行参数设置
    • 目的:使用终端命令行的方式去运行程序,并设置参数
    • 定义命令行参数:tf.app.flags.DEFINE_integer('train_step',0,'迭代次数')
      • 用于定义值是整型的参数
      • 第一个参数是定义的参数名
      • 第二个参数是默认值
      • 第三个参数是注释(不能省略)
    • tf.app.flags.DEFINE_string('model_dir', '', '模型的保存路径+模型名字')
      • 定义值是字符串的参数
    • 定义获取命令行的参数:FLAGS = tf.app.flags.FLAGS
    • 使用命令行使用的参数:FLAGS.参数名称
      • 例如:FLAGS.model_dir---->获取模型的保存路径+模型名字

7.4 示例代码

"""
使用Tensorflow实现线性回归
"""
import tensorflow as tf
class MyLinearRegression(object):
    """
    实现一个线性回归
    """
    def __init__(self, FLAGS):
        self.FLAGS = FLAGS
        # 定义学习率
        self.learning_rate = 0.1
        # 定义迭代次数
        self.train_step = 1000
        # 定义并初始化线性回归模型的权重与偏置参数,因为权重与偏置须要被训练与优化,所以需用使用变量OP定义
        self.height = tf.Variable(tf.random_normal(shape=[1, 1], mean=0.0, stddev=1.0, name='height'))
        self.bais = tf.Variable(tf.random_normal(shape=[1], mean=0.0, stddev=1.0, name='bais'))

    def inputs(self):
        """
        获取须要训练的数据
        :return: x_feature,y_target
        """
        # 定义一个命名空间
        with tf.variable_scope('get_inputs'):
            # 公式:y = x*0.7 + 0.8
            # 生成符合正态分布的特征值
            x_feature = tf.random_normal(shape=[100, 1], mean=0.0, stddev=1.0, name='x_feature')
            # 利用矩阵乘法计算得出目标值,注意矩阵相乘,维数要相同
            y_target = tf.matmul(x_feature, [[0.7]]) + 0.8

            return x_feature, y_target

    def inference(self, x_feature):
        """
        根据数据创建线性回归模型
        :param x_feature: 特征值
        :return: y_predic:预测结果
        """
        # 定义一个命名空间
        with tf.variable_scope('linear_model'):
            # 计算预测值
            y_predict = tf.matmul(x_feature, self.height) + self.bais

        return y_predict

    def loss(self, y_predict, y_target):
        """
        由线性回归模型的损失函数计算损失值
        :param y_predict: 预测值
        :param y_target: 目标值
        :return: 损失值
        """
        # 定义一个命名空间
        with tf.variable_scope('get_loss'):
            # 损失函数使目标值与预测值的均方差公式
            # 先用tf.square()计算差的平方
            # 再用tf.reduce_mean()对列表数据求和后再求平均值
            loss = tf.reduce_mean(tf.square(y_predict - y_target))
            return loss

    def sgd_op(self, loss):
        """
        使用随机梯度降低优化器来优化损失(优化模型参数)
        :param loss: 损失大小
        :return: 梯度降低OP
        """
        # 定义一个命名空间
        with tf.variable_scope("sgd_op"):
            # 使用随机梯度降低API得出优化结果
            train_op = tf.train.GradientDescentOptimizer(learning_rate=self.learning_rate).minimize(loss)

            return train_op

    def merge_summary(self, loss):
        """
        增长变量显示:在TensorBoard当中观察模型的参数、损失值等变量值的变化
        :param loss:损失
        :return: merged:合并的变量
        """
        # 1. 收集变量
        tf.summary.scalar('loss', loss)
        tf.summary.histogram('w', self.height)
        tf.summary.histogram('b', self.bais)

        # 2. 合并变量
        merged = tf.summary.merge_all()
        return merged

    def train(self):
        """
        用于实现线性回归模型训练逻辑
        :return:
        """
        # 开启一个默认的图
        g = tf.get_default_graph()
        # 在默认的计算图中进行操做
        with g.as_default():
            # 获得输入数据
            x_feature, y_target = self.inputs()
            # 创建线性回归模型,获得预测值
            y_predict = self.inference(x_feature)
            # 根据损失函数求解损失值
            loss = self.loss(y_predict, y_target)
            # 使用梯度降低优化器优化损失
            train_op = self.sgd_op(loss)
            # 增长变量显示:在TensorBoard当中观察模型的参数、损失值等变量值的变化
            merged = self.merge_summary(loss)
            # 定义一个保存模型的saveOP,设置要保留的最近检测点的模型的数量
            saver = tf.train.Saver(max_to_keep=5)
            # 开启一个会话
            with tf.Session() as sess:

                # 初始化变量,初始化后才能够在会话中运行
                sess.run(tf.global_variables_initializer())
                # 生成事件文件观察图结构
                file_writer = tf.summary.FileWriter('F:/tensorflow/logs', graph=sess.graph)

                print("模型随机初始化的权重参数:{},偏置参数:{}".format(self.height.eval(), self.bais.eval()))

                # 加载模型,从模型当中找出与当前训练的模型代码当中(名字同样的op操做),覆盖原来的值
                checkpoint = tf.train.latest_checkpoint('./ckpt/')
                # 判断模型是否存在,若存在则提取保存的模型
                if checkpoint:
                    saver.restore(sess, checkpoint)
                print("第一次加载保存模型的权重参数:{},偏置参数:{}".format(self.height.eval(), self.bais.eval()))

                # 使用梯度降低优化损失
                for i in range(1, self.FLAGS.train_step + 1):
                    # 运行梯度降低OP与合并OP
                    _, summary = sess.run([train_op, merged])
                    print("第{}步优化,损失为{},权重参数:{},偏置参数:{}".format(i, loss.eval(), self.height.eval(), self.bais.eval()))
                    # 将优化的变量写入事件文件
                    file_writer.add_summary(summary, i)

                    # 每隔一百步保存一次模型
                    if i % 100 == 0:
                        saver.save(sess, self.FLAGS.model_dir, global_step=i)  # './ckpt/myregression.ckpt'


if __name__ == '__main__':
    # 定义命令行参数
    tf.app.flags.DEFINE_integer('train_step', 0, '训练步数')
    tf.app.flags.DEFINE_string('model_dir', '', "模型的保存路径")
    # 定义获取命令行的参数
    FLAGS = tf.app.flags.FLAGS
    lr = MyLinearRegression(FLAGS)
    lr.train()

8 Tensorflow文件读取

8.1 获取数据到Tensorflow的方法

  • TensorFlow程序读取数据一共有3种方法:
  • 供给数据(Feeding): Python产生数据,再把数据喂给后端。
    import tensorflow as tf
    # 设计Graph
    x1 = tf.placeholder(tf.int16)
    x2 = tf.placeholder(tf.int16)
    y = tf.add(x1, x2)
    # 用Python产生数据
    li1 = [2, 3, 4]
    li2 = [4, 0, 1]
    # 打开一个session --> 喂数据 --> 计算y
    with tf.Session() as sess:
    print sess.run(y, feed_dict={x1: li1, x2: li2})

    """
    说明:在这里x1, x2只是占位符,没有具体的值,那么运行的时候去哪取值呢?这时候就要用到sess.run()中的feed_dict参数,将Python产生的数据喂给后端,并计算y。
    """
    • 缺点是:用占位符替代数据,待运行的时候填充数据。中间环节的增长也是不小的开销,遇到大型数据的时候就会很吃力。
  • 预加载数据: 在TensorFlow图中定义常量或变量来保存全部数据(仅适用于数据量比较小的状况)。
    import tensorflow as tf
    # 设计Graph
    x1 = tf.constant([2, 3, 4])
    x2 = tf.constant([4, 0, 1])
    y = tf.add(x1, x2)
    # 打开一个session --> 计算y
    with tf.Session() as sess:
    print sess.run(y)

    • 经过预加载的方式加载数据的缺点在于,将数据直接内嵌到Graph中,再把Graph传入Session中运行。当数据量比较大时,Graph的传输会遇到效率问题。
  • 从文件读取数据(QueueRunner):简单来讲就是将数据读取模块的图搭好

8.2 TensorFlow文件读取数据流程

  • 步骤:
    • 第一阶段:生成文件名+路径的列表。读取该列表并将其放入文件名队列。
    • 第二阶段:从队列当中读取文件内容,而且进行解码操做。
    • 第三阶段:解码以后,咱们能够直接获取默认的一个样本内容了,可是若是想要获取多个样本,这个时候须要结合管道进行批处理。
  • 第一阶段
    • 咱们称之为构造文件队列,将须要读取的文件装入到一个固定的队列当中
      • tf.train.string_input_producer(string_tensor,num_epochs,shuffle=True)
        • string_tensor:含有文件名+路径的一阶张量
        • num_epochs:将数据集中的数据所有计算几遍
        • shuffle:设置在一个epochs内文件顺序是否被打乱
        • return:一个文件队列
  • 第二阶段:读取文件内容,并进行解码操做
    • 读取文件内容:
      • TensorFlow默认每次只读取一个样本,具体到:文本文件读取一行、二进制文件读取指定字节数(最好一个样本)、图片文件默认读取一张图片、TFRecords默认读取一个example。
      • tf.TextLineReader:
        • 阅读文本文件,默认按行读取
        • return:读取器实例
      • tf.WholeFileReader:用于读取图片文件
      • tf.TFRecordReader:读取TFRecords文件
      • tf.FixedLengthRecordReader:读取二进制文件
        • 要读取每一个记录是固定数量字节的二进制文件
        • record_bytes:整型,指定每次读取(一个样本)的字节数
        • return:读取器实例
          一、他们有共同的读取方法:read(file_queue):从队列中指定数量内容返回一个Tensors元组(key文件名字,value默认的内容(一个样本))
          二、因为默认只会读取一个样本,因此一般想要进行批处理。使用tf.train.batch或tf.train.shuffle_batch进行多样本获取,便于训练时候指定每批次多个样本的训练
    • 对文件内容进行解码
      • 对于读取不通的文件类型,内容须要解码操做,解码成统一的Tensor格式
      • tf.decode_csv:解码文本文件内容
        • 与tf.TextLineReader搭配使用
      • tf.decode_raw:解码二进制文件内容
        • 与tf.FixedLengthRecordReader搭配使用,二进制读取为uint8格式
      • tf.image.decode_jpeg(contents):解码图片文件
        • 将JPEG编码的图像解码为uint8张量
        • return:unit8张量,3-D形状[height,width,channels]
      • tf.image_decode_png(contents):解码图片文件
        • 将PNG编码的图像解码为uint8张量
        • return:张量类型,3-D形状[height,width,channels]
          解码阶段,默认全部的内容都解码成tf.uint8格式,若是须要后续的类型处理继续处理
  • 第三阶段:结合管道进行批处理
    • 在解码以后,咱们能够直接获取默认的一个样本内容了,可是若是想要获取多个样本,这个时候须要结合管道的末尾进行批处理
      • tf.train_batch(tensors,batch_size,num_threads=n,capacity=m,name='')
        • 做用:读取指定大小(个数)的张量
        • tensors:列表形式,须要批处理的张量放到列表中
        • batch_size:设置从队列中读取批处理的大小
        • num_threads:设置进入队列的线程数
        • capacity:设置队列中元素的最大数量
        • return:tensors
      • tf.train.shuffle_batch(tensors,batch_size,num_threads=n,capacity=m,name='')
        • 做用:对队列中的样本进行乱序处理,读取指定大小(个数)的张量
        • tensors:列表形式,须要批处理的张量放到列表中
        • batch_size:设置从队列中读取批处理的大小
        • num_threads:设置进入队列的线程数
        • capacity:设置队列中元素的最大数量
        • return:tensors

8.3 TensorFlow读取机制图解

首先须要思考的一个问题是,什么是数据读取?以图像数据为例,读取数据的过程能够用下图来表示:



    假设咱们的硬盘中有一个图片数据集0001.jpg,0002.jpg,0003.jpg……咱们只须要把它们读取到内存中,而后提供给GPU或是CPU进行计算就能够了。这听起来很容易,但事实远没有那么简单。

    事实上,咱们必需要把数据先读入后才能进行计算,假设读入用时0.1s,计算用时0.9s,那么就意味着每过1s,GPU都会有0.1s无事可作,这就大大下降了运算的效率。

如何解决这个问题?方法就是将读入数据和计算分别放在两个线程中,将数据读入内存的一个队列,以下图所示:

读取线程源源不断地将文件系统中的图片读入到一个内存的队列中,而负责计算的是另外一个线程,计算须要数据时,直接从内存队列中取就能够了。这样就能够解决GPU由于IO而空闲的问题!

    而在tensorflow中,为了方便管理,在内存队列前又添加了一层所谓的“文件名队列”。

    为何要添加这一层文件名队列?咱们首先得了解机器学习中的一个概念:epoch。对于一个数据集来说,运行一个epoch就是将这个数据集中的图片所有计算一遍。如一个数据集中有三张图片A.jpg、B.jpg、C.jpg,那么跑一个epoch就是指对A、B、C三张图片都计算了一遍。两个epoch就是指先对A、B、C各计算一遍,而后再所有计算一遍,也就是说每张图片都计算了两遍。

    tensorflow使用文件名队列+内存队列双队列的形式读入文件,能够很好地管理epoch。下面咱们用图片的形式来讲明这个机制的运行方式。以下图,仍是以数据集A.jpg, B.jpg, C.jpg为例,假定咱们要跑一个epoch,那么咱们就在文件名队列中把A、B、C各放入一次,并在以后标注队列结束。

程序运行后,内存队列首先读入A(此时A从文件名队列中出队):

再依次读入B和C:

此时,若是再尝试读入,系统因为检测到了“结束”,就会自动抛出一个异常(OutOfRange)。外部捕捉到这个异常后就能够结束程序了。这就是tensorflow中读取数据的基本机制。若是咱们要跑2个epoch而不是1个epoch,那只要在文件名队列中将A、B、C依次放入两次再标记结束就能够了。

8.4 TensorFlow读取数据机制的对应函数

如何在tensorflow中建立文件名队列与内存队列呢?

对于文件名队列,咱们使用tf.train.string_input_producer函数。这个函数须要传入一个文件名list,系统会自动将它转为一个文件名队列。

    此外tf.train.string_input_producer还有两个重要的参数,一个是num_epochs,它就是咱们上文中提到的epoch数。另一个就是shuffle,shuffle是指在一个epoch内文件的顺序是否被打乱。若设置shuffle=False,以下图,每一个epoch内,数据仍是按照A、B、C的顺序进入文件名队列,这个顺序不会改变:

若是设置shuffle=True,那么在一个epoch内,数据的先后顺序就会被打乱,以下图所示:

在tensorflow中,内存队列不须要咱们本身创建,咱们只须要使用reader对象从文件名队列中读取数据就能够了,具体实现能够参考下面的实战代码。

    除了tf.train.string_input_producer外,咱们还要额外介绍一个函数:tf.train.start_queue_runners。在咱们使用tf.train.string_input_producer建立文件名队列后,整个系统其实仍是处于“停滞状态”的,也就是说,咱们文件名并无真正被加入到队列中(以下图所示)。此时若是咱们开始计算,由于内存队列中什么也没有,计算单元就会一直等待,致使整个系统被阻塞。

而使用tf.train.start_queue_runners以后,才会启动填充队列的线程,这时系统就再也不“停滞”。此后计算单元就能够拿到数据并进行计算,整个程序也就跑起来了,这就是函数tf.train.start_queue_runners的用处。

8.5 TensorFlow读取数据的线程操做

  • 建立队列和排队操做称之为tf.train.QueueRunner。每一个QueueRunner都负责一个阶段,并拥有须要在线程中运行的排队操做列表。一旦图形被构建, tf.train.start_queue_runners函数就会要求图中的每一个QueueRunner启动它运行排队操做的线程。(这些操做须要在会话中开启)
    • tf.train.start_queue_runners(sess=,coord=)
      • 做用:收集图中的全部队列线程,并启动线程
      • sess:设置所在的会话
      • coord:设置线程协调器
      • return:返回全部线程
    • coord = tf.train.Coordinator()
      • 做用:建立一个线程协调器实例,实现一个简单的机制来协调一组线程的终止
      • request_stop():请求中止
      • should_stop():询问是否结束
      • join(threads=,stop_grace_period_secs=120):回收线程
      • return:返回一个线程调节器实例

8.6 TensorFlow读取图片数据

图像基本知识

对于图像文件,咱们怎么进行转换成机器学习可以理解的数据?对于图片来说,组成图片的最基本单位是像素,因此咱们获取的是每张图片的像素值。接触的图片有两种,一种是黑白图片,另外一种是彩色图片。
  • 图片三要素
    • 组成一张图片特征值是全部的像素值,有以下几个要素:
      • 图片长度(height)
      • 图片宽度(width)
      • 图片通道数(channel)
    • 什么是图片的通道数?
      • 灰度图:单通道
      • 彩色图片:三通道
      • 假设一张彩色图片的长200,宽200,通道数为3,那么总的像素数量为200X200X3
  • 张量形状
    • 读取图片以后,怎么用张量形状来表示?
      • 一张图片就是一个3D张量,[height, width, channel]咱们会常常遇到3D和4D的表示
      • 单个图片:[height, width, channel]
      • 多个图片:[batch,height, width, channel],batch表示批数量
  • 图片的特征值处理
    • 为何要进行特征值处理?
      • 一方面,在进行图片识别的时候,每一个图片样本的特征数量要保持相同。因此须要将全部图片张量大小统一转换。
      • 另外一方面,若是图片的像素量太大,也能够经过这种方式适当减小像素的数量,减小训练的计算开销。
  • 特征值处理的经常使用API:
    • 数据类型转化:tf.convert_image_dtype(image,dtype,name=None)
      • 做用:把图片元素类型,转成想要的类型,返回转换后的图片,注意,要是转成了float类型以后,像素值会在 [0,1)这个范围内。
      • 参数:
        image: 图像
        dtype: 待转换类型
        name: 操做名
    • 形状变换:
      • tf.image.resize_images(images,size)
        • 做用:将输入图像从新缩放或者放大到固定的尺寸
        • images:形状为[batch, height, width, channels]的4-D张量或形状为[height, width, channels]的3-D张量.
        • size:1维 int32类型的 Tensor,包含两个元素:new_height, new_width.
        • method:改变形状的方法,默认是ResizeMethod.BILINEAR双线性插值
        • align_corners:布尔型,若是为True,则输入和输出张量的4个拐角像素的中心对齐,而且保留角落像素处的值;默认为False.
          可能引起的异常:ValueError
        • 若是images的形状与此函数的形状参数不兼容.
        • 若是size有无效的形状或类型.
        • 若是指定了不支持的调整大小方法.
      • resize_image_with_crop_or_pad(…):裁剪或将图像填充到目标宽度和高度
      • central_crop(…): 裁剪图像的中心区域
      • crop_and_resize(…): 从输入图像张量中提取裁剪物并对其进行双线性的调整
      • crop_to_bounding_box(…): 将图像裁剪到指定的边框。
    • 图像翻转
      神经网络实际上是很”笨”的,要是你提供的图片都是像上面那样朝向左边,那么当出现一幅朝向右边的图片的时候,它极可能没法识别. 因此,能够人为的多加一些翻转上去,使得各个角度的图片都有一点,无形中,扩充了数据量,还缓解了识别错误的问题.
      • flip_left_right(image): 左右翻转
        • 做用:左右翻转一幅图片,返回一个形状和原图片相同的图片(翻转后)
          image:3维tensor,形状为[height, width, channels].
      • flip_up_down(…): 上下翻转
      • transpose_image(…): 对角线翻转
      • random_flip_left_right(…): 随机左右翻转
    • 颜色变换
      使用翻转能够来”增长数据集”之外,调节颜色属性也是一样颇有用的方法,这里主要有调整亮度,对比度,饱和度,色调等方法.以下:
      • adjust_brightness(…): 调整亮度
        • 做用:调节亮度
          参数:
          image: tensor,原图片
          delta: 标量,待加到像素值上面的值.
      • random_brightness(…): 随机调整亮度
      • adjust_contrast(…): 调整对比度
      • random_contrast(…): 随机调整亮度
      • adjust_saturation(…): 调整饱和度
      • random_saturation(…): 随机调整饱和度
      • adjust_hue(…): 调整色调
      • random_hue(…): 随机调整色调

案例:狗图片读取

  • 读取流程:
    • 构造图片文件名队列
    • 创造一个图片读取器,利用图片读取器去读取队列中的内容
    • 对读取到的图片内容进行解码
    • 对解码后的图片内容进行特征值处理,进行形状和大小的固定
    • 对图片数据进行批处理
    • 手动开启子线程去进行批处理读取到队列的操做
      • 建立用于线程回收的协调器
      • 开启子线程去读取数据
    • 获取样本数据
    • 回收子线程
  • 代码

    """
    狗图片读取案例

    """
    import tensorflow as tf

    import os

    def picture_read(file_list):
    """
    读取狗图片数据到张量
    :param file_list:路径+文件名的列表
    :return:
    """
    # 1. 构造图片文件名队列
    # 返回文件名队列
    file_queue = tf.train.string_input_producer(file_list)
    # 2. 构造一个图片读取器,利用图片读取器去读取队列中的内容
    reader = tf.WholeFileReader()
    # 默认一次读取一张图片,没有形状
    key, value = reader.read(file_queue)
    # 3. 对读取到的图片内容进行解码
    # 数据类型从string---->unit8
    # 形状从()---->(???)
    image = tf.image.decode_jpeg(value)
    # 4. 图片特征值处理:对图片进行批处理以前,进行形状与大小的固定
    # 把图片固定成统一大小的目的是由于训练的数据集的每一个样本的特征值应该相同。
    # 设置成固定的大小
    image_resized = tf.image.resize_images(image, size=[200, 200])
    # 设置固定的通道数,使用静态改变形状的方法:set_shape()
    image_resized.set_shape([200, 200, 3])
    # 5. 对图片数据进行批处理,批处理以前,每一个样本的形状必须固定
    image_batch = tf.train.batch([image_resized], batch_size=10, num_threads=4, capacity=10)

    # 在默认图中进行会话操做
      g = tf.get_default_graph()
      with g.as_default():
          # 开启会话
          with tf.Session() as sess:
              # 1 手动开启子线程去进行批处理读取到队列操做
              # 1.1 建立用于线程回收的协调器
              coord = tf.train.Coordinator()
              # 1.2 开启子线程去读取数据,返回子线程实例
              threads = tf.train.start_queue_runners(sess=sess, coord=coord)
              # 2. 获取样本数据
              pic_data = sess.run([image_batch])
              print(pic_data)
              # 3. 回收子线程
              coord.request_stop()
              coord.join(threads)

    if name == 'main':
    # 生成文件名列表
    file_name_list = os.listdir('../data/dog/')
    # 构造文件名+路径列表
    file_list = [os.path.join('../data/dog/', file) for file in file_name_list]
    # 图片文件读取
    picture_read(file_list)

8.7 TensorFlow利用二进制方式读取CIFAR10数据集

  • CIFAR10数据集的二进制版本介绍
    • 二进制版本包含文件data_batch_1.bin,data_batch_2.bin,...,data_batch_5.bin以及test_batch.bin。这些文件中的每个格式以下,数据中每一个样本包含了特征值和目标值:
      • <1×标签> <3072×像素> 
        ... 
        <1×标签> <3072×像素>
    • 第一个字节是第一个图像的标签,它是一个0-9范围内的数字。接下来的3072个字节是图像像素的值。前1024个字节是红色通道值,下1024个绿色,最后1024个蓝色。值以行优先顺序存储,所以前32个字节是图像第一行的红色通道值。 每一个文件都包含10000个这样的3073字节的“行”图像,但没有任何分隔行的限制。所以每一个文件应该彻底是30730000字节长。
  • CIFAR10 二进制数据读取
    • 读取流程:
      • 构造文件名队列
        • 生成文件名列表:os.listdir(path)
        • 生成文件名+路径列表:os.path.join(path,file_name)
        • 构造文件名队列:tf.train.string_input_producer(file_name_list)
      • 使用二进制读取器读取内容
        • 建立二进制读取器实例:tf.FixedLengthRecordReader(一个样本占的字节数)
        • 读取内容:reader.read(file_queue)
      • 对读取的数据进行解码:tf.decode_raw(value,tf.uint8)
      • 标签、图片的数据类型转换、形状转换
        • 数据类型转换:tf.cast(须要转换的数据,转换的数据类型)
        • 固定图片的特征值形状:
          • depth_major=tf.shape(image,[channel,height,width])从(3072,)--->[channel,height,width]
          • tf.transpose(depth_major,[1,2,0])[channel,height,width]--->[heigt,width,channel]
      • 批处理图片数据:image_batch,label_batch=tf.train.batch([image_reshaped,label_cast],batch_size=,num_threads=,capacity=)
      • 手动开启子线程去进行数据的批处理读取到队列的操做
        • 建立用于线程回收的协调器:coord=tf.train.Coordinator()
        • 开启子线程读取数据到队列:threads=tf.train.start_queue_runners(sess=sess,coord=coord)
      • 获取图片样本数据:pic_data = sess.run(image_batch,label_batch)
      • 回收子线程:
        • coord.request_stop()
        • coord.join(threads)
    • 代码
      """
      CIFAR10数据集二进制版本的读取
      """
      import tensorflow as tf
      import os

      class CifarRead(object):
      """
      CIFAR二进制文件的读取
      """

      def __init__(self, filename_list):
            """
            定义一些图片的样本属性
            :param filename_list:
            """
            self.filename_list = filename_list
            self.height = 32
            self.width = 32
            self.channel = 3
      
            self.label_bytes = 1
            self.image_bytes = self.height * self.width * self.channel
            self.bytes = self.label_bytes + self.image_bytes
      
        def read_and_decode(self):
            """
            读取二进制原始数据并解码成张量
            :return:
            """
            # 1. 构造文件队列
            file_queue = tf.train.string_input_producer(self.filename_list)
            # 2. 使用二进制读取器读取内容
            # 2.1 建立二进制读取器实例,实在一次读取的字节数
            reader = tf.FixedLengthRecordReader(self.bytes)
            # 2.2 读取内容,一共读取1+3072=3073个字节,此时的value是某个文件的某个样本
            key, value = reader.read(file_queue)
            # 3. 对二进制数据进行解码 decode_raw
            label_image = tf.decode_raw(value, tf.uint8)
            # 4. 切分图片的特征值与目标值:使用tf.slice(数据集,[起始下标],[结束下标])切片
            label = tf.slice(label_image, [0], [self.label_bytes])
            image = tf.slice(label_image, [self.label_bytes], [self.image_bytes])
            # 5. 标签、图片的类型转换、形状转换
            # 5.1 标签数据类型转换 tf.cast()
            label_cast = tf.cast(label, tf.int32)
            image = tf.cast(image, tf.float32)
            # 5.2 固定图片的特征值形状
            # 5.2.1 shape:(3072,)--->[channel,height,width]
            depth_major = tf.reshape(image, [self.channel, self.height, self.width])
            print(depth_major)
            # 5.2.2 tf.transpose:[channel,height,width]--->[heigt,width,channel]
            image_reshaped = tf.transpose(depth_major, [1, 2, 0])
            print(image_reshaped)
            # 6.批处理图片数据
            image_batch, label_batch = tf.train.batch([image_reshaped, label_cast], batch_size=10, num_threads=4,
                                                      capacity=10)
            print(image_batch)
            print(label_batch)
            return image_batch, label_batch
      
        def run(self):
            """
            执行文件读取逻辑
            :return:
            """
            # 读取二进制原始数据并解码成张量,形状固定于批处理
            image_batch, label_batch = self.read_and_decode()
            # 开启会话
            with tf.Session() as sess:
                # 1. 手动开启子线程去进行数据的批处理读取到队列的操做
                # 1.1 建立用于线程回收的协调器
                coord = tf.train.Coordinator()
                # 1.2 开启子线程去读取数据
                threads = tf.train.start_queue_runners(sess=sess, coord=coord)
                # 2. 获取样本数据
                pic_data = sess.run([image_batch, label_batch])
                print(pic_data)
                # 3. 回收子线程
                coord.request_stop()
                coord.join(threads)

      if name == 'main':
      # 生成文件名列表
      filename_list = os.listdir('../data/cifar10/cifar-10-batches-bin/')
      # 生成文件名加路径列表
      filename_list = [os.path.join('../data/cifar10/cifar-10-batches-bin/', file) for file in filename_list if
      file[-3:] == 'bin']
      # 实例化类
      cr = CifarRead(filename_list)
      cr.run()

  • 注意:
    • 一、image (3072, ) —>tf.reshape(image, [])里面的shape默认是[channel, height, width], 因此得先从[depth height width] to [depth, height, width]。
    • 二、而后使用tf.transpose, 将刚才的数据[depth, height, width],变成Tensorflow默认的[height, width, channel]
      • 假设1, 2, 3, 4-红色 5, 6, 7, 8-绿色 9, 10, 11, 12-蓝色
        若是通道在最低维度0[channel, height, width],RGB三颜色分红三组,在第一维度上找到三个RGB颜色
        若是通道在最高维度2[height, width, channel],在第三维度上找到RGB三个颜色

      一、想要变成:[2 height, 2width, 3channel],可是输出结果不对

      In [7]: tf.reshape([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [2, 2, 3]).eval()
      Out[7]:
      array([[[ 1, 2, 3],
      [ 4, 5, 6]],

      [[ 7,  8,  9],
          [10, 11, 12]]], dtype=int32)

      二、因此要这样去作

      In [8]: tf.reshape([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], [3, 2, 2]).eval()
      Out[8]:
      array([[[ 1, 2],
      [ 3, 4]],

      [[ 5,  6],
          [ 7,  8]],
      
         [[ 9, 10],
          [11, 12]]], dtype=int32)

      接着使用tf.transpose ,0,1,2表明三个维度标记

      Convert from [depth, height, width] to [height, width, depth].

      0,1,2-----> 1, 2, 0

      In [17]: tf.transpose(depth_major, [1, 2, 0]).eval()
      Out[17]:
      array([[[ 1, 5, 9],
      [ 2, 6, 10]],

      [[ 3,  7, 11],
          [ 4,  8, 12]]], dtype=int32)

8.8 CIFAR10类图片的数据的TFRecords存储和读取

数据存入TFRecords文件

什么是TFRecords文件

  • TFRecords实际上是一种二进制文件,它能更好的利用内存,更方便复制和移动,而且不须要单独的标签文件。
  • TFRecords文件是TensorFlow的标准格式,使用TFRecords文件保存记录的方法能够容许你将任意数据转换为TensorFlow所支持的格式,这种方法可使TensorFlow的数据集更容易与网络应用架构相匹配。
  • 文件格式: *.tfrecords

什么是Example结构?

  • tf.train.Example为协议内存块(protocol buffer)
    • 协议内存块包含了字段Features
    • Features包含了一个Feature字段
    • Feature字段中包含要写入的数据、并指明数据类型。这是一个样本的结构,批数据须要循环的存入这样的结构。
      example = tr.train.Example(features=tf.train.Features(feature={
      "特征值字段名":tf.train.Feature(特征值数据类型列表=tf.train.特征值数据类型列表类(value=[特征值字段名])),
      "标签值字段名":tf.train.Feature(标签值数据类型列表=tf.train.标签值数据类型列表类(value=[标签值字段名]))
      }))

    • tf.train.Example(features=)
      • 做用:生成协议内存块
      • features:tf.train.Features类型的特征实例
    • tf.train.Features(feature=)
      • 做用:构建每一个样本的信息键值对
      • feature:字典数据,key为要保存的名字
      • value:为tf.train.Feature实例
    • tf.train.Feature(options)
      • options:特征值数据类型列表,例如
        • bytes_list=tf.train. BytesList(value=[Bytes])
        • int64_list=tf.train. Int64List(value=[Value])
      • 属性的取值能够为字符串(BytesList ),浮点数列表(FloatList )或整数列表(Int64List )。例如咱们能够将图片转换为字符串进行存储,图像对应的类别标号做为整数存储,而用于回归任务的ground-truth能够做为浮点数存储。
      • 支持存入的类型以下:
        • tf.train.Int64List(value=[Value])
        • tf.train.BytesList(value=[Bytes])
        • tf.train.FloatList(value=[value])
  • 协议内存块结构很好的解决了数据和标签或者其它属性数据存储到同一个文件中的问题。

写入数据到TFRecords中的主要步骤:

  • 获取数据, 将数据填入到tf.train.Example协议内存块(protocol buffer)。
  • 将协议内存块序列化为一个字符串。example.SerializeToString()
  • 经过tf.python_io.TFRecordWriter写入到TFRecords文件。

写入数据到TFRecords中的示例代码:

def wite_to_tfrecords(self, image_batch, label_batch):
        """
        将文件写入到TFRecords文件中
        :param image_batch:
        :param label_batch:
        :return:
        """
        # 1. 创建TFRecords文件存储器
        writer = tf.python_io.TFRecordWriter('./1.tfrecords')  # 设置路径及文件名
        # 2. 循环取出每一个样本的值,构造example协议块
        for i in range(10):  # 由于每批量有10个样本数据
            # 2.1 取出图片的值
            # 写入文件的是值,而不是tensor类型,写入example须要bytes类型数据,须要用tostring()来转化
            image = image_batch[i].eval().tostring()
            # 2.2 取出标签值,写入example中须要int类型,因此须要强制转换成int类型
            label = int(label_batch[i].eval()[0])
            # 2.3 构造每一个样本的example协议块
            feature = {
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }
            features = tf.train.Features(feature=feature)
            example = tf.train.Example(features=features)
            # 2.4 将example协议块序列化
            example_serialized = example.SerializeToString()
            # 2.5 写入序列化后的值
            writer.write(example_serialized)  # 此处实际上是将其压缩成一个二进制数据

        # 3. 关闭文件存储器
        writer.close()
        return None

8.9 从TFRecords文件中读取数据

解析example

feature = tr.parse_single_example(values,features={
    "image":tf.FixedLenFeature([],tf.string),
    "label":tf.FixedLenFeature([],tf.int64)
})
  • tf.parse_single_example(serialized,features=None,name=None)
    • 做用:解析一个单一的Example原型
    • serialize:标量字符串Tensor,一个序列化的Example
    • features:dict字典数据,键为读取的名字,值为FixedLenFeature
  • tf.FixedLenFeature(shape,dtype)
    • shape:输入数据的形状,通常不指定,为空列表
    • dtype:输入数据类型,与存储进文件的类型要一致
    • 类型只能是float32,int64,string

从TFRecords文件中读取数据的主要步骤:

  • 构造文件队列:tf.train.string_input_producer([文件名列表])
  • 实例化TFRecords文件阅读器:reader = tf.TFRecordReader()
    • 读取数据:key, value = reader.read(file_queue)
  • 解析example协议块:feature=tf.parse_single_example(serialized,features=None,name=None)
    • 解析一个单一的Example原型
    • serialized:标量字符串Tensor,一个序列化的Example
    • features:dict字典数据,键为读取的名字,值为FixedLenFeature
    • return:一个键值对组成的字典,键为读取的名字
  • 处理数据
    • 处理标签数据:label = tf.cast(feature["label"], tf.int32)
      • cast()只能在int和float之间进行转换,将数据类型int64 转换为int32
    • 处理特征值数据:image = tf.decode_raw(feature['image'], tf.uint8)
      • 若是以前用了tostring(),那么必需要用decode_raw()转换为最初的int类型
      • decode_raw()能够将数据从string,bytes转换为int,float类型
  • 转换特征值的形状(形状未固定,静态方式:tf.set_shape;形状已固定,动态方式:tf.reshape):
    • image_tensor = tf.reshape(image,[self.height,self.width,self.channel])
  • 批处理:image_batch,label_batch = tf.train.batch([image_tensor,label],batch_size=10,num_threads=4,capacity=10)

从TFRecords文件中读取数据示例代码:

def read_to_tfrecords(self):
        """
        从TFRecords文件中读取图片数据(解析example)
        :return:
        """
        pass
        # 1. 构造文件队列
        file_queue = tf.train.string_input_producer(['./1.tfrecords'])  # 参数为文件名列表
        # 2. 实例化TFRecords文件阅读器
        reader = tf.TFRecordReader()
        # 读取数据
        key, value = reader.read(file_queue)
        # 3. 解析example协议块,返回值是字典
        features = {
            "image": tf.FixedLenFeature([], tf.string),
            "label": tf.FixedLenFeature([], tf.int64)
        }
        feature = tf.parse_single_example(value, features=features)
        # 4. 处理标签数据
        # cast()只能在int和float之间进行转换,将数据类型int64 转换为int32
        label = tf.cast(feature["label"], tf.int32)
        # 处理图片数据
        # 因为是一个string,要进行解码,
        # 将字节转换为数字向量表示,字节为一字符串类型的张量
        # 若是以前用了tostring(),那么必需要用decode_raw()转换为最初的int类型
        # decode_raw()能够将数据从string,bytes转换为int,float类型
        image = tf.decode_raw(feature['image'], tf.uint8)
        # 5. 转换图片的形状,此处须要用动态形状进行转换
        image_tensor = tf.reshape(image, [self.height, self.width, self.channel])
        # 6. 批处理
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=4, capacity=10)
        return image_batch, label_batch

CIFAR10类图片的数据的TFRecords存储和读取示例代码

import tensorflow as tf
import os

"""
读取二进制文件转换成张量,写进TFRecords,同时读取TFRcords
"""

# 命令行参数
FLAGS = tf.app.flags.FLAGS  # 获取值
tf.app.flags.DEFINE_string("tfrecord_dir", "./tmp/cifar10.tfrecords", "写入图片数据文件的文件名")

# 读取二进制转换文件
class CifarRead(object):
    """
    读取二进制文件转换成张量,写进TFRecords,同时读取TFRcords
    """

    def __init__(self, file_list):
        """
        初始化图片参数
        :param file_list:图片的路径名称列表
        """

        # 文件列表
        self.file_list = file_list

        # 图片大小,二进制文件字节数
        self.height = 32
        self.width = 32
        self.channel = 3
        self.label_bytes = 1
        self.image_bytes = self.height * self.width * self.channel
        self.bytes = self.label_bytes + self.image_bytes

    def read_and_decode(self):
        """
        解析二进制文件到张量
        :return: 批处理的image,label张量
        """
        # 1.构造文件队列
        file_queue = tf.train.string_input_producer(self.file_list)

        # 2.阅读器读取内容
        reader = tf.FixedLengthRecordReader(self.bytes)

        key, value = reader.read(file_queue)  # key为文件名,value为元组

        print(value)

        # 3.进行解码,处理格式
        label_image = tf.decode_raw(value, tf.uint8)
        print(label_image)

        # 处理格式,image,label
        # 进行切片处理,标签值
        # tf.cast()函数是转换数据格式,此处是将label二进制数据转换成int32格式
        label = tf.cast(tf.slice(label_image, [0], [self.label_bytes]), tf.int32)

        # 处理图片数据
        image = tf.slice(label_image, [self.label_bytes], [self.image_bytes])
        print(image)

        # 处理图片的形状,提供给批处理
        # 由于image的形状已经固定,此处形状用动态形状来改变
        depth_major = tf.reshape(image, [self.channel, self.height, self.width])
        image_tensor = tf.transpose(depth_major, [1, 2, 0])
        print(image_tensor)

        # 批处理图片数据
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=1, capacity=10)

        return image_batch, label_batch

    def write_to_tfrecords(self, image_batch, label_batch):
        """
        将文件写入到TFRecords文件中
        :param image_batch:
        :param label_batch:
        :return:
        """

        # 创建TFRecords文件存储器
        writer = tf.python_io.TFRecordWriter('./1.tfrecords')  # 传进去命令行参数

        # 循环取出每一个样本的值,构造example协议块
        for i in range(10):
            # 取出图片的值,  #写进去的是值,而不是tensor类型,
            # 写入example须要bytes文件格式,将tensor转化为bytes用tostring()来转化
            image = image_batch[i].eval().tostring()

            # 取出标签值,写入example中须要使用int形式,因此须要强制转换int
            label = int(label_batch[i].eval()[0])

            # 构造每一个样本的example协议块
            example = tf.train.Example(features=tf.train.Features(feature={
                "image": tf.train.Feature(bytes_list=tf.train.BytesList(value=[image])),
                "label": tf.train.Feature(int64_list=tf.train.Int64List(value=[label]))
            }))

            # 写进去序列化后的值
            writer.write(example.SerializeToString())  # 此处实际上是将其压缩成一个二进制数据

        writer.close()

        return None

    def read_from_tfrecords(self):
        """
        从TFRecords文件当中读取图片数据(解析example)
        :param self:
        :return: image_batch,label_batch
        """

        # 1.构造文件队列
        file_queue = tf.train.string_input_producer(['./1.tfrecords'])  # 参数为文件名列表

        # 2.构造阅读器
        reader = tf.TFRecordReader()

        key, value = reader.read(file_queue)

        # 3.解析协议块,返回的值是字典
        feature = tf.parse_single_example(value, features={
            "image": tf.FixedLenFeature([], tf.string),
            "label": tf.FixedLenFeature([], tf.int64)
        })

        # feature["image"],feature["label"]
        # 处理标签数据    ,cast()只能在int和float之间进行转换
        label = tf.cast(feature["label"], tf.int32)  # 将数据类型int64 转换为int32

        # 处理图片数据,因为是一个string,要进行解码,  #将字节转换为数字向量表示,字节为一字符串类型的张量
        # 若是以前用了tostring(),那么必需要用decode_raw()转换为最初的int类型
        # decode_raw()能够将数据从string,bytes转换为int,float类型的
        image = tf.decode_raw(feature["image"], tf.uint8)

        # 转换图片的形状,此处须要用动态形状进行转换
        image_tensor = tf.reshape(image, [self.height, self.width, self.channel])

        # 4.批处理
        image_batch, label_batch = tf.train.batch([image_tensor, label], batch_size=10, num_threads=1, capacity=10)

        return image_batch, label_batch

    def run(self):
        """
        实现读取数据的主逻辑
        :return:
        """
        # 读取二进制文件
        # image_batch,label_batch = self.read_and_decode()

        # 从已经存储的TFRecords文件中解析出原始数据
        image_batch, label_batch = self.read_from_tfrecords()

        with tf.Session() as sess:
            # 线程协调器
            coord = tf.train.Coordinator()

            # 开启线程
            threads = tf.train.start_queue_runners(sess, coord=coord)

            print(sess.run([image_batch, label_batch]))

            print("存进TFRecords文件")
            # self.write_to_tfrecords(image_batch,label_batch)
            print("存进文件完毕")

            # 回收线程
            coord.request_stop()
            coord.join(threads)


if __name__ == '__main__':
    # 找到文件路径,名字,构造路径+文件名的列表,"A.csv"...
    # os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表
    filename = os.listdir('../data/cifar10/cifar-10-batches-bin/')

    # 加上路径
    file_list = [os.path.join('../data/cifar10/cifar-10-batches-bin/', file) for file in filename if file[-3:] == "bin"]

    # 建立一个文件读取对象
    cr = CifarRead(file_list)
    # 开始运行
    cr.run()

9 Tensorflow中其它基础API与高级API

9.1 基础API

  • tf.app:用于命令行参数设置
    • 这个模块至关于为 TensorFlow 进行的脚本撮供了一个 main 函数入口,能够定义脚本运行的 flags。
    • 使用终端命令行的方式去运行程序,并设置参数
  • tf.image
    • TensorFlow 的图像处理操做。主要是一些颜色变换、变形和图像的编码和解码。
  • tf.gfile
    • 这个模块提供了一组文件操做函数。
  • tf.summary
    • 用来生成 TensorBoard 可用的统计日志,目前 Summary 主要提供了 4 种类型:audio、image、histogram、scalar
  • tf.python_io
    • 用来读写 TFRecords文件
  • tf.train
    • 这个模块提供了一些训练器,与 tf.nn 组合起来,实现一些网络的优化计算。
  • tf.nn
    • 这个模块提供了一些构建神经网络的底层函数。 TensorFlow 构建网络的核心模块。其中包含了添加各类层的函数,好比添加卷积层、池化层等。

9.2 高级API

  • tf.keras
    • Keras 原本是一个独立的深度学习库,tensorflow将其学习过来,增长这部分模块在于快速构建模型。
  • tf.layers
    • 高级 API,以更高级的概念层来定义一个模型。相似tf.keras。
  • tf.contrib
    • tf.contrib.layers提供够将计算图中的 网络层、正则化、摘要操做、是构建计算图的高级操做,可是tf.contrib包含不稳定和实验代码,有可能之后API会改变。
  • tf.estimator
    • 一个 Estimator 至关于 Model + Training + Evaluate 的合体。在模块中,已经实现了几种简单的分类器和回归器,包括:Baseline,Learning 和 DNN。这里的 DNN 的网络,只是全链接网络,没有提供卷积之类的。

10 TensorFlow训练稀疏模型

  • 全部提出的优化算法都会产生一个密集的模型,这意味着大多数参数都是非零的。 若是咱们须要一个在运行时速度很是快速的模型,或者须要模型占用较少的内存,咱们可能更喜欢用一个稀疏模型来代替。
  • 训练一个稀疏模型的一个小方法就是像日常同样训练模型,而后摆脱值小的权重(将它们设置为 0)。另外一个方法是在训练过程当中应用强 l1 正则化,由于它会推进优化器尽量多地消除权重。
  • 可是,在某些状况下,这些技术可能仍然不足。最后一个方法时应用对偶平均,一般称为FTRL(Follow The Regularized Leader)。当与 l1 正则化一块儿使用时,这种技术一般会导出一个很是稀疏的模型。 TensorFlow 在FTRLOptimizer类中实现称为 FTRL-Proximal 的 FTRL 变体。

TensorFlow 中的 layers 模块

TensorFlow 中的 layers 模块提供用于深度学习的更高层次封装的 API,利用它咱们能够轻松地构建模型,这一节咱们就来看下这个模块的 API 的具体用法。

11 TensorFlow中的layers模块

11.1 概览

layers 模块的路径写法为 tf.layers,这个模块定义在 tensorflow/python/layers/layers.py,其官方文档地址为:https://www.tensorflow.org/api_docs/python/tf/layers,TensorFlow 版本为 1.5。

这里面提供了多个类和方法以供使用,下面咱们分别予以介绍。

11.2 方法

tf.layers 模块提供的方法有:

  • Input(…): 用于实例化一个输入 Tensor,做为神经网络的输入。
  • average_pooling1d(…): 一维平均池化层
  • average_pooling2d(…): 二维平均池化层
  • average_pooling3d(…): 三维平均池化层
  • batch_normalization(…): 批量标准化层
  • conv1d(…): 一维卷积层
  • conv2d(…): 二维卷积层
  • conv2d_transpose(…): 二维反卷积层
  • conv3d(…): 三维卷积层
  • conv3d_transpose(…): 三维反卷积层
  • dense(…): 全链接层
  • dropout(…): Dropout层
  • flatten(…): Flatten层,即把一个 Tensor 展平
  • max_pooling1d(…): 一维最大池化层
  • max_pooling2d(…): 二维最大池化层
  • max_pooling3d(…): 三维最大池化层
  • separable_conv2d(…): 二维深度可分离卷积层

11.2.1 tf.layers.Input

tf.layers.Input() 这个方法是用于输入数据的方法,其实相似于 tf.placeholder,至关于一个占位符的做用,固然也能够经过传入 tensor 参数来进行赋值。

Input(
    shape=None,
    batch_size=None,
    name=None,
    dtype=tf.float32,
    sparse=False,
    tensor=None
)

参数说明以下:

  • shape:可选,默认 None,是一个数字组成的元组或列表,可是这个 shape 比较特殊,它不包含 batch_size,好比传入的 shape 为 [32],那么它会将 shape 转化为 [?, 32],这里必定须要注意。
  • batch_size:可选,默认 None,表明输入数据的 batch size,能够是数字或者 None。
  • name:可选,默认 None,输入层的名称。
  • dtype:可选,默认 tf.float32,元素的类型。
  • sparse:可选,默认 False,指定是否以稀疏矩阵的形式来建立 placeholder。
  • tensor:可选,默认 None,若是指定,那么建立的内容便再也不是一个 placeholder,会用此 Tensor 初始化。

返回值: 返回一个包含历史 Meta Data 的 Tensor。

咱们用一个实例来感觉一下:

x = tf.layers.Input(shape=[32])
print(x)
y = tf.layers.dense(x, 16, activation=tf.nn.softmax)
print(y)

首先咱们用 Input() 方法初始化了一个 placeholder,这时咱们没有传入 tensor 参数,而后调用了 dense() 方法构建了一个全链接网络,激活函数使用 softmax,而后将两者输出,结果以下:

Tensor("input_layer_1:0", shape=(?, 32), dtype=float32)
Tensor("dense/Softmax:0", shape=(?, 16), dtype=float32)

这时咱们发现,shape 它给咱们作了转化,原本是 [32],结果它给转化成了 [?, 32],即第一维表明 batch_size,因此咱们须要注意,在调用此方法的时候不须要去关心 batch_size 这一维。

若是咱们在初始化的时候传入一个已有 Tensor,例如:

data = tf.constant([1, 2, 3])
x = tf.layers.Input(tensor=data)
print(x)

结果以下:

data = tf.constant([1, 2, 3])
x = tf.layers.Input(tensor=data)
print(x)

能够看到它能够自动计算出其 shape 和 dtype。

11.2.2 tf.layers.batch_normalization

此方法是批量标准化的方法,通过处理以后能够加速训练速度,其定义在 tensorflow/python/layers/normalization.py,论文能够参考:http://arxiv.org/abs/1502.03167 “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift”。

batch_normalization(
    inputs,
    axis=-1,
    momentum=0.99,
    epsilon=0.001,
    center=True,
    scale=True,
    beta_initializer=tf.zeros_initializer(),
    gamma_initializer=tf.ones_initializer(),
    moving_mean_initializer=tf.zeros_initializer(),
    moving_variance_initializer=tf.ones_initializer(),
    beta_regularizer=None,
    gamma_regularizer=None,
    beta_constraint=None,
    gamma_constraint=None,
    training=False,
    trainable=True,
    name=None,
    reuse=None,
    renorm=False,
    renorm_clipping=None,
    renorm_momentum=0.99,
    fused=None,
    virtual_batch_size=None,
    adjustment=None
)

参数说明以下:

  • inputs:必需,即输入数据。
  • axis:可选,默认 -1,即进行标注化操做时操做数据的哪一个维度。
  • momentum:可选,默认 0.99,即动态均值的动量。
  • epsilon:可选,默认 0.01,大于0的小浮点数,用于防止除0错误。
  • center:可选,默认 True,若设为True,将会将 beta 做为偏置加上去,不然忽略参数 beta
  • scale:可选,默认 True,若设为True,则会乘以gamma,不然不使用gamma。当下一层是线性的时,能够设False,由于scaling的操做将被下一层执行。
  • beta_initializer:可选,默认 zeros_initializer,即 beta 权重的初始方法。
  • gamma_initializer:可选,默认 ones_initializer,即 gamma 的初始化方法。
  • moving_mean_initializer:可选,默认 zeros_initializer,即动态均值的初始化方法。
  • moving_variance_initializer:可选,默认 ones_initializer,即动态方差的初始化方法。
  • beta_regularizer: 可选,默认None,beta 的正则化方法。
  • gamma_regularizer: 可选,默认None,gamma 的正则化方法。
  • beta_constraint: 可选,默认None,加在 beta 上的约束项。
  • gamma_constraint: 可选,默认None,加在 gamma 上的约束项。
  • training:可选,默认 False,返回结果是 training 模式。
  • trainable:可选,默认为 True,布尔类型,若是为 True,则将变量添加 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可选,默认 None,层名称。
  • reuse:可选,默认 None,根据层名判断是否重复利用。
  • renorm:可选,默认 False,是否要用 Batch Renormalization (https://arxiv.org/abs/1702.03275)
  • renorm_clipping:可选,默认 None,是否要用 rmax、rmin、dmax 来 scalar Tensor。
  • renorm_momentum,可选,默认 0.99,用来更新动态均值和标准差的 Momentum 值。
  • fused,可选,默认 None,是否使用一个更快的、融合的实现方法。
  • virtual_batch_size,可选,默认 None,是一个 int 数字,指定一个虚拟 batch size。
  • adjustment,可选,默认 None,对标准化后的结果进行适当调整的方法。

最后的一些参数说明不够详尽,更详细的用法参考:https://www.tensorflow.org/api_docs/python/tf/layers/batch_normalization

其用法很简单,在输入数据后面加一层 batch_normalization() 便可:

x = tf.layers.Input(shape=[32])
x = tf.layers.batch_normalization(x)
y = tf.layers.dense(x, 20)

11.2.3 tf.layers.dense

dense,即全链接网络,layers 模块提供了一个 dense() 方法来实现此操做,定义在 tensorflow/python/layers/core.py 中,下面咱们来讲明一下它的用法。

dense(
    inputs,
    units,
    activation=None,
    use_bias=True,
    kernel_initializer=None,
    bias_initializer=tf.zeros_initializer(),
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    trainable=True,
    name=None,
    reuse=None
)

参数说明以下:

  • inputs:必需,即须要进行操做的输入数据。
  • units:必须,即神经元的数量。
  • activation:可选,默认为 None,若是为 None 则是线性激活。
  • use_bias:可选,默认为 True,是否使用偏置。
  • kernel_initializer:可选,默认为 None,即权重的初始化方法,若是为 None,则使用默认的 Xavier 初始化方法。
  • bias_initializer:可选,默认为零值初始化,即偏置的初始化方法。
  • kernel_regularizer:可选,默认为 None,施加在权重上的正则项。
  • bias_regularizer:可选,默认为 None,施加在偏置上的正则项。
  • activity_regularizer:可选,默认为 None,施加在输出上的正则项。
  • kernel_constraint,可选,默认为 None,施加在权重上的约束项。
  • bias_constraint,可选,默认为 None,施加在偏置上的约束项。
  • trainable:可选,默认为 True,布尔类型,若是为 True,则将变量添加到 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可选,默认为 None,卷积层的名称。
  • reuse:可选,默认为 None,布尔类型,若是为 True,那么若是 name 相同时,会重复利用。

返回值: 全链接网络处理后的 Tensor。

下面咱们用一个实例来感觉一下它的用法:

x = tf.layers.Input(shape=[32])
print(x)
y1 = tf.layers.dense(x, 16, activation=tf.nn.relu)
print(y1)
y2 = tf.layers.dense(y1, 5, activation=tf.nn.sigmoid)
print(y2)

首先咱们用 Input 定义了 [?, 32] 的输入数据,而后通过第一层全链接网络,此时指定了神经元个数为 16,激活函数为 relu,接着输出结果通过第二层全链接网络,此时指定了神经元个数为 5,激活函数为 sigmoid,最后输出,结果以下:

x = tf.layers.Input(shape=[32])
print(x)
y1 = tf.layers.dense(x, 16, activation=tf.nn.relu)
print(y1)
y2 = tf.layers.dense(y1, 5, activation=tf.nn.sigmoid)
print(y2)

能够看到输出结果的最后一维度就等于神经元的个数,这是很是容易理解的。

11.2.4 tf.layers.convolution

convolution,即卷积,这里提供了多个卷积方法,如 conv1d()、conv2d()、conv3d(),分别表明一维、二维、三维卷积,另外还有 conv2d_transpose()、conv3d_transpose(),分别表明二维和三维反卷积,还有 separable_conv2d() 方法表明二维深度可分离卷积。它们定义在 tensorflow/python/layers/convolutional.py 中,其用法都是相似的,在这里以 conv2d() 方法为例进行说明。

conv2d(
    inputs,
    filters,
    kernel_size,
    strides=(1, 1),
    padding='valid',
    data_format='channels_last',
    dilation_rate=(1, 1),
    activation=None,
    use_bias=True,
    kernel_initializer=None,
    bias_initializer=tf.zeros_initializer(),
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    trainable=True,
    name=None,
    reuse=None
)

参数说明以下:

  • inputs:必需,即须要进行操做的输入数据。
  • filters:必需,是一个数字,表明了输出通道的个数,即 output_channels。
  • kernel_size:必需,卷积核大小,必须是一个数字(高和宽都是此数字)或者长度为 2 的列表(分别表明高、宽)。
  • strides:可选,默认为 (1, 1),卷积步长,必须是一个数字(高和宽都是此数字)或者长度为 2 的列表(分别表明高、宽)。
  • padding:可选,默认为 valid,padding 的模式,有 valid 和 same 两种,大小写不区分。
  • data_format:可选,默认 channels_last,分为 channels_last 和 channels_first 两种模式,表明了输入数据的维度类型,若是是 channels_last,那么输入数据的 shape 为 (batch, height, width, channels),若是是 channels_first,那么输入数据的 shape 为 (batch, channels, height, width)。
  • dilation_rate:可选,默认为 (1, 1),卷积的扩张率,如当扩张率为 2 时,卷积核内部就会有边距,3×3 的卷积核就会变成 5×5。
  • activation:可选,默认为 None,若是为 None 则是线性激活。
  • use_bias:可选,默认为 True,是否使用偏置。
  • kernel_initializer:可选,默认为 None,即权重的初始化方法,若是为 None,则使用默认的 Xavier 初始化方法。
  • bias_initializer:可选,默认为零值初始化,即偏置的初始化方法。
  • kernel_regularizer:可选,默认为 None,施加在权重上的正则项。
  • bias_regularizer:可选,默认为 None,施加在偏置上的正则项。
  • activity_regularizer:可选,默认为 None,施加在输出上的正则项。
  • kernel_constraint,可选,默认为 None,施加在权重上的约束项。
  • bias_constraint,可选,默认为 None,施加在偏置上的约束项。
  • trainable:可选,默认为 True,布尔类型,若是为 True,则将变量添加到 GraphKeys.TRAINABLE_VARIABLES 中。
  • name:可选,默认为 None,卷积层的名称。
  • reuse:可选,默认为 None,布尔类型,若是为 True,那么若是 name 相同时,会重复利用。

返回值: 卷积后的 Tensor。

下面咱们用实例感觉一下它的用法:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2, padding='same')
print(y)


      这里咱们首先声明了一个 [?, 20, 20, 3] 的输入 x,而后将其传给 conv2d() 方法,filters 设定为 6,即输出通道为 6,kernel_size 为 2,即卷积核大小为 2 x 2,padding 方式设置为 same,那么输出结果的宽高和原来必定是相同的,可是输出通道就变成了 6,结果以下:

Tensor("conv2d/BiasAdd:0", shape=(?, 20, 20, 6), dtype=float32)

但若是咱们将 padding 方式不传入,使用默认的 valid 模式,代码改写以下:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2)
print(y)

结果以下:

Tensor("conv2d/BiasAdd:0", shape=(?, 19, 19, 6), dtype=float32)

结果就变成了 [?, 19, 19, 6],这是由于步长默认为 1,卷积核大小为 2 x 2,因此获得的结果的高宽即为 (20 – (2 – 1)) x (20 – (2 – 1)) = 19 x 19。

固然卷积核咱们也能够变换大小,传入一个列表形式:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=[2, 3])
print(y)

这时咱们的卷积核大小变成了 2 x 3,即高为 2,宽为 3,结果就变成了 [?, 19, 18, 6],这是由于步长默认为 1,卷积核大小为 2 x 2,因此获得的结果的高宽即为 (20 – (2 – 1)) x (20 – (3 – 1)) = 19 x 18。

若是咱们将步长也设置一下,也传入列表形式:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=[2, 3], strides=[2, 2])
print(y)

这时卷积核大小变成了 2 x 3,步长变成了 2 x 2,因此结果的高宽为 ceil(20 – (2- 1)) / 2 x ceil(20 – (3- 1)) / 2 = 10 x 9,获得的结果即为 [?, 10, 9, 6]。

运行结果以下:

Tensor("conv2d_4/BiasAdd:0", shape=(?, 10, 9, 6), dtype=float32)

另外咱们还能够传入激活函数,或者禁用 bias 等操做,实例以下:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d(x, filters=6, kernel_size=2, activation=tf.nn.relu, use_bias=False)
print(y)

这样咱们就将激活函数改为了 relu,同时禁用了 bias,运行结果以下:

Tensor("conv2d_5/Relu:0", shape=(?, 19, 19, 6), dtype=float32)

另外还有反卷积操做,反卷积顾名思义即卷积的反向操做,即输入卷积的结果,获得卷积前的结果,其参数用法是彻底同样的,例如:

x = tf.layers.Input(shape=[20, 20, 3])
y = tf.layers.conv2d_transpose(x, filters=6, kernel_size=2, strides=2)
print(y)

例如此处输入的图像高宽为 20 x 20,通过卷积核为 2,步长为 2 的反卷积处理,获得的结果高宽就变为了 40 x 40,结果以下:

Tensor("conv2d_transpose/BiasAdd:0", shape=(?, 40, 40, 6), dtype=float32)

11.2.5 tf.layers.pooling

pooling,即池化,layers 模块提供了多个池化方法,这几个池化方法都是相似的,包括 max_pooling1d()、max_pooling2d()、max_pooling3d()、average_pooling1d()、average_pooling2d()、average_pooling3d(),分别表明一维二维三维最大和平均池化方法,它们都定义在 tensorflow/python/layers/pooling.py 中,这里以 max_pooling2d() 方法为例进行介绍。

max_pooling2d(
    inputs,
    pool_size,
    strides,
    padding='valid',
    data_format='channels_last',
    name=None
)

参数说明以下:

  • inputs: 必需,即须要池化的输入对象,必须是 4 维的。
  • pool_size:必需,池化窗口大小,必须是一个数字(高和宽都是此数字)或者长度为 2 的列表(分别表明高、宽)。
  • strides:必需,池化步长,必须是一个数字(高和宽都是此数字)或者长度为 2 的列表(分别表明高、宽)。
  • padding:可选,默认 valid,padding 的方法,valid 或者 same,大小写不区分。
  • data_format:可选,默认 channels_last,分为 channels_last 和 channels_first 两种模式,表明了输入数据的维度类型,若是是 channels_last,那么输入数据的 shape 为 (batch, height, width, channels),若是是 channels_first,那么输入数据的 shape 为 (batch, channels, height, width)。
  • name:可选,默认 None,池化层的名称。

返回值: 通过池化处理后的 Tensor。

下面咱们用一个实例来感觉一下:

x = tf.layers.Input(shape=[20, 20, 3])
print(x)
y = tf.layers.conv2d(x, filters=6, kernel_size=3, padding='same')
print(y)
p = tf.layers.max_pooling2d(y, pool_size=2, strides=2)
print(p)

在这里咱们首先指定了输入 x,shape 为 [20, 20, 3],而后对其进行了卷积计算,而后池化,最后获得池化后的结果。结果以下:

Tensor("input_layer_1:0", shape=(?, 20, 20, 3), dtype=float32)
Tensor("conv2d/BiasAdd:0", shape=(?, 20, 20, 6), dtype=float32)
Tensor("max_pooling2d/MaxPool:0", shape=(?, 10, 10, 6), dtype=float32)

能够看到这里池化窗口用的是 2,步长也是 2,因此本来卷积后 shape 为 [?, 20, 20, 6] 的结果就变成了 [?, 10, 10, 6]。

11.2.6 tf.layers.dropout

dropout 是指在深度学习网络的训练过程当中,对于神经网络单元,按照必定的几率将其暂时从网络中丢弃,能够用来防止过拟合,layers 模块中提供了 dropout() 方法来实现这一操做,定义在 tensorflow/python/layers/core.py。下面咱们来讲明一下它的用法。

dropout(
    inputs,
    rate=0.5,
    noise_shape=None,
    seed=None,
    training=False,
    name=None
)

参数说明以下:

  • inputs:必须,即输入数据。
  • rate:可选,默认为 0.5,即 dropout rate,如设置为 0.1,则意味着会丢弃 10% 的神经元。
  • noise_shape:可选,默认为 None,int32 类型的一维 Tensor,它表明了 dropout mask 的 shape,dropout mask 会与 inputs 相乘对 inputs 作转换,例如 inputs 的 shape 为 (batch_size, timesteps, features),但咱们想要 droput mask 在全部 timesteps 都是相同的,咱们能够设置 noise_shape=[batch_size, 1, features]。
  • seed:可选,默认为 None,即产生随机熟的种子值。
  • training:可选,默认为 False,布尔类型,即表明了是否标志位 training 模式。
  • name:可选,默认为 None,dropout 层的名称。

返回: 通过 dropout 层以后的 Tensor。

咱们用一个实例来感觉一下:

x = tf.layers.Input(shape=[32])
print(x)
y = tf.layers.dense(x, 16, activation=tf.nn.softmax)
print(y)
d = tf.layers.dropout(y, rate=0.2)
print(d)

运行结果:

Tensor("input_layer_1:0", shape=(?, 32), dtype=float32)
Tensor("dense/Softmax:0", shape=(?, 16), dtype=float32)
Tensor("dropout/Identity:0", shape=(?, 16), dtype=float32)

在这里咱们使用 dropout() 方法实现了 droput 操做,并制定 dropout rate 为 0.2,最后输出结果的 shape 和原来是一致的。

11.2.7 tf.layers.flatten

flatten() 方法能够对 Tensor 进行展平操做,定义在 tensorflow/python/layers/core.py。

flatten(
    inputs,
    name=None
)

参数说明以下:

  • inputs:必需,即输入数据。
  • name:可选,默认为 None,即该层的名称。

返回结果: 展平后的 Tensor。

下面咱们用一个实例来感觉一下:

x = tf.layers.Input(shape=[5, 6])
print(x)
y = tf.layers.flatten(x)
print(y)

运行结果:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

这里输入数据的 shape 为 [?, 5, 6],通过 flatten 层以后,就会变成 [?, 30],即将除了第一维的数据维度相乘,对原 Tensor 进行展平。

假如第一维是一个已知的数的话,它依然仍是一样的处理,示例以下:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

结果以下:

Tensor("input_layer_1:0", shape=(?, 5, 6), dtype=float32)
Tensor("flatten/Reshape:0", shape=(?, 30), dtype=float32)

12 TensorFlow 计算交叉熵错误问题解决

sparse_softmax_cross_entropy_with_logits(_sentinel=None, labels=None, logits=None, name=None)

在使用tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels)语句时产生以下错误:

import tensorflow as tf

labels = [[0.2,0.3,0.5],
          [0.1,0.6,0.3]]
logits = [[2,0.5,1],
          [0.1,1,3]]result1 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)若是这样用就会报这个错ValueError: Rank mismatch: Rank of labels (received 2) should equal rank of logits minus 1 (received 2).
  • 缘由是logits和labels在使用时有labels应该少一维的限制。
    好比一个tensorflow的分类问题,logits应该是batch×classes的一个矩阵,classes为类别数量labels应该是长batch的一个数组,当logits判断图片为某一类时,对应classes的位置为1
    • 例如:
      • 例子,好比猫狗大战吧。当你一批次训练10张图片时,batch为10,猫狗总共两种分类。logits就应该是相似这样的一个10×2的矩阵:
        [
        0 1
        1 0
        0 1
        0 1
        0 1
        1 0
        1 0
        1 0
        1 0
        0 1]
      • 10是10张图片, 每行第一个位置若是为1,那么这一张图片是狗,每行第二个位置若是为1,那么这一张图片是猫。而此时的label应该是一个这样的一维矩阵:[2 1 2 2 2 1 1 1 1 2]logits和label知足这种形式时使用tf.nn.sparse_softmax_cross_entropy_with_logits(logits, labels)语句是正确的。

下面演示一下,两个方法的正确用法:

import tensorflow as tf

labels = [[0.2,0.3,0.5],
          [0.1,0.6,0.3]]
logits = [[2,0.5,1],
          [0.1,1,3]]result1 = tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits)
import tensorflow as tf

labels = [0,2]

logits = [[2,0.5,1],
          [0.1,1,3]]

result1 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits)

总结:

  • sparse_softmax_cross_entropy_with_logits 主要用于结果是排他惟一性的计算, 若是是非惟一的须要用到 softmax_cross_entropy_with_logits。
  • sparse_softmax_cross_entropy的logits一般是最后的全链接层的输出结果,labels是具体哪一类的标签,这个函数是直接使用标签数据的,而不是采用one-hot编码形式。
  • .softmax_cross_entropy使用标签数据的one-hot编码形式。

tf.nn.in_top_k函数的用法

  • tf.nn.in_top_k主要是用于计算预测的结果和实际结果的是否相等,返回一个bool类型的张量
  • tf.nn.in_top_k(predictions,targets,k,name=None)函数
    • 这输出了一个batch_size bool数组,若是目标类的预测是全部预测(例如i)中的前k个预测,则条目out[i]为true.请注意,InTopK的行为在处理关系时与TopK操做不一样;若是多个类具备相同的预测值并跨越top-k边界,则全部这些类都被认为是在前k个.
    • 参数:
      • predictions:float32类型的Tensor.batch_size x classes张量.
      • targets:一个Tensor,必须是如下类型之一:int32,int64.类ids的batch_size向量.
      • k:int类型,要计算精度的顶级元素数.
      • name:操做的名称(可选).
    • 返回:
      • bool类型的Tensor.以bool Tensor计算k处的精度.

      '''
      predictions: 你的预测结果(通常也就是你的网络输出值)大小是预测样本的数量乘以输出的维度
      target: 实际样本类别的标签,大小是样本数量的个数
      k: 每一个样本中前K个最大的数里面(序号)是否包含对应target中的值

      '''
      import tensorflow as tf
      A = tf.Variable([[0.8, 0.4, 0.5, 0.6],[0.1, 0.9, 0.2, 0.4],[0.1, 0.9, 0.4, 0.2]])
      B = tf.Variable([1, 1, 2])
      result = tf.nn.in_top_k(A, B, 2)
      with tf.Session() as sess:
      sess.run(tf.global_variables_initializer())
      print(sess.run(A))
      print(sess.run(B))
      print(sess.run(result))

      k=1 [False True False]

      k=2 [False True True]

      '''
      解释:
      k取1的时候:
      由于A中第一个元素的最大值为0.8,索引(序号)是0,而B是1,不包含B,因此返回False.
      A中第二个元素的最大值为0.9,索引(序号)是1,而B是1,包含B,因此返回True.
      A中第三个元素的最大值为0.9,索引(序号)是1,而B是2,不包含B,因此返回False.
      k取2的时候:
      由于A中前两个元素的最大值为0.8,0.6,索引(序号)是0,3,而B是1,不包含B,因此返回False.
      A中前两个元素的最大值为0.9,0.4,索引(序号)是1,3,而B是1,包含B,因此返回True.
      A中前两个元素的最大值为0.9,0.4,索引(序号)是1,2,而B是2,包含B,因此返回True.
      '''

案例:TensorFlow1实现全链接神经网络识别手势数据集

"""
一天下午,咱们和一些朋友决定教咱们的电脑破译手语。咱们花了几个小时在白色的墙壁前拍照,因而就有了了如下数据集。如今,你的任务是创建一个算法,使有语音障碍的人与不懂手语的人交流。
训练集:有从0到5的数字的1080张图片(64x64像素),每一个数字拥有180张图片。
测试集:有从0到5的数字的120张图片(64x64像素),每一个数字拥有5张图片。
须要注意的是这是完整数据集的一个子集,完整的数据集包含更多的符号。
下面是每一个数字的样本,以及咱们如何表示标签的解释。这些都是原始图片,咱们实际上用的是64 * 64像素的图片。
"""
import numpy as np
import h5py
import matplotlib.pyplot as plt
import time
import math
import tensorflow as tf
# %matplotlib inline #若是你使用的是jupyter notebook取消注释
np.random.seed(1)

class GestureSymbolRecognition(object):
    def __init__(self):
        self.learning_rate = 0.0001
        self.num_epochs = 1500
        self.minibatch_size = 32
        self.regularazion_rate = 0.001

        self.n_inputs = 12288
        self.n_hidden1 = 100
        self.n_hidden2 = 100
        self.n_hidden3 = 100
        self.n_outputs = 6

        self.batch_norm_momentum = 0.9

    def load_data(self):
        """
        加载数据集
        :return:
        """
        # 从文件中读取训练集数据
        train_dataset = h5py.File('datasets/train_signs.h5', "r")
        # 从训练集数据中提取特征值与标签值数据
        train_set_x_orig = np.array(train_dataset["train_set_x"][:])
        train_set_y_orig = np.array(train_dataset["train_set_y"][:])

        # 从文件中读取测试集数据
        test_dataset = h5py.File('datasets/test_signs.h5', "r")
        # 从测试集数据中提取特征值与标签值数据
        test_set_x_orig = np.array(test_dataset["test_set_x"][:])
        test_set_y_orig = np.array(test_dataset["test_set_y"][:])
        classes = np.array(test_dataset["list_classes"][:])  # 类别列表
        classes_num = len(classes)  # 数据集标签分为几类

        train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
        test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))

        X_train_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1)  # 每一列就是一个样本
        X_test_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1)

        # 归一化数据
        X_train = X_train_flatten / 255
        X_test = X_test_flatten / 255

        # 转换为one-hot编码矩阵
        Y_train = self.convert_to_one_hot(train_set_y_orig, len(classes)).T
        Y_test = self.convert_to_one_hot(test_set_y_orig, len(classes)).T

        return X_train, Y_train, X_test, Y_test, classes_num

    def convert_to_one_hot(self, Y, C):
        """
        实现one-hot编码
        :param Y:标签矩阵
        :param C:分类数量
        :return:one_hot:one-hot矩阵
        """
        one_hot = np.eye(C)[Y.reshape(-1)].T
        return one_hot

    def random_mini_batches(self, X, Y, mini_batch_size, seed=0):
        """
        从数据集中建立一个随机的mini-batch列表
        :param X: 输入数据,维度为(输入节点数量,样本数量)
        :param Y: 对应的目标值,维度为(1,样本数量)
        :param mini_batch_size: 每一个mini-batch的样本数量
        :param seed: 随机数种子
        :return: mini_batches:一个同步列表,维度为(mini_batch_X, mini_batch_Y)
        """
        # 指定随机数种子
        np.random.seed(seed)
        # 获取样本数
        m = X.shape[0]
        # print('样本数量',m)
        # 建立一个同步列表
        mini_batches = []

        # 第一步:打乱样本的顺序
        permutation = list(np.random.permutation(m))  # 返回一个长度为m的随机数组,且里面的数是0到m-1
        shuffled_X = X[permutation, :]  # 将每一列特征值数据按permutation的顺序来从新排列
        shuffled_Y = Y[permutation, :]  # .reshape((Y.shape[0], m))  # 将每一列的目标值数据按permutation的顺序来从新排列

        # 第二步:分割训练集数据
        num_complete_minibatches = math.floor(m / mini_batch_size)  # 迭代完成整个训练集的次数
        for k in range(0, num_complete_minibatches):
            mini_batch_X = shuffled_X[k * mini_batch_size:(k + 1) * mini_batch_size, :]
            mini_batch_Y = shuffled_Y[k * mini_batch_size:(k + 1) * mini_batch_size, :]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)

        # 若是训练集的大小不是mini_batch_size的整数倍,那么最后确定会剩下一些,取剩余的数据
        if m % mini_batch_size != 0:
            # 获取剩余部分的数据
            mini_batch_X = shuffled_X[mini_batch_size * num_complete_minibatches:, :]
            mini_batch_Y = shuffled_Y[mini_batch_size * num_complete_minibatches:, :]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)

        # 返回同步列表
        return mini_batches

    def train(self, print_cost=True, is_plot=True):
        """
        实现一个三层的TensorFlow神经网络训练逻辑:LINEAR->RELU->LINEAR->RELU->LINEAR->SOFTMAX
        参数:
            print_cost - 是否打印成本,每100代打印一次
            is_plot - 是否绘制曲线图

        """
        # 加载数据集
        x_train, y_train, x_test, y_test, label_classes_num = self.load_data()
        print("训练集样本数 = " + str(x_train.shape[0]))
        print("测试集样本数 = " + str(x_test.shape[0]))
        print("X_train.shape: " + str(x_train.shape))
        print("Y_train.shape: " + str(y_train.shape))
        print("X_test.shape: " + str(x_test.shape))
        print("Y_test.shape: " + str(y_test.shape))
        # 设置图级别的随机数种子
        tf.set_random_seed(1)
        # 设置操做级别的随机数种子
        seed = 3
        # 获取输入节点数量和样本数
        (m, n_x) = x_train.shape
        # 成本函数值列表
        costs = []

        # 给X和Y建立占位符节点
        X = tf.placeholder(tf.float32, shape=(None, self.n_inputs), name="X")
        Y = tf.placeholder(tf.int32, shape=(None, label_classes_num), name="y")

        # 定义神经网络全链接层
        with tf.name_scope("dnn"):

            # 使用He权重初始化方法初始化权重
            he_init = tf.variance_scaling_initializer(mode="fan_avg")
            # 定义全链接层,使用elu激活函数
            hidden1 = tf.layers.dense(X, self.n_hidden1, name="hidden1",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu, )
            hidden2 = tf.layers.dense(hidden1, self.n_hidden2, name="hidden2",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu)
            hidden3 = tf.layers.dense(hidden2, self.n_hidden3, name="hidden3",
                                      kernel_initializer=he_init,
                                      activation=tf.nn.elu)
            outputs = tf.layers.dense(hidden3, self.n_outputs, name="outputs")
            # 输出预测的类别
            y_proba = tf.nn.softmax(outputs)

        # 定义损失函数计算损失
        with tf.name_scope('loss'):
            # 前向传播要在Z3处中止,由于在TensorFlow中最后的线性输出层的输出做为计算损失函数的输入,因此不须要A3.
            xentropy = tf.nn.softmax_cross_entropy_with_logits(labels=Y,
                                                               logits=outputs)
            loss = tf.reduce_mean(xentropy, name="loss")

        # 定义优化器
        with tf.name_scope("train_optimizer"):
            optimizer = tf.train.AdamOptimizer(self.learning_rate)
            training_op = optimizer.minimize(loss)

            # # 使用梯度裁剪
            # threshold = 1.0  # 设置梯度阈值
            # # 定义优化器
            # optimizer = tf.train.AdamOptimizer(self.learning_rate)
            # # 调用优化器的compute_gradients()方法计算梯度
            # grads_and_vars = optimizer.compute_gradients(loss)
            # # 使用tf.clip_by_value()函数建立一个裁剪梯度的操做
            # capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var) for grad, var in grads_and_vars]
            # # 建立一个节点使用优化器的apply_gradients()函数应用裁剪的梯度
            # training_op = optimizer.apply_gradients(capped_gvs)

            # 评估模型,使用准确性做为咱们的绩效指标
        with tf.name_scope("eval"):
            # 计算当前的预测结果
            # 检测使用了滑动平静模型的神经网络前向传播是否正确。tf.argmax(average_y,1)
            # 计算每个样例的预测答案。其中average_y是一个batch_size*10的二维数组,每
            # 一行表示案例向前传播的结果。tf.argmax的第二个参数为1,表示选取最大值的
            # 操做只在第一个维度上进行(x轴上),也就是说只在每一行选取最大值对应的下标
            # 因而获得的结果是一个长度为batch的一维数组,这个一维数组中的值就表示了每
            # 一个数字对应的样例识别的结果.tf.equal()判断每一个Tensor的每一维度是否相同
            # 若是相等返回True,不然返回False.
            correct_prediction = tf.equal(tf.argmax(y_proba, 1), tf.argmax(Y, 1))

            # 计算准确率
            # 这个运算首先将一个布尔型的值转换为实数型,而后计算平均值。这一个平均值
            # 就表明模型在这一组数据上的正确率
            accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

        # 建立初始化全部变量的节点
        init = tf.global_variables_initializer()

        # 建立一个保存模型的saveOP,设置要保留的最近检测点的模型的数量
        saver = tf.train.Saver(max_to_keep=1)

        # 开始会话并计算
        with tf.Session() as sess:
            # 初始化全部变量
            sess.run(init)

            # 加载模型,从模型中找出与当前训练的模型代码当中(名字同样的OP操做),覆盖原来的值
            checkpoin = tf.train.latest_checkpoint('./ckpt/')
            # 若是模型已经保存则加载模型
            if checkpoin:
                saver.restore(sess, checkpoin)
                # 计算模型预测的准确率
                train_acc = accuracy.eval({X: x_train[100:200], Y: y_train[100:200]})
                test_acc = accuracy.eval({X: x_test[100:200], Y: y_test[100:200]})
                print("训练集的准确率:", train_acc)
                print("测试集的准确率:", test_acc)
            else:  # 模型未保存,则开始训练模型
                # 正常训练模型
                for epoch in range(self.num_epochs):

                    epoch_cost = 0  # 每轮的成本函数值
                    num_minibatches = math.floor(m / self.minibatch_size)  # 分红多少个mini-batch组
                    seed = seed + 1
                    minibatches = self.random_mini_batches(x_train, y_train, self.minibatch_size, seed)

                    for minibatch in minibatches:
                        # 选择一个minibatch
                        (minibatch_X, minibatch_Y) = minibatch

                        # 数据已经准备好了,开始运行session
                        _, minibatch_cost = sess.run([training_op, loss], feed_dict={X: minibatch_X, Y: minibatch_Y})
                        # 计算这个minibatch在这一代中所占的偏差
                        epoch_cost = epoch_cost + minibatch_cost / num_minibatches

                    if epoch % 5 == 0:
                        # 将每轮迭代的代价函数值存入列表
                        costs.append(epoch_cost)
                        # 打印每轮迭代的代价函数值:
                        if print_cost and epoch % 100 == 0:
                            print("epoch = " + str(epoch) + "    epoch_cost = " + str(epoch_cost))
                # 保存学习后的参数
                saver.save(sess, './ckpt/my_test1_model.ckpt')

                # 绘制代价函数值的学习曲线
                if is_plot:
                    plt.plot(np.squeeze(costs))
                    plt.ylabel('cost')
                    plt.xlabel('iterations (per tens)')
                    plt.title("Learning rate =" + str(self.learning_rate))
                    plt.show()

                # 计算模型学习完成后的训练集及测试集准确率

                train_acc = accuracy.eval({X: x_train, Y: y_train})
                test_acc = accuracy.eval({X: x_test, Y: y_test})
                print("训练集的准确率:", train_acc)
                print("测试集的准确率:", test_acc)

if __name__ == '__main__':
    # 开始时间
    start_time = time.clock()
    # 开始训练
    nn = GestureSymbolRecognition()
    nn.train()
    # 结束时间
    end_time = time.clock()
    # 计算时差
    print("CPU的执行时间 = " + str(end_time - start_time) + " 秒")
相关文章
相关标签/搜索