当前位置:首页 » 《资源分享》 » 正文

TensorFlow2 入门指南 | 06 TensorFLow2 高阶操作汇总_不积跬步,无以至千里!

16 人参与  2021年05月08日 11:03  分类 : 《资源分享》  评论

点击全文阅读


前言:

本专栏在保证内容完整性的基础上,力求简洁,旨在让初学者能够更快地、高效地入门TensorFlow2 深度学习框架。如果觉得本专栏对您有帮助的话,可以给一个小小的三连,各位的支持将是我创作的最大动力!

系列文章汇总:TensorFlow2 入门指南
Github项目地址:https://github.com/Keyird/TensorFlow2-for-beginner


文章目录

    • 一、合并与分割
      • (1)合并
      • (2)分割
    • 二、 数据统计
      • (1)向量范数
      • (2)最大值、最小值
      • (3)和、均值
      • (4)最大值、最小值索引
    • 三、张量比较
      • (1)tf.equal()
      • (2)tf.cast()
    • 四、张量排序
      • (1)tf.sort()
      • (2)tf.argsort()
      • (3)tf.gather()
      • (4)tf.math.top_k()
    • 五、填充与复制
      • (1)填充
      • (2)复制
    • 六、数据限幅
      • (1)tf.maximum()
      • (2)tf.minimum()
      • (3)tf.clip_by_value()
    • 七、其它操作
      • (1)tf.gather()
      • (2)tf.gather_nd()
      • (3)tf.boolean_mask
      • (4)scatter_nd
      • (5)meshgrid

一、合并与分割

(1)合并

合并是指将多个张量在某个维度上合并为一个张量。在 TensorFlow 中,可以通过 tf.concat()和tf.stack()进行合并操作。

  • tf.concat(tensors, axis) 有两个参数,其中 tensors 保存了所有需要合并的张量,axis 指定需要合并的维度,例如:
# 随机生成a,b两个张量
a = tf.random.normal([5, 40, 10])
b = tf.random.normal([3, 40, 10])
# 在第一个维度合并,c最终的shape为:[8, 40, 10]
c = tf.concat([a, b], axis=0)

如果需要在合并数据时,产生一个新的维度,那就需要用堆叠操作:tf.stack()

  • tf.stack(tensors, axis) 可以合并多个张量tensors,其中 axis指定插入新维度的位置。当axis ≥ 0时,在 axis 之前插入;当axis < 0时,在 axis 之后插入新维度。如下图所示,不同的 axis 值代表的插入位置如下:

在这里插入图片描述
还是通过上面的例子,通过tf.stack()得到不同的合并结果:

a = tf.random.normal([3, 40, 10])
b = tf.random.normal([3, 40, 10])
c = tf.stack([a, b], axis=0)
print(c.shape) # 输出的shape为:【2,3,40,10】

需要注意的是:使用 tf.stack() 进行合并时,a、b 的 shape 必须一样。

(2)分割

合并操作的逆过程就是分割,是指将一个张量分拆为多个张量。在TensorFlow2中,可以通过 tf.unstack() 和 tf.split() 进行分割。

  • tf.unstack(x, axis) 会将张量x在某个维度上全部 按长度为 1 的方式分割,例如:
x = tf.random.normal([8,28,28,3])
result = tf.unstack(x,axis=0)

通过tf.unstack分割后,维度由 [8,28,28,3] 变为 [28,28,3]。准确来说变成了10个维度是 [28,28,3] 的张量。

  • tf.split(x, axis, num_or_size_splits) 更加灵活,可以通过设置 num_or_size_splits 将张量维度分割的更细致。比如,要将 shape 为 [8, 28, 28, 3] 的张量按第一维度切分成3份,每份长度分别为:2,4,2
x = tf.random.normal([8,28,28,3])
result = tf.split(x, axis=0, num_or_size_splits=[2,4,2])

二、 数据统计

(1)向量范数

向量范数是表征向量“长度”的一种度量方法,在神经网络中,常用来表示张量的权值大小,梯度大小等。常用的向量范数有:

  • L1 范数,定义为向量𝒙的所有元素绝对值之和:
    在这里插入图片描述
  • L2 范数,定义为向量𝒙的所有元素的平方和,再开根号:
    在这里插入图片描述
  • ∞ − 范数,定义为向量𝒙的所有元素绝对值的最大值:
    在这里插入图片描述

对于矩阵、张量,同样可以利用向量范数的计算公式,等价于将矩阵、张量打平成向量后计算。在 TensorFlow 中,可以通过 tf.norm(x, ord)求解张量的 L1, L2, ∞等范数,其中参数 ord 指定为 1,2 时计算 L1, L2 范数,指定为 np.inf 时计算∞ −范数:

x = tf.ones([2,2])
tf.norm(x, ord=1)
tf.norm(x, ord=2)
tf.norm(x, ord=np.inf)

(2)最大值、最小值

通过 tf.reduce_max, tf.reduce_min 可以求解张量在某个维度上的最大、最小值,也可以求全局最大、最小值。

考虑 shape 为 [4,10] 的张量,其中第一个维度代表样本数量,第二个维度代表了当前样本分别属于 10 个类别的概率,需要求出每个样本的概率最大值、最小值为:

x = tf.random.normal([4,10])
tf.reduce_max(x, axis=1) # 统计概率维度上的最大值(第2个维度)
tf.reduce_min(x,axis=1) # 统计概率维度上的最小值(第2个维度)

(3)和、均值

tf.reduce_mean, tf.reduce_sum 可以求解张量在某个维度上的均值、和,也可以求全局最均值、和信息。

同样利用上面的例子,求出每个样本的概率的均值以及和:

tf.reduce_mean(x,axis=1) # 统计概率维度上的均值(第2个维度)
tf.reduce_sum(out,axis=-1# 统计概率维度上的和(第2个维度)

注意:当不指定 axis 参数时,tf.reduce_* 函数会求解出全局元素的最大、最小、均值、和。

(4)最大值、最小值索引

通过 tf.argmax(x, axis),tf.argmin(x, axis) 可以求解在 axis 轴上,x 的最大值、最小值所在的索引号。比如求输出向量out在概率维度上(第二维度)的最大值、最小值索引:

pred_max_index = tf.argmax(out, axis=1)  # 最大值索引(第二维度)
pred_min_index = tf.argmin(out, axis=1)  # 最小值索引 (第二维度)

三、张量比较

为了计算分类任务的准确率等指标,一般需要将预测结果和真实标签比较,统计结果中正确的数量来计算准确率。

(1)tf.equal()

考虑 100 个样本、10类的预测结果,先选取每个向量维度上的最大值索引,即预测值(比如第一个样本的概率最大索引是5,表示预测的结果是第5类)

out = tf.random.normal([100,10]) # 随机生成一个张量,用来模拟输出结果
out = tf.nn.softmax(out, axis=1) # 输出转换为概率值,缩放到0-1,且概率和为1
pred = tf.argmax(out, axis=1) # 选取预测值(概率维度上的最大值),得到的是长度为100的向量

模拟100个真实标签,采用上节讲的均匀分布tf.random.uniform()来创建长度为100,值属于[0,9]区间的向量:

y = tf.random.uniform([100],dtype=tf.int64,maxval=10) # 标签

接下来,通过 tf.equal(pred, y) 可以比较这 2个张量是否相等。tf.equal()函数返回布尔型的张量比较结果:

out = tf.equal(pred,y) # 预测值与真实值比较

注意:tf.math.equal(a, b) 与 tf.equal(a, b) 用法一样

(2)tf.cast()

由于 tf.equal() 返回的是bool型的张量,所以要统计张量中 True 元素的个数,即可知道预测正确的个数。为了达到这个目的,我们需要通过tf.cast(x, type)将布尔型转换为整形张量:

out = tf.cast(out, dtype=tf.float32) # 布尔型转 int 型

于是,比较结果都转换成0和1了,然后再求和,统计其中 1 的个数,即预测正确的样本个数:

correct_num = tf.reduce_sum(out) # 统计 True 的个数

假设最终得到的correct_num=95,那么本次预测数据的准确度是:95/100=95%

四、张量排序

(1)tf.sort()

tf.sort() 能对列表中的元素按照大小进行排序,默认情况下进行升序排列,关键字 DESCENDING 指定降序排序。

先创建一打乱后的列表:

# 创建列表 [0,1,2,3,4],并打乱顺序
a = tf.random.shuffle(tf.range(5))
print("打乱后的列表:", a)

使用tf.sort()对其进行升序和降序排列:

# 升序
b = tf.sort(a)
print("升序排列后的列表:", b)

# 降序
c = tf.sort(a, direction="DESCENDING")
print("降序排列后的列表:", c)

输出结果:
在这里插入图片描述

(2)tf.argsort()

tf.argsort(a) 返回按照升(降)序排列的各元素在原列表a中的索引号。通过这种方式,可以方便地找到原数组中最大值或者最小值的索引值。

arise_index = tf.argsort(a)
print("升序列表各元素在原列表a中的索引号:", arise_index)

descend_index = tf.argsort(a, direction="DESCENDING")
print("降序列表各元素在原列表a中的索引号:", descend_index)

输出结果:
在这里插入图片描述
分析:该结果表明,元素 0、1、2、3、4 在原数组 a=[4,0,2,1,3] 中的索引号分别是:1、3、2、4、0;同理,元素 4、3、2、1、0 在原数组 a=[4,0,2,1,3] 中的索引号分别是:0、4、2、3、1

以上是对一维向量的排序结果,采用 tf.sort() 和 tf.argsort() 也能对二维数组进行排序操作,作用如下:

""" 对二维数组进行排序 """
x = tf.random.uniform([3, 3], maxval=10, dtype=tf.int32)
print("二维矩阵x:", x)
x_arise = tf.sort(x)
print("排序后的二维矩阵x:", x_arise)
x_arise_index = tf.argsort(x)
print("升序列表各元素在原列表x中的索引号:", x_arise_index)

输出结果:
在这里插入图片描述

(3)tf.gather()

tf.gather(a, index) 通过升(降)序索引,可将输入列表 a 还原成升(降)序列表:

Arise_List = tf.gather(a, arise_index)
print("升序列表:", Arise_List)

DES_List = tf.gather(a, descend_index)
print("降序列表:", DES_List)

程序输出:
在这里插入图片描述

(4)tf.math.top_k()

在 TensorFlow2 中,通过 tf.math.top_k() 、values、indices 可以很轻松地获取数组的top-k值以及他们的索引值:

""" 获取top-K的数值以及索引 """
x = tf.random.uniform([3, 3], maxval=10, dtype=tf.int32)
print("二维矩阵x:", x)
res = tf.math.top_k(x, 2)
print("top-2值:", res.values)
print("top-2的索引值:", res.indices)

程序输出:
在这里插入图片描述

五、填充与复制

(1)填充

对二维矩阵进行填充:

在 TensorFlow2 中,可通过 tf.pad(x, [[上, 下], [左, 右]]) 指定在矩阵 x 的上下左右边上填充一行(列)0 元素:

""" 填充 Padding """
a = tf.reshape(tf.range(9), [3, 3])
print("原数组a:", a)

b = tf.pad(a, [[0, 0], [0, 0]])
print("经过第一次填充后:", b)

c = tf.pad(a, [[1, 0], [0, 0]])
print("经过第二次填充后:", c)

程序输出:
在这里插入图片描述
更常见的,会对四维张量进行填充操作:

比如,下面对图像张量 [4,28,28,3] 进行填充,4是batch_size,一般不需要填充,通常情况是对图像的size 28x28 进行填充,现在对 28x28 向上下左右各填充两行(列)0,其操作如下:

""" 对四维张量进行填充 """
x = tf.random.normal([4, 28, 28, 3])
print("x.shape:",x.shape)
x_pad = tf.pad(x, [[0, 0], [2, 2], [2, 2], [0, 0]])
print("x_pad.shape:", x_pad.shape)

填充前后,张量的 shape 分别如下所示:
在这里插入图片描述

(2)复制

在 TensorFlow2 中,通过 tf.tile() 选择性地在行或者列的维度上进行复制,其操作如下所示:

""" 复制 """
a1 = tf.reshape(tf.range(9), [3, 3])
print("原数组a1:", a1)
b1 = tf.tile(a1, [1, 2])
print("在列方向上复制一倍:", b1)
c1 = tf.tile(a1, [2, 1])
print("在行方向上复制一倍:", c1)
d1 = tf.tile(a1, [2, 2])
print("在行、列方向上各复制一倍:", d1)

在行、列上进行复制操作后,得到的矩阵分别如下:
在这里插入图片描述
在 TensorFlow2 中,通过 tf.tile() 不仅可以在同一维度内进行复制,也可以通过复制扩充张量的维度,比如将shape为[3,3]的矩阵进行维度上的复制,使其变为shape为[2,3,3]的张量:

# 维度上的复制:[3,3] -> [2,3,3]
a2 = tf.reshape(tf.range(9), [3, 3])
print("原数组a2:", a2)
a2 = tf.expand_dims(a2, axis=0)  # 增加一个维度
b2 = tf.tile(a2, [2, 1, 1])
print("原数组b2:", b2)

输出结果:
在这里插入图片描述

六、数据限幅

(1)tf.maximum()

在 TensorFlow2 中,可以通过 tf.maximum(x, low) 实现数据的下限幅:𝑥 ∈ [low, +∞);示例如下:

a = tf.range(9)
print("a:", a)
b = tf.maximum(a, 2)  # 设置下限是2
print("下限限制为2后:", b)

如下是限制前后的向量对比:
在这里插入图片描述
通过 tf.maximum() 限制下限,还可以实现基本的激活函数ReLU:

def relu(x):
    return tf.maximum(x, 0)

x = a - 5
print("x:", x)
y = relu(x)
print("y:", y)

对ReLU函数输入x,可以看到输出的y满足激活函数的特性:
在这里插入图片描述
PS:ReLU是常见的激活函数的一种,其函数模型如下:
在这里插入图片描述
在 TensorFlow2 中,可以直接调用 tf.nn.relu() 来实现以上功能。

(2)tf.minimum()

在 TensorFlow 中,也可以通过 tf.minimum(x, high) 实现数据的上限幅:𝑥 ∈ (−∞, high],举例如下:

b1 = tf.minimum(a, 6)
print("上限限制为6后:", b1)

设置上限为6后,向量限幅后变为:
在这里插入图片描述

(3)tf.clip_by_value()

如果想同时对数据设置上限和下限,那么可以使用 clip_by_value(x, low, high) 来将数据限制在 [low, high] 之间。例如下面要将向量a的数据限制在5~8之间:

b2 = tf.clip_by_value(a, 5, 8)
print("下限设置为5,上限设置为8后:", b2)

下限设置为5,上限设置为8后:
在这里插入图片描述

七、其它操作

(1)tf.gather()

通过 tf.gather(x, [indexes], axis) 可以实现根据索引号收集数据的目的,其中 x 表示输入张量,[indexes]表示要获取的数据索引,axis表示要获取的维度。比如下面通过 tf.gather 获取第0至第3张图片:

# [b, w, h, c]
images = tf.random.normal([8, 28, 28, 3])
# 取第0至第3张图片
images_0_3 = tf.gather(images, [0, 1, 2, 3], axis=0)
print("images_0_3的shape:", images_0_3.shape)

对于0~3这种连续索引的情况,也可直接通过索引实现,如下所示:

# 也可通过索引来实现
images_0_3 = images[:4,...]
print("images_0_3的shape:", images_0_3.shape)

对于 tf.gather() 来说,其特殊之处,在于对于非连续索引时操作更为方便。比如,加下来我们要对第0至3张图片的第0和第2通道进行数据提取:

# 对第0和第2通道进行提取
out_image = tf.gather(images_0_3, [0, 2], axis=3)
print("out_image的shape:", out_image.shape)

可见,输出的通道数变为了2:
在这里插入图片描述

(2)tf.gather_nd()

通过 tf.gather_nd,可以通过指定每次采样的坐标来实现采样多个点的目的。考虑班级成绩册的例子,共有 4 个班级,每个班级 35 个学生,8 门科目,保存成绩册的张量 shape 为[4,35,8]。

x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)

现希望抽查第 2 个班级的第 2 个同学的所有科目,第 3 个班级的第 3 个同学的所有科目,第 4 个班级的第 4 个同学的所有科目。那么这 3 个采样点的索引坐标可以记为:[1,1],[2,2],[3,3],我们将这个采样方案合并为一个 List 参数:[[1,1],[2,2],[3,3]],通过tf.gather_nd 实现如下:

y = tf.gather_nd(x,[[1,1],[2,2],[3,3]])

抽查的3个学生的所有8个科目的成绩信息如下:
在这里插入图片描述
当然,也可以通过 tf.gather_nd 多维度坐标收集数据。例如抽出班级 1,学生 1 的科目 2;班级 2,学生 2 的科目 3;班级 3,学生 3 的科目 4 的成绩,共有 3 个成绩数据,结果汇总为一个 shape 为[3]的张量:

y1 = tf.gather_nd(x,[[1,1,2],[2,2,3],[3,3,4]])

输出的对应信息如下:
在这里插入图片描述

(3)tf.boolean_mask

除了可以通过给定索引号的方式采样,还可以通过给定掩码(mask)的方式采样。继续以 shape 为[4,35,8]的成绩册为例,这次我们以掩码方式进行数据提取。

例如,我们要对第1个班级和第3个班级的数据进行提取,可以设置掩码为:Mask=[True, False, True, False]。然后进行如下操作:

x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
y2 = tf.boolean_mask(x, mask=[True, False, True, False], axis=0)
print("y2.shape:", y2.shape)

获得提取数据的shape为:
在这里插入图片描述

(4)scatter_nd

通过 tf.scatter_nd(indices, updates, shape)可以高效地刷新张量的部分数据,但是只能在全 0 张量的白板上面刷新,因此可能需要结合其他操作来实现现有张量的数据刷新功能。

如下图所示,演示了一维张量白板的刷新运算,白板的形状表示为 shape 参数,需要刷新的数据索引为 indices,新数据为 updates,其中每个需要刷新的数据对应在白板中的位置,根据 indices 给出的索引位置将 updates 中新的数据依次写入白板中,并返回更新后的白板张量。
在这里插入图片描述
在索引4的位置插入4,在索引3的位置插入5,在索引1的位置插入1,在索引7的位置插入8,实现如下:

# 构造需要刷新数据的位置
indices = tf.constant([[4], [3], [1], [7]])
# 构造需要写入的数据
updates = tf.constant([4, 5, 1, 8])
# 在长度为 8 的全 0 向量上根据 indices 写入 updates
out = tf.scatter_nd(indices, updates, [8])
print("out:", out)

更新后的向量输出out:
在这里插入图片描述

(5)meshgrid

通过 tf.meshgrid 可以方便地生成二维网格采样点坐标,方便可视化等应用场合。下面绘制 Sinc 函数在𝑥 ∈ [−8,8], 𝑦 ∈ [−8,8]区间的 3D 曲面。

首先在 x 轴上进行采样 100 个数据点,y 轴上采样 100 个数据点,然后通过tf.meshgrid(x, y)即可返回这 10000 个数据点的张量数据,shape 为[100,100,2]。为了方便计算,tf.meshgrid 会返回在 axis=2 维度切割后的 2 个张量 a,b,其中张量 a 包含了所有点的 x 坐标,b 包含了所有点的 y 坐标,shape 都为[100,100]。

TensorFLow2实现如下:

首先需要导入相关库:

import tensorflow as tf
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

然后,使用 tf.meshgrid() 进行如下操作,并采用 matplotlib 将结果显示出来:

# 5. meshgrid()
x = tf.linspace(-8.,8,100) # 设置 x 坐标的间隔
y = tf.linspace(-8.,8,100) # 设置 y 坐标的间隔
x,y = tf.meshgrid(x,y) # 生成网格点,并拆分后返回
# x.shape, y.shape # 打印拆分后的所有点的 x,y 坐标张量 shape

z = tf.sqrt(x**2+y**2)
z = tf.sin(z)/z  # sinc 函数实现

fig = plt.figure()
ax = Axes3D(fig)

fig = plt.figure()
ax = Axes3D(fig)
# 根据网格点绘制 sinc 函数 3D 曲面
ax.contour3D(x.numpy(), y.numpy(), z.numpy(), 50)
plt.show()

最终,Sinc 函数在𝑥 ∈ [−8,8], 𝑦 ∈ [−8,8]区间的 3D 曲面图如下所示:

在这里插入图片描述


本教程所有代码会逐渐上传github仓库:https://github.com/Keyird/TensorFlow2-for-beginner
如果对你有帮助的话,欢迎star收藏~

最好的关系是互相成就,各位的「三连」就是【AI 菌】创作的最大动力,我们下期见!

在这里插入图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/18602.html

张量  维度  索引  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1