TensorFlow的使用2

TensorFlow2兼容TensorFlow1代码

使用2.0中的v1兼容包来沿用1.x代码

TensorFlow 2.0中提供了tensorflow.compat.v1代码包来兼容原有1.x的代码,可以做到几乎不加修改的运行。社区的contrib库因为涉及大量直接的TensorFlow引用代码或者自己写的Python扩展包,所以无法使用这种模式。TensorFlow 2.0中也已经移除了contrib库,这让人很有点小遗憾的。 使用这种方式升级原有代码,只需要把原有程序开始的TensorFlow引用:

import tensorflow as tf 替换为以下两行就可以正常的继续使用: import tensorflow.compat.v1 as tf

tf.disable_v2_behavior() 其它代码无需修改。个人觉得,如果是稳定使用中、并且没有重构意愿的代码,这种方式算的上首选。

使用迁移工具来自动迁移1.x代码到2.0

TensorFlow 2.0中提供了命令行迁移工具,来自动的把1.x的代码转换为2.0的代码。工具使用方法如下(假设我们的程序文件名称为first-tf.py): tf_upgrade_v2 --infile first-tf.py --outfile first-tf-v2.py 迁移工具还可以对整个文件夹的程序做升级,请参考工具自身的帮助文档。

TensorFlow的数据类型

    • Scalar: 标量。 Dim=0

      Vector:向量。Dim=1

      Matrix:矩阵。Dim=2

      Tensor:Dim>2

    • 创建一个常数张量,传入list或者数值来填充

       # Constant 1-D Tensor populated with value list.
        tensor = tf.constant([1, 2, 3, 4, 5, 6, 7]) => [1 2 3 4 5 6 7]
      # 2-D tensor `a`
       a = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3]) => [[1. 2. 3.]
                                                            [4. 5. 6.]]
       # 2-D tensor `b`
       b = tf.constant([7, 8, 9, 10, 11, 12], shape=[3, 2]) => [[7. 8.]
                                                                [9. 10.]
                                                                [11. 12.]]
       # 3-D tensor `a`
       a = tf.constant(np.arange(1, 13, dtype=np.int32),
                       shape=[2, 2, 3])                 => [[[ 1.  2.  3.]
                                                              [ 4.  5.  6.]],
                                                            [[ 7.  8.  9.]
                                                              [10. 11. 12.]]]
    • 需要注意的是:

      Constant返回值中的numpy属性指的是对象的值,rank返回值中的numpy属性指的是对象的维度

      • Isinstance: 对象的类型判断

        Is_tensor: 对象的类型判断(推荐使用)

        Dtype:具体的子类型

    • Device:用什么设备创建环境(cpu、gpu)

      Gpu():切换成gpu设备环境

      Cpu():切换成cpu设备环境

      Numpy(): 返回对象在numpy下的定义

      Ndim: 返回对象的dim维度

      Rank(): 返回对象的维度,以tensor的形式表示

    • 一个tensor对象可以被包装成variable对象,一个variable对象具有可求导等属性。

      trainable : 表示是否可训练。含义有是否记录梯度信息,是否可求导。

    • Numpy:tensor对象转numpy对象

      Int、float:将tensor对象转成numpy对象的标量值

  • 检查张量的类型

  • 转换张量的数据类型:

TensorFlow中创建Tensor

  • From Numpy List:

    • tf.convert_to_tensor用于将不同数据变成张量:比如可以让数组变成张量、也可以让列表变成张量。

  • Tensors常量值函数

    • tf.zeros(shape, dtype=tf.float32, name=None)

    • tf.zeros_like(tensor, dtype=None, name=None)

    • tf.ones(shape, dtype=tf.float32, name=None)

    • tf.ones_like(tensor, dtype=None, name=None)

    • tf.fill(dims, value, name=None)

    • tf.constant(value, dtype=None, shape=None, name='Const')

     

    • 在Tensorflow中,任何参数的运算和操作都需要转换成对应的TensorFlow数据类型,例如现实中的字符串类型是不能直接在TensorFlow中进行运算的,必须转换成对应的TensorFlow类型才行。故而Tensorflow提供了一些函数用于生成常量值.如上是一些基本的生成常量的方法.

    • tf.zeros(shape, dtype=tf.float32, name=None):创建一个所有的参数为0的tensor对象

      • 参数:shape: 用于表示维度,通常为一个int32类型数组,或者一个一维(1-D)的tf.int32数字.注意不能直接使用数字

      • dtype: 所要创建的tensor对象的数据类型

      • name: 一个该操作的别名(可选的).

      • 返回:所有参数都为0的tensor对象

    • tf.zeros_like(tensor, dtype=None, name=None)该方法用于创建一个所有参数均为0的tensor对象

    • tf.ones(shape, dtype=tf.float32, name=None)创建一个所有的参数为1的tensor对象

    • tf.ones_like(tensor, dtype=None, name=None)该方法用于创建一个所有参数均为1的tensor对象

    • tf.fill(dims, value, name=None)创建一个维度为dims,值为value的tensor对象.该操作会创建一个维度为dims的tensor对象,并将其值设置为value,该tensor对象中的值类型和value一致

    • tf.constant(value,dtype=None,shape=None,name=’Const’) 创建一个常量tensor,按照给出value来赋值,可以用shape来指定其形状。value可以是一个数,也可以是一个list。 如果是一个数,那么这个常亮中所有值的按该数来赋值。 如果是list,那么len(value)一定要小于等于shape展开后的长度。赋值时,先将value中的值逐个存入。不够的部分,则全部存入value的最后一个值。

TensorFlow 生成随机数张量

  • random_normal(shape,mean=0.0,stddev=1.0,dtype=tf.float32,seed=None,name=None):从正态分布中输出随机值。

    • shape:一个一维整数张量或Python数组。代表张量的形状。 mean:数据类型为dtype的张量值或Python值。是正态分布的均值。 stddev:数据类型为dtype的张量值或Python值。是正态分布的标准差。 dtype: 输出的数据类型。 seed:一个Python整数。是随机种子。

  • tf.random_uniform:从均匀分布中返回随机值。

    random_uniform(
       shape,# 生成的张量的形状
       minval=0,
       maxval=None,
       dtype=tf.float32,
       seed=None,
       name=None
    )
    • 返回值的范围默认是0到1的左闭右开区间,即[0,1)。minval为指定最小边界,默认为1。maxval为指定的最大边界,如果是数据浮点型则默认为1,如果数据为整形则必须指定。

  • tf.truncated_normal:截断的正态分布函数。生成的值遵循一个正态分布,但不会大于*均值2个标准差

    truncated_normal(
        shape,#一个一维整数张量或Python数组。代表张量的形状。
        mean=0.0,#数据类型为dtype的张量值或Python值。是正态分布的均值。
        stddev=1.0,#数据类型为dtype的张量值或Python值。是正态分布的标准差
        dtype=tf.float32,#输出的数据类型。
        seed=None,#一个Python整数。是随机种子。
        name=None#操作的名称(可选)
    )
  • tf.random_shuffle:沿着要被洗牌的张量的第一个维度,随机打乱。


    random_shuffle(
       value,# 要被洗牌的张量
       seed=None,
       name=None
    )

TensorFlow中的一维张量

TensorFlow的索引与切片

  • Basic indexing


  • numpy-style indexing

    arr = tf.range(60)
    t1 = tf.reshape(arr, [3, 4, 5])
    print(t1) # tf.Tensor(
             # [[[ 0 1 2 3 4]
             #   [ 5 6 7 8 9]
             #   [10 11 12 13 14]
             #   [15 16 17 18 19]]
             #
             # [[20 21 22 23 24]
             #   [25 26 27 28 29]
             #   [30 31 32 33 34]
             #   [35 36 37 38 39]]
             #
             # [[40 41 42 43 44]
             #   [45 46 47 48 49]
             #   [50 51 52 53 54]
             #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
    print(t1[0]) # tf.Tensor(
                # [[ 0 1 2 3 4]
                # [ 5 6 7 8 9]
                # [10 11 12 13 14]
                # [15 16 17 18 19]], shape=(4, 5), dtype=int32)
    print(t1[0, 0]) # tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int32)
    print(t1[0, 0, 0]) # tf.Tensor(0, shape=(), dtype=int32)
    • start:end截取

    t = tf.range(10)
    t1 = t[2:]
    t2 = t[-2:]
    t3 = t[:2]
    t4 = t[:-2]
    print(t) # tf.Tensor([0 1 2 3 4 5 6 7 8 9], shape=(10,), dtype=int32)
    print(t1) # tf.Tensor([2 3 4 5 6 7 8 9], shape=(8,), dtype=int32)
    print(t2) # tf.Tensor([8 9], shape=(2,), dtype=int32)
    print(t3) # tf.Tensor([0 1], shape=(2,), dtype=int32)
    print(t4) # tf.Tensor([0 1 2 3 4 5 6 7], shape=(8,), dtype=int32)
  • indexing by :

arr = tf.range(60)
t = tf.reshape(arr, [3, 4, 5])
t1 = t[0, :, :]
t2 = t[:, 0, :]
t3 = t[:, :, 0]
print(t) # tf.Tensor(
        # [[[ 0 1 2 3 4]
        #   [ 5 6 7 8 9]
        #   [10 11 12 13 14]
        #   [15 16 17 18 19]]
        #
        # [[20 21 22 23 24]
        #   [25 26 27 28 29]
        #   [30 31 32 33 34]
        #   [35 36 37 38 39]]
        #
        # [[40 41 42 43 44]
        #   [45 46 47 48 49]
        #   [50 51 52 53 54]
        #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
print(t1) # tf.Tensor(
        # [[ 0 1 2 3 4]
        # [ 5 6 7 8 9]
        # [10 11 12 13 14]
        # [15 16 17 18 19]], shape=(4, 5), dtype=int32)
print(t2) # tf.Tensor(
        # [[ 0 1 2 3 4]
        # [20 21 22 23 24]
        # [40 41 42 43 44]], shape=(3, 5), dtype=int32)
print(t3) # tf.Tensor(
        # [[ 0 5 10 15]
        # [20 25 30 35]
        # [40 45 50 55]], shape=(3, 4), dtype=int32)

  • indexing by ::

arr = tf.range(60)
t = tf.reshape(arr, [3, 4, 5])
t1 = t[:, ::2, :]
t2 = t[0::2, :, :]
t3 = t[0, ::2, :]
print(t) # tf.Tensor(
         # [[[ 0  1  2  3  4]
         #   [ 5  6  7  8  9]
         #   [10 11 12 13 14]
         #   [15 16 17 18 19]]
         # 
         #  [[20 21 22 23 24]
         #   [25 26 27 28 29]
         #   [30 31 32 33 34]
         #   [35 36 37 38 39]]
         # 
         #  [[40 41 42 43 44]
         #   [45 46 47 48 49]
         #   [50 51 52 53 54]
         #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
print(t1) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [10 11 12 13 14]]
          # 
          #  [[20 21 22 23 24]
          #   [30 31 32 33 34]]
          # 
          #  [[40 41 42 43 44]
          #   [50 51 52 53 54]]], shape=(3, 2, 5), dtype=int32)
print(t2) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [ 5  6  7  8  9]
          #   [10 11 12 13 14]
          #   [15 16 17 18 19]]
          # 
          #  [[40 41 42 43 44]
          #   [45 46 47 48 49]
          #   [50 51 52 53 54]
          #   [55 56 57 58 59]]], shape=(2, 4, 5), dtype=int32)
print(t3) # tf.Tensor(
          # [[ 0  1  2  3  4]
          #  [10 11 12 13 14]], shape=(2, 5), dtype=int32)

arr = tf.range(60)
t = tf.reshape(arr, [3, 4, 5])
t1 = t[0, ...]
t2 = t[..., 0]
t3 = t[0, 0, ...]
t4 = t[..., 0, 0]
print(t) # tf.Tensor(
         # [[[ 0  1  2  3  4]
         #   [ 5  6  7  8  9]
         #   [10 11 12 13 14]
         #   [15 16 17 18 19]]
         # 
         #  [[20 21 22 23 24]
         #   [25 26 27 28 29]
         #   [30 31 32 33 34]
         #   [35 36 37 38 39]]
         # 
         #  [[40 41 42 43 44]
         #   [45 46 47 48 49]
         #   [50 51 52 53 54]
         #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
print(t1) # tf.Tensor(
          # [[ 0  1  2  3  4]
          #  [ 5  6  7  8  9]
          #  [10 11 12 13 14]
          #  [15 16 17 18 19]], shape=(4, 5), dtype=int32)
print(t2) # tf.Tensor(
          # [[ 0  5 10 15]
          #  [20 25 30 35]
          #  [40 45 50 55]], shape=(3, 4), dtype=int32)
print(t3) # tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int32)
print(t4) # tf.Tensor([ 0 20 40], shape=(3,), dtype=int32)

  • gather

    • tf.gather:用一个一维的索引数组,将张量中对应索引的向量提取出来

arr = tf.range(60)
t = tf.reshape(arr, [3, 4, 5])
t1 = tf.gather(t, axis=0, indices=[0, 2])
t2 = tf.gather(t, axis=0, indices=[0, 2, 1])
t3 = tf.gather(t, axis=1, indices=[0, 2, 1])
t4 = tf.gather(t, axis=1, indices=[0, 2, 1, 3])
t5 = tf.gather(t, axis=2, indices=[0, 2, 1, 4])
t6 = tf.gather(t, axis=2, indices=[0, 2, 1, 4, 3])
print(t1) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [ 5  6  7  8  9]
          #   [10 11 12 13 14]
          #   [15 16 17 18 19]]
          #  [[40 41 42 43 44]
          #   [45 46 47 48 49]
          #   [50 51 52 53 54]
          #   [55 56 57 58 59]]], shape=(2, 4, 5), dtype=int32)
print(t2) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [ 5  6  7  8  9]
          #   [10 11 12 13 14]
          #   [15 16 17 18 19]]
          #  [[40 41 42 43 44]
          #   [45 46 47 48 49]
          #   [50 51 52 53 54]
          #   [55 56 57 58 59]]
          #  [[20 21 22 23 24]
          #   [25 26 27 28 29]
          #   [30 31 32 33 34]
          #   [35 36 37 38 39]]], shape=(3, 4, 5), dtype=int32)
print(t3) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [10 11 12 13 14]
          #   [ 5  6  7  8  9]]
          #  [[20 21 22 23 24]
          #   [30 31 32 33 34]
          #   [25 26 27 28 29]]
          #  [[40 41 42 43 44]
          #   [50 51 52 53 54]
          #   [45 46 47 48 49]]], shape=(3, 3, 5), dtype=int32)
print(t4) # tf.Tensor(
          # [[[ 0  1  2  3  4]
          #   [10 11 12 13 14]
          #   [ 5  6  7  8  9]
          #   [15 16 17 18 19]]
          #  [[20 21 22 23 24]
          #   [30 31 32 33 34]
          #   [25 26 27 28 29]
          #   [35 36 37 38 39]]
          #  [[40 41 42 43 44]
          #   [50 51 52 53 54]
          #   [45 46 47 48 49]
          #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
print(t5) # tf.Tensor(
          # [[[ 0  2  1  4]
          #   [ 5  7  6  9]
          #   [10 12 11 14]
          #   [15 17 16 19]]
          #  [[20 22 21 24]
          #   [25 27 26 29]
          #   [30 32 31 34]
          #   [35 37 36 39]]    
          #  [[40 42 41 44]
          #   [45 47 46 49]
          #   [50 52 51 54]
          #   [55 57 56 59]]], shape=(3, 4, 4), dtype=int32)
print(t6) # tf.Tensor(
          # [[[ 0  2  1  4  3]
          #   [ 5  7  6  9  8]
          #   [10 12 11 14 13]
          #   [15 17 16 19 18]]
          #  [[20 22 21 24 23]
          #   [25 27 26 29 28]
          #   [30 32 31 34 33]
          #   [35 37 36 39 38]]
          #  [[40 42 41 44 43]
          #   [45 47 46 49 48]
          #   [50 52 51 54 53]
          #   [55 57 56 59 58]]], shape=(3, 4, 5), dtype=int32)
  • gather_nd

    • gather_nd(params,indices,name=None):将参数中的切片收集到由索引指定的形状的张量中.

      • 索引(indices)是一个 k 维整数张量,最好作为一个 (k-1) 维的索引(indices)张量的参数,其中每个元素定义了一个参数切片:

arr = tf.range(60)
t = tf.reshape(arr, [3, 4, 5])
t1 = tf.gather_nd(t, [0])
t2 = tf.gather_nd(t, [0, 1])
t3 = tf.gather_nd(t, [0, 1, 2])
t4 = tf.gather_nd(t, [[0, 1, 2]])
t5 = tf.gather_nd(t, [[0, 0], [1, 1]])
t6 = tf.gather_nd(t, [[0, 0], [1, 1], [2, 2]])
t7 = tf.gather_nd(t, [[0, 0, 0], [1, 1, 1], [2, 2, 2]])
t8 = tf.gather_nd(t, [[[0, 0, 0], [1, 1, 1], [2, 2, 2]]])
print(t) # tf.Tensor(
         # [[[ 0  1  2  3  4]
         #   [ 5  6  7  8  9]
         #   [10 11 12 13 14]
         #   [15 16 17 18 19]]
         # 
         #  [[20 21 22 23 24]
         #   [25 26 27 28 29]
         #   [30 31 32 33 34]
         #   [35 36 37 38 39]]
         # 
         #  [[40 41 42 43 44]
         #   [45 46 47 48 49]
         #   [50 51 52 53 54]
         #   [55 56 57 58 59]]], shape=(3, 4, 5), dtype=int32)
print(t1) # tf.Tensor(
          # [[ 0  1  2  3  4]
          #  [ 5  6  7  8  9]
          #  [10 11 12 13 14]
          #  [15 16 17 18 19]], shape=(4, 5), dtype=int32)
print(t2) # tf.Tensor([5 6 7 8 9], shape=(5,), dtype=int32)
print(t3) # tf.Tensor(7, shape=(), dtype=int32)
print(t4) # tf.Tensor([7], shape=(1,), dtype=int32)
print(t5) # tf.Tensor(
          # [[ 0  1  2  3  4]
          #  [25 26 27 28 29]], shape=(2, 5), dtype=int32)
print(t6) # tf.Tensor(
          # [[ 0  1  2  3  4]
          #  [25 26 27 28 29]
          #  [50 51 52 53 54]], shape=(3, 5), dtype=int32)
print(t7) # tf.Tensor([ 0 26 52], shape=(3,), dtype=int32)
print(t8) # tf.Tensor([[ 0 26 52]], shape=(1, 3), dtype=int32)

  • boolean_mask

    • tf.boolean_mask(tensor,mask,name='boolean_mask',axis=None):将布尔掩码应用于张量

      • 参数:tensor是N维度的tensor,mask是K维度的,注意K小于等于N,name可选项也就是这个操作的名字,axis是一个0维度的int型tensor,表示的是从参数tensor的哪个axis开始mask,默认的情况下,axis=0表示从第一维度进行mask,因此K+axis小于等于N。

      • 返回的是N-K+1维度的tensor,也就是mask为True的地方保存下来。

TensorFlow的维度变换

图片视图

  • [b, 28, 28] # 保存b张图片,28行,28列(保存数据一般行优先),图片的数据没有被破坏

  • [b, 28*28] # 保存b张图片,不考虑图片的行和列,只保存图片的数据,不关注图片数据的细节

  • [b, 2, 14*28] # 保存b张图片,把图片分为上下两个部分,两个部分具体多少行是不清楚的

  • [b, 28, 28, 1] # 保存b张图片,28行,28列,1个通道

tf.reshape()重塑视图与恢复视图

  • 重塑视图

import tensorflow as tf
a = tf.random.normal([4, 28, 28, 3])
a.shape, a.ndim
# out:(TensorShape([4, 28, 28, 3]), 4)
    
tf.reshape(a, [4, 784, 3]).shape  # 给出一张图片某个通道的数据,丢失行、宽的信息
# out: TensorShape([4, 784, 3])
    
tf.reshape(a, [4, -1, 3]).shape  # 4*(-1)*3 = 4*28*28*3
# out:TensorShape([4, 784, 3])
    
tf.reshape(a, [4, 784*3]).shape  # 给出一张图片的所有数据,丢失行、宽和通道的信息
# out:TensorShape([4, 2352])
    
tf.reshape(a, [4, -1]).shape
# out:TensorShape([4, 2352])
  • 恢复视图

tf.reshape(tf.reshape(a, [4, -1]), [4, 28, 28, 3]).shape
# out:TensorShape([4, 28, 28, 3])
tf.reshape(tf.reshape(a, [4, -1]), [4, 14, 56, 3]).shape
# out:TensorShape([4, 14, 56, 3])
tf.reshape(tf.reshape(a, [4, -1]), [4, 1, 784, 3]).shape
# out:TensorShape([4, 1, 784, 3])

tf.transpose()转置

a = tf.random.normal((4, 3, 2, 1))
a.shape
# out:TensorShape([4, 3, 2, 1])
# 将张量进行转置
tf.transpose(a).shape
# out:TensorShape([1, 2, 3, 4])
tf.transpose(a, perm=[0, 1, 3, 2]).shape  # 按照索引替换维度
# out:TensorShape([4, 3, 1, 2])
a = tf.random.normal([4, 28, 28, 3])  # b,h,w,c
a.shape
# out:TensorShape([4, 28, 28, 3])
tf.transpose(a, [0, 2, 1, 3]).shape  # b,w,h,c
# out:TensorShape([4, 28, 28, 3])
tf.transpose(a, [0, 3, 2, 1]).shape  # b,c,w,h
# out:TensorShape([4, 3, 28, 28])
tf.transpose(a, [0, 3, 1, 2]).shape  # b,c,h,w
# out:TensorShape([4, 3, 28, 28])

tf.expand_dims()增加维度

  • TensorFlow中,想要维度增加一维,可以使用tf.expand_dims(input, dim, name=None)函数。当然,我们常用tf.reshape(input, shape=[])也可以达到相同效果,但是有些时候在构建图的过程中,placeholder没有被feed具体的值,这时就会包下面的错误:TypeError: Expected binary or unicode string, got 1

    • 在这种情况下,我们就可以考虑使用expand_dims来将维度加1。比如我自己代码中遇到的情况,在对图像维度降到二维做特定操作后,要还原成四维[batch, height, width, channels],前后各增加一维。如果用reshape,则因为上述原因报错

    one_img2 = tf.reshape(one_img, shape=[1, one_img.get_shape()[0].value, one_img.get_shape()[1].value, 1])
    
    • 用下面的方法可以实现:

    one_img = tf.expand_dims(one_img, 0)
    one_img = tf.expand_dims(one_img, -1) #-1表示最后一维
    
  • tf.expand_dims增加维度的例子:

    • a:[classes, students, classes] add school dim(增加学校的维度):

      • [1, 4, 35, 8] + [1, 4, 35, 8] = [2, 4, 35, 8]

      a = tf.random.normal([4, 25, 8])
      a.shape
      # out:TensorShape([4, 25, 8])
          
      tf.expand_dims(a, axis=0).shape  # 索引0前增加维度
      # out:TensorShape([1, 4, 25, 8])
          
      tf.expand_dims(a, axis=3).shape  # 索引3前增加维度
      # out:TensorShape([4, 25, 8, 1])
          
      tf.expand_dims(a,axis=-1).shape  # 索引-1后增加维度
      # out:TensorShape([4, 25, 8, 1])
          
      tf.expand_dims(a,axis=-4).shape  # 索引-4后,即左边空白处增加维度
      # out:TensorShape([1, 4, 25, 8])
      

tf.squeeze()挤压维度

  • tf.squeeze(input, squeeze_dims=None, name=None):从tensor中删除所有大小为1的维度

  • 示例:

    tf.squeeze(tf.zeros([1,2,1,1,3])).shape
    # out:TensorShape([2, 3])
    a = tf.zeros([1,2,1,3])
    a.shape
    
    # out:TensorShape([1, 2, 1, 3])
    
    tf.squeeze(a,axis=0).shape
    
    # out:TensorShape([2, 1, 3])
    
    tf.squeeze(a,axis=2).shape
    
    # out:TensorShape([1, 2, 3])
    
    tf.squeeze(a,axis=-2).shape
    
    # out:TensorShape([1, 2, 3])
    
    tf.squeeze(a,axis=-4).shape
    
    # out:TensorShape([2, 1, 3])
    
    

TensorFlow中的Broadcasting

什么是broadcasting广播机制

  • TensorFlow支持广播机制(Broadcast),可以广播元素间操作(elementwise operations)。正常情况下,当你想要进行一些操作如加法,乘法时,你需要确保操作数的形状是相匹配的,如:你不能将一个具有形状[3, 2]的张量和一个具有[3,4]形状的张量相加。但是,这里有一个特殊情况,那就是当你的其中一个操作数是一个具有单独维度(singular dimension)的张量的时候,TF会隐式地在它的单独维度方向填满(tile),以确保和另一个操作数的形状相匹配。所以,对一个[3,2]的张量和一个[3,1]的张量相加在TF中是合法的。(译者:这个机制继承自numpy的广播功能。其中所谓的单独维度就是一个维度为1,或者那个维度缺失。)

为什么使用广播机制

  • 广播机制允许我们在隐式情况下进行填充(tile),而这可以使得我们的代码更加简洁,并且更有效率地利用内存,因为我们不需要另外储存填充操作的结果。一个可以表现这个优势的应用场景就是在结合具有不同长度的特征向量的时候。为了拼接具有不同长度的特征向量,我们一般都先填充输入向量,拼接这个结果然后进行之后的一系列非线性操作等。这是一大类神经网络架构的共同套路(common pattern)

  • for real demanding

    • [classes, students, scores]

    • Add bias for every student: +5 score

    • [4,32,8] + [4,32,8]

    • [4,32,8] + [5.0]

  • memory consumption

    • [4,32,8] -> 1024

    • bias = [8]: [5.0,5.0,5.0,...] -> 8

什么时候能使用广播机制

  • Match from Last dim!

    • if current dim=1, expand to same

    • if either has no dim, insert one dim and expand to same

    • otherwise, Not Broadcastable

  • 总结:

    • 只要把他们从右边对齐,如果相应的位置上没有维度,我们插入一个维度,他的shape为1。

    • 如果有维度的情况下,我们比较维度数量是否是相等的,如果不相等的话不能进行broadcasting

  • 具体的例子:

    • [4,32,14,14] # 原张量的维度

    • [1,32,1,1] -> [4,32,14,14] √ # 可以与原张量broadcasting

    • [14,14] -> [1,1,14,14] -> [4,32,14,14] √

    • [2,32,14,14] × # 不可以与原张量broadcasting

    • [3] ×

    • [32,32,1] ×

    • [4,1,1,1] √

    x = tf.random.normal([4,32,32,3])
    x.shape
    # out:TensorShape([4, 32, 32, 3])
    
    (x+tf.random.normal([3])).shape
    # out:TensorShape([4, 32, 32, 3])
    
    (x+tf.random.normal([32,32,1])).shape
    # out:TensorShape([4, 32, 32, 3])
    
    (x+tf.random.normal([4,1,1,1])).shape
    # out:TensorShape([4, 32, 32, 3])
    
    try:
        (x+tf.random.normal([1,4,1,1])).shape
    except Exception as e:
        print(e)
    Incompatible shapes: [4,32,32,3] vs. [1,4,1,1] [Op:Add] name: add/
    (x+tf.random.normal([4,1,1,1])).shape
    # out:TensorShape([4, 32, 32, 3])
    
    b = tf.broadcast_to(tf.random.normal([4,1,1,1]),[4,32,32,3])
    b.shape
    # out:TensorShape([4, 32, 32, 3])
    

TensorFlow的Broadcast与Tile的区别

a = tf.ones([3,4])
a.shape
# out:TensorShape([3, 4])

a1 = tf.broadcast_to(a,[2,3,4])
a1.shape
# out:TensorShape([2, 3, 4])

a2 = tf.expand_dims(a,axis=0)  # 0前插入一维
a2.shape
# out:TensorShape([1, 3, 4])

a2 = tf.tile(a2,[2,1,1])  # 复制一维2次,复制二、三维1次
a2.shape
# out:TensorShape([2, 3, 4])

"""
a1和a2的区别:完全等价,但是a2占用的内存空间更大。
"""

TensorFlow中的数学运算

  • 对应元素之间的运算:+-*/%//

    • 加法:+

    • 加法:-

    • 乘法:*

    • 普通除法:/

    • 整除://

    • 取余:%

    • 求指数:tf.math.log()

    • 求对数:tf.exp()

    • 求*方:**/tf.pow()

    • 开方运算:tf.sqrt()

  • 矩阵运算:

    • 矩阵乘法@,tf.matmul()

TensorFlow 算术运算符

TensorFlow 提供了几种操作,您可以使用它们将基本算术运算符添加到图形中。

TensorFlow 基本数学函数

TensorFlow 提供了几种可用于向图形添加基本数学函数的操作。

TensorFlow 矩阵数学函数

TensorFlow 提供了几种操作,您可以使用它们将曲线上的线性代数函数添加到图形中。

TensorFlow 张量数学函数

TensorFlow 提供可用于向图形添加张量函数的操作。

TensorFlow 复数函数

TensorFlow 提供了多种操作,您可以使用它们将复数函数添加到图形中。

TensorFlow 减少张量的计算

TensorFlow 提供了几种操作,您可以使用这些操作来执行减少张量的各种维度的常规数学计算。

TensorFlow 张量扫描

TensorFlow 提供了几种操作,您可以使用它们在张量的一个轴上执行扫描(运行总计)。

分段

TensorFlow 提供了几种可用于在张量片段上执行常规数学计算的操作。这里,分割是沿着第一维度的张量的分割,即它定义从第一维度到的映射 segment_ids.segment_ids 张量应该是第一尺寸的大小,d0与在范围内的连续的ID 0到k,在那里 k<d0。特别地,矩阵张量的分割是行到段的映射。

例如:

c = tf.constant([[1,2,3,4], [-1,-2,-3,-4], [5,6,7,8]])
tf.segment_sum(c, tf.constant([0, 0, 1]))
  ==>  [[0 0 0 0]
        [5 6 7 8]]

序列比较和索引

TensorFlow 提供了几种操作,您可以使用它们将序列比较和索引提取添加到图形中.您可以使用这些操作来确定序列差异,并确定张量中特定值的索引.

TensorFlow中的合并与分割

tf.concat

  • tf.concat是连接两个矩阵的操作

  • tf.concat(values,concat_dim,name='concat')

    • concat_dim表示你在哪个维度上进行连接,他是整数,从0开始计数,0表示第一个维度,1表示第二个维度......

    • values是一个列表。列表里面是要连接的矩阵或者数组。

# 6个班级的学生分数情况
a = tf.ones([4, 35, 8])
b = tf.ones([2, 35, 8])
c = tf.concat([a, b], axis=0)
c.shape
# out:TensorShape([6, 35, 8])
    
# 3个学生学生补考
a = tf.ones([4, 32, 8])
b = tf.ones([4, 3, 8])
tf.concat([a, b], axis=1).shape
# out:TensorShape([4, 35, 8])

tf.stack

  • 将秩为 R 的张量列表堆叠成一个秩为 (R+1) 的张量。即可以产生一个新的维度

  • tf.stack(values,axis=0,name='stack')

    • 函数参数

      • values:具有相同形状和类型的 Tensor 对象列表.

      • axis:一个 int,要一起堆叠的轴,默认为第一维,负值环绕,所以有效范围是[-(R+1), R+1).

      • name:此操作的名称(可选).

    • 函数返回值:

      • output:与 values 具有相同的类型的堆叠的 Tensor.

    • 可能引发的异常:

      • ValueError:如果 axis 超出范围 [ - (R + 1),R + 1),则引发此异常.

    a = tf.ones([4, 35, 8])
    b = tf.ones([4, 35, 8])
    a.shape
    # out:TensorShape([4, 35, 8])
        
    b.shape
    # out:TensorShape([4, 35, 8])
        
    tf.concat([a, b], axis=-1).shape
    # out:TensorShape([4, 35, 16])
        
    tf.stack([a, b], axis=0).shape
    # out:TensorShape([2, 4, 35, 8])
        
    tf.stack([a, b], axis=3).shape
    # out:TensorShape([4, 35, 8, 2])
    

tf.concat与tf.stack的区别

  • 相同点:都是组合重构数据.

    不同点:concat()不改变维数,而stack改变了维数

  • 如果要合并的张量维度不匹配时:

    a = tf.ones([4, 35, 8])
    b = tf.ones([3, 33, 8])
    try:
        tf.concat([a, b], axis=0).shape
    except Exception as e:
        print(e)
    # out:ConcatOp : Dimensions of inputs should match: shape[0] = [4,35,8] vs. shape[1] = [3,33,8] [Op:ConcatV2] name: concat
                    
    # concat保证只有一个维度不相等
    b = tf.ones([2, 35, 8])
    c = tf.concat([a, b], axis=0)
    c.shape
    # out:TensorShape([6, 35, 8])
        
    # stack保证所有维度相等
    try:
        tf.stack([a, b], axis=0)
    except Exception as e:
        print(e)
    # out:Shapes of all inputs must match: values[0].shape = [4,35,8] != values[1].shape = [2,35,8] [Op:Pack] name: stack
    

tf.unstack

  • 作用:将秩为 R 的张量的给定维度出栈为秩为 (R-1) 的张量。

    • 通过沿 axis 维度将 num 张量从 value 中分离出来.如果没有指定 num(默认值),则从 value 的形状推断.如果 value.shape[axis] 不知道,则引发 ValueError.

      • 例如,给定一个具有形状 (A, B, C, D) 的张量.

        • 如果 axis == 0,那么 output 中的第 i 个张量就是切片 value[i, :, :, :],并且 output 中的每个张量都具有形状 (B, C, D).(请注意,出栈的维度已经消失,不像split).

        • 如果 axis == 1,那么 output 中的第 i 个张量就是切片 value[:, i, :, :],并且 output 中的每个张量都具有形状 (A, C, D).

  • tf.unstack(value,num=None,axis=0,name='unstack')

    • 函数参数:

      • value:一个要出栈的秩 R > 0 的 Tensor.

      • num:一个 int,指定如果为 None(默认值),则自动从形状中推断.

      • axis:一个 int,沿着这个轴出栈,默认为第一维,负值环绕,所以有效范围是 [-R, R).

      • name:操作的名称(可选).

    • 函数返回值:

      • tf.unstac k函数从 value 中出栈的 Tensor 对象列表.

    • 可能引发的异常:

      • ValueError:如果 num 没有指定并且无法推断.

      • ValueError:如果 axis 超出范围 [-R,R).

    a = tf.ones([4, 35, 8])
    a.shape
    # out:TensorShape([4, 35, 8])
        
    b = tf.ones([4, 35, 8])
    c = tf.stack([a, b])
    c.shape
    # out:TensorShape([2, 4, 35, 8])
        
    # [2,4,35,8]   
    aa, bb = tf.unstack(c, axis=0)
    # 2个[4,35,8]的Tensor
    aa.shape, bb.shape
    # out:(TensorShape([4, 35, 8]), TensorShape([4, 35, 8]))
        
    # [2,4,35,8]
    res = tf.unstack(c, axis=3)
    # 8个[2, 4, 35]的Tensor
    res[0].shape, res[1].shape, res[7].shape
    out(TensorShape([2, 4, 35]), TensorShape([2, 4, 35]), TensorShape([2, 4, 35]))
    
    # [2,4,35,8]
    res = tf.unstack(c, axis=2)
    # 35个[2, 4, 8]的Tensor
    res[0].shape, res[1].shape, res[34].shape
    # out:(TensorShape([2, 4, 8]), TensorShape([2, 4, 8]), TensorShape([2, 4, 8]))
    

tf.split

  • split(value,num_or_size_splits,axis=0,num=None,name='split')

    • 函数参数:

      • value:要分割的 Tensor.

      • num_or_size_splits:指示沿 split_dim 分割数量的 0-D 整数 Tensor 或包含沿 split_dim 每个输出张量大小的 1-D 整数 Tensor ;如果为一个标量,那么它必须均匀分割 value.shape[axis];否则沿分割维度的大小总和必须与该 value 相匹配.

      • axis:A 0-D int32 Tensor;表示分割的尺寸;必须在[-rank(value), rank(value))范围内;默认为0.

      • num:可选的,用于指定无法从 size_splits 的形状推断出的输出数.

      • name:操作的名称(可选).

    • 函数返回值:

      • 如果 num_or_size_splits 是标量,返回 num_or_size_splits Tensor对象;如果num_or_size_splits 是一维张量,则返回由 value 分割产生的 num_or_size_splits.get_shape[0] Tensor对象.

    • 函数可能引发的异常:

      • ValueError:如果 num 没有指定并且无法推断.

  • 作用:将张量分割成子张量。

    • 如果 num_or_size_splits 是整数类型,num_split,则 value 沿维度 axis 分割成为 num_split 更小的张量.要求 num_split 均匀分配 value.shape[axis].

    • 如果 num_or_size_splits 不是整数类型,则它被认为是一个张量 size_splits,然后将 value 分割成 len(size_splits) 块.第 i 部分的形状与 value 的大小相同,除了沿维度 axis 之外的大小 size_splits[i].

    c = tf.ones([2, 4, 35, 8])
    # 8个Tensor,全为1
    res = tf.unstack(c, axis=3)
    len(res)
    # out:8
        
    # 2个Tensor,一个6、一个2
    res = tf.split(c, axis=3, num_or_size_splits=2)
    len(res)
    # out:2
        
    res[0].shape
    # out:TensorShape([2, 4, 35, 4])
    
    # 分割成3个tensor,第三个维度分别是2,2,4
    res = tf.split(c, axis=3, num_or_size_splits=[2, 2, 4])
    res[0].shape, res[1].shape, res[2].shape
    # out:(TensorShape([2, 4, 35, 2]),
         TensorShape([2, 4, 35, 2]),
         TensorShape([2, 4, 35, 4]))
    

TensorFlow中的数据统计

tf.norm

  • norm(tensor,ord='euclidean',axis=None,keep_dims=False,name=None)

    • 作用:计算向量、矩阵和张量的范数。

      • 这个函数可以计算几个不同的向量范数和矩阵范数。

    • 参数:

      • tensor:float32,float64,complex64,complex128 类型的张量.

      • ord:范数的顺序.支持的值是“fro”、“euclidean”、0、1 、2、np.inf 和任意正实数,得到相应的 p-norm。缺省值是 'euclidean',如果张量是一个矩阵,则相当于 Frobenius 范数;如果是向量,则相当于 2-norm.一些限制适用:1、所述的 Frobenius 范数不是为向量所定义;2、若轴为 2 元组(矩阵范数),仅支持 “euclidean”、“fro”、1 、np.inf。

      • axis:如果 axis 是 None(默认值),那么输入被认为是一个向量,并且在张量的整个值集合上计算单个向量范数,即 norm(tensor,ord=ord)是等价于norm(reshape(tensor, [-1]), ord=ord).如果 axis 是 Python 整数,则输入被认为是一组向量,轴在张量中确定轴,以计算向量的范数.如果 axis 是一个2元组的 Python 整数,则它被认为是一组矩阵和轴,它确定了张量中的坐标轴,以计算矩阵范数.支持负数索引.示例:如果您在运行时传递可以是矩阵或一组矩阵的张量,则通过 axis=[-2,-1],而不是 axis=None 确保计算矩阵范数。

      • keep_dims:如果为 True,则 axis 中指定的轴将保持为大小 1.否则,坐标轴中的尺寸将从 "输出" 形状中移除.

      • name:操作的名字.

    • 返回值:

      • output:与张量具有相同类型的 Tensor,包含向量或矩阵的范数.如果 keep_dims 是 True,那么输出的排名等于张量的排名.否则, 如果轴为 none,则输出为标量;如果轴为整数,则输出的秩小于张量的秩;如果轴为2元组,则输出的秩比张量的秩低两倍.

    • 可能引发的异常:

      • ValueError:如果 ord 或者 axis 是无效的

    """
    L2范数
    """
    a = tf.ones([2, 2])
    a
    # out:<tf.Tensor: id=11, shape=(2, 2), dtype=float32, numpy=
    array([[1., 1.],
           [1., 1.]], dtype=float32)>
    
    # 对a张量求L2范数
    tf.norm(a)
    # out:<tf.Tensor: id=7, shape=(), dtype=float32, numpy=2.0>
    
    # 对a张量求L2范数
    tf.sqrt(tf.reduce_sum(tf.square(a)))
    # out:<tf.Tensor: id=16, shape=(), dtype=float32, numpy=2.0>
        
    a = tf.ones([4, 28, 28, 3])
    a.shape
    # out:TensorShape([4, 28, 28, 3])
    
    # 对a张量求L2范数
    tf.norm(a)
    # out:<tf.Tensor: id=25, shape=(), dtype=float32, numpy=96.99484>
    
    # 对a张量求L2范数       
    tf.sqrt(tf.reduce_sum(tf.square(a)))
    # out:<tf.Tensor: id=30, shape=(), dtype=float32, numpy=96.99484>
    
    '''
    L1范数
    '''
    b = tf.ones([2, 2])
    tf.norm(b)
    # out:<tf.Tensor: id=45, shape=(), dtype=float32, numpy=2.0>
    
    # 对b张量的维度1计算L1范数
    tf.norm(b, ord=2, axis=1)
    # out:<tf.Tensor: id=51, shape=(2,), dtype=float32, numpy=array([1.4142135, 1.4142135], dtype=float32)>
    
    # 对b张量计算L1范数    
    tf.norm(b, ord=1)
    <tf.Tensor: id=56, shape=(), dtype=float32, numpy=4.0>
        
    # 列为整体
    tf.norm(b, ord=1, axis=0)
    # out:<tf.Tensor: id=66, shape=(2,), dtype=float32, numpy=array([2., 2.], dtype=float32)>
        
    # 行为整体
    tf.norm(b, ord=1, axis=1)
    # out:<tf.Tensor: id=71, shape=(2,), dtype=float32, numpy=array([2., 2.], dtype=float32)>
    

tf.reduce_min/max/mean

  • 有reduce的原因,因为操作可能会有减维的功能,如[2,2],对行求max,会变成[2]

  • reduce_mean(input_tensor,axis=None,keep_dims=False,name=None,reduction_indices=None)

    • 作用:计算张量的各个维度上的元素的*均值。

      • axis是tf.reduce_mean函数中的参数,按照函数中axis给定的维度减少input_tensor.除非keep_dims是true,否则张量的秩将在axis的每个条目中减少1.如果keep_dims为true,则缩小的维度将保留为1.

      • 如果axis没有条目,则减少所有维度,并返回具有单个元素的张量.

    • 参数:

      • input_tensor:要减少的张量.应该有数字类型.

      • axis:要减小的尺寸.如果为None(默认),则减少所有维度.必须在[-rank(input_tensor), rank(input_tensor))范围内.

      • keep_dims:如果为true,则保留长度为1的缩小尺寸.

      • name:操作的名称(可选).

      • reduction_indices:axis的不支持使用的名称.

    • 返回:

      • 该函数返回减少的张量.

  • reduce_max(input_tensor,axis=None,keep_dims=False,name=None,reduction_indices=None)

    • 作用:计算一个张量的各个维度上元素的最大值.

      • 按照axis给定的维度减少input_tensor.除非 keep_dims 是true,否则张量的秩将在axis的每个条目中减少1.如果keep_dims为true,则减小的维度将保留为长度1.

      • 如果axis没有条目,则减少所有维度,并返回具有单个元素的张量.

    • 参数:

      • input_tensor:要减少的张量.应该有数字类型.

      • axis:要减小的尺寸.如果为 None(默认),则减少所有维度.必须在[-rank(input_tensor), rank(input_tensor))范围内.

      • keep_dims:如果为true,则保留长度为1的减少维度.

      • name:操作的名称(可选).

      • reduction_indices:axis的废弃的名称.

    • 返回:

      • 该函数返回减少的张量.

  • reduce_sum ( input_tensor , axis = None , keep_dims = False , name = None , reduction_indices = None )

    • 作用:此函数计算一个张量的各个维度上元素的总和.

      • 函数中的input_tensor是按照axis中已经给定的维度来减少的;除非 keep_dims 是true,否则张量的秩将在axis的每个条目中减少1;如果keep_dims为true,则减小的维度将保留为长度1.

      • 如果axis没有条目,则缩小所有维度,并返回具有单个元素的张量.

    • 参数:

      • input_tensor:要减少的张量.应该有数字类型.

      • axis:要减小的尺寸.如果为None(默认),则缩小所有尺寸.必须在范围[-rank(input_tensor), rank(input_tensor))内.

      • keep_dims:如果为true,则保留长度为1的缩小尺寸.

      • name:操作的名称(可选).

      • reduction_indices:axis的废弃的名称.

    • 返回:

      • 该函数返回减少的张量.

    a = tf.random.normal([4, 10])
    tf.reduce_min(a), tf.reduce_max(a), tf.reduce_mean(a)
    # out:(<tf.Tensor: id=80, shape=(), dtype=float32, numpy=-2.215113>,
     <tf.Tensor: id=82, shape=(), dtype=float32, numpy=1.9458845>,
     <tf.Tensor: id=84, shape=(), dtype=float32, numpy=-0.045550883>)
    
    # 对某一行求max
    tf.reduce_min(a, axis=1), tf.reduce_max(a, axis=1), tf.reduce_mean(a, axis=1)
    # out:(<tf.Tensor: id=98, shape=(4,), dtype=float32, numpy=array([-2.215113 , -1.5824796, -1.4861531, -1.3477703], dtype=float32)>,
     <tf.Tensor: id=100, shape=(4,), dtype=float32, numpy=array([0.9380455, 1.1625607, 1.9458845, 1.492183 ], dtype=float32)>,
     <tf.Tensor: id=102, shape=(4,), dtype=float32, numpy=array([-0.48791748,  0.25639585,  0.07420422, -0.02488617], dtype=float32)>)
    

tf.argmax/argmin

  • tf.argmax(input, dimension, name=None)

    • 作用:返回最大的那个数值所在的下标。

    • 参数:

      • input:输入数据

      • dimension:按某维度查找。

          dimension=0:按列查找;

          dimension=1:按行查找;

    • 返回:

      • 最大值的下标

    a.shape
    # out:TensorShape([4, 10])
        
    tf.argmax(a).shape
    # out:TensorShape([10])
        
    # 返回index
    tf.argmax(a)
    # out:<tf.Tensor: id=112, shape=(10,), dtype=int64, numpy=array([1, 1, 2, 3, 2, 1, 3, 1, 2, 1])>
            
    # 对第1维作用
    tf.argmin(a).shape
    # out:TensorShape([10])
        
    # 对第2维作用
    tf.argmin(a, axis=1).shape
    TensorShape([4])
    

tf.equal

  • equal( x, y,name=None)

    • 作用:对输入的 xy 两个 Tensor 逐元素(element-wise)做 (x == y) 逻辑比较,返回 bool 类型的 Tensor

    • 参数:

      • x 只支持以下类型:half, float32, float64, uint8, int8, int16, int32, int64, complex64, quint8, qint8, qint32, string, bool, complex128

      • y 的类型必须与 x 相同

      • name 给这个操作取一个名称,可选

    • 返回

      • bool 类型的 Tensor

    a = tf.constant([1, 2, 3, 2, 5])
    b = tf.range(5)
    tf.equal(a, b)
    # out:<tf.Tensor: id=186, shape=(5,), dtype=bool, numpy=array([False, False, False, False, False])>
            
    res = tf.equal(a, b)
    # 对True和False转换为1和0
    tf.reduce_sum(tf.cast(res, dtype=tf.int32))
    # out:<tf.Tensor: id=191, shape=(), dtype=int32, numpy=0>
    

tf.unique

  • tf.unique(x,out_idx=tf.int32,name=None)

    • 作用:在一维张量中找到唯一的元素.

      • 该操作返回一个张量 y,该张量包含所有发生在 x 中的所有唯一的元素 x,它们按照相同的顺序排序.此操作还会返回一个与 x 具有相同大小的张量 idx,包含唯一的输出 y 中 x 的每个值的索引.也就是说:

        y[idx[i]] = x[i] for i in [0, 1,...,rank(x) - 1]
        
    • 参数:

      • x:一个 Tensor,是1维的.

      • out_idx:可选 tf.DType 来自:tf.int32, tf.int64,默认为 tf.int32.

      • name:操作的名称(可选).

    • 函数返回值:

      • Tensor对象(y, idx)的元型态组.

        • y:一个 Tensor,与 x 类型相同.

        • idx:一个 out_idx 类型的 Tensor.

    a = tf.range(5)
    a
    # out:<tf.Tensor: id=235, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>
        
    # 返回索引
    tf.unique(a)
    # out:Unique(y=<tf.Tensor: id=237, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>, idx=<tf.Tensor: id=238, shape=(5,), dtype=int32, numpy=array([0, 1, 2, 3, 4], dtype=int32)>)
    
    a = tf.constant([4, 2, 2, 4, 3])
    a
    # out:<tf.Tensor: id=226, shape=(5,), dtype=int32, numpy=array([4, 2, 2, 4, 3], dtype=int32)>
        
    res = tf.unique(a)
    # out:Unique(y=<tf.Tensor: id=228, shape=(3,), dtype=int32, numpy=array([4, 2, 3], dtype=int32)>, idx=<tf.Tensor: id=229, shape=(5,), dtype=int32, numpy=array([0, 1, 1, 0, 2], dtype=int32)>)
    

TensorFlow计算Accuracy

TensorFlow的填充与复制

tf.pad

  • pad ( ensor, paddings, mode ='CONSTANT',name= None, constant_values= 0 )
    
    • 作用:填充张量。

      • 此操作根据您指定的 paddings 来填充一个 tensor.paddings 是一个具有形状 [n, 2] 的整数张量,其中 n 是 tensor 的秩.对于每个输入维度 D,paddings [D, 0] 表示在该维度的 tensor 内容之前要添加多少个值,而 paddings[D, 1] 表示在该维度中的 tensor 内容之后要添加多少值.如果 mode 是 “REFLECT”,那么这两个paddings[D, 0] 和 paddings[D, 1] 不得大于 tensor.dim_size(D) - 1.如果 mode 是 “SYMMETRIC”,那么这两个 paddings[D, 0] 和 paddings[D, 1] 不得大于tensor.dim_size(D)。

    • 参数:

      • tensor:张量.

      • paddings:int32 类型的张量.

      • mode:取值为 "CONSTANT"、"REFLECT" 或 "SYMMETRIC"(不区分大小写)

      • name:操作的名称(可选).

      • constant_values:在 “CONSTANT” 模式下,要使用的标量填充值,必须与 tensor 具有相同类型.

    • 返回:

      • 该函数返回一个张量,与 tensor 具有相同的类型.

    • 可能引发的异常:

      • ValueError:模式不是 "CONSTANT"、"REFLECT" 或 "SYMMETRIC" 中的一种时。

  • 示例:

    a = tf.reshape(tf.range(9), [3, 3])
    a
    # out:<tf.Tensor: id=17, shape=(3, 3), dtype=int32, numpy=
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]], dtype=int32)>
    
    tf.pad(a, [[0, 0], [0, 0]])
    # out:<tf.Tensor: id=20, shape=(3, 3), dtype=int32, numpy=
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]], dtype=int32)>
    
    tf.pad(a, [[1, 0,], [0, 0]])
    # out:<tf.Tensor: id=23, shape=(4, 3), dtype=int32, numpy=
    array([[0, 0, 0],
           [0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]], dtype=int32)>
    
    tf.pad(a, [[1, 1], [0, 0]])
    # out:<tf.Tensor: id=26, shape=(5, 3), dtype=int32, numpy=
    array([[0, 0, 0],
           [0, 1, 2],
           [3, 4, 5],
           [6, 7, 8],
           [0, 0, 0]], dtype=int32)>
    
    tf.pad(a, [[1, 1], [1, 0]])
    # out:<tf.Tensor: id=29, shape=(5, 4), dtype=int32, numpy=
    array([[0, 0, 0, 0],
           [0, 0, 1, 2],
           [0, 3, 4, 5],
           [0, 6, 7, 8],
           [0, 0, 0, 0]], dtype=int32)>
    
    tf.pad(a, [[1, 1], [1, 1]])
    # out:<tf.Tensor: id=32, shape=(5, 5), dtype=int32, numpy=
    array([[0, 0, 0, 0, 0],
           [0, 0, 1, 2, 0],
           [0, 3, 4, 5, 0],
           [0, 6, 7, 8, 0],
           [0, 0, 0, 0, 0]], dtype=int32)>
    
  • Image padding

    a = tf.random.normal([4, 28, 28, 3])
    a.shape
    # out:TensorShape([4, 28, 28, 3])
        
    # 对图片的行和列padding两行
    b = tf.pad(a, [[0, 0], [2, 2], [2, 2], [0, 0]])
    b.shape
    # out:TensorShape([4, 32, 32, 3])
    [1,5,5,1]
    [[0,0],[2,2],[2,2],[0,0]]
    [1,9,9,1]
    

tf.tile

  • tf.tile(input,multiples,name=None)

    • 作用:通过*铺(tile)给定的张量来构造张量。

      • 此操作通过复制 input multiples 时间来创建新的张量.输出张量的第 i 维具有 input.dims(i) * multiples[i] 元素,并且沿着 'i' 维度 input 值被复制 multiples[i] 次.例如,[a b c d] 由 [2] *铺将得到 [a b c d a b c d]。

    • 参数:

      • input:一个 Tensor,1-D或更高.

      • multiples:一个 Tensor,必须是以下类型之一:int32,int64,它是 1-d,长度必须与 input 中的维度数量相同.

      • name:操作的名称(可选).

    • 函数返回值:

      • tf.tile函数返回一个 Tensor,与 input 具有相同的类型.

  • 示例:

    • repeat data along dim n times

    • [a,b,c],2

    • --> [a,b,c,a,b,c]

    a = tf.reshape(tf.range(9), [3, 3])
    a
    # out:<tf.Tensor: id=76, shape=(3, 3), dtype=int32, numpy=
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]], dtype=int32)>
    
    # 1表示行不复制,2表示列复制为两倍
    tf.tile(a, [1, 2])
    <tf.Tensor: id=79, shape=(3, 6), dtype=int32, numpy=
    array([[0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8]], dtype=int32)>
    
    # 行复制为两倍,列不复制
    tf.tile(a, [2, 1])
    <tf.Tensor: id=82, shape=(6, 3), dtype=int32, numpy=
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8],
           [0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]], dtype=int32)>
    
    # 行列均复制为两倍
    tf.tile(a, [2, 2])
    <tf.Tensor: id=85, shape=(6, 6), dtype=int32, numpy=
    array([[0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8],
           [0, 1, 2, 0, 1, 2],
           [3, 4, 5, 3, 4, 5],
           [6, 7, 8, 6, 7, 8]], dtype=int32)>
    

tf.tile与tf.broadcast_to的区别

  • 都能实现张量维度复制功能,但是 tf.broadcast_to不占用内存,性能更优。

a = tf.ones([3,4])
a.shape
# out:TensorShape([3, 4])

a1 = tf.broadcast_to(a,[2,3,4])
a1.shape
# out:TensorShape([2, 3, 4])

a2 = tf.expand_dims(a,axis=0)  # 0前插入一维
a2.shape
# out:TensorShape([1, 3, 4])

a2 = tf.tile(a2,[2,1,1])  # 复制一维2次,复制二、三维1次
a2.shape
# out:TensorShape([2, 3, 4])

"""
a1和a2的区别:完全等价,但是a2占用的内存空间更大。
"""

a = tf.reshape(tf.range(9), [3, 3])
a
# out:<tf.Tensor: id=76, shape=(3, 3), dtype=int32, numpy=
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]], dtype=int32)>

aa = tf.expand_dims(a, axis=0)
aa
# out:<tf.Tensor: id=90, shape=(1, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]]], dtype=int32)>

tf.tile(aa, [2, 1, 1])
# out:<tf.Tensor: id=93, shape=(2, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]],

       [[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]]], dtype=int32)>

# 不占用内存,性能更优
tf.broadcast_to(aa, [2, 3, 3])
# out:<tf.Tensor: id=96, shape=(2, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]],

       [[0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]]], dtype=int32)>

TensorFlow的张量限幅操作

tf.clip_by_value

  • tf.clip_by_value(v,a,b)

    • 功能:可以将一个张量中的数值限制在一个范围之内。(可以避免一些运算错误)

    • 参数:

      • (1)v:input数据

      • (2)a、b是对数据的限制。

        • 当v小于a时,输出a; 当v大于a小于b时,输出原值; 当v大于b时,输出b;

    a = tf.range(10)
    a
    # out:<tf.Tensor: id=3, shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int32)>
        
    # a中小于2的元素值为2
    tf.maximum(a, 2)
    # out:<tf.Tensor: id=6, shape=(10,), dtype=int32, numpy=array([2, 2, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int32)>
        
    # a中大于8的元素值为8
    tf.minimum(a, 8)
    # out:<tf.Tensor: id=9, shape=(10,), dtype=int32, numpy=array([0, 1, 2, 3, 4, 5, 6, 7, 8, 8], dtype=int32)>
        
    # a中的元素值限制在[2,8]区间内
    tf.clip_by_value(a, 2, 8)
    # out:<tf.Tensor: id=14, shape=(10,), dtype=int32, numpy=array([2, 2, 2, 3, 4, 5, 6, 7, 8, 8], dtype=int32)>
    
  • tf.nn.relu激活函数的实现:使用函数tf.maximum函数即可实现tf.nn.relu激活函数的功能

    a = a - 5
    a
    # out:<tf.Tensor: id=17, shape=(10,), dtype=int32, numpy=array([-5, -4, -3, -2, -1,  0,  1,  2,  3,  4], dtype=int32)>
        
    tf.nn.relu(a)
    # out:<tf.Tensor: id=19, shape=(10,), dtype=int32, numpy=array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4], dtype=int32)>
        
    tf.maximum(a, 0)
    # out:<tf.Tensor: id=22, shape=(10,), dtype=int32, numpy=array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4], dtype=int32)>
    

tf.clip_by_norm

  • tf.clip_by_norm(t,clip_norm,axes=None,name=None)

    • 作用:指对梯度进行裁剪,通过控制梯度的最大范式,防止梯度爆炸的问题,是一种比较常用的梯度规约的方式。

      • 此种方式可以防止梯度爆炸,利用此种方式进行缩放不会改变梯度的方向。

    • 参数:

      • t: 输入tensor,也可以是list

      • clip_norm: 一个具体的数,如果normnorm, 则t不变化;否则

    a = tf.random.normal([2, 2], mean=10)
    a
    # out:<tf.Tensor: id=35, shape=(2, 2), dtype=float32, numpy=
    array([[ 8.630464, 10.737844],
           [ 9.764073, 10.382202]], dtype=float32)>
    
    tf.norm(a)
    # out:<tf.Tensor: id=41, shape=(), dtype=float32, numpy=19.822044>
    
    # 等比例的放缩a, 可以将张量a的范数约束为15
    aa = tf.clip_by_norm(a, 15)
    aa
    # out:<tf.Tensor: id=58, shape=(2, 2), dtype=float32, numpy=
    array([[6.5309587, 8.125684 ],
           [7.388799 , 7.8565574]], dtype=float32)>
    
    tf.norm(aa)
    # out:<tf.Tensor: id=64, shape=(), dtype=float32, numpy=15.0>
    

tf.clip_by_global_norm

  • Gradient Clipping(梯度裁剪)的引入是为了处理gradient explosion或者gradients vanishing的问题。当在一次迭代中权重的更新过于迅猛的话,很容易导致loss divergence。Gradient Clipping的直观作用就是让权重的更新限制在一个合适的范围。

  • tf.clip_by_global_norm(t_list, clip_norm, use_norm=None, name=None)

    • 作用:通过权重梯度的总和的比率来截取多个张量的值。主要用于梯度裁剪。

      • 给定一个元组或张量 t_list 的列表,以及一个剪辑比率 clip_norm,,此操作返回 t_list 中所有张量的 list_clipped 和全局范数 (global_norm) 的列表。或者, 如果您已经计算了 t_list 的全局范数,则可以使用 use_norm 指定全局范数。

    • 参数:

      • t_list 是梯度张量;

      • clip_norm 是截取的比率, 这个函数返回截取过的梯度张量和一个所有张量的全局范数。

  • 注意:它比 clip_by_norm() 慢,因为在执行剪辑操作之前必须准备好所有的参数。

TensorFlow的高级操作

tf.boolean_mask

  • tf.boolean_mask(tensor,mask,name='boolean_mask',axis=None)

    • 作用: 将使tensor (m维)矩阵仅保留与mask中“True”元素同下标的部分,并将结果展开到m-1维。

    • 参数:

      • tensor是N维度的tensor,mask是K维度的,注意K小于等于N,name可选项也就是这个操作的名字,axis是一个0维度的int型tensor,表示的是从参数tensor的哪个axis开始mask,默认的情况下,axis=0表示从第一维度进行mask,因此K+axis小于等于N。

    • 返回的是N-K+1维度的tensor,也就是mask为True的地方保存下来。

a = tf.random.normal([3, 3])
a
# out:<tf.Tensor: id=11, shape=(3, 3), dtype=float32, numpy=
array([[-0.02527909, -0.09084062,  0.34427297],
       [-0.45223615,  1.1085868 , -1.9480664 ],
       [-2.3520288 , -1.8698558 , -0.30862013]], dtype=float32)>

mask = a > 0
mask
# out:<tf.Tensor: id=16, shape=(3, 3), dtype=bool, numpy=
array([[False, False,  True],
       [False,  True, False],
       [False, False, False]])>

# 选取a张量中,与mask张量对应位置为True元素的值
tf.boolean_mask(a, mask)
# out:<tf.Tensor: id=44, shape=(2,), dtype=float32, numpy=array([0.34427297, 1.1085868 ], dtype=float32)>

tf.where

  • tf.where(condition,x=None,y=None,name=None)

    • 作用:根据condition返回x或y中的元素.

      • 如果x和y都为None,则该操作将返回condition中true元素的坐标.坐标以二维张量返回,其中第一维(行)表示真实元素的数量,第二维(列)表示真实元素的坐标.请记住,输出张量的形状可以根据输入中的真实值的多少而变化.索引以行优先顺序输出.

      • 如果两者都不是None,则x和y必须具有相同的形状.如果x和y是标量,则condition张量必须是标量.如果x和y是更高级别的矢量,则condition必须是大小与x的第一维度相匹配的矢量,或者必须具有与x相同的形状.

      • condition张量作为一个可以选择的掩码(mask),它根据每个元素的值来判断输出中的相应元素/行是否应从 x (如果为 true) 或 y (如果为 false)中选择.

      • 如果condition是向量,则x和y是更高级别的矩阵,那么它选择从x和y复制哪个行(外部维度).如果condition与x和y具有相同的形状,那么它将选择从x和y复制哪个元素.

    • 参数:

      • condition:一个bool类型的张量(Tensor).

      • x:可能与condition具有相同形状的张量;如果condition的秩是1,则x可能有更高的排名,但其第一维度必须匹配condition的大小.

      • y:与x具有相同的形状和类型的张量.

      • name:操作的名称(可选).

    • 返回值:

      • 如果它们不是None,则返回与x,y具有相同类型与形状的张量;张量具有形状(num_true, dim_size(condition)).

    • 可能引发的异常:

      • ValueError:当一个x或y正好不是None.

  • 用法1:tf.where(tensor)

a = tf.random.normal([3, 3])
a
# out:<tf.Tensor: id=11, shape=(3, 3), dtype=float32, numpy=
array([[-0.02527909, -0.09084062,  0.34427297],
       [-0.45223615,  1.1085868 , -1.9480664 ],
       [-2.3520288 , -1.8698558 , -0.30862013]], dtype=float32)>

mask = a > 0
mask
# out:<tf.Tensor: id=16, shape=(3, 3), dtype=bool, numpy=
array([[False, False,  True],
       [False,  True, False],
       [False, False, False]])>

# 为True元素的值
tf.boolean_mask(a, mask)
# out:<tf.Tensor: id=44, shape=(2,), dtype=float32, numpy=array([0.34427297, 1.1085868 ], dtype=float32)>

# 为True元素,即>0的元素的索引
indices = tf.where(mask)
indices
# out:<tf.Tensor: id=47, shape=(2, 2), dtype=int64, numpy=
array([[0, 2],
       [1, 1]])>

# 取回>0的值
tf.gather_nd(a, indices)
# out:<tf.Tensor: id=49, shape=(2,), dtype=float32, numpy=array([0.34427297, 1.1085868 ], dtype=float32)>
  • 用法2:tf.where(cond,A,B)

a = tf.random.normal([3, 3])
a
# out:<tf.Tensor: id=11, shape=(3, 3), dtype=float32, numpy=
array([[-0.02527909, -0.09084062,  0.34427297],
       [-0.45223615,  1.1085868 , -1.9480664 ],
       [-2.3520288 , -1.8698558 , -0.30862013]], dtype=float32)>

mask = a > 0
mask
# out:<tf.Tensor: id=16, shape=(3, 3), dtype=bool, numpy=
array([[False, False,  True],
       [False,  True, False],
       [False, False, False]])>

A = tf.ones([3, 3])
B = tf.zeros([3, 3])
# True的元素会从A中选值,False的元素会从B中选值
tf.where(mask, A, B)
# out:<tf.Tensor: id=61, shape=(3, 3), dtype=float32, numpy=
array([[0., 0., 1.],
       [0., 1., 0.],
       [0., 0., 0.]], dtype=float32)>

tf.scatter_nd

  • scatter_nd(indices,updates,shape,name=None)

    • 作用:根据indices将updates散布到新的(初始为零)张量.

      • 根据索引对给定shape的零张量中的单个值或切片应用稀疏updates来创建新的张量.此运算符是tf.gather_nd运算符的反函数,它从给定的张量中提取值或切片.

      • 警告:更新应用的顺序是非确定性的,所以如果indices包含重复项的话,则输出将是不确定的.

        indices是一个整数张量,其中含有索引形成一个新的形状shape张量.indices的最后的维度可以是shape的最多的秩:

        indices.shape[-1] <= shape.rank
        

        indices的最后一个维度对应于沿着shape的indices.shape[-1]维度的元素的索引(if indices.shape[-1] = shape.rank)或切片(if indices.shape[-1] < shape.rank)的索引.updates是一个具有如下形状的张量:

        indices.shape[:-1] + shape[indices.shape[-1]:]
        

        最简单的分散形式是通过索引将单个元素插入到张量中.例如,假设我们想要在8个元素的1级张量中插入4个分散的元素.

    • 参数 :

      • indices:一个Tensor;必须是以下类型之一:int32,int64;指数张量.

      • updates:一个Tensor;分散到输出的更新.

      • shape:一个Tensor;必须与indices具有相同的类型;1-d;得到的张量的形状.

      • name:操作的名称(可选).

    • 返回值:

    • 此函数将返回一个Tensor,它与updates有相同的类型;根据indices应用的一个新具有给定的形状和更新的张量.

    ndices = tf.constant([[4], [3], [1], [7]])
    updates = tf.constant([9, 10, 11, 12])
    shape = tf.constant([8])
    # 把updates按照indices的索引放在底板shape上
    tf.scatter_nd(indices, updates, shape)
    # out:<tf.Tensor: id=71, shape=(8,), dtype=int32, numpy=array([ 0, 11,  0, 10,  9,  0,  0, 12], dtype=int32)>
    

tf.meshgrid

  • meshgrid(*args,**kwargs)

    • 作用:广播用于计算 N 维网格的参数.

      • 给定 N 个一维坐标数组 *args,返回 N 维坐标数组的列表输出,用于计算 N 维网格上的表达式。

    • 参数:

      • args:秩为1的张量.

      • indexing 1.:“xy”或“ij”(可选值,默认值为:'xy').

      • * name:操作的名称(可选).

    • 返回:

      • outputs:秩为 N 的 N 张量列表.

  • 示例:

    • [y,x,w]

      • [5,5,2]

    • [N,2]

    • numpy实现

import numpy as np
points = []

for y in np.linspace(-2, 2, 5):
    for x in np.linspace(-2, 2, 5):
        points.append([x, y])

np.array(points)
array([[-2., -2.],
       [-1., -2.],
       [ 0., -2.],
       [ 1., -2.],
       [ 2., -2.],
       [-2., -1.],
       [-1., -1.],
       [ 0., -1.],
       [ 1., -1.],
       [ 2., -1.],
       [-2.,  0.],
       [-1.,  0.],
       [ 0.,  0.],
       [ 1.,  0.],
       [ 2.,  0.],
       [-2.,  1.],
       [-1.,  1.],
       [ 0.,  1.],
       [ 1.,  1.],
       [ 2.,  1.],
       [-2.,  2.],
       [-1.,  2.],
       [ 0.,  2.],
       [ 1.,  2.],
       [ 2.,  2.]])
  • tensorflow实现:

import tensorflow as tf
import matplotlib.pyplot as plt


def func(x):
    """

    :param x:[b,2]
    :return:
    """
    z = tf.math.sin(x[..., 0]) + tf.math.sin(x[..., 1])

    return z


x = tf.linspace(0., 2 * 3.14, 500)
y = tf.linspace(0., 2 * 3.14, 500)

# [50,50]
point_x, point_y = tf.meshgrid(x, y)
# [50,50,2]
points = tf.stack([point_x, point_y], axis=2)

print('points:', points.shape)

z = func(points)
print("z:", z.shape)

# 画出热力图
plt.figure('plot 2d func value')
plt.imshow(z, origin='lower', interpolation='none')
plt.colorbar()

# 画出等高线图
plt.figure('plot 2d func contour')
plt.contour(point_x, point_y, z)
plt.colorbar()
plt.show()

TensorFlow加载内置数据集

CIFAR10 小图像分类数据集

50,000 张 32x32 彩色训练图像数据,以及 10,000 张测试图像数据,总共分为 10 个类别。

用法:

from tensorflow.keras.datasets import cifar10

(x_train, y_train), (x_test, y_test) = cifar10.load_data()
  • 返回:

    • 2 个元组:

      • x_train, x_test: uint8 数组表示的 RGB 图像数据,尺寸为 (num_samples, 3, 32, 32) 或 (num_samples, 32, 32, 3),基于 image_data_format 后端设定的 channels_firstchannels_last

      • y_train, y_test: uint8 数组表示的类别标签(范围在 0-9 之间的整数),尺寸为 (num_samples,)。


CIFAR100 小图像分类数据集

50,000 张 32x32 彩色训练图像数据,以及 10,000 张测试图像数据,总共分为 100 个类别。

用法:

from tensorflow.keras.datasets import cifar100

(x_train, y_train), (x_test, y_test) = cifar100.load_data(label_mode='fine')
  • 返回:

    • 2 个元组:

      • x_train, x_test: uint8 数组表示的 RGB 图像数据,尺寸为 (num_samples, 3, 32, 32) 或 (num_samples, 32, 32, 3),基于 image_data_format 后端设定的 channels_firstchannels_last

      • y_train, y_test: uint8 数组表示的类别标签,尺寸为 (num_samples,)。

  • 参数:

    • label_mode: "fine" 或者 "coarse"


IMDB 电影评论情感分类数据集

数据集来自 IMDB 的 25,000 条电影评论,以情绪(正面/负面)标记。评论已经过预处理,并编码为词索引(整数)的序列表示。为了方便起见,将词按数据集中出现的频率进行索引,例如整数 3 编码数据中第三个最频繁的词。这允许快速筛选操作,例如:「只考虑前 10,000 个最常用的词,但排除前 20 个最常见的词」。

作为惯例,0 不代表特定的单词,而是被用于编码任何未知单词。

用法

from tensorflow.keras.datasets import imdb

(x_train, y_train), (x_test, y_test) = imdb.load_data(path="imdb.npz",
                                                      num_words=None,
                                                      skip_top=0,
                                                      maxlen=None,
                                                      seed=113,
                                                      start_char=1,
                                                      oov_char=2,
                                                      index_from=3)
  • 返回:

    • 2 个元组:

    • x_train, x_test: 序列的列表,即词索引的列表。如果指定了 num_words 参数,则可能的最大索引值是 num_words-1。如果指定了 maxlen 参数,则可能的最大序列长度为 maxlen

    • y_train, y_test: 整数标签列表 (1 或 0)。

  • 参数:

    • path: 如果你本地没有该数据集 (在 '~/.keras/datasets/' + path),它将被下载到此目录。

    • num_words: 整数或 None。要考虑的最常用的词语。任何不太频繁的词将在序列数据中显示为 oov_char 值。

    • skip_top: 整数。要忽略的最常见的单词(它们将在序列数据中显示为 oov_char 值)。

    • maxlen: 整数。最大序列长度。 任何更长的序列都将被截断。

    • seed: 整数。用于可重现数据混洗的种子。

    • start_char: 整数。序列的开始将用这个字符标记。设置为 1,因为 0 通常作为填充字符。

    • oov_char: 整数。由于 num_wordsskip_top 限制而被删除的单词将被替换为此字符。

    • index_from: 整数。使用此数以上更高的索引值实际词汇索引的开始。


路透社新闻主题分类

数据集来源于路透社的 11,228 条新闻文本,总共分为 46 个主题。与 IMDB 数据集一样,每条新闻都被编码为一个词索引的序列(相同的约定)。

用法:

from tensorflow.keras.datasets import reuters

(x_train, y_train), (x_test, y_test) = reuters.load_data(path="reuters.npz",
                                                         num_words=None,
                                                         skip_top=0,
                                                         maxlen=None,
                                                         test_split=0.2,
                                                         seed=113,
                                                         start_char=1,
                                                         oov_char=2,
                                                         index_from=3)

规格与 IMDB 数据集的规格相同,但增加了:

  • test_split: 浮点型。用作测试集的数据比例。

该数据集还提供了用于编码序列的词索引:

word_index = reuters.get_word_index(path="reuters_word_index.json")
  • 返回: 一个字典,其中键是单词(字符串),值是索引(整数)。 例如,word_index["giraffe"]可能会返回 1234

  • 参数:

    • path: 如果在本地没有索引文件 (at '~/.keras/datasets/' + path), 它将被下载到该目录。


MNIST 手写字符数据集

训练集为 60,000 张 28x28 像素灰度图像,测试集为 10,000 同规格图像,总共 10 类数字标签。

用法:

from tensorflow.keras.datasets import mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
  • 返回:

    • 2 个元组:

      • x_train, x_test: uint8 数组表示的灰度图像,尺寸为 (num_samples, 28, 28)。

      • y_train, y_test: uint8 数组表示的数字标签(范围在 0-9 之间的整数),尺寸为 (num_samples,)。

  • 参数:

    • path: 如果在本地没有索引文件 (at '~/.keras/datasets/' + path), 它将被下载到该目录。


Fashion-MNIST 时尚物品数据集

训练集为 60,000 张 28x28 像素灰度图像,测试集为 10,000 同规格图像,总共 10 类时尚物品标签。该数据集可以用作 MNIST 的直接替代品。类别标签是:

类别描述中文
0 T-shirt/top T恤/上衣
1 Trouser 裤子
2 Pullover 套头衫
3 Dress 连衣裙
4 Coat 外套
5 Sandal 凉鞋
6 Shirt 衬衫
7 Sneaker 运动鞋
8 Bag 背包
9 Ankle boot 短靴

用法:

from tensorflow.keras.datasets import fashion_mnist

(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
  • 返回:

    • 2 个元组:

      • x_train, x_test: uint8 数组表示的灰度图像,尺寸为 (num_samples, 28, 28)。

      • y_train, y_test: uint8 数组表示的数字标签(范围在 0-9 之间的整数),尺寸为 (num_samples,)。


Boston 房价回归数据集

数据集来自卡内基梅隆大学维护的 StatLib 库。

样本包含 1970 年代的在波士顿郊区不同位置的房屋信息,总共有 13 种房屋属性。 目标值是一个位置的房屋的中值(单位:k$)。

用法:

from tensorflow.keras.datasets import boston_housing

(x_train, y_train), (x_test, y_test) = boston_housing.load_data()
  • 参数:

    • path: 缓存本地数据集的位置 (相对路径 ~/.keras/datasets)。

    • seed: 在计算测试分割之前对数据进行混洗的随机种子。

    • test_split: 需要保留作为测试数据的比例。

  • 返回: Numpy 数组的元组: (x_train, y_train), (x_test, y_test)

Tensorflow的数据处理中的Dataset和Iterator

  • 我们在训练模型的时候,必须经过的第一个步骤是数据处理。在机器学习领域有一个说法,数据处理的好坏直接影响了模型结果的好坏。数据处理是至关重要的一步。

  • 我们今天关注数据处理的另一个问题:假设我们做深度学习,数据的量随随便便就到GB的级别,那数据处理的速度对于模型的训练也很重要。经常遇到的一个情况是,数据处理的时间占了训练整个模型的大部分。

  • Tensorflow中tf.data.Dataset是最常用的数据集类,我们也使用这个类做转换数据、迭代数据等操作。

Dataset原理

Google官方给出的Dataset API中的类图如下所示:

Dataset创建方法

Dataset API还提供了四种创建Dataset的方式:

  • tf.data.Dataset.from_tensor_slices():这个函数直接从内存中读取数据,数据的形式可以是数组、矩阵、dict等。

    dataset = tf.data.Dataset.from_tensor_slices(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
    #实例化make_one_shot_iterator对象,该对象只能读取一次
    iterator = dataset.make_one_shot_iterator()
    # 从iterator里取出一个元素
    one_element = iterator.get_next()
    with tf.Session() as sess:
        for i in range(5):
            print(sess.run(one_element))
    
    • 同时,Dataset提供了repeat()batch()方法方便我们建立循环的数据,repeat参数给定一个整型值就可以使数据重复几份,而batch则是将数据以多少条进行批处理,也就是按照batch参数大小切割数据。

      • 注意,repeat和batch的先后顺序不一样 ,结果是不同的,先repeat再batch会把数据先复制N份变成一个大数据,然后batch是根据这个大的数据来做的。

        • 例如,上面我们构造了10个数据,先repeat10份就有100个,再假设batch设置为6,那么最终数据是100/6+1=17份,那么也就是循环17次,如果先batch设置为6,那么数据先变成了10/6+1=2份,再repeat10次就有了20份数据了,循环要20次。

      # dataset = tf.data.Dataset.from_tensor_slices((features1, features2, labels)).repeat(10).batch(6)
      dataset = tf.data.Dataset.from_tensor_slices((features1, features2, labels)).batch(batch_size).repeat(n_repeats)
      
  • tf.data.TFRecordDataset():顾名思义,这个函数是用来读TFRecord文件的,dataset中的每一个元素就是一个TFExample。

# Creates a dataset that reads all of the examples from two files.
filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
dataset = tf.data.TFRecordDataset(filenames)
  • tf.data.TextLineDataset():这个函数的输入是一个文件的列表,输出是一个dataset。dataset中的每一个元素就对应了文件中的一行。可以使用这个函数来读入CSV文件。

filenames = ["/var/data/file1.txt", "/var/data/file2.txt"]
dataset = tf.data.TextLineDataset(filenames)
  • tf.data.FixedLengthRecordDataset():这个函数的输入是一个文件的列表和一个record_bytes,之后dataset的每一个元素就是文件中固定字节数record_bytes的内容。通常用来读取以二进制形式保存的文件,如CIFAR10数据集就是这种形式。

Dataset数据进行转换(Transformation)

一个Dataset通过Transformation变成一个新的Dataset。通常我们可以通过Transformation完成数据变换,打乱,组成batch,生成epoch等一系列操作,常用的Transformation有:

  • map:接收一个函数对象,Dataset中的每个元素都会被当作这个函数的输入,并将函数返回值作为新的Dataset。

    def prepare_mnist_features_and_labels(x, y):
        """
        数据预处理
        """
        # 将特征值数据归一化,并转换数据类型
        x = tf.cast(x, tf.float32) / 255.
        # 将标签值数据转换数据类型
        y = tf.cast(y, tf.int64)
        return x, y
    
    #创建Dataset数据集对象
    ds = tf.data.Dataset.from_tensor_slices((x, y))
    # 接收一个函数对象,对dataset对象中的每个数据集进行预处理
    ds = ds.map(prepare_mnist_features_and_labels)
    
  • apply:应用一个转换函数到dataset。

    dataset = dataset.apply(group_by_window(key_func, reduce_func, window_size))
    
  • batch:根据接收的整数值将该数个元素组合成batch,如下面的程序将dataset中的元素组成了大小为32的batch。

    dataset = dataset.batch(32)
    
  • shuffle:打乱dataset中的元素,它有一个参数buffersize,表示打乱时使用的buffer的大小。

    dataset = dataset.shuffle(buffer_size=10000)
    
  • repeat:整个序列重复多次,主要用来处理机器学习中的epoch,假设原先的数据是一个epoch,使用repeat(5)就可以将之变成5个epoch。

    # 将数据集变成5个epoch
    dataset = dataset.repeat(5)
    # 如果repeat没有参数,则一直重复循环数据
    dataset = dataset.repeat()
    
  • padded_batch:对dataset中的数据进行padding(填充)到一定的长度。

    dataset.padded_batch(
        batch_size,
        padded_shapes=(
            tf.TensorShape([None]),  # src
            tf.TensorShape([]),  # tgt_output
            tf.TensorShape([]),
            tf.TensorShape([src_max_len])),  # src_len
        padding_values=(
            src_eos_id,  # src
            0,  # tgt_len -- unused
            0,  # src_len -- unused
            0)) # mask
    
  • shard:根据多GPU进行分片操作。

    dataset.shard(num_shards, shard_index)
    

比较完整的生成dataset的代码:

def prepare_mnist_features_and_labels(x, y):
    """
    数据预处理
    """
    # 将特征值数据归一化,并转换数据类型
    x = tf.cast(x, tf.float32) / 255.
    # 将标签值数据转换数据类型
    y = tf.cast(y, tf.int64)
    return x, y

def mnist_dataset():
    """
    加载fashion_mnist数据集
    """
    (x, y), (x_val, y_val) = datasets.fashion_mnist.load_data()
    # 将标签值数据one-hot编码,长度是10
    y = tf.one_hot(y, depth=10)
    y_val = tf.one_hot(y_val, depth=10)

    # 创建Dataset数据集对象
    ds = tf.data.Dataset.from_tensor_slices((x, y))
    # 
    ds = ds.map(prepare_mnist_features_and_labels)
    ds = ds.shffle(60000).batch(100)
    ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
    ds_val = ds_val.map(prepare_mnist_features_and_labels)
    ds_val = ds_val.shuffle(10000).batch(100)
    return ds, ds_val

获取数据迭代器

  • 数据准备完成之后需要获取数据迭代器供后面迭代使用,TensorFlow有四种方法创建迭代器,其中单次迭代器和可初始化的迭代器是最常见的两种:

    """
    TensorFlow1.X中的数据迭代器
    """
    # 单次迭代器只能循环使用一次数据,而且单次迭代器不需要手动显示调用sess.run()进行初始化即可使用
    iterator = dataset.make_one_shot_iterator()
    # 可初始化的迭代器可以重新初始化进行循环,但是需要手动显示调用sess.run()才能循环
    iterator = dataset.make_initializable_iterator()
    # 创建了迭代器之后,我们获取迭代器结果便于后面的运行,注意,这里不会产生迭代,只是建立tensorflow的计算图,因此不会消耗迭代
    next_element = iterator.get_next()
    
    """
    TensorFlow2.X中的数据迭代器
    """
    # dataset数据集可迭代(但不是迭代器),就像其他Python迭代器在Eager模式下工作一样。您可以通过将代码包装在tf.function()中来充分利用数据集异步预取/流功能,该代码将Python迭代替换为使用AutoGraph的等效图形操作。
    @tf.function
    def train(model, dataset, optimizer):
      for x, y in dataset:
        with tf.GradientTape() as tape:
          prediction = model(x)
          loss = loss_fn(prediction, y)
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(zip(gradients, model.trainable_variables))
    

创建了迭代器之后就可以循环数据了

  • 迭代器循环的停止通过捕获数据越界的错误进行。

    count = 0
    with tf.Session() as sess:
        # 这是显示初始化,当我们的迭代器是dataset.make_initializable_iterator()的时候,才需要调用这个方法,否则不需要
        sess.run(iterator.initializer)
        # 无线循环数据,直到越界
        while True:
            try:
                features1_batch, features2_batch, labels_batch = sess.run(next_element)
                count += 1
                print(count)
            except tf.errors.OutOfRangeError:
                break
    
    • 这里的count输出与上面repeat和batch的先后顺序有关,大家可以自己更换代码查看。

使用tqdm循环输出

  • 除了上述捕获越界错误外,我们也可手动计算epoch循环次数和batch循环次数来确定终止的情况。可以配合tqdm包进行输出。tqdm是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator)即可。我们先引入必要的包:

import timefrom tqdm 
import trange

接下来我们使用自己计算的结果循环:

# 注意,这里实例总数显然是先repeat再batch的结果,要根据实际情况做改变
total_instances = data_size * n_repeats
steps_per_epoch = data_size // batch_size if data_size / batch_size == 0 else data_size // batch_size + 1
with tf.Session() as sess:
    sess.run(iterator.initializer)
    for epoch in range(n_repeats):
        tqr = trange(steps_per_epoch, desc="%2d" % (epoch + 1), leave=False)
        for _ in tqr:
            features1_batch, features2_batch, labels_batch = sess.run(next_element)
            # 由于这里循环没有计算过程,速度很快,看不到进度条,我们加了暂停0.5秒便于观察结果
            time.sleep(0.5)
    # 由于所有数据都已经循环完毕,如下代码将会报越界的错误,证明我们是对的
    sess.run(next_element)

TensorFlow实现损失函数的计算

什么是损失函数

  • 损失函数(loss function)是机器学习中非常重要的内容,它是度量模型输出值与目标值的差异,也就是作为评估模型效果的一种重要指标,损失函数越小,表明模型的鲁棒性就越好。

怎样使用损失函数

在TensorFlow中训练模型时,通过损失函数告诉TensorFlow预测结果相比目标结果是好还是坏。在多种情况下,我们会给出模型训练的样本数据和目标数据,损失函数即是比较预测值与给定的目标值之间的差异。下面将介绍在TensorFlow中常用的损失函数。

回归模型的损失函数

(1)L1正则损失函数(即绝对值损失函数)
  • L1正则损失函数是对预测值与目标值的差值求绝对值,公式如下:

  • 在TensorFlow中调用方式如下:

l1_loss = tf.abs(y_preb-y_target)
  • L1正则损失函数在目标值附*不*滑,会导致模型不能很好地收敛。

(2)L2正则损失函数(即欧拉损失函数)
  • L2正则损失函数是预测值与目标值差值的*方和,公式如下:

  • 当对L2取*均值,就变成均方误差(MSE, mean squared error),公式如下:

  • 在TensorFlow中调用方式如下:

    #L2损失
    l2_loss = tf.square(y_pred - y_target)
    
    #MSE
    mse_loss = tf.reduce_mean(tf.squre(y_pred - y_target))
    
  • L2正则损失函数在目标值附*有很好的曲度,离目标越*收敛越慢,是非常有用的损失函数。

分类模型的损失函数

(1)Hinge损失函数
  • Hinge损失常用于二分类问题,主要用来评估向量机算法,但有时也用来评估神经网络算法,公式如下:

  • 在TensorFlow中的调用方式如下:

    hinge_loss = tf.maximum(0.,1-tf.mul(y_target,y_pred))
    
(2)二分类交叉熵(Cross-entropy)损失函数
  • 交叉熵来自于信息论,是分类问题中使用广泛的损失函数。交叉熵刻画了两个概率分布之间的距离,当两个概率分布越接*时,它们的交叉熵也就越小,给定两个概率分布p和q,则距离如下:

  • 对于两类问题,当一个概率p=y,则另一个概率q=1-y,因此代入化简后的公式如下:

  • 在TensorFlow中的调用方式如下:

    ce_loss = tf.mul(y_target, tf.log(y_pred)) – tf.mul((1. – y_target), tf.log(1. – y_pred))
    
  • Cross-entropy损失函数主要应用在二分类问题上,预测值为概率值,取值范围为[0,1]。

(3)Sigmoid交叉熵损失函数
  • 与二分类交叉熵类似,只是将预测值y_pred值通过sigmoid函数进行转换,再计算交叉熵损失。在TensorFlow中有内置了该函数,调用方式如下:

    sce_loss = tf.nn.sigmoid_cross_entropy_with_logits(y_pred,y_target)
    
  • 由于sigmoid函数会将输入值变小很多,从而*滑了预测值,使得sigmoid交叉熵在预测值离目标值比较远时,其损失的增长没有那么的陡峭。

  • tf.losses.sigmoid_cross_entropy(
        multi_class_labels,
        logits,
        weights=1.0,
        label_smoothing=0,
        scope=None,
        loss_collection=tf.GraphKeys.LOSSES,
        reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
    )
    
    • 作用:使用tf.nn.sigmoid_cross_entropy_with_logits创建交叉熵loss.

      • weights作为loss的系数.如果提供了标量,那么loss只是按给定值进行缩放.如果weights是形状为[batch_size]的张量,则loss权重适用于每个相应的样本.

      • 如果label_smoothing非零,则将标签*滑为1/2

    • 参数:

      • multi_class_labels:{0, 1}中的[batch_size, num_classes]目标整数标签.

      • logits:Float [batch_size, num_classes]记录网络的输出.

      • weights:可选的Tensor,其秩为0或与labels具有相同的秩,并且必须可广播到labels(即,所有维度必须为1与相应的losses维度相同).

      • label_smoothing:如果大于0,则*滑标签.

      • scope:计算loss时执行的操作范围.

      • loss_collection:将添加loss的集合.

      • reduction:适用于loss的减少类型.

    • 返回:

      • 与logits具有相同类型的加权损失Tensor;如果reduction是NONE,则它的形状与logits相同;否则,它是标量.

    • 可能引发的异常:

      • ValueError:如果logits的形状与multi_class_labels不匹配,或形状weights无效,或者如果weights是NONE;或者如果multi_class_labels或logits为NONE.

(4)加权交叉熵损失函数
  • 加权交叉熵损失函数是Sigmoid交叉熵损失函数的加权,是对正目标的加权。假定权重为0.5,在TensorFlow中的调用方式如下:

    weight = tf.constant(0.5)
    wce_loss = tf.nn.weighted_cross_entropy_with_logits(y_pred, y_targets, weight)
    
(5)Softmax交叉熵损失函数
  • Softmax交叉熵损失函数是作用于非归一化的输出结果,只针对单个目标分类计算损失。

  • 通过softmax函数将输出结果转化成概率分布,从而便于输入到交叉熵里面进行计算(交叉熵要求输入为概率),softmax定义如下:

  • 结合前面的交叉熵定义公式,则Softmax交叉熵损失函数公式如下:

  • tf.losses.softmax_cross_entropy(
        onehot_labels,
        logits,
        weights=1.0,
        label_smoothing=0,
        scope=None,
        loss_collection=tf.GraphKeys.LOSSES,
        reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
    )
    
    • 作用:使用tf.nn.softmax_cross_entropy_with_logits创建交叉熵(cross-entropy)loss。

      • weights作为loss的系数.如果提供了标量,那么loss只是按给定值缩放.如果weights是形状为[batch_size]的张量,则loss权重适用于每个相应的样本.

      • 如果label_smoothing非零,则将标签*滑为:1/num_classes: new_onehot_labels = onehot_labels * (1 - label_smoothing) + label_smoothing / num_classes

    • 参数:

      • onehot_labels:[batch_size, num_classes]目标是一个onehot-encoded标签.

      • logits:[batch_size, num_classes]logits网络的输出.

      • weights:可选的Tensor,其秩为0或1,并且可以广播到形状为[batch_size]的Tensor的loss.

      • label_smoothing:如果大于0,则*滑标签.

      • scope:计算loss时执行的操作范围.

      • loss_collection:将添加loss的集合.

      • reduction:适用于loss的减少类型.

    • 返回:

      • 与logits具有相同类型的加权损失Tensor.如果reduction是NONE,这有形状[batch_size];否则,它是标量.

    • 可能引发的异常:

      • ValueError:如果logits的形状与onehot_labels不匹配,或weights的形状无效,或者如果weights是None,如果onehot_labels或logits是None.

  • tf.losses.sparse_softmax_cross_entropy(
        labels,
        logits,
        weights=1.0,
        scope=None,
        loss_collection=tf.GraphKeys.LOSSES,
        reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
    )
    
    • 作用:使用tf.nn.sparse_softmax_cross_entropy_with_logits的交叉熵(cross-entropy)loss.

      • weights作为loss的系数.如果提供了标量,那么loss只是按给定值缩放.如果weights是形状为[batch_size]的张量,则loss权重适用于每个相应的样本.

    • 参数:

      • labels:形状为[d_0, d_1, ..., d_{r-1}]的Tensor(其中,r是labels和结果的秩)

      • logits:形状为[d_0, d_1, ..., d_{r-1}, num_classes],并且是dtype float32或float64的未缩放的日志概率.

      • weights:loss的系数.这必须是标量或可广播的labels(即相同的秩,每个维度是1或者是相同的).

      • scope:计算loss时执行的操作范围.

      • loss_collection:将添加loss的集合.

      • reduction:适用于loss的减少类型.

    • 返回:

      • 与logits具有相同类型的加权损失Tensor.如果reduction是NONE,它的形状与labels相同;否则,它是标量.

    • 可能引发的异常:

      • ValueError:如果logits,labels和weights的形状不兼容,或者如果它们中的任何一个为None.

  • tf.losses.sparse_softmax_cross_entropy与tf.losses.softmax_cross_entropy的区别:

    • tf.losses.sparse_softmax_cross_entropy的logits通常是最后的全连接层的输出结果,labels是具体哪一类的标签,这个函数是直接使用标签数据的,而不是采用one-hot编码形式。

    • tf.losses.softmax_cross_entropy适用标签数据的one-hot编码形式。

  • tf.losses.categorical_crossentropy(
        target,
        output,
        from_logits=False,
        axis=-1
    )
    
    • 作用:输出张量和目标张量之间的分类交叉熵。

    • 参数:

      • target:标签值张量,为one-hot编码形式。

      • output:由softmax产生的张量(除非from_logits是True,在这种情况下output预计是logits)。

      • from_logits:Boolean,判断output是softmax的结果,还是logits的张量。

      • axis:Int,用于指定通道轴。axis=-1对应于数据格式channels_last', axis = 1对应于数据格式channels_first'。

    • 返回:输出张量

TensorFlow实现梯度下降

  • 用于自动求梯度的类tf.GradientTape(persistent=False,watch_accessed_variables=True)

    • 参数:

      • persistent: 布尔值,用来指定新创建的gradient tape是否是可持续性的。默认是False,意味着只能够调用一次gradient()函数。

      • watch_accessed_variables: 布尔值,表明这个gradien tap是不是会自动追踪任何能被训练(trainable)的变量。默认是True。要是为False的话,意味着你需要手动去指定你想追踪的那些变量。

    • 常用的方法:

      • watch(tensor)

        • 作用:确保某个tensor被tape追踪

        • 参数:

          • tensor: 一个Tensor或者一个Tensor列表

      • gradient(target,sources,output_gradients=None,unconnected_gradients=tf.UnconnectedGradients.NONE)

        • 作用:根据tape上面的上下文来计算某个或者某些tensor的梯度

        • 参数:

          • target: 被微分的Tensor或者Tensor列表,你可以理解为经过某个函数之后的值

          • sources: Tensors 或者Variables列表(当然可以只有一个值). 你可以理解为函数的某个变量

          • output_gradients: 一个梯度列表, one for each element of target. Defaults to None.

        • 返回:

          • 一个列表表示各个变量的梯度值,和source中的变量列表一一对应,表明这个变量的梯度。

  • 示例1:

    def gradient_test():
        # -------------------一元梯度案例---------------------------
        print("一元梯度")
        x = tf.constant(value=3.0)
        with tf.GradientTape(persistent=True, watch_accessed_variables=True) as tape:
            tape.watch(x)
            y1 = 2 * x
            y2 = x * x + 2
            y3 = x * x + 2 * x
        # 一阶导数
        dy1_dx = tape.gradient(target=y1, sources=x)
        dy2_dx = tape.gradient(target=y2, sources=x)
        dy3_dx = tape.gradient(target=y3, sources=x)
        print("dy1_dx:", dy1_dx)
        print("dy2_dx:", dy2_dx)
        print("dy3_dx:", dy3_dx)
    
        # # -------------------二元梯度案例---------------------------
        print("二元梯度")
        x = tf.constant(value=3.0)
        y = tf.constant(value=2.0)
        with tf.GradientTape(persistent=True, watch_accessed_variables=True) as tape:
            tape.watch([x, y])
            z1 = x * x * y + x * y
        # 一阶导数
        dz1_dx = tape.gradient(target=z1, sources=x)
        dz1_dy = tape.gradient(target=z1, sources=y)
        dz1_d = tape.gradient(target=z1, sources=[x, y])
        print("dz1_dx:", dz1_dx)
        print("dz1_dy:", dz1_dy)
        print("dz1_d:", dz1_d)
        print("type of dz1_d:", type(dz1_d))
    
    if __name__ == "__main__":
        gradient_test()
    

    结果:

    一元梯度
    dy1_dx: tf.Tensor(2.0, shape=(), dtype=float32)
    dy2_dx: tf.Tensor(6.0, shape=(), dtype=float32)
    dy3_dx: tf.Tensor(8.0, shape=(), dtype=float32)
    二元梯度
    dz1_dx: tf.Tensor(14.0, shape=(), dtype=float32)
    dz1_dy: tf.Tensor(12.0, shape=(), dtype=float32)
    dz1_d: [<tf.Tensor: id=55, shape=(), dtype=float32, numpy=14.0>, <tf.Tensor: id=56, shape=(), dtype=float32, numpy=12.0>]
    type of dz1_d: <class 'list'>
    
  • 示例2:

    # 遍历整个数据集,遍历epochs_num次
    for epoch in range(epochs_num):
    
        for step, (x, y) in enumerate(train_db):
            # 梯度下降算法train every train_db
            with tf.GradientTape() as tape:  # only data types of tf.variable are logged
                # x:[b,28*28]
                # h1 = x@w1+b1
                # [b,784]@[784,256]+[256] ==> [b,256] + [256] ==> [b,256] + [b,256]
                # 前向传播,计算预测值
                h1 = x @ w1 + b1
                z1 = tf.nn.relu(h1)
                h2 = z1 @ w2 + b2
                z2 = tf.nn.relu(h2)
                out = z2 @ w3 + b3
    
                # 计算均方差损失值:mse = mean(sum(y-out)^2)
                loss = tf.reduce_mean(tf.square(y - out))
                # 反向传播计算梯度值
                grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
                # 更新梯度值,使用assign_sub()函数可以在原地更新
                #  w1 = w1 - lr * grads[0]  不是原地更新
                w1.assign_sub(learning_rate * grads[0])
                b1.assign_sub(learning_rate * grads[1])
                w2.assign_sub(learning_rate * grads[2])
                b2.assign_sub(learning_rate * grads[3])
                w3.assign_sub(learning_rate * grads[4])
                b3.assign_sub(learning_rate * grads[5])
    
                cache = (w1, b1, w2, b2, w3, b3)
    
                print("第{}次迭代整个数据集,损失值为:{}".format(epoch, loss))
    

案例1:TensorFlow2实现全连接神经网络预测mnist数据集

import time

from tensorflow import keras
import tensorflow as tf
from tensorflow.keras import datasets
from sklearn.model_selection import train_test_split

import numpy as np
import os


def mnist_dataset():
    """
    加载mnist数据集
    :return:
    """
    # 加载数据集
    # x:(60000, 28, 28),y:(60000,)
    (train_x, train_y), (test_x, test_y) = datasets.mnist.load_data()

    # 划分数据集
    validation_size = 0.1

    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=validation_size)

    # 将数据集中的特征值数据及标签数据转换为张量,并指定数据类型
    train_x = tf.convert_to_tensor(train_x, dtype=tf.float32)
    train_y = tf.convert_to_tensor(train_y, dtype=tf.int32)
    val_x = tf.convert_to_tensor(val_x, dtype=tf.float32)
    val_y = tf.convert_to_tensor(val_y, dtype=tf.int32)
    test_x = tf.convert_to_tensor(test_x, dtype=tf.float32)
    test_y = tf.convert_to_tensor(test_y, dtype=tf.int32)

    # 改变形状(60000, 28, 28) --->(60000, 28*28) 并归一化
    train_x = tf.reshape(train_x, [-1, 28 * 28]) / 255.
    test_x = tf.reshape(test_x, [-1, 28 * 28]) / 255.
    val_x = tf.reshape(val_x, [-1, 28 * 28]) / 255.

    # 将标签值数据进行one-hot转换
    train_y = tf.one_hot(train_y, depth=10)
    val_y = tf.one_hot(val_y, depth=10)
    test_y = tf.one_hot(test_y, depth=10)

    return train_x, train_y, val_x, val_y, test_x, test_y


def get_accuracy(x, y, cache):
    """
    计算正确率
    :param x: 特征值数据集
    :param y: 目标值数据集
    :return: acc:正确率
    """
    (w1, b1, w2, b2, w3, b3) = cache
    # 测试预测正确率
    correct_num, total_num = 0, 0  # 正确的数量与总数量
    # 创建一个数据集对象,打乱顺序,并批量化,每个批量数据集设为128个
    data_db = tf.data.Dataset.from_tensor_slices((x, y)).shuffle(buffer_size=1000).batch(128)

    # 迭代测试集与验证集
    for step, (x, y) in enumerate(data_db):
        # 前向传播,计算预测值
        h1 = x @ w1 + b1
        z1 = tf.nn.relu(h1)
        h2 = z1 @ w2 + b2
        z2 = tf.nn.relu(h2)
        out = z2 @ w3 + b3

        # softmax分类,将类别转换成概率
        prob = tf.nn.softmax(out, axis=1)
        # 取概率值最大的结果的坐标
        pred = tf.argmax(prob, axis=1)
        # 将结果的数据类型转换为int32
        pred = tf.cast(pred, dtype=tf.int32)
        # 将真实值的标签从one-hot形式转换成普通形式
        y = tf.cast(tf.argmax(y, axis=1), dtype=tf.int32)

        # 比较预测结果与真实值 y: [b]
        # [b], int32
        correct = tf.cast(tf.equal(pred, y), dtype=tf.int32)
        # 得到本批次数据集中预测正确的个数
        correct = tf.reduce_sum(correct)
        # print(int(correct))

        # 求整个数据集中总的正确的数量
        correct_num += int(correct)
        # 总的数据集的样本数量
        total_num += x.shape[0]

    # 计算正确率
    acc = correct_num / total_num
    return acc


def train():
    """
    训练模型
    :return:
    """
    # 加载数据集
    train_x, train_y, val_x, val_y, test_x, test_y = mnist_dataset()
    # 创建一个训练数据集对象,打乱顺序,并批量化,每个批量数据集设为128个
    train_db = tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(train_x.shape[0]).batch(64)

    # 初始化参数:[b,784] =>[b,256] =>[b,128] =>[b,10]
    # 权重:[dim_in,dim_out],偏置:[dim_out]
    w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
    b1 = tf.Variable(tf.zeros([256]))
    w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
    b2 = tf.Variable(tf.zeros([128]))
    w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
    b3 = tf.Variable(tf.zeros([10]))

    # 学习率
    learning_rate = 1e-3
    epochs_num = 20  # 迭代整个数据集的次数

    # 遍历整个数据集,遍历epochs_num次
    for epoch in range(epochs_num):

        for step, (x, y) in enumerate(train_db):
            # 梯度下降算法train every train_db
            with tf.GradientTape() as tape:  # only data types of tf.variable are logged
                # x:[b,28*28]
                # h1 = x@w1+b1
                # [b,784]@[784,256]+[256] ==> [b,256] + [256] ==> [b,256] + [b,256]
                # 前向传播,计算预测值
                h1 = x @ w1 + b1
                z1 = tf.nn.relu(h1)
                h2 = z1 @ w2 + b2
                z2 = tf.nn.relu(h2)
                out = z2 @ w3 + b3

                # 计算均方差损失值:mse = mean(sum(y-out)^2)
                mse_loss = tf.reduce_mean(tf.square(y - out))

            # 反向传播计算梯度值
            grads = tape.gradient(mse_loss, [w1, b1, w2, b2, w3, b3])
            # 更新梯度值,使用assign_sub()函数可以在原地更新
            #  w1 = w1 - lr * grads[0]  不是原地更新
            w1.assign_sub(learning_rate * grads[0])
            b1.assign_sub(learning_rate * grads[1])
            w2.assign_sub(learning_rate * grads[2])
            b2.assign_sub(learning_rate * grads[3])
            w3.assign_sub(learning_rate * grads[4])
            b3.assign_sub(learning_rate * grads[5])

            cache = (w1, b1, w2, b2, w3, b3)

        print("第{}次迭代整个数据集,损失值为:{}".format(epoch, mse_loss))

    # 计算正确率
    train_acc = get_accuracy(train_x, train_y, cache)
    val_acc = get_accuracy(val_x, val_y, cache)
    test_acc = get_accuracy(test_x, test_y, cache)
    print("训练集正确率为:", train_acc)
    print("验证集正确率为:", val_acc)
    print("测试集正确率为:", test_acc)


if __name__ == '__main__':
    # 开始时间
    start_time = time.time()
    train()
    # 结束时间
    end_time = time.time()
    # 计算时差
    print("CPU的执行时间 = " + str(end_time - start_time) + " 秒")

案例2:TensorFlow2组装网络层来构建神经网络模型实现mnist数据集的预测

import tensorflow as tf
from tensorflow.keras import datasets, layers, Sequential, optimizers, metrics
from sklearn.model_selection import train_test_split

def data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y):
    """
    数据预处理
    :param train_x:
    :param train_y:
    :param val_x:
    :param val_y:
    :param test_x:
    :param test_y:
    :return:
    """
    # 将数据转换为张量形式并指定数据类型
    train_x = tf.convert_to_tensor(train_x, dtype=tf.float32)
    train_y = tf.convert_to_tensor(train_y, dtype=tf.int32)
    val_x = tf.convert_to_tensor(val_x, dtype=tf.float32)
    val_y = tf.convert_to_tensor(val_y, dtype=tf.int32)
    test_x = tf.convert_to_tensor(test_x, dtype=tf.float32)
    test_y = tf.convert_to_tensor(test_y, dtype=tf.int32)

    # 改变输入数据的形状,并归一化;[b,28,28]--->[b,28*28]
    train_x = tf.reshape(train_x, [-1, train_x.shape[1] * train_x.shape[2]]) / 255.
    val_x = tf.reshape(val_x, [-1, val_x.shape[1] * val_x.shape[2]]) / 255.
    test_x = tf.reshape(test_x, [-1, test_x.shape[1] * test_x.shape[2]]) / 255.

    # 将标签值数据进行one-hot编码
    train_y = tf.one_hot(train_y, depth=10)
    val_y = tf.one_hot(val_y, depth=10)
    test_y = tf.one_hot(test_y, depth=10)

    return train_x, train_y, val_x, val_y, test_x, test_y

def load_mnist_dataset():
    """
    加载mnist数据集
    :return:
    """

    # 加载数据集
    (train_x, train_y), (test_x, test_y) = datasets.mnist.load_data()

    # 划分训练集为训练集与验证集
    val_size = 0.1
    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=val_size)

    # 数据预处理
    train_x, train_y, val_x, val_y, test_x, test_y = data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y)

    return train_x, train_y, val_x, val_y, test_x, test_y

def get_accuracy(data_db, model):
    """
    计算正确率
    :param data_db: 数据集对象
    :param model: 模型
    :return:
    """
    # 预测正确的数量,数据集总数量
    correct_num, total_num = 0, 0

    # 迭代数据集
    for step, (x, y) in enumerate(data_db):
        # 前向传播计算预测值
        logits = model(x)

        # 使用softmax函数将预测结果转换成概率值
        prob = tf.nn.softmax(logits, axis=1)

        # 概率值最大的结果的下标
        index = tf.argmax(prob, axis=1)
        # 转换数据类型;int64-->int32 [b,]
        out = tf.cast(index, dtype=tf.int32)

        # 将标签值数据从one-hot形式转换成类别形式并指定数据类型;[b,10] -->[b,]
        y = tf.cast(tf.argmax(y, axis=1), dtype=tf.int32)

        # 比较预测结果值与真实标签值
        correct = tf.reduce_sum(tf.cast(tf.equal(out, y), dtype=tf.int32))

        # 预测正确的总数量
        correct_num += int(correct)

        # 数据集的总样本数量
        total_num += x.shape[0]

    # 计算正确率
    acc = correct_num / total_num

    return acc

def train():
    """
    创建模型并训练
    :return:
    """
    epochs_num = 10  # 迭代整个数据集的次数
    batch_size = 64  # 每个批次的数据量
    learning_rate = 0.001  # 学习率

    # 加载数据集
    train_x, train_y, val_x, val_y, test_x, test_y = load_mnist_dataset()

    # 创建数据集对象并批量化
    train_db = tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(10000).batch(batch_size=batch_size)
    test_db = tf.data.Dataset.from_tensor_slices((test_x, test_y)).batch(batch_size=batch_size)
    val_db = tf.data.Dataset.from_tensor_slices((val_x, val_y)).batch(batch_size=batch_size)

    # 设计神经网络结构
    model = Sequential([
        layers.Dense(256, activation=tf.nn.relu),  # [b,784] --> [b,256];神经元的个数为256
        layers.Dense(128, activation=tf.nn.relu),  # [b,256] --> [b,128];神经元的个数为128
        layers.Dense(64, activation=tf.nn.relu),  # [b,128] --> [b,64];神经元的个数为64
        layers.Dense(32, activation=tf.nn.relu),  # [b,64] -->[b,32];神经元的个数为32
        layers.Dense(10),  # [b,32] -->[b,10];输出类别有10类
    ], name='myModel')

    # 传入输入数据的维度,创建权重与偏置
    model.build(input_shape=train_x.shape)
    # 调试,打印模型的网络结构
    model.summary()

    # 创建优化器,加快模型的训练速度
    optimizer = optimizers.Adam(lr=learning_rate)

    # 迭代整个数据集
    for epoch in range(1, epochs_num + 1):

        # 迭代每个batch的训练数据集
        for step, (x, y) in enumerate(train_db):
            # 运行梯度下降算法
            with tf.GradientTape() as tape:
                # 前向传播算法计算预测值;[b,784] --> [b,10]
                logits = model(x)

                # 计算损失函数值
                loss_mse = tf.reduce_mean(tf.losses.MSE(y, logits))  # 均方误差损失MSE
                loss_ce = tf.reduce_mean(
                    tf.losses.categorical_crossentropy(y,
                                                       logits,
                                                       from_logits=True))  # softmax交叉熵损失

            # 计算参数梯度
            grads = tape.gradient(loss_ce, model.trainable_variables)
            # 使用优化器在原参数上更新参数值
            optimizer.apply_gradients(zip(grads, model.trainable_variables))

        print('epoch:{},loss:{}'.format(epoch, loss_ce))

    # 计算正确率
    train_acc = get_accuracy(train_db, model)
    val_acc = get_accuracy(val_db, model)
    test_acc = get_accuracy(test_db, model)

    print("----------------------------------accuracy--------------------------------------")
    print("train dataset accuracy:", train_acc)
    print("val dataset accuracy:", val_acc)
    print("test dataset accuracy:", test_acc)
if __name__ == '__main__':
    train()

TensorBoard可视化

TensorFlow2.x实现TensorBoard显示

  • 步骤1:run listener启动TensorBoard

    • 在终端中到当前程序所在目录,使用命令:tensorboard --logdir logs

    • 在浏览器中打开 TensorBoard 的图页面 http://localhost:6006会在GRAPHS模块加载我们可以看到图结构。

  • 步骤2:build summary 构建日志文件

    import datetime
    import tensorflow as tf
    
    # 获取当前时间并转换成字符串
    current_time = datetime.datetime.now().strftime('%Y%m%d-%H%M%s')
    log_dir = 'logs/' + current_time
    # 构建事件文件
    summary_writer = tf.summary.create_file_writer(log_dir)
    
  • 步骤3:收集变量

    # 收集矢量变量
    with summary_writer.as_default():
        tf.summary.scalar('loss', float(loss), step=epoch)
        tf.summary.scalar('accuracy', float(train_accuracy), step=epoch)
        
    '''
    tf.summary.histogram(name='',tensor):收集高维度的变量参数,如线性回归的偏置,权重等。
    '''
    
    # 收集单张图片变量
    sample_img = next(iter(db))[0]
    sample_img = sample_img[0]
    sample_img = tf.reshape(sample_img, [1, 28, 28, 1])
    with summary_writer.as_default():
        tf.summary.image('Traning sample:', sample_img, step=0)
       
    # 收集多张图片变量
    val_images = x[:25]
    val_images = tf.reshape(val_images, [-1, 28, 28, 1])
    with summary_writer.as_default():
        tf.summary.scalar('test-acc', float(loss), step=step)
        tf.summary.image('val-onebyone-images:',
                         val_images,
                         max_output=25,
                         step=step)
    

TensorFlow实现可视化的示例

import tensorflow as tf
from tensorflow.keras import datasets, layers, Sequential, optimizers, metrics
from sklearn.model_selection import train_test_split
import datetime
from matplotlib import pyplot as plt
import io


def data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y):
    """
    数据预处理
    :param train_x:
    :param train_y:
    :param val_x:
    :param val_y:
    :param test_x:
    :param test_y:
    :return:
    """
    # 将数据转换为张量形式并指定数据类型
    train_x = tf.convert_to_tensor(train_x, dtype=tf.float32)
    train_y = tf.convert_to_tensor(train_y, dtype=tf.int32)
    val_x = tf.convert_to_tensor(val_x, dtype=tf.float32)
    val_y = tf.convert_to_tensor(val_y, dtype=tf.int32)
    test_x = tf.convert_to_tensor(test_x, dtype=tf.float32)
    test_y = tf.convert_to_tensor(test_y, dtype=tf.int32)

    # 改变输入数据的形状,并归一化;[b,28,28]--->[b,28*28]
    train_x = tf.reshape(train_x, [-1, train_x.shape[1] * train_x.shape[2]]) / 255.
    val_x = tf.reshape(val_x, [-1, val_x.shape[1] * val_x.shape[2]]) / 255.
    test_x = tf.reshape(test_x, [-1, test_x.shape[1] * test_x.shape[2]]) / 255.

    # 将标签值数据进行one-hot编码
    train_y = tf.one_hot(train_y, depth=10)
    val_y = tf.one_hot(val_y, depth=10)
    test_y = tf.one_hot(test_y, depth=10)

    return train_x, train_y, val_x, val_y, test_x, test_y


def load_mnist_dataset():
    """
    加载mnist数据集
    :return:
    """

    # 加载数据集
    (train_x, train_y), (test_x, test_y) = datasets.mnist.load_data()

    # 划分训练集为训练集与验证集
    val_size = 0.1
    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=val_size)

    # 数据预处理
    train_x, train_y, val_x, val_y, test_x, test_y = data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y)

    return train_x, train_y, val_x, val_y, test_x, test_y


def plot_to_image(figure):
    """Converts the matplotlib plot specified by 'figure' to a PNG image and
  returns it. The supplied figure is closed and inaccessible after this call."""
    # Save the plot to a PNG in memory.
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    # Closing the figure prevents it from being displayed directly inside the notebook.
    plt.close(figure)
    buf.seek(0)
    # Convert PNG buffer to TF image
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    # Add the batch dimension
    image = tf.expand_dims(image, 0)
    return image


def image_grid(images):
    """Return a 5x5 grid of the MNIST images as a matplotlib figure."""
    # Create a figure to contain the plot.
    figure = plt.figure(figsize=(10, 10))
    for i in range(25):
        # Start next subplot.
        plt.subplot(5, 5, i + 1, title='name')
        plt.xticks([])
        plt.yticks([])
        plt.grid(False)
        plt.imshow(images[i], cmap=plt.cm.binary)

    return figure


def get_accuracy(data_db, model):
    """
    计算正确率
    :param data_db: 数据集对象
    :param model: 模型
    :return:
    """
    # 预测正确的数量,数据集总数量
    correct_num, total_num = 0, 0

    # 迭代数据集
    for step, (x, y) in enumerate(data_db):
        # 前向传播计算预测值
        logits = model(x)

        # 使用softmax函数将预测结果转换成概率值
        prob = tf.nn.softmax(logits, axis=1)

        # 概率值最大的结果的下标
        index = tf.argmax(prob, axis=1)
        # 转换数据类型;int64-->int32 [b,]
        out = tf.cast(index, dtype=tf.int32)

        # 将标签值数据从one-hot形式转换成类别形式并指定数据类型;[b,10] -->[b,]
        y = tf.cast(tf.argmax(y, axis=1), dtype=tf.int32)

        # 比较预测结果值与真实标签值
        correct = tf.reduce_sum(tf.cast(tf.equal(out, y), dtype=tf.int32))

        # 预测正确的总数量
        correct_num += int(correct)

        # 数据集的总样本数量
        total_num += x.shape[0]

    # 计算正确率
    acc = correct_num / total_num

    return acc


def train():
    """
    创建模型并训练
    :return:
    """
    epochs_num = 10  # 迭代整个数据集的次数
    batch_size = 64  # 每个批次的数据量
    learning_rate = 0.001  # 学习率

    # 加载数据集
    train_x, train_y, val_x, val_y, test_x, test_y = load_mnist_dataset()

    # 创建数据集对象并批量化
    train_db = tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(10000).batch(batch_size=batch_size)
    test_db = tf.data.Dataset.from_tensor_slices((test_x, test_y)).batch(batch_size=batch_size)
    val_db = tf.data.Dataset.from_tensor_slices((val_x, val_y)).batch(batch_size=batch_size)

    # 设计神经网络结构
    model = Sequential([
        layers.Dense(256, activation=tf.nn.relu),  # [b,784] --> [b,256];神经元的个数为256
        layers.Dense(128, activation=tf.nn.relu),  # [b,256] --> [b,128];神经元的个数为128
        layers.Dense(64, activation=tf.nn.relu),  # [b,128] --> [b,64];神经元的个数为64
        layers.Dense(32, activation=tf.nn.relu),  # [b,64] -->[b,32];神经元的个数为32
        layers.Dense(10),  # [b,32] -->[b,10];输出类别有10类
    ], name='myModel')

    # 传入输入数据的维度,创建权重与偏置
    model.build(input_shape=train_x.shape)
    # 调试,打印模型的网络结构
    model.summary()

    # 创建优化器,加快模型的训练速度
    optimizer = optimizers.Adam(lr=learning_rate)

    # 可视化
    current_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    log_dir = 'logs/' + current_time
    # 构建一个日志文件,传入一个带时间戳的路径
    summary_writer = tf.summary.create_file_writer(log_dir)

    # 迭代整个数据集
    for epoch in range(1, epochs_num + 1):

        # 迭代每个batch的训练数据集
        for step, (x, y) in enumerate(train_db):
            # 运行梯度下降算法
            with tf.GradientTape() as tape:
                # 前向传播算法计算预测值;[b,784] --> [b,10]
                logits = model(x)

                # 计算损失函数值
                loss_mse = tf.reduce_mean(tf.losses.MSE(y, logits))  # 均方误差损失MSE
                loss_ce = tf.reduce_mean(
                    tf.losses.categorical_crossentropy(y,
                                                       logits,
                                                       from_logits=True))  # softmax交叉熵损失

            # 计算参数梯度
            grads = tape.gradient(loss_ce, model.trainable_variables)
            # 使用优化器在原参数上更新参数值
            optimizer.apply_gradients(zip(grads, model.trainable_variables))

        # 评估验证集上的正确率
        print(epoch, 'loss:', float(loss_ce))
        # 收集损失函数的变量
        with summary_writer.as_default():
            tf.summary.scalar('train-loss', float(loss_ce), step=epoch)

        correct_num, total_num = 0, 0

        for step, (x, y) in enumerate(val_db):
            # 前向传播计算预测值
            logits = model(x)

            # 使用softmax函数将预测结果转换成概率值
            prob = tf.nn.softmax(logits, axis=1)

            # 概率值最大的结果的下标
            index = tf.argmax(prob, axis=1)
            # 转换数据类型;int64-->int32 [b,]
            out = tf.cast(index, dtype=tf.int32)

            # 将标签值数据从one-hot形式转换成类别形式并指定数据类型;[b,10] -->[b,]
            y = tf.cast(tf.argmax(y, axis=1), dtype=tf.int32)

            # 比较预测结果值与真实标签值
            correct = tf.reduce_sum(tf.cast(tf.equal(out, y), dtype=tf.int32))

            # 预测正确的总数量
            correct_num += int(correct)

            # 数据集的总样本数量
            total_num += x.shape[0]

            val_images = x[:25]
            val_images = tf.reshape(val_images, [-1, 28, 28, 1])
            with summary_writer.as_default():
                tf.summary.scalar('test-acc',
                                  float(correct_num / total_num),
                                  step=epoch)
                tf.summary.image("val-onebyone-images:",
                                 val_images,
                                 max_outputs=25,
                                 step=epoch)

                val_images = tf.reshape(val_images, [-1, 28, 28])
                # 合并多张图片为一张
                figure = image_grid(val_images)
                tf.summary.image('val-images:', plot_to_image(figure), step=epoch)
        print(epoch, 'val Acc:', correct_num / total_num)

    # 计算正确率
    train_acc = get_accuracy(train_db, model)
    val_acc = get_accuracy(val_db, model)
    test_acc = get_accuracy(test_db, model)
    print("----------------------------------accuracy--------------------------------------")
    print("train dataset accuracy:", train_acc)
    print("val dataset accuracy:", val_acc)
    print("test dataset accuracy:", test_acc)


if __name__ == '__main__':
    train()

TensorFlow中的keras高级API

Keras 简介

  • Keras 是一个用于构建和训练深度学习模型的高阶 API。它可用于快速设计原型、高级研究和生产,具有以下三个主要优势:

    • 方便用户使用 Keras 具有针对常见用例做出优化的简单而一致的界面。它可针对用户错误提供切实可行的清晰反馈。

    • 模块化和可组合 将可配置的构造块连接在一起就可以构建 Keras 模型,并且几乎不受限制。

    • 易于扩展 可以编写自定义构造块以表达新的研究创意,并且可以创建新层、损失函数并开发先进的模型。

  • tf.kerasKeras API 在TensorFlow 里的实现。这是一个高级API,用于构建和训练模型,同时兼容 TensorFlow 的绝大部分功能,比如,eager executiontf.data模块及 Estimatorstf.keras使得 TensorFlow 更容易使用,且保持 TF 的灵活性和性能。 首先需要在您的代码开始时导入tf.keras:

    import tensorflow as tf
    from tensorflow import keras
    
    • tf.keras可以运行任何与Keras兼容的代码,但请记住:

      • 最新TensorFlow版本中的tf.keras版本可能与PyPI的最新keras版本不同。 检查tf.keras.version。

      • 保存模型的权重时,tf.keras默认为 checkpoint 格式。 通过save_format ='h5'使用HDF5。

TensorFlow利用tf.keras.layers构建模型

构建一个简单的模型

  • 在Keras中,我们可以组装网络层来构建模型。模型通常是一个网络层构成的图。最常见的模型类型是一个叫做序贯模型的叠加层:tf.keras.models.Sequential模型。

  • 我们可以通过将网络层实例的列表传递给 Sequential 的构造器,来创建一个 Sequential 模型:

    mmodel1 = keras.models.Sequential([
        layers.Dense(32, activation='relu'),
        layers.Dense(10, activation='softmax'),
    
    ])
    
    model1.build([None, 728])
    model1.summary()
    
  • 也可以简单地使用 .add() 方法将各层添加到模型中:

    model = keras.models.Sequential()
    # 添加一个含有64个神经网络单元的全连接层到模型中,并且指定输入数据的维度,激活函数
    model.add(keras.layers.Dense(64, activation='relu',input_shape=(64,)))
    # 添加另外一个神经网络层
    model.add(keras.layers.Dense(64, activation='relu'))
    # 添加一个具有10个输出单元且使用softmax为激活函数的全连接层
    model.add(keras.layers.Dense(10, activation='softmax'))
    
    • 通过上面的代码一个具有一个输入层,一个隐藏层,一个输出层的神经网络模型就构建好了。

tf.keras.layers.Dense

  • keras.layers.Dense(units, 
                       activation=None, 
                       use_bias=True, 
                       kernel_initializer='glorot_uniform', 
                       bias_initializer='zeros', 
                       kernel_regularizer=None, 
                       bias_regularizer=None, 
                       activity_regularizer=None, 
                       kernel_constraint=None, 
                       bias_constraint=None)
    
    • 功能:Dense 实现以下操作: output = activation(dot(input, kernel) + bias) 其中 activation 是按逐个元素计算的激活函数,kernel 是由网络层创建的权值矩阵,以及 bias 是其创建的偏置向量 (只在 use_biasTrue 时才有用)。

    • 参数:

      • units: 正整数,输出空间维度,也是该层神经元的个数。

      • activation: 激活函数 (详见 activations)。 若不指定,则不使用激活函数。

      • use_bias: 布尔值,该层是否使用偏置向量。

      • kernel_initializer: kernel 权值矩阵的初始化器 (详见 initializers)。

      • bias_initializer: 偏置向量的初始化器 (see initializers).

      • kernel_regularizer: 运用到 kernel 权值矩阵的正则化函数 (详见 regularizer)。

      • bias_regularizer: 运用到偏置向的的正则化函数 (详见 regularizer)。

      • activity_regularizer: 运用到层的输出的正则化函数 (它的 "activation")。 (详见 regularizer)。

      • kernel_constraint: 运用到 kernel 权值矩阵的约束函数 (详见 constraints)。

      • bias_constraint: 运用到偏置向量的约束函数 (详见 constraints)。

配置网络层

  • tf.keras.layers.Dense层还可以使用很多的参数, 主要的功能如下:

    • activation:设置网络层的激活函数。此参数由内置函数的名称或可调用对象指定。默认情况下,不应用任何激活。

    • kernel_initializer和bias_initializer:创建网络层时内核或者偏差权重的初始化方案。此参数是名称或可调用对象。这里默认为"Glorot uniform"初始化方案。

    • kernel_regularizer和bias_regularizer:设置应用于网络层中内核或者偏差权重的正则化方案,例如L1或L2正则化。默认情况下,不应用正则化。

  • 以下是tf.keras.layers.Dense使用构造函数参数实例化网络层:

    # 创建一个使用sigmoid为激活函数的layer
    tf.keras.layers.Dense(64, activation='sigmoid')
    # 也可以这样创建,效果一样
    tf.keras.layers.Dense(64, activation=tf.sigmoid)
    # 一个kernel矩阵使用正则化因子为0.01的L1正则项的全连接层
    tf.keras.layers.Dense(64, kernel_regularizer=keras.regularizers.l1(0.01))
    # 一个偏差向量使用正则化因子为0.01的L2正则项的全连接层
    tf.keras.layers.Dense(64, bias_regularizer=keras.regularizers.l2(0.01))
    # 一个使用随机正交矩阵初始化Kernel的全连接层
    tf.keras.layers.Dense(64, kernel_initializer='orthogonal')
    # 一个偏差初始化时全为2的全连接层
    tf.keras.layers.Dense(64, bias_initializer=keras.initializers.constant(2.0))
    

创建一个多层神经网络案例

from tensorflow import keras

x = tf.random.normal([2, 3])
# 通过将网络层实例的列表传递给 Sequential 的构造器,来创建一个 Sequential 模型
model = keras.Sequential([
    keras.layers.Dense(2, activation='relu'),
    keras.layers.Dense(2, activation='relu'),
    keras.layers.Dense(2)
])
# 传入输入维度,创建权重与偏置
model.build(input_shape=[None, 3])
# 调试功能,打印网络结构信息
model.summary()

# 打印模型中的权重和偏置
# [w1,b1,w2,b2,w3,b3]
for p in model.trainable_variables:
    print(p.name, p.shape)
  • 输出:

Model: "My_molde"  # 模型名称
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                multiple                  8         
_________________________________________________________________
dense_1 (Dense)              multiple                  6         
_________________________________________________________________
dense_2 (Dense)              multiple                  6         
=================================================================
Total params: 20     # 模型的总的参数量
Trainable params: 20  # 模型需要训练的参数量
Non-trainable params: 0
_________________________________________________________________
# 打印模型中每层网路的权重与偏置的名称与形状。
dense/kernel:0 (3, 2)
dense/bias:0 (2,)
dense_1/kernel:0 (2, 2)
dense_1/bias:0 (2,)
dense_2/kernel:0 (2, 2)
dense_2/bias:0 (2,)

TensorFlow利用tf.keras.metrics评估当前训练模型的性能

tf.keras.metrics函数的用法

  • metrics为评价函数用于评估当前训练模型的性能。当模型编译后(compile),评价函数应该作为 metrics 的参数来输入。

    from keras import metrics
    
    model.compile(loss='mean_squared_error',
                  optimizer='sgd',
                  metrics=[metrics.mae, metrics.categorical_accuracy])
    
    • 评价函数和 损失函数 相似,只不过评价函数的结果不会用于训练过程中。

  • 使用示例:

    • 步骤1:build a meter

      acc_meter = metrics.Accuarcy()
      loss_meter = metrics.Mean
      
    • 步骤2:update data

      loss_meter.update_state(loss)
      acc_meter.update_state(y,pred)
      
    • 步骤3:get average data

      print(step, 'loss:', loss_meter.result().numpy())
      
    • 步骤4 clear buffer

      if step % 100 == 0:
          print(step, 'loss:', loss_meter.result().numpy())
          loss_meter.reset_states()
          
      # ...
      
      if step % 500 == 0:
          total, total_correct = 0., 0
          acc_meter.reset_states()
      

可使用的评价函数

  • binary_accuracy(y_true, y_pred): 对二分类问题,计算在所有预测值上的*均正确率

  • categorical_accuracy(y_true, y_pred):对多分类问题,计算再所有预测值上的*均正确率

  • sparse_categorical_accuracy(y_true, y_pred):与categorical_accuracy相同,在对稀疏的目标值预测时有用

  • top_k_categorical_accracy(y_true, y_pred): 计算top-k正确率,当预测值的前k个值中存在目标类别即认为预测正确

  • sparse_top_k_categorical_accuracy(y_true, y_pred):与top_k_categorical_accracy作用相同,但适用于稀疏情况

  • 评价函数的参数:

    • y_true:真实标签,tensorflow张量

    • y_pred:预测值, 与y_true形式相同的tensorflow张量

TensorFlow利用tf.keras模块训练和评估模型

训练模型

  • 构建模型后,通过调用compile方法配置其训练过程:

    model.compile(optimizer=tf.train.AdamOptimizer(0.001),
                 loss='categorical_crossentropy',
                 metrics=['accuracy'])
    
  • tf.keras.Model.compile有三个重要参数:

    • optimizer:训练过程的优化方法。此参数通过 tf.train 模块的优化方法的实例来指定,比如:AdamOptimizerRMSPropOptimizerGradientDescentOptimizer

    • loss:训练过程中使用的损失函数(通过最小化损失函数来训练模型)。 常见的选择包括:均方误差(mse),categorical_crossentropy和binary_crossentropy。 损失函数由名称或通过从tf.keras.losses模块传递可调用对象来指定。

    • metrics:训练过程中,监测的指标(Used to monitor training)。 指定方法:名称 或 可调用对象 from the tf.keras.metrics 模块。 以下显示了配置培训模型的几个示例:

      # Configure a model for mean-squared error regression.
      model.compile(optimizer=tf.train.AdamOptimizer(0.01),
                    loss='mse',       # mean squared error
                    metrics=['mae'])  # mean absolute error
      
      # Configure a model for categorical classification.
      model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),
                    loss=keras.losses.categorical_crossentropy,
                    metrics=[keras.metrics.categorical_accuracy])
      
  • 对于小的数据集,可以直接使用 NumPy 格式的数据进行训练、评估模型。模型使用 fit 方法训练数据:

    import numpy as np
    
    data = np.random.random((1000, 32))
    labels = np.random.random((1000, 10))
    
    model.fit(data, labels, epochs=10, batch_size=32)
    
    • tf.keras.Model.fit 有三个重要的参数:

      • epochs:训练多少轮。(小批量)

      • batch_size:当传递NumPy数据时,模型将数据分成较小的批次,并在训练期间迭代这些批次。 此整数指定每个批次的大小。 请注意,如果样本总数不能被批量大小整除,则最后一批可能会更小。

      • validation_data:在对模型进行原型设计时,您希望轻松监控其在某些验证数据上的性能。 传递这个参数 - 输入和标签的元组 - 允许模型在每个epoch的末尾以传递数据的推理模式显示损失和度量。 这是使用validation_data的示例:

    import numpy as np
    
    data = np.random.random((1000, 32))
    labels = np.random.random((1000, 10))
    
    val_data = np.random.random((100, 32))
    val_labels = np.random.random((100, 10))
    
    model.fit(data, labels, epochs=10, batch_size=32,
              validation_data=(val_data, val_labels))
    
  • 使用 Datasets API 可扩展到大型数据集或多设备训练。 给 fit 方法传递一个 tf.data.Dataset 实例:

    # Instantiates a toy dataset instance:
    dataset = tf.data.Dataset.from_tensor_slices((data, labels))
    dataset = dataset.batch(32)
    dataset = dataset.repeat()
    
    # Don't forget to specify `steps_per_epoch` when calling `fit` on a dataset.
    model.fit(dataset, epochs=10, steps_per_epoch=30)
    
    • 这里,fit方法使用steps_per_epoch参数 - 这是模型在移动到下一个epoch之前运行的训练步数。 由于数据集生成批量数据,因此此代码段不需要batch_size。

      • Dataset API 也可以用于验证:

      dataset = tf.data.Dataset.from_tensor_slices((data, labels))
      dataset = dataset.batch(32).repeat()
      
      val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
      val_dataset = val_dataset.batch(32).repeat()
      
      model.fit(dataset, epochs=10, steps_per_epoch=30,
                validation_data=val_dataset,
                validation_steps=3)
      

评估和预测模型

  • tf.keras.Model.evaluatetf.keras.Model.predict 方法能够使用 NumPy 数据 和 tf.data.Dataset 数据。

    • 要评估所提供数据的推理模式损失和指标:

    model.evaluate(x, y, batch_size=32)
    model.evaluate(dataset, steps=30)
    
    • 并且作为NumPy数组,预测所提供数据的推断中最后一层的输出:

    model.predict(x, batch_size=32)
    model.predict(dataset, steps=30)
    

TensorFlow利用tf.keras模块自定义层或网络

注:

  1. 要想使用 Sequential 必须遵循一些协议。

    • 自定的层继承至 Keras.layers.Layer 类。

    • 自己的模型也必须要继承至 Keras.Model 类 。

1.构建一个简单的网络层

import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
# 定义网络层就是:设置网络权重和输出到输入的计算过程
# 继承 Keras.layers.Layer类
class MyLayer(layers.Layer):
    def __init__(self, input_dim=32, unit=32):
        super(MyLayer, self).__init__()
        
       # 权重初始化
    	w_init = tf.random_normal_initializer()
        # 定义权重变量
        self.weight = tf.Variable(initial_value=w_init(
            shape=(input_dim, unit), dtype=tf.float32), trainable=True)
        # 偏置初始化
        b_init = tf.zeros_initializer()
        self.bias = tf.Variable(initial_value=b_init(
            shape=(unit,), dtype=tf.float32), trainable=True)
    # 实现前向传播的计算过程
    def call(self, inputs):
        return tf.matmul(inputs, self.weight) + self.bias
        
x = tf.ones((3,5))
my_layer = MyLayer(5, 4)
out = my_layer(x)
print(out)
        
输出:        
    tf.Tensor(
    [[0.06709253 0.06818779 0.09926171 0.0179923 ]
     [0.06709253 0.06818779 0.09926171 0.0179923 ]
     [0.06709253 0.06818779 0.09926171 0.0179923 ]], shape=(3, 4), dtype=float32)
  • 按上面构建网络层,图层会自动跟踪权重w和b,当然我们也可以直接用add_weight的方法构建权重

class MyLayer(layers.Layer):
    def __init__(self, input_dim=32, unit=32):
        super(MyLayer, self).__init__()
        self.weight = self.add_weight(shape=(input_dim, unit),
                                     initializer=keras.initializers.RandomNormal(),
                                     trainable=True)
        self.bias = self.add_weight(shape=(unit,),
                                   initializer=keras.initializers.Zeros(),
                                   trainable=True)
    
    def call(self, inputs):
        return tf.matmul(inputs, self.weight) + self.bias
        
x = tf.ones((3,5))
my_layer = MyLayer(5, 4)
out = my_layer(x)
print(out)

输出:
    tf.Tensor(
    [[-0.10401802 -0.05459599 -0.08195674  0.13151655]
     [-0.10401802 -0.05459599 -0.08195674  0.13151655]
     [-0.10401802 -0.05459599 -0.08195674  0.13151655]], shape=(3, 4), dtype=float32)
  • 当定义网络时不知道网络的维度是可以重写build()函数,用获得的shape构建网络

class MyLayer(layers.Layer):
    def __init__(self, unit=32):
        super(MyLayer, self).__init__()
        self.unit = unit
        
    def build(self, input_shape):
        self.weight = self.add_weight(shape=(input_shape[-1], self.unit),
                                     initializer=keras.initializers.RandomNormal(),
                                     trainable=True)
        self.bias = self.add_weight(shape=(self.unit,),
                                   initializer=keras.initializers.Zeros(),
                                   trainable=True)
    
    def call(self, inputs):
        return tf.matmul(inputs, self.weight) + self.bias
        
​
my_layer = MyLayer(3)
x = tf.ones((3,5))
out = my_layer(x)
print(out)
my_layer = MyLayer(3)
​
x = tf.ones((2,2))
out = my_layer(x)
print(out)

输出:
tf.Tensor(
[[ 0.00949192 -0.02009935 -0.11726624]
 [ 0.00949192 -0.02009935 -0.11726624]
 [ 0.00949192 -0.02009935 -0.11726624]], shape=(3, 3), dtype=float32)
tf.Tensor(
[[-0.00516411 -0.04891593 -0.0181773 ]
 [-0.00516411 -0.04891593 -0.0181773 ]], shape=(2, 3), dtype=float32)

2.使用子层递归构建网络层

class MyBlock(layers.Layer):
    def __init__(self):
        super(MyBlock, self).__init__()
        self.layer1 = MyLayer(32)
        self.layer2 = MyLayer(16)
        self.layer3 = MyLayer(2)
    def call(self, inputs):
        h1 = self.layer1(inputs)
        h1 = tf.nn.relu(h1)
        h2 = self.layer2(h1)
        h2 = tf.nn.relu(h2)
        return self.layer3(h2)
    
my_block = MyBlock()
print('trainable weights:', len(my_block.trainable_weights))
y = my_block(tf.ones(shape=(3, 64)))
# 构建网络在build()里面,所以执行了才有网络
print('trainable weights:', len(my_block.trainable_weights)) 
trainable weights: 0
trainable weights: 6

可以通过构建网络层的方法来收集loss

class LossLayer(layers.Layer):
  
  def __init__(self, rate=1e-2):
    super(LossLayer, self).__init__()
    self.rate = rate
  
  def call(self, inputs):
    self.add_loss(self.rate * tf.reduce_sum(inputs))
    return inputs

class OutLayer(layers.Layer):
    def __init__(self):
        super(OutLayer, self).__init__()
        self.loss_fun=LossLayer(1e-2)
    def call(self, inputs):
        return self.loss_fun(inputs)
    
my_layer = OutLayer()
print(len(my_layer.losses)) # 还未call
y = my_layer(tf.zeros(1,1))
print(len(my_layer.losses)) # 执行call之后
y = my_layer(tf.zeros(1,1))
print(len(my_layer.losses)) # call之前会重新置0


0
1
1

如果中间调用了keras网络层,里面的正则化loss也会被加入进来

class OuterLayer(layers.Layer):

    def __init__(self):
        super(OuterLayer, self).__init__()
        self.dense = layers.Dense(32, kernel_regularizer=tf.keras.regularizers.l2(1e-3))
    
    def call(self, inputs):
        return self.dense(inputs)


my_layer = OuterLayer()
y = my_layer(tf.zeros((1,1)))
print(my_layer.losses) 
print(my_layer.weights) 
[<tf.Tensor: id=413, shape=(), dtype=float32, numpy=0.0018067828>]
[<tf.Variable 'outer_layer_1/dense_1/kernel:0' shape=(1, 32) dtype=float32, numpy=
array([[-0.11054656,  0.34735924, -0.22560999,  0.38415992,  0.13070339,
         0.15960163,  0.20130599,  0.40365922, -0.09471637, -0.02402192,
         0.16438413,  0.2716753 ,  0.0594548 , -0.06913272, -0.40491152,
         0.00894281,  0.3199494 ,  0.0228827 , -0.18515846,  0.32210535,
         0.41672045,  0.1942389 , -0.4254937 ,  0.07178113,  0.00740242,
         0.23780417, -0.24449413, -0.15526545, -0.2200018 , -0.2426699 ,
        -0.17750363, -0.16994882]], dtype=float32)>, <tf.Variable 'outer_layer_1/dense_1/bias:0' shape=(32,) dtype=float32, numpy=
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
      dtype=float32)>]

3.构建自己的模型

  • 通常,我们使用Layer类来定义内部计算块,并使用Model类来定义外部模型 - 即要训练的对象。

  • Model类与Layer的区别:

    • 它公开了内置的训练,评估和预测循环(model.fit(),model.evaluate(),model.predict())。

    • 它通过model.layers属性公开其内层列表。

    • 它公开了保存和序列化API。

  • 下面通过构建一个变分自编码器(VAE),来介绍如何构建自己的网络。

    # 采样网络
    class Sampling(layers.Layer):
        def call(self, inputs):
            z_mean, z_log_var = inputs
            batch = tf.shape(z_mean)[0]
            dim = tf.shape(z_mean)[1]
            epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
            return z_mean + tf.exp(0.5*z_log_var) * epsilon
    # 编码器
    class Encoder(layers.Layer):
        def __init__(self, latent_dim=32, 
                    intermediate_dim=64, name='encoder', **kwargs):
            super(Encoder, self).__init__(name=name, **kwargs)
            self.dense_proj = layers.Dense(intermediate_dim, activation='relu')
            self.dense_mean = layers.Dense(latent_dim)
            self.dense_log_var = layers.Dense(latent_dim)
            self.sampling = Sampling()
            
        def call(self, inputs):
            h1 = self.dense_proj(inputs)
            z_mean = self.dense_mean(h1)
            z_log_var = self.dense_log_var(h1)
            z = self.sampling((z_mean, z_log_var))
            return z_mean, z_log_var, z
            
    # 解码器
    class Decoder(layers.Layer):
        def __init__(self, original_dim, 
                     intermediate_dim=64, name='decoder', **kwargs):
            super(Decoder, self).__init__(name=name, **kwargs)
            self.dense_proj = layers.Dense(intermediate_dim, activation='relu')
            self.dense_output = layers.Dense(original_dim, activation='sigmoid')
        def call(self, inputs):
            h1 = self.dense_proj(inputs)
            return self.dense_output(h1)
        
    # 变分自编码器
    # 继承tf.keras.Model
    class VAE(tf.keras.Model):
        def __init__(self, original_dim, latent_dim=32, 
                    intermediate_dim=64, name='encoder', **kwargs):
            # 继承父类的初始化方法
            super(VAE, self).__init__(name=name, **kwargs)
        
            self.original_dim = original_dim
            self.encoder = Encoder(latent_dim=latent_dim,
                                  intermediate_dim=intermediate_dim)
            self.decoder = Decoder(original_dim=original_dim,
                                  intermediate_dim=intermediate_dim)
        def call(self, inputs):
            z_mean, z_log_var, z = self.encoder(inputs)
            reconstructed = self.decoder(z)
            
            kl_loss = -0.5*tf.reduce_sum(
                z_log_var-tf.square(z_mean)-tf.exp(z_log_var)+1)
            self.add_loss(kl_loss)
            return reconstructed
    
    (x_train, _), _ = tf.keras.datasets.mnist.load_data()
    x_train = x_train.reshape(60000, 784).astype('float32') / 255
    vae = VAE(784,32,64)
    optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
    
    vae.compile(optimizer, loss=tf.keras.losses.MeanSquaredError())
    vae.fit(x_train, x_train, epochs=3, batch_size=64)
    Epoch 1/3
    60000/60000 [==============================] - 3s 44us/sample - loss: 0.7352
    Epoch 2/3
    60000/60000 [==============================] - 2s 33us/sample - loss: 0.0691
    Epoch 3/3
    60000/60000 [==============================] - 2s 33us/sample - loss: 0.0679
    
  • 自己编写训练方法

    train_dataset = tf.data.Dataset.from_tensor_slices(x_train)
    train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
    
    original_dim = 784
    vae = VAE(original_dim, 64, 32)
    
    optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3)
    mse_loss_fn = tf.keras.losses.MeanSquaredError()
    
    loss_metric = tf.keras.metrics.Mean()
    
    # 每个epoch迭代.
    for epoch in range(3):
      print('Start of epoch %d' % (epoch,))
    
      # 取出每个batch的数据并训练.
      for step, x_batch_train in enumerate(train_dataset):
        with tf.GradientTape() as tape:
          reconstructed = vae(x_batch_train)
          # 计算 reconstruction loss
          loss = mse_loss_fn(x_batch_train, reconstructed)
          loss += sum(vae.losses)  # 添加 KLD regularization loss
          
        grads = tape.gradient(loss, vae.trainable_variables)
        optimizer.apply_gradients(zip(grads, vae.trainable_variables))
        
        loss_metric(loss)
        
        if step % 100 == 0:
          print('step %s: mean loss = %s' % (step, loss_metric.result()))
    
    输出:    
    Start of epoch 0
    step 0: mean loss = tf.Tensor(213.26726, shape=(), dtype=float32)
    step 100: mean loss = tf.Tensor(6.5270114, shape=(), dtype=float32)
    ...
    step 900: mean loss = tf.Tensor(0.3061987, shape=(), dtype=float32)
    

TensorFlow利用keras 接口实现模型的加载与保存

1. 保存/加载模型权重

  1. 保存权重:model.save_weights('路径+文件名.ckpt')

  2. 建立同样结构的模型: Sequentian([...])

  3. 加载权值:model.load_weights('路径+文件名.ckpt')

  4. 使用/评估模型:model.evaluate(x,y)

import  tensorflow as tf
from    tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

def preprocess(x, y):
    """
    x is a simple image, not a batch
    """
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x, [28*28])
    y = tf.cast(y, dtype=tf.int32)
    y = tf.one_hot(y, depth=10)
    return x,y
batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())

db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz)
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz) 

network = Sequential([layers.Dense(256, activation='relu'),
                     layers.Dense(128, activation='relu'),
                     layers.Dense(64, activation='relu'),
                     layers.Dense(32, activation='relu'),
                     layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()

network.compile(optimizer=optimizers.Adam(lr=0.01),
		loss=tf.losses.CategoricalCrossentropy(from_logits=True),
		metrics=['accuracy']
	)
# 训练模型
network.fit(db, epochs=3, validation_data=ds_val, validation_freq=2)
# 评估模型
network.evaluate(ds_val)
# 保存模型权重
network.save_weights('../save_weights/weights.ckpt')
print('saved weights.')
# 删除模型
del network
# 新建一个与保存的模型权重相同的模型
network = Sequential([layers.Dense(256, activation='relu'),
                     layers.Dense(128, activation='relu'),
                     layers.Dense(64, activation='relu'),
                     layers.Dense(32, activation='relu'),
                     layers.Dense(10)])
network.compile(optimizer=optimizers.Adam(lr=0.01),
		loss=tf.losses.CategoricalCrossentropy(from_logits=True),
		metrics=['accuracy']
	)
# 加载模型权重
network.load_weights('../save_weights/weights.ckpt')
print('loaded weights!')
# 评估模型
network.evaluate(ds_val)

2. 保存/加载模型

  1. 保存模型:model.save('路径+模型名.h5')

  2. 加载模型:mode = tf.keras.models.load_model('路径+模型名.h5')

  3. 使用/评估模型:model.evaluate(x,y)

注:

  • 相比保存与加载权值,不需要额外创建模型,模型网络参数被全部保存。

  • 但劣势是效率不高。

import  tensorflow as tf
from    tensorflow.keras import datasets, layers, optimizers, Sequential, metrics
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
def preprocess(x, y):
    """
    x is a simple image, not a batch
    """
    x = tf.cast(x, dtype=tf.float32) / 255.
    x = tf.reshape(x, [28*28])
    y = tf.cast(y, dtype=tf.int32)
    y = tf.one_hot(y, depth=10)
    return x,y

batchsz = 128
(x, y), (x_val, y_val) = datasets.mnist.load_data()
print('datasets:', x.shape, y.shape, x.min(), x.max())

db = tf.data.Dataset.from_tensor_slices((x,y))
db = db.map(preprocess).shuffle(60000).batch(batchsz)
ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))
ds_val = ds_val.map(preprocess).batch(batchsz)

sample = next(iter(db))
print(sample[0].shape, sample[1].shape)

network = Sequential([layers.Dense(256, activation='relu'),
                     layers.Dense(128, activation='relu'),
                     layers.Dense(64, activation='relu'),
                     layers.Dense(32, activation='relu'),
                     layers.Dense(10)])
network.build(input_shape=(None, 28*28))
network.summary()

custom_loss = tf.losses.CategoricalCrossentropy

network.compile(optimizer=optimizers.Adam(lr=0.01),
		loss=tf.losses.CategoricalCrossentropy(from_logits=True),
		metrics=['accuracy']
	)
network.fit(db, epochs=3, validation_data=ds_val, validation_freq=2)
network.evaluate(ds_val)
# 保存模型
network.save('./save_model/model.h5')
print('saved total model.')
# 删除模型
del network
print('load model from file')
# 加载保存的模型
network = tf.keras.models.load_model('./save_model/model.h5')
# 评估模型
network.evaluate(ds_val)

3. 使用tf.saved_model模块保存模型

  • 为什么要采用SavedModel格式呢?其主要优点是SaveModel与语言无关,比如可以使用python语言训练模型,然后在Java中非常方便的加载模型。当然这也不是说checkpoints模型格式做不到,只是在跨语言时比较麻烦。另外如果使用Tensorflow Serving server来部署模型,必须选择SavedModel格式

  • 一般应用于工业环境的部署。

  • 更加通用。

  • 步骤:

    1. 保存模型:tf.saved_model.save(要保存的模型,'路径')

    2. 加载模型:imported = tf.saved_model.load('路径')

    3. 与保存的模型相关的特征可作为函数使用。

tf.saved_model.save(m, '/tmp/save_model/')
imported = tf.saved_model.load(path)
model = imported.signaures['serving_default'] 
print(model(x=tf.ones([1, 28, 28, 3])))

TensorFlow利用keras接口自定义模型实现CIFAR10识别

import tensorflow as tf
from tensorflow.keras import datasets, layers, Sequential, optimizers, metrics, Model
from sklearn.model_selection import train_test_split
import datetime
from matplotlib import pyplot as plt

class MyDense(layers.Layer):
    """
    自定义神经网络的层类,继承keras.layers.Layer类
    """
    def __init__(self, input_dim, output_dim):
        super(MyDense, self).__init__()

        # 定义权重参数
        self.kernel = self.add_variable('w', [input_dim, output_dim])
        self.bias = self.add_variable('b', [output_dim])

    def call(self, inputs, training=None):
        """
        实现前向传播逻辑
        :param inputs:
        :param training:
        :return:
        """
        z = inputs @ self.kernel + self.bias

        return z

class MyNetwork(Model):
    """
    定义神经网络模型类,继承keras.Model类
    """
    def __init__(self, input_dim):
        super(MyNetwork, self).__init__()

        self.fc1 = MyDense(input_dim, 256)
        self.fc2 = MyDense(256, 128)
        self.fc3 = MyDense(128, 64)
        self.fc4 = MyDense(64, 32)
        self.fc5 = MyDense(32, 10)

    def call(self, inputs, training=None):
        """
        实现前向传播算法逻辑
        :param inputs:
        :param training:
        :return:
        """
        z1 = self.fc1(inputs)
        a1 = tf.nn.relu(z1)
        z2 = self.fc2(a1)
        a2 = tf.nn.relu(z2)
        z3 = self.fc3(a2)
        a3 = tf.nn.relu(z3)
        z4 = self.fc4(a3)
        a4 = tf.nn.relu(z4)
        z5 = self.fc5(a4)
        a5 = tf.nn.relu(z5)
        return a5

def data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y):
    """
    数据预处理
    :param train_x:
    :param train_y:
    :param val_x:
    :param val_y:
    :param test_x:
    :param test_y:
    :return:
    """
    # 将数据转换为张量形式并指定数据类型
    train_x = tf.convert_to_tensor(train_x, dtype=tf.float32)
    train_y = tf.convert_to_tensor(train_y, dtype=tf.int32)
    val_x = tf.convert_to_tensor(val_x, dtype=tf.float32)
    val_y = tf.convert_to_tensor(val_y, dtype=tf.int32)
    test_x = tf.convert_to_tensor(test_x, dtype=tf.float32)
    test_y = tf.convert_to_tensor(test_y, dtype=tf.int32)

    # 改变输入数据的形状,并归一化;[b,32,32,3]--->[b,32*32*3]
    train_x = tf.reshape(train_x, [-1, train_x.shape[1] * train_x.shape[2] * train_x.shape[3]]) / 255.
    val_x = tf.reshape(val_x, [-1, val_x.shape[1] * val_x.shape[2] * val_x.shape[3]]) / 255.
    test_x = tf.reshape(test_x, [-1, test_x.shape[1] * test_x.shape[2] * test_x.shape[3]]) / 255.

    # 将标签值删除所有为1的维度并进行one-hot编码
    train_y = tf.one_hot(tf.squeeze(train_y), depth=10)
    val_y = tf.one_hot(tf.squeeze(val_y), depth=10)
    test_y = tf.one_hot(tf.squeeze(test_y), depth=10)

    return train_x, train_y, val_x, val_y, test_x, test_y

def load_mnist_dataset():
    """
    加载mnist数据集
    :return:
    """
    # 加载数据集
    (train_x, train_y), (test_x, test_y) = datasets.cifar10.load_data()

    # 划分训练集为训练集与验证集
    val_size = 0.01
    train_x, val_x, train_y, val_y = train_test_split(train_x, train_y, test_size=val_size)

    # 数据预处理
    train_x, train_y, val_x, val_y, test_x, test_y = data_preprocess(train_x, train_y, val_x, val_y, test_x, test_y)

    return train_x, train_y, val_x, val_y, test_x, test_y

def train():
    """
    创建模型并训练
    :return:
    """
    epochs_num = 5  # 迭代整个数据集的次数
    batch_size = 64  # 每个批次的数据量
    learning_rate = 0.001  # 学习率

    # 加载数据集
    train_x, train_y, val_x, val_y, test_x, test_y = load_mnist_dataset()
    # print(train_x.shape, train_y.shape)

    # 创建数据集对象并批量化
    train_db = tf.data.Dataset.from_tensor_slices((train_x, train_y)).shuffle(10000).batch(batch_size=batch_size)
    val_db = tf.data.Dataset.from_tensor_slices((val_x, val_y)).batch(128)
    test_db = tf.data.Dataset.from_tensor_slices((test_x, test_y)).batch(128)
    # sample = next(iter(train_db))
    # print(sample[0].shape, sample[1].shape)

    # 自定义神经网络结构
    input_dim = train_x.shape[1]  # 输入节点的维度
    network = MyNetwork(input_dim)  # 构建自定义网络对象
    network.build(input_shape=train_x.shape)
    network.summary()  # 打印网络结构信息
    # 配置训练模型
    network.compile(optimizer=optimizers.Adam(lr=learning_rate),
                    loss=tf.losses.CategoricalCrossentropy(from_logits=True),
                    metrics=['accuracy'])
    # 以给定数量的轮次(数据集上的迭代)训练模型
    history = network.fit(train_db, epochs=epochs_num, validation_data=val_db, validation_freq=1)

    # 绘制训练 & 验证的准确率值
    plt.plot(history.history['accuracy'])
    plt.plot(history.history['val_accuracy'])
    plt.title('Model accuracy')
    plt.ylabel('Accuracy')
    plt.xlabel('Epoch')
    plt.legend(['Train', 'Test'], loc='upper left')
    plt.show()

    # 绘制训练 & 验证的损失值
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('Model loss')
    plt.ylabel('Loss')
    plt.xlabel('Epoch')
    plt.legend(['Train', 'Test'], loc='upper left')
    plt.show()

    # 测试集上评估模型
    network.evaluate(test_db)
    # 保存网络权重信息
    current_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    save_path = 'ckpt/weights' + current_time + '.ckpt'
    network.save_weights('ckpt/weights' + current_time + '.ckpt')
    print('saved to ' + save_path)

if __name__ == '__main__':
    train()

 

posted @ 2019-08-13 13:44  Harger  阅读(362)  评论(0)    收藏  举报