美文网首页
Tensorflow的基础知识(二)

Tensorflow的基础知识(二)

作者: climb66的夏天 | 来源:发表于2020-09-24 15:05 被阅读0次

    1. 张量的索引与切片操作

    通过索引与切片操作可以提取张量的部分数据,它们的使用频率非常高。

    1.1 索引操作

    在Tensorflow中,支持基本的[i][j]···标准索引方式,也支持通过逗号分隔索引号的索引方式。例如:

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

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

    x[1,9,2]#取第二张图片,第十行,第三列的数据
    

    1.2 切片操作

    通过start:end:step切片方式可以方便地提取一段数据,其中start为开始读取位置的索引,end 为结束读取位置的索引(不包含end 位),step为采样步长。
    以下为切片操作的示例:

    x[1:3]#读取第2,3张图片
    

    start: end: step切片方式有很多简写方式,其中start、end、step 3个参数可以根据需要选择性地省略,全部省略时即为::,表示从最开始读取到最末尾,步长为1,即不跳过任何元素。如x[0,::]表示读取第1张图片的所有行,其中::表示在行维度上读取所有行,它等价于x[0]的写法:

    x[0,::]#读取第一张图片
    

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

    x[:,0:28:2,0:28:2,:]#表示读取所有图片,隔行采样,隔列采样,读取所有通道数据
    

    总结一下start: end: step切片的简写方式,其中从第一个元素读取时start可以省略,即start=0 是可以省略的。取到最后一个元素时end 可以省略,步长为1 时step 可以省略。

    特别地,step可以为负数,考虑最特殊的一种例子,当step = -1时,start: end: -1表示从start开始,逆序读取至end 结束(不包含end),索引号 end <= start。考虑一个0~9的简单序列向量,逆序取到第1 号元素,不包含第1 号:

    x = tf.range(9) #创建0~9向量
    x[8:0:-1]# 从8取到0,逆序,不含0
    x[::-1]#逆序取全部元素
    x[::-2]#逆序间隔取样
    
    x = tf.random.normal([4,32,32,3])
    x[0,::-2,::-2,:]#取第一张图片的所有通道,行按逆序隔行取样,列按逆序隔行取样
    

    当采样的维度数较多时,不需要采样的维度一般用单冒号: 表示采样所有元素,此时有可能出现大量的:冒号,例如:

    x = tf.random.normal([4,32,32,3])
    x[:,:,:,1]#只需要采样G通道上的数据
    

    为了避免出现像[:,:,:,1]这样过多冒号的情况,可以使用···表示多个维度上所有的数据,其中维度的数量需根据规则自动推断:当切片方式出现···符号时,···符号左边的维度将自动对齐到最左边,···符号右边的维度将自动对齐到最右边,而系统将自动推断···符号代表的维度数量。下面是一些示例:

    x = tf.random.normal([4,32,32,3])
    x[0:2,...,1:]#取第1,2张图片的G/B通道数据
    x[2:,...]#读取最后2张图片
    x[...,:2]#读取 R/G 通道数据
    

    小结:
    张量的索引与切片方式多种多样,尤其是切片操作,刚开始学习时很容易犯迷糊。但本质上切片操作只有start: end: step这一种基本形式,通过这种基本形式有目的地省略掉默认参数,从而衍生出多种简写方法,这也是很好理解的。另外,由于深度学习一般处理的维度在4维以下,···操作符完全可以用: 符号代替,因此理解了这些就会发现张量切片操作并不复杂。

    2. 张量的维度变换操作

    在神经网络运算过程中,维度变换是最核心的张量操作,通过维度变换可以将数据任意地切换形式,满足不同场合的运算需求。

    基本的维度变换操作函数包括:

    • reshape():改变视图函数
    • expand_dims():插入新维度
    • squeeze():删除维度
    • transpose():交换维度
    • tile():复制数据

    2.1 reshape操作

    在TensorFlow中,可以通过张量的 ndim 和 shape 成员属性获得张量的维度数和形状。

    x = tf.random.normal([4,32,32,3])
    print(x.ndim,x.shape)
    4 (4, 32, 32, 3)
    

    通过tf.reshape(x, new_shape),可以将张量的视图任意地合法改变。例如:

    x = tf.range(96)
    tf.reshape(x,[2,-1])
    <tf.Tensor: shape=(2, 48), dtype=int32, numpy=
    array([[ 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, 60, 61, 62, 63,
            64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
            80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95]],
          dtype=int32)>
    

    其中的参数-1表示当前轴上长度需要根据张量总元素不变的法则自动推导,从而方便用户书写。

    tf.reshape(x,[2,4,12])#改变张量数据的视图
    tf.reshape(x,[2,-1,3])#再次改变张量数据的视图
    

    2.2 张量增、删维度

    增加一个长度为1的维度相当于给原有的数据添加一个新维度的概念,维度长度为1,故数据并不需要改变,仅仅是改变数据的理解方式,因此可以理解为改变视图的一种特殊方式。
    下面是示例:

    x = tf.random.uniform([2,2],maxval=10,dtype=tf.int32)
    x = tf.expand_dims(x,axis=2)
    print(x)
    tf.Tensor(
    [[[3]
      [5]]
    
     [[6]
      [2]]], shape=(2, 2, 1), dtype=int32)
    

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

    同样的方法,我们可以在最前面插入一个维度。

    x = tf.expand_dims(x, axis=0)
    print(x)
    tf.Tensor(
    [[[[6]
       [9]]
    
      [[1]
       [1]]]], shape=(1, 2, 2, 1), dtype=int32)
    

    需要注意的是,tf.expand_dims 的 axis 为正时,表示在当前维度之前插入一个新维度;为负时,表示当前维度之后插入一个新的维度。

    通过 tf.squeeze(x, axis)函数可以删除张量的维度,axis 参数为待删除的维度的索引号。下面看一下用法:

    x = tf.random.normal([1,32,32,1])
    print('brfore: ',x.shape)
    x = tf.squeeze(x,axis=0)#删除图片数量维度
    print('after: ',x.shape)
    brfore:  (1, 32, 32, 1)
    after:  (32, 32, 1)
    
    #继续删除通道数维度
    x = tf.squeeze(x, axis=2)
    print(x.shape)
    (32, 32)
    

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

    x = tf.random.normal([1,28,28,1])
    print('before:', x.shape)
    x = tf.squeeze(x)
    print('after:',x.shape)
    before: (1, 28, 28, 1)
    after: (28, 28)
    

    建议使用tf.squeeze()时逐一指定需要删除的维度参数,防止Tensorflow意外删除某些长度为1的维度。

    2.3 交换维度操作

    通过交换维度操作,改变了张量的存储顺序,同时也改变了张量的视图。
    通过使用 tf.transpose(x, perm) 函数完成维度交换操作,其中perm参数表示新维度的顺序List。
    下面看操作示例:

    x = tf.random.normal([4,32,32,3])
    x = tf.transpose(x, [0,3,1,2])#把图片的通道维度移动到图片数量维度后
    print(x.shape)
    (4, 3, 32, 32)
    

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

    2.4 复制数据操作

    通过 tf.tile(x, multiples) 函数完成数据在指定维度上的复制操作,multiples参数分别指定了每个维度上面的复制倍数,对应位置为1表明不复制,为2表明新长度为原来长度的2倍,即数据复制一份,经此类推。
    下面看操作示例:

    b = tf.constant([1,2])
    b = tf.expand_dims(b, axis=0)#插入一个新的维度
    print(b.shape)
    (1, 2)
    
    #在 Batch 维度上复制数据1份,实现如下:
    b = tf.tile(b, multiples=[2,1])
    print(b.shape)
    (2, 2)
    

    再看另一个例子:

    x = tf.range(4)
    x = tf.reshape(x,[2,2])
    print(x.shape)
    (2, 2)
    
    # 然后在列维度上复制1份数据
    x = tf.tile(x,multiples=[1,2])#在列维度复制数据
    print(x.shape)
    (2, 4)
    
    # 然后在行维度复制1份数据
    x = tf.tile(x, multiples=[2,1])#在行维度复制数据
    print(x.shape)
    (4, 4)
    

    注意:tf.tile 会创建一个新的张量来保存复制后的数据,由于复制操作涉及大量数据的读写IO操作,计算代价相对较高。然而神经网络中不同shape之间的张量运算操作十分频繁,那么有没有轻量级的复制操作呢?这就要看接下来的Broadcasting操作了。

    3. Broadcasting操作

    Broadcasting称为广播机制(或自动扩展机制),它是一种轻量级的张量复制操作,它在逻辑上扩展张量数据的形状,但是只会在需要时才会执行实际存储复制操作。对于大部分场景,Broadcasting机制都能通过优化手段避免实际复制数据而完成逻辑运算,从面相对于 tf.tile 函数,减少了计算代价。
    下面看使用示例:

    a = tf.random.normal([2,32,32,1])
    b = tf.random.normal([32,32])
    
    # 张量相加
    c = a+b
    print(c.shape)
    (2, 32, 32, 32)
    
    # 张量相减
    c = a-b
    print(c.shape)
    (2, 32, 32, 32)
    
    # 张量相乘
    c = a*b
    print(c.shape)
    (2, 32, 32, 32)
    
    # 张量相除
    c = a/b
    print(c.shape)
    (2, 32, 32, 32)
    

    可以看到,上面这些运算都能Broadcasting成[2,32,32,32]的公共shape,再进行运算。

    4. 数学运算

    4.1 加、减、乘、除运算

    加、减、乘、除是最基本的数学运算,分别通过tf.add、tf.subtract、tf.multiply、tf.divide函数实现,Tensorflow已经重载了+、-、*、/运算符,可以直接使用运算符来进行运算。
    整除和余除也是常见的运算之一,分别使用//和%运算符实现。
    下面看使用示例:

    a = tf.range(5)
    b = tf.constant(2)
    
    a//b#整除运算
    <tf.Tensor: shape=(5,), dtype=int32, numpy=array([0, 0, 1, 1, 2], dtype=int32)>
    
    a%b#余除运算
    <tf.Tensor: shape=(5,), dtype=int32, numpy=array([0, 1, 0, 1, 0], dtype=int32)>
    

    4.2 乘方运算

    通过 tf.pow(x,a) 可以方便的完成乘方运算,也可以通过运算符实现xa运算。示例如下:

    x = tf.range(4)
    tf.pow(x,3)
    <tf.Tensor: shape=(4,), dtype=int32, numpy=array([ 0,  1,  8, 27], dtype=int32)>
    
    x**2
    <tf.Tensor: shape=(4,), dtype=int32, numpy=array([0, 1, 4, 9], dtype=int32)>
    
    x = tf.constant([1,4,9,16])
    x = tf.cast(x, dtype=tf.float32)#求平方根
    x**0.5
    <tf.Tensor: shape=(4,), dtype=float32, numpy=array([1., 2., 3., 4.], dtype=float32)>
    

    对于常见的求平方、求平方根运算,可以使用 tf.square(x) 和 tf.sqrt(x)实现。
    示例如下:

    x = tf.range(5)
    x = tf.cast(x, dtype=tf.float32)
    x = tf.square(x)#求平方
    print(x)
    tf.Tensor([ 0.  1.  4.  9. 16.], shape=(5,), dtype=float32)
    
    x = tf.sqrt(x)# 求平方根
    print(x)
    tf.Tensor([0. 1. 2. 3. 4.], shape=(5,), dtype=float32)
    

    4.3 指数和对数运算

    通过tf.pow(a,x)或者**运算符也可以方便的实现指数运算。
    对于自然指数e𝑥,可以通过 tf.exp(x)实现。
    示例如下:

    x = tf.constant([1,2,3])
    2**x #指数运算
    <tf.Tensor: shape=(3,), dtype=int32, numpy=array([2, 4, 8], dtype=int32)>
    
    tf.exp(1.0)
    <tf.Tensor: shape=(), dtype=float32, numpy=2.7182817>
    

    在 TensorFlow 中,自然对数可以通过 tf.math.log(x)实现。

    x = tf.exp(3.)
    tf.math.log(x)
    <tf.Tensor: shape=(), dtype=float32, numpy=3.0>
    

    Tensorflow还没有推出任意底数的log函数,但我们可以换一种方法实现它,示例如下:

    x = tf.constant([1.,2.])
    x = 10 ** x
    print(x)
    tf.Tensor([ 10. 100.], shape=(2,), dtype=float32)
    
    tf.math.log(x) / tf.math.log(10.)#计算以10为底数的对数
    <tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 2.], dtype=float32)>
    

    相关文章

      网友评论

          本文标题:Tensorflow的基础知识(二)

          本文链接:https://www.haomeiwen.com/subject/aaviyktx.html