第4章 TensorFlow基础

4.1数据类型

基本数据类型,包含数值型、字符串型和布尔型。

4.1.1数值类型

  • 张量:所有维度数dim > 2的数组统称为张量 ,张量的每个维度也做轴 。

  • 标量:在TensorFlow中创建标量

    a = 1.2
    aa = tf.constant(1.2)
    
  • 向量:向量的定义须通过 List 类型传给 tf.constant()

    a = tf.constant([1.2])  # 一个元素向量
    aa = tf.constant([1, 2, 3.])  # 两个元素向量
    aaa = tf.constant([[1, 2],[3, 4]])  # 定义矩阵
    
  • 3维张量

    a = tf.constant([[[1,2],[3,4]],[[5,6],[7,8]]])
    

4.1.2 字符串类型

  • 创建字符串类型张量

    a = tf.constant('Hello, Deep Learning')
    

    tf.strings 模块中,提供了常见的字符串型的工具函数,如拼接 join(),长度 length(),切分 split()

4.1.3 布尔类型

布尔类型的张量只需要传入 Python 语言的布尔类型数据,转换成 TensorFlow 内部布尔型即可:

  • 布尔类型张量

    g = tf.constant(True)
    g1 = tf.constant([True, False])
    

需要注意的是, TensorFlow 的布尔类型和 Python 语言的布尔类型并不对等,不能通用 :

a = tf.constant(True)
a == True
# 输出:False

4.2 数值精度

在创建张量的时,可以指定张量的保存精度:

tf.constant(123456789, dtype = tf.int32)

4.2.2 读取精度

  • 通过访问张量的 dtype 成员属性可以判断张量的保存精度

    print('before:', a.dtype)
    if a.dtype != tf.float32:
        a = tf.cast(a, tf.float32)
    print('after:', a.dtype)
    

4.2.2 类型转换

  • 转换精度需要用到tf.cast(低精度转换为高精度)

    a = tf.constant(np.pi, dtype=tf.float16)
    tf.cast(a, tf.double)
    
  • 高精度的张量转换为低精度的张量时,可能发生数据溢出隐患

  • 布尔型与整型之间相互转换

    j = tf.constant([True, False])
    j1 = tf.cast(j, tf.int32)
    # 输出[1,0]
    

    一般默认 0 表示 False, 1 表示 True,在 TensorFlow 中,将非 0 数字都视为 True

4.3 待优化张量

作用:为了区分需要计算梯度信息的张量和不需要计算梯度信息的张量,TensorFlow增加了一种专门的数据类型来支持梯度信息的记录: tf.Variable.

  • 将普通张量转换为待优化张量

    k = tf.constant([-1, 0, 1, 2])
    k1 = tf.Variable(k)
    print(k1.name, k1.trainable)
    # 输出:Variable:0 True
    

    nametrainable 是 Variable 特有的属性, name 属性用于命名计算图中的变量, trainable表征当前张量是否需要被优化,创建 Variable 对象是默认启用优化标志,可以设置trainable=False 来设置张量不需要优化。

  • 直接创建张量

    a = tf.Variable([[1,2],[3,4]])
    

待优化张量可看做普通张量的特殊类型,普通张量也可以通过GradientTape.watch()方法临时加入跟踪梯度信息的列表。

4.4创建张量

4.4.1从Numpy,List对象创建

通过 tf.convert_to_tensor 可以创建新 Tensor,并将保存在 Python List 对象或者 Numpy Array 对象中的数据导入到新 Tensor 中 :

tf.convert_to_tensor([1, 2.])
tf.convert_to_tensor([[1,2.],[3,4]])
  • 注:Numpy 中浮点数数组默认使用 64-Bit 精度保存数据,转换到 Tensor 类型时精度为 tf.float64,可以在需要的时候转换为 tf.float32 类型 。
  • tf.constant()tf.convert_to_tensor()都能够自动的把 Numpy 数组或者 Python List 数据类型转化为 Tensor 类型 ,使用其一即可。

4.4.2 创建全0,全1张量

通过 tf.zeros()tf.ones()即可创建任意形状全 0 或全 1 的张量 。

  • 创建为0和为1的标量张量:

    tf.zeros([]),tf.ones([])
    
  • 创建全 0 和全 1 的向量:

    l5 = tf.zeros([1])
    l6 = tf.ones([1])
    
  • 创建全 0 的矩阵:

    l7 = tf.zeros([2, 2])
    
  • 创建全 1 的矩阵:

    l8 = tf.ones([3, 2])
    
  • 通过 tf.zeros_like, tf.ones_like 可以方便地新建与某个张量 shape 一致,内容全 0 或全 1的张量

  • 创建与张量 l9 形状一样的全 0 张量

    l9 = tf.ones([2, 3])
    l10 = tf.zeros_like(l9)
    
  • 创建与张量 l11 形状一样的全 1 张量

    l11 = tf.zeros([3, 2])
    l12 = tf.ones_like(l11)
    

tf.*_like 是一个便捷函数,可以通过 tf.zeros(a.shape)等方式实现 。

4.4.3创建自定义数值张量

通过 tf.fill(shape, value)可以创建全为自定义数值 value 的张量 :

  • 创建元素为-1 的标量

    l13 = tf.fill([], -1)
    
  • 创建所有元素为-1 的向量:

    l14 = tf.fill([1], -1)
    
  • 创建所有元素为 99 的矩阵:

    l15 = tf.fill([2, 2], 99)
    

4.4.4 创建已知分布的张量

在卷积神经网络中,卷积核张量 W 初始化为正态分布有利于网络的训练。

在对抗生成网络中,隐藏变量 z 一般采样自均匀分布。

通过 tf.random.normal(shape, mean=0.0, stddev=1.0)可以创建形状为 shape,均值为mean,标准差为 stddev 的正态分布。

  • 创建均值为 0, 标准差为 1 的正态分布

    tf.random.normal([2, 2])
    
  • 创建均值为 1,标准差为 2 的正态分布

    tf.random.normal([2, 2], mean=1, stddev=2)
    
  • 通过tf.random.uniform(shape,minval=0,maxval=None,dtype=tf.float32)可以创建采样自[minval, maxval]区间的均匀分布的张量

    # 创建采样自区间[0,1], shape 为[2,2]的矩阵
    l18 = tf.random.uniform([2, 2], maxval=1, minval=0)
    # 创建采样自区间[0,10], shape 为[2,2]的矩阵
    l19 = tf.random.uniform([2, 2], maxval=10)
    # 均匀采样整形类型的数据
    l20 = tf.random.uniform([2, 2], maxval=100, dtype=tf.int32)
    

4.4.5 创建序列

在循环计算或者对张量进行索引时,经常需要创建一段连续的整形序列,可以通过tf.range()函数实现

# 创建0-9,步长为1的整形序列
l21 = tf.range(10)
# 创建0-9,步长为2的整形序列
l22 = tf.range(10, delta=2)

通过 tf.range(start, limit, delta=1)可以创建[𝑠𝑡𝑎𝑟𝑡, 𝑙𝑖𝑚𝑖𝑡),步长为 delta 的序列,不包含 limit本身

# 创建[1,10),步长为2的整型序列
tf.range(1, 10, delta=2)

4.6 索引与切片

4.6.1 索引

x = tf.random.normal([4, 32, 32, 3])
# 取第一张图片的数据
print(x[0])
# 取第一张图片的第2行
print(x[0][1])
# 取第1张图片,第2行,第3列的像素
print(x[0][1][2])
# 取第 3 张图片,第 2 行,第 1 列的像素, B 通道(第 2 个通道)颜色强度值
print(x[2][1][0][1])

当张量的维度数较高时, 使用[𝑖][𝑗]. . . [𝑘]的方式书写不方便,可以采用[𝑖, 𝑗, … , 𝑘]的方式索引,它们是等价的。

4.6.2切片

  • 通过𝑠𝑡𝑎𝑟𝑡: 𝑒𝑛𝑑: 𝑠𝑡𝑒𝑝切片方式可以方便地提取一段数据

  • 简写方式 :

    • 3个参数可根据需要选择性地省略,全部省略时::,表示从最开始读取到最末尾,步长为 1,即不跳过任何元素 。

    • x[0,::]表示读取第一张图片的所有行,::表示在行维度上读取所有行,等价于x[0]

    • 为了更加简洁,::可以简写为单个冒号

      # 取所有图片,隔行采样,隔列采样,所有通道信息,相当于在图片的高宽各缩放至原来的50%
      x[:,0:28:2,0:28:2,:]
      
切片方式 意义
start:end:step 从 start 开始读取到 end(不包含 end),步长为 step
start:end 从 start 开始读取到 end(不包含 end),步长为 1
start: 从 start 开始读取完后续所有元素,步长为 1
start::step 从 start 开始读取完后续所有元素,步长为 step
:end:step 从 0 开始读取到 end(不包含 end),步长为 step
:end 从 0 开始读取到 end(不包含 end),步长为 1
::step 每隔 step-1 个元素采样所有
:: 读取所有元素
: 读取所有元素
  • step为负数的时候表示逆序读取。

    # 读取每张图片的所有通道,其中行按着逆序隔行采样,列按着逆序隔行采样
    print(x[0, ::-2, ::-2])
    
  • 当张量的维度数量较多时,不需要采样的维度一般用单冒号:表示采样所有元素 ,为了避免出现像𝑥[: , : , : ,1]这样出现过多冒号的情况,可以使用符号表示取多个维度 ,符号左边的维度将自动对齐到最左边 , 符号右边的维度将自动对齐到最右边

    ...切片方式小结

    切片方式 意义
    a,⋯,b a维度对齐到最左边,b维度对齐到最右边,中间的维度全部读取,其他维度按a/b的方式读取
    a,⋯ a维度对齐到最左边,a维度后的所有维度全部读取,a维度按a方式读取。
    ⋯, b b维度对齐到最右边,b 之前的所有维度全部读取, b 维度按 b 方式读取
    读取所有张量数据

4.7 维度变换

当现有的数据格式不满足算法要求时,需要通过维度变换将数据调整为正确的格式, 这就是维度变换的功能 。

基本的维度变换包括:

  • 改变视图 reshape

  • 插入新维度 expand_dims

    通过tf.expand_dims(x,axis)可在指定的axis轴前可以插入一个新的维度

    # 若x的维度是(1,5,7) axis =  0
    a = tf.expand_dims(x , 0)
    #则转换后是a的维度是(1, 1, 5, 7)
    
    # 若x的维度是(1,5,7) axis =  2
    a = tf.expand_dims(x , 2)
    #则转换后是a的维度是(1, 5,1,  7)
    
    #若x的维度是(1,5,7) axis =  3
    a = tf.expand_dims(x , 3)
    #则转换后是a的维度是(1, 5,7,  1)
    

    注:axis 为正时,表示在当前维度之前插入一个新维度;

    axis 为负时,表示当前维度之后插入一个新的维度 。

  • 删除维度 squeeze

    删除维度只能删除长度为 1 的维度,也不会改变张量的存储

    图片数量维度删除,可以通过 tf.squeeze(x, axis)函数, axis 参数为待删除的维度的索引号

    x = tf.squeeze(x, axis=0)
    

    如果不指定维度参数 axis,即 tf.squeeze(x), 那么他会默认删除所有长度为 1 的维度:

    x=tf.random.uniform([1,28,28,1],maxval=10,dtype=tf.int32)
    print(tf.squeeze(x)
    
  • 交换维度 transpose

    改变视图、 增删维度都不会影响张量的存储。通过交换维度,改变了张量的存储顺序,同时也改变了张量的视图 。

    tf.transpose(x, perm)函数完成维度交换操作,其中 perm 表示新维度的顺序 List

    # 将[b,h,w,c]格式转为[b,c,h,w]格式
    x = tf.random.normal([2, 32, 32, 3])
    a = tf.transpose(x, perm=[0, 3, 1, 2])
    print(a, a.shape)
    

    通过 tf.transpose 完成维度交换后,张量的存储顺序已经改变, 视图也随之改变, 后续的所有操作必须基于新的存续顺序进行

  • 复制数据 tile

    x = tf.range(4)
    x = tf.reshape(x, [2, 2])
    x = tf.tile(x, multiples=[1, 2])
    print(x, x.shape)
    # shape(2,4),array([[0 1 0 1]
    #                    2 3 2 3])
    

    tf.tile 会创建一个新的张量来保存复制后的张量

4.8 Broadcasting

Broadcasting 也叫广播机制(自动扩展也许更合适), 它是一种轻量级张量复制的手段,在逻辑上扩展张量数据的形状, 但是只要在需要时才会执行实际存储复制操作。

需满足普适性原则:在验证普适性之前,需要将张量 shape 靠右对齐, 然后进行普适性判断: 对于长度为 1 的维度,默认这个数据普遍适合于当前维度的其他位置对于不存在的维度, 则在增加新维度后默认当前数据也是普适性于新维度的, 从而可以扩展为更多维度数、 其他长度的张量形状。

  • 通过 tf.broadcast_to(x, new_shape) 可以显式将现有shape扩张为new_shape

    A = tf.random.normal([32, 1])
    B = tf.broadcast_to(A, [2, 32, 32, 3])
    print(B, B.shape)
    

4.9 数学运算

4.9.1加减乘除

  • + - * / 也可通过tf.add,tf.subtract,tf.multiply,tf.divide实现

  • 整除:a//b

  • 余除:a%b

4.9.2 乘方

  • 通过tf.pow(x,a)可以方便地完成$$y = x^a$$

    的乘方运算,也可以通过运算符**实现𝑥 ∗∗ 𝑎运算

x = tf.range(4)
y = tf.pow(x, 3)
print(y)
print(x**2)
  • 设置指数为1/𝑎形式即可实现根号运算: $$a\sqrt x$$

  • 对于常见的平方和平方根运算,可以使用 tf.square(x)tf.sqrt(x)实现

4.9.3 指数、对数

通过tf.pow(a,x)或者**运算符可以方便实现指数运算$$a^x$$

对于自然指数 $$e^x$$可以通过tf.exp(x)实现。

自然对数$$log_ex$$可以通过tf.math.log(x)实现

如果希望计算其他底数的对数,可以根据对数的换底公式 :$$log_ax=\frac{log_ex}{log_ea}$$

例如:计算$$log_10x$$可以通过$$log_ex/log_e10$$

4.9.4矩阵相乘

@运算符可以方便的实现矩阵相乘,还可以通过 tf.matmul(a, b)实现。

根据矩阵相乘的定义, a 和 b 能够矩阵相乘的条件是, a 的倒数第一个维度长度(列)和b 的倒数第二个维度长度(行)必须相等。

4.9.10 前项传播实战

  • tf.reduce_mean函数用于计算张量tensor沿着指定的数轴(tensor的某一维度)上的的平均值,主要用作降维或者计算tensor(图像)的平均值。
posted @ 2021-10-26 21:09  Reversal-destiny  阅读(183)  评论(0编辑  收藏  举报