• 41张量的结构操作——eat_tensorflow2_in_30_days


    4-1张量的结构操作

    张量的操作主要包括张量的结构操作和张量的数学运算

    张量的结构操作诸如:张量创建,索引切片,维度变换,合并分割

    张量数学运算主要有:标量运算,向量运算,矩阵运算。另外,这里会介绍张量运算的广播机制

    创建张量

    • 张量创建的许多方法和numpy中创建array的方法很像
    import tensorflow as tf
    import numpy as np 
    
    a = tf.constant([1,2,3], dtype = tf.float32)
    tf.print(a)
    
    """
    [1 2 3]
    """
    
    # tf.range(start,limit=None,delta=1,name='range') 返回一个tensor等差数列,
    # 该tensor中的数值在start到limit之间,不包括limit,delta是等差数列的差值。
    b = tf.range(1, 10, delta=2)
    tf.print(b)
    print(b)
    
    """
    [1 3 5 7 9]
    tf.Tensor([1 3 5 7 9], shape=(5,), dtype=int32)
    """
    
    # tf.linspace(start,stop,num,name=None) 返回一个tensor,
    # 该tensor中的数值在start到stop区间之间取等差数列(包含start和stop)
    c = tf.linspace(0.0, 2*3.14, 100)
    tf.print(c, len(c))
    
    """
    [0 0.0634343475 0.126868695 ... 6.15313148 6.21656609 6.28] 100
    """
    
    d = tf.zeros([3, 3])
    tf.print(d)
    
    """
    [[0 0 0]
     [0 0 0]
     [0 0 0]]
    """
    
    a = tf.ones([3, 3])  # 创建全1矩阵
    b = tf.ones_like(a)  # 新建一个与给定的tensor类型大小一致的tensor,其所有元素为1
    c = tf.zeros_like(a, dtype=tf.float32)  # 新建一个与给定tensor大小一致,类型自定义的tensor,其所有元素为0
    tf.print(a)
    tf.print(b)
    tf.print(c)
    
    """
    [[1 1 1]
     [1 1 1]
     [1 1 1]]
    [[1 1 1]
     [1 1 1]
     [1 1 1]]
    [[0 0 0]
     [0 0 0]
     [0 0 0]]
    """
    
    # tf.fill(dim, value, name=None)创建一个形状大小为dim的tensor,其初始值为value
    b =  tf.fill([3, 2], 5)
    tf.print(b)
    
    """
    [[5 5]
     [5 5]
     [5 5]]
    """
    
    # 均匀分布,元素服从minval和maxval之间的均匀分布
    tf.random.set_seed(42)
    a = tf.random.uniform([5], minval=0, maxval=10)
    tf.print(a)
    
    """
    [6.6456213 4.41006756 3.52882504 4.64482546 0.336604118]
    """
    
    # 正态分布
    b = tf.random.normal([3, 3], mean=0.0, stddev=1.0)
    tf.print(b)
    
    """
    [[0.0842245817 -0.86090374 0.378123045]
     [-0.00519627379 -0.494531959 0.61781919]
     [-0.330820471 -0.00138408062 -0.423734099]]
    """
    
    # 正态分布,剔除2倍方差以外2数据重新生成
    c = tf.random.truncated_normal((5, 5), mean=0.0, stddev=1.0, dtype=tf.float32)
    tf.print(c)
    
    """
    [[-0.559097409 -0.534721375 -1.57259309 0.805505633 -0.00413081655]
     [0.172509521 0.292330414 0.447463274 -0.205126196 1.06962538]
     [-1.62758112 0.45803377 -0.150247112 -1.12316787 1.42139065]
     [-0.589732349 0.156277 0.379462808 -1.34369051 0.0508602373]
     [0.481147289 -1.28629398 0.173078209 -0.173267394 1.01196456]]
    """
    
    # 特殊矩阵
    I = tf.eye(3, 3)  # 单位矩阵
    tf.print(I)
    tf.print("")
    
    t = tf.linalg.diag([1, 2, 3])  # 对角阵
    tf.print(t)
    
    """
    [[1 0 0]
     [0 1 0]
     [0 0 1]]
    
    [[1 0 0]
     [0 2 0]
     [0 0 3]]
    """
    

    索引切片

    • 张量的索引切片方式和numpy几乎是一样的。切片时支持缺省参数和省略号
    • 对于tf.Variable,可以通过索引和切片对部分元素进行修改
    • 对于提取张量的连续子区域,也可以使用tf.slice.
    • 此外,对于不规则的切片提取,可以使用tf.gather, tf.gather_nd, tf.boolean_mask
    • tf.boolean_mask功能最为强大,它可以实现tf.gather, tf.gather_nd的功能,并且tf.boolean_mask还可以实现布尔索引
    • 如果要通过修改张量的某些元素得到新的张量,可以使用tf.where, tf.scatter_nd
    tf.random.set_seed(42)
    t = tf.random.uniform([5, 5], minval=0, maxval=10, dtype=tf.int32)
    tf.print(t)
    
    """
    [[7 9 1 6 2]
     [4 3 3 1 1]
     [2 0 1 1 0]
     [8 9 2 9 9]
     [1 2 7 4 9]]
    """
    
    tf.print("第0行 t[0]:", t[0])
    tf.print("倒数第一行 t[-1]:", t[-1])
    
    # 第一行第三列
    tf.print("第一行第三列 t[1, 3]:", t[1, 3])
    tf.print("第一行第三列 t[1][3]:", t[1][3])
    
    """
    第0行 t[0]: [7 9 1 6 2]
    倒数第一行 t[-1]: [1 2 7 4 9]
    第一行第三列 t[1, 3]: 1
    第一行第三列 t[1][3]: 1
    """
    
    # 第一行至第三行
    tf.print("第一行至第三行 \n")
    tf.print("t[1:4, :]", "\n", t[1:4, :])
    
    # tf.slice(input, begin_vector, size_vector)
    tf.print("\n tf.slice切片法")
    tf.print(tf.slice(t, [1, 0], [3, 5]))  # 第一行第0列开始切,切3行5列
    
    """
    第一行至第三行 
    
    t[1:4, :] 
     [[4 3 3 1 1]
     [2 0 1 1 0]
     [8 9 2 9 9]]
    
     tf.slice切片法
    [[4 3 3 1 1]
     [2 0 1 1 0]
     [8 9 2 9 9]]
    """
    
    # 第一行至最后一行,第0列到最后一列每隔两列取一列
    tf.print(t[1:, ::2])
    
    """
    [[4 3 1]
     [2 1 0]
     [8 2 9]
     [1 7 9]]
    """
    
    # 第一行至倒数第二行,第0列到倒数第二列每隔两列取一列
    tf.print(t[1:-1, :-1:2])
    
    """
    [[4 3]
     [2 1]
     [8 2]]
    """
    
    # 对变量来说,还可以使用索引和切片修改部分元素
    x = tf.Variable([[1, 2], [3, 4]], dtype=tf.float32)
    x[1, :].assign(tf.constant([0.0, 0.0]))
    tf.print(x)
    
    """
    [[1 2]
     [0 0]]
    """
    
    a = tf.random.uniform([3, 3, 3], minval=0, maxval=10, dtype=tf.int32)
    tf.print(a)
    
    """
    [[[8 3 9]
      [4 2 3]
      [4 2 6]]
    
     [[4 1 3]
      [6 0 9]
      [9 0 1]]
    
     [[4 7 0]
      [8 1 6]
      [2 4 9]]]
    """
    
    # 省略号可以表示多个冒号
    tf.print(a[..., 1])  # 第一个维度和第二个维度全部取,第三个维度取第二列(维度1-三个矩阵,维度2-行,维度3-列)
    
    """
    [[3 2 2]
     [1 0 0]
     [7 1 4]]
    """
    

    以上切片方式相对规则,对于不规则的切片提取,可以使用tf.gather, tf.gather_nd, tf.boolean_mask。

    • tf.gather: 类似于数组的索引,可以把向量中某些索引值提取出来,得到新的向量,适用于要提取的索引为不连续的情况。根据indices从params的指定轴axis索引元素(类似于仅能在指定轴进行一维索引).
    • tf.gather_nd:将params索引为indices指定形状的切片数组中(indices代表索引后的数组形状) indices将切片定义为params的前N个维度,其中N = indices.shape [-1]
    • tf.greater 判断函数。首先张量x和张量y的尺寸要相同,输出的tf.greater(x, y)也是一个和x,y尺寸相同的张量。如果x的某个元素比y中对应位置的元素大,则tf.greater(x, y)对应位置返回True,否则返回False。与此类似的函数还有tf.greater_equal。

    考虑班级成绩册的例子,有4个班级,每个班级10个学生,每个学生7门科目成绩。可以用一个4107的张量来表示。

    scores = tf.random.uniform((4, 10, 7), minval=0, maxval=100, dtype=tf.int32)
    tf.print(scores)
    
    """
    [[[4 92 77 ... 23 7 84]
      [19 12 27 ... 74 62 94]
      [27 9 87 ... 33 14 17]
      ...
      [67 92 3 ... 36 62 6]
      [69 86 88 ... 78 60 89]
      [80 38 72 ... 16 84 99]]
    
     [[65 54 78 ... 0 68 9]
      [2 51 28 ... 63 78 87]
      [19 75 20 ... 39 72 21]
      ...
      [23 37 23 ... 92 31 25]
      [98 25 92 ... 86 27 57]
      [95 44 33 ... 56 5 62]]
    
     [[2 38 23 ... 74 90 94]
      [7 80 46 ... 53 14 96]
      [97 49 2 ... 36 32 33]
      ...
      [88 73 99 ... 51 36 71]
      [45 47 91 ... 64 16 31]
      [97 50 40 ... 62 91 90]]
    
     [[4 24 12 ... 45 65 60]
      [53 89 56 ... 14 92 11]
      [10 11 64 ... 58 72 38]
      ...
      [89 12 25 ... 58 21 23]
      [93 99 32 ... 97 62 61]
      [92 43 98 ... 43 50 75]]]
    """
    
    # 抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
    p = tf.gather(scores, [0, 5, 9], axis=1)
    tf.print(p)
    
    """
    [[[4 92 77 ... 23 7 84]
      [30 78 10 ... 61 18 38]
      [80 38 72 ... 16 84 99]]
    
     [[65 54 78 ... 0 68 9]
      [82 23 21 ... 90 6 54]
      [95 44 33 ... 56 5 62]]
    
     [[2 38 23 ... 74 90 94]
      [2 95 58 ... 28 86 55]
      [97 50 40 ... 62 91 90]]
    
     [[4 24 12 ... 45 65 60]
      [82 74 67 ... 84 72 90]
      [92 43 98 ... 43 50 75]]]
    """
    
    # 抽取每个班级第0个学生,第5个学生,第9个学生的第一门课程,第三门课程,第六门课程成绩
    q = tf.gather(tf.gather(scores, (0, 5, 9), axis=1), [1, 3, 6], axis=2)
    tf.print(q)
    
    """
    [[[92 24 84]
      [78 67 38]
      [38 99 99]]
    
     [[54 61 9]
      [23 3 54]
      [44 64 62]]
    
     [[38 45 94]
      [95 1 55]
      [50 10 90]]
    
     [[24 60 60]
      [74 13 90]
      [43 70 75]]]
    """
    
    # 抽取第0个班级第0个学生,第2个班级的第四个学生,第三个班级的第6个学生的全部成绩
    # indices的长度为采样样本的个数,每个元素为采样位置的坐标
    s = tf.gather_nd(scores, indices=[(0, 0), (2, 4), (3, 6)])
    tf.print(s)
    
    """
    [[4 92 77 ... 23 7 84]
     [80 66 37 ... 27 35 99]
     [35 69 41 ... 91 3 97]]
    """
    
    # 抽取每个班级第0个学生,第5个学生,第9个学生的全部成绩
    p = tf.boolean_mask(scores, [True, False, False, False, False, True, False, False, False, True], axis=1)
    tf.print(p)
    
    """
    [[[4 92 77 ... 23 7 84]
      [30 78 10 ... 61 18 38]
      [80 38 72 ... 16 84 99]]
    
     [[65 54 78 ... 0 68 9]
      [82 23 21 ... 90 6 54]
      [95 44 33 ... 56 5 62]]
    
     [[2 38 23 ... 74 90 94]
      [2 95 58 ... 28 86 55]
      [97 50 40 ... 62 91 90]]
    
     [[4 24 12 ... 45 65 60]
      [82 74 67 ... 84 72 90]
      [92 43 98 ... 43 50 75]]]
    """
    
    # 抽取第0个班级第0个学生,第二个班级的第四个学生,第三个班级的第6个学生的全部成绩
    s = tf.boolean_mask(
        scores,          
        [[True,False,False,False,False,False,False,False,False,False],
         [False,False,False,False,False,False,False,False,False,False],
         [False,False,False,False,True,False,False,False,False,False],
         [False,False,False,False,False,False,True,False,False,False]])
    tf.print(s)
    
    """
    [[4 92 77 ... 23 7 84]
     [80 66 37 ... 27 35 99]
     [35 69 41 ... 91 3 97]]
    """
    
    # 利用tf.boolean_mask可以实现布尔索引
    # 找到矩阵中小于0的元素
    c = tf.constant([[-1, 1, -1], [2, 2, -2], [3, -3, 3]], dtype=tf.float32)
    tf.print(c, "\n")
    tf.print(tf.boolean_mask(c, c<0), "\n")
    tf.print(c[c<0])  # 布尔索引,为boolean_mask的语法糖形式
    
    """
    [[-1 1 -1]
     [2 2 -2]
     [3 -3 3]] 
    
    [-1 -1 -2 -3] 
    
    [-1 -1 -2 -3]
    """
    

    以上这些方法仅能提取张量的部分元素值,但不能更改张量的部分元素值得到新的张量。

    如果要通过修改张量的部分元素值得到新的张量,可以使用tf.where和tf.scatter_nd。

    • tf.where可以理解为if的张量版本,此外它还可以用于找到满足条件的所有元素的位置坐标。
    • tf.scatter_nd的作用和tf.gather_nd有些相反,tf.gather_nd用于收集张量的给定位置的元素,tf.scatter_nd可以将某些值插入到一个给定shape的全0的张量的指定位置处。
    # 如果where只有一个参数,将返回所有满足条件的位置坐标
    indices = tf.where(c<0)
    tf.print(indices)
    
    """
    [[0 0]
     [0 2]
     [1 2]
     [2 1]]
    """
    
    # 将张量的第[0, 0], [2, 1]两个位置元素替换为0得到新的张量
    d = c - tf.scatter_nd([[0, 0], [2, 1]], [c[0, 0], c[2, 1]], c.shape)
    tf.print(d)
    
    """
    [[0 1 -1]
     [2 2 -2]
     [3 0 3]]
    """
    
    # scatter_nd的作用和gather_nd有些相反
    # 可以将某些值插到一个给定shape的全0的张量的指定位置处
    indices = tf.where(c<0)
    tf.scatter_nd(indices, tf.gather_nd(c, indices), c.shape)
    
    """
    <tf.Tensor: shape=(3, 3), dtype=float32, numpy=
    array([[-1.,  0., -1.],
           [ 0.,  0., -2.],
           [ 0., -3.,  0.]], dtype=float32)>
    """
    

    维度变换

    • 维度变换相关函数主要有 tf.reshape, tf.squeeze, tf.expand_dims, tf.transpose.
      • tf.reshape 可以改变张量的形状。
      • tf.squeeze 可以减少维度。
      • tf.expand_dims 可以增加维度。
      • tf.transpose 可以交换维度
    a = tf.random.uniform(shape=[1, 3, 3, 2], minval=0, maxval=255, dtype=tf.int32)
    tf.print(a.shape)
    tf.print(a)
    
    """
    TensorShape([1, 3, 3, 2])
    [[[[29 120]
       [14 54]
       [132 110]]
    
      [[27 202]
       [12 3]
       [90 161]]
    
      [[57 129]
       [191 227]
       [71 128]]]]
    """
    
    # 改成(3, 6)的形状
    b = tf.reshape(a, [3, 6])
    tf.print(b.shape)
    tf.print(b)
    
    """
    TensorShape([3, 6])
    [[29 120 14 54 132 110]
     [27 202 12 3 90 161]
     [57 129 191 227 71 128]]
    """
    
    # 改回成[1, 3, 3, 2]形状的张量
    c = tf.reshape(b, [1, 3, 3, 2])
    tf.print(c)
    
    """
    [[[[29 120]
       [14 54]
       [132 110]]
    
      [[27 202]
       [12 3]
       [90 161]]
    
      [[57 129]
       [191 227]
       [71 128]]]]
    """
    

    如果张量在某个维度上只有一个元素,利用tf.squeeze可以消除这个维度。和tf.reshape相似,它本质上不会改变张量元素的存储顺序。

    张量的各个元素在内存中是线性存储的,其一般规律是,同一层级中的相邻元素的物理地址也相邻。

    s = tf.squeeze(a)
    tf.print(s.shape)
    tf.print(s)
    
    """
    TensorShape([3, 3, 2])
    [[[29 120]
      [14 54]
      [132 110]]
    
     [[27 202]
      [12 3]
      [90 161]]
    
     [[57 129]
      [191 227]
      [71 128]]]
    """
    
    d = tf.expand_dims(s, axis=0)  # 在第0维插入长度为1的一个维度
    tf.print(d)
    tf.print(d.shape)
    
    """
    [[[[29 120]
       [14 54]
       [132 110]]
    
      [[27 202]
       [12 3]
       [90 161]]
    
      [[57 129]
       [191 227]
       [71 128]]]]
    TensorShape([1, 3, 3, 2])
    """
    
    d = tf.expand_dims(s, axis=2)  # 在第2维度插入长度为1的一个维度
    tf.print(d)
    tf.print(d.shape)
    
    """
    [[[[29 120]]
    
      [[14 54]]
    
      [[132 110]]]
    
    
     [[[27 202]]
    
      [[12 3]]
    
      [[90 161]]]
    
    
     [[[57 129]]
    
      [[191 227]]
    
      [[71 128]]]]
    TensorShape([3, 3, 1, 2])
    """
    

    tf.transpose可以交换张量的维度,与tf.reshape不同,它会改变张量元素的存储顺序。 tf.transpose常用于图片存储格式的变换上。

    # Batch, Height, Width, Chanel
    a = tf.random.uniform(shape=[100,600,600,4], minval=0, maxval=255, dtype=tf.int32)
    tf.print(a.shape)
    
    # 转换成Channel, Height, Width, Batch
    s = tf.transpose(a, perm=[3, 1, 2, 0])
    tf.print(s.shape)
    
    """
    TensorShape([100, 600, 600, 4])
    TensorShape([4, 600, 600, 100])
    """
    

    合并分割

    • 和numpy类似,可以用:
      • tf.concat和tf.stack方法对多个张量进行合并;
      • tf.split方法把一个张量分割成多个张量。
      • tf.concat和tf.stack有略微的区别:
        • tf.concat是连接,不会增加维度;
        • tf.stack是堆叠,会增加维度。
    a = tf.constant([[1.0, 2.0], [3.0, 4.0]])
    b = tf.constant([[5.0, 6.0], [7.0, 8.0]])
    c = tf.constant([[9.0, 10.0], [11.0, 12.0]])
    
    tf.print(tf.concat([a, b, c], axis=0))
    
    """
    [[1 2]
     [3 4]
     [5 6]
     [7 8]
     [9 10]
     [11 12]]
    """
    
    tf.concat([a, b, c], axis=1)
    
    """
    tf.concat([a, b, c], axis=1)
    
    <tf.Tensor: shape=(2, 6), dtype=float32, numpy=
    array([[ 1.,  2.,  5.,  6.,  9., 10.],
           [ 3.,  4.,  7.,  8., 11., 12.]], dtype=float32)>
    """
    
    tf.print((tf.concat([a,b,c], axis=0)).shape)
    tf.print((tf.concat([a,b,c], axis=1)).shape)
    
    """
    TensorShape([6, 2])
    TensorShape([2, 6])
    """
    
    t = tf.stack([a, b, c])
    tf.print(t)
    tf.print(t.shape)
    
    """
    [[[1 2]
      [3 4]]
    
     [[5 6]
      [7 8]]
    
     [[9 10]
      [11 12]]]
    TensorShape([3, 2, 2])
    """
    
    t = tf.stack([a, b, c], axis=1)
    tf.print(t)
    tf.print(t.shape)
    
    """
    [[[1 2]
      [5 6]
      [9 10]]
    
     [[3 4]
      [7 8]
      [11 12]]]
    TensorShape([2, 3, 2])
    """
    
    a = tf.constant([[1.0,2.0],[3.0,4.0]])
    b = tf.constant([[5.0,6.0],[7.0,8.0]])
    c = tf.constant([[9.0,10.0],[11.0,12.0]])
    
    c = tf.concat([a,b,c],axis = 0)
    

    tf.split是tf.concat的逆运算,可以指定分割份数平均分割,也可以通过指定每份的记录数量进行分割。

    # tf.split(value, num_or_size_splits, axis)
    tf.split(c, 3, axis=0)  # 指定分割分数,平均分割
    
    """
    [<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
     array([[1., 2.],
            [3., 4.]], dtype=float32)>,
     <tf.Tensor: shape=(2, 2), dtype=float32, numpy=
     array([[5., 6.],
            [7., 8.]], dtype=float32)>,
     <tf.Tensor: shape=(2, 2), dtype=float32, numpy=
     array([[ 9., 10.],
            [11., 12.]], dtype=float32)>]
    """
    
    tf.split(c, [2, 1, 3], axis=0)  # 指定每份的记录数量
    
    """
    [<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
     array([[1., 2.],
            [3., 4.]], dtype=float32)>,
     <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[5., 6.]], dtype=float32)>,
     <tf.Tensor: shape=(3, 2), dtype=float32, numpy=
     array([[ 7.,  8.],
            [ 9., 10.],
            [11., 12.]], dtype=float32)>]
    """
    
  • 相关阅读:
    代码操作
    购物车
    利息计算器
    生成海报
    知识库
    JavaScript处理字符串--参照W3C
    C#输入排序-冒泡
    enum举例
    C# 表达式计算器----数据结构
    C# 测试单词的完美度
  • 原文地址:https://www.cnblogs.com/lotuslaw/p/16391077.html
Copyright © 2020-2023  润新知