《TensorFlow 2.0深度学习算法实战教材》学习笔记(四、TensorFlow 进阶)

合并与分割

合并

张量的合并可以使用拼接(Concatenate)和堆叠(Stack)操作实现,拼接并不会产生新的维度,而堆叠会创建新维度。选择使用拼接还是堆叠操作来合并张量,取决于具体的场景是否需要创建新维度。

拼接 在TensorFlow 中,可以通过tf.concat(tensors, axis),其中tensors 保存了所有需要合并的张量List,axis 指定需要合并的维度。

a = tf.random.normal([4,35,8]) # 模拟成绩册A
b = tf.random.normal([6,35,8]) # 模拟成绩册B
tf.concat([a,b],axis=0) # 合并成绩册

堆叠tf.concat 直接在现有维度上面合并数据,并不会创建新的维度。如果在合并数据时,希望创建一个新的维度,则需要使用tf.stack 操作。

使用 tf.stack(tensors, axis)可以合并多个张量tensors,其中axis 指定插入新维度的位置,axis 的用法与tf.expand_dims 的一致,当axis ≥ 0时,在axis 之前插入;当axis < 0时,在axis 之后插入新维度。例如shape 为[𝑏, 𝑐, ℎ, 𝑤]的张量,在不同位置通过stack 操作插入新维度,axis 参数对应的插入位置设置如图 5.1 所示:

在这里插入图片描述

tf.stack 也需要满足张量堆叠合并条件,它需要所有合并的张量shape 完全一致才可合并。

分割

合并操作的逆过程就是分割,将一个张量分拆为多个张量。

通过 tf.split(x, axis, num_or_size_splits)可以完成张量的分割操作,其中
❑ x:待分割张量
❑ axis:分割的维度索引号
❑ num_or_size_splits:切割方案。当num_or_size_splits 为单个数值时,如10,表示切割为10 份;当num_or_size_splits 为List时,每个元素表示每份的长度,如[2,4,2,2]表示切割为4 份,每份的长度分别为2,4,2,2

x = tf.random.normal([10,35,8])
result = tf.split(x,axis=0,num_or_size_splits=10) # 等长切割
In [10]: x = tf.random.normal([10,35,8])
result = tf.split(x,axis=0,num_or_size_splits=[4,2,2,2]) # 自定义长度的切割

如果希望在某个维度上全部按长度为1 的方式分割,还可以直接使用tf.unstack(x,axis)。这种方式是tf.split 的一种特殊情况,切割长度固定为1,只需要指定切割维度即可。例如,将总成绩册张量在班级维度进行unstack:

In [11]: x = tf.random.normal([10,35,8])
result = tf.unstack(x,axis=0) # Unstack 为长度为1
len(result)

数据统计

向量范数

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

❑ L1 范数,定义为向量𝒙的所有元素绝对值之和
‖𝒙‖1 = Σ|𝑥𝑖 |

❑ L2 范数,定义为向量𝒙的所有元素的平方和,再开根号
‖𝒙‖2 = √Σ|𝑥𝑖 |2

❑ ∞ −范数,定义为向量𝒙的所有元素绝对值的最大值:
‖𝒙‖∞ = 𝑚𝑎𝑥𝑖(|𝑥𝑖 |)
对于矩阵、张量,同样可以利用向量范数的计算公式,等价于将矩阵、张量打平成向量后
计算。

In [13]: x = tf.ones([2,2])
tf.norm(x,ord=1) # 计算L1 范数
Out[13]: <tf.Tensor: id=183, shape=(), dtype=float32, numpy=4.0>
In [14]: tf.norm(x,ord=2) # 计算L2 范数
Out[14]: <tf.Tensor: id=189, shape=(), dtype=float32, numpy=2.0>
In [15]: import numpy as np
tf.norm(x,ord=np.inf) # 计算∞范数
Out[15]: <tf.Tensor: id=194, shape=(), dtype=float32, numpy=1.0>

最大最小值、均值、和

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

In [16]: x = tf.random.normal([4,10])
tf.reduce_max(x,axis=1) # 统计概率维度上的最大值
Out[16]:<tf.Tensor: id=203, shape=(4,), dtype=float32,
numpy=array([1.2410722 , 0.88495886, 1.4170984 , 0.9550192 ],
dtype=float32)>
同样求出每个样本概率的最小值:
In [17]: tf.reduce_min(x,axis=1) # 统计概率维度上的最小值
Out[17]:<tf.Tensor: id=206, shape=(4,), dtype=float32, numpy=array([-
0.27862206, -2.4480672 , -1.9983795 , -1.5287997 ], dtype=float32)>
求出每个样本的概率的均值:
In [18]: tf.reduce_mean(x,axis=1) # 统计概率维度上的均值
Out[18]:<tf.Tensor: id=209, shape=(4,), dtype=float32,
numpy=array([ 0.39526337, -0.17684573, -0.148988 , -0.43544054],
dtype=float32)>

当不指定axis 参数时,tf.reduce_*函数会求解出全局元素的最大、最小、均值、和:
In [19]:x = tf.random.normal([4,10]) # 统计全局的最大、最小、均值、和

tf.reduce_max(x),tf.reduce_min(x),tf.reduce_mean(x)
Out [19]: (<tf.Tensor: id=218, shape=(), dtype=float32, numpy=1.8653786>,
<tf.Tensor: id=220, shape=(), dtype=float32, numpy=-1.9751656>,
<tf.Tensor: id=222, shape=(), dtype=float32, numpy=0.014772797>)

通过TensorFlow 的MSE 误差函数可以求得每个样本的误差,需要计算样本的平均误差,此时可以通过tf.reduce_mean 在样本数维度上计算均值:

In [20]:
out = tf.random.normal([4,10]) # 网络预测输出
y = tf.constant([1,2,2,0]) # 真实标签
y = tf.one_hot(y,depth=10) # one-hot 编码
loss = keras.losses.mse(y,out) # 计算每个样本的误差
loss = tf.reduce_mean(loss) # 平均误差
loss
Out[20]:
<tf.Tensor: id=241, shape=(), dtype=float32, numpy=1.1921183>

与均值函数相似的是求和函数tf.reduce_sum(x,axis),它可以求解张量在axis 轴上所有特征的和:

In [21]:out = tf.random.normal([4,10])
tf.reduce_sum(out,axis=-1) # 求和
Out[21]:<tf.Tensor: id=303, shape=(4,), dtype=float32, numpy=array([-0.588144 , 2.2382064, 2.1582587, 4.962141 ], dtype=float32)>

通过 tf.argmax(x, axis),tf.argmin(x, axis)可以求解在axis 轴上,x 的最大值、最小值所在的索引号:

In [23]:pred = tf.argmax(out, axis=1) # 选取概率最大的位置
pred
Out[23]:<tf.Tensor: id=262, shape=(2,), dtype=int64, numpy=array([0, 0],dtype=int64)>

张量比较

通过tf.equal(a, b)(或tf.math.equal(a, b))函数可以比较这2个张量是否相等:

In [26]:out = tf.equal(pred,y) # 预测值与真实值比较
Out[26]:<tf.Tensor: id=288, shape=(100,), dtype=bool, numpy=
array([False, False, False, False, True, False, False, False, False,
False, False, False, False, False, True, False, False, True,…

tf.equal()函数返回布尔型的张量比较结果,只需要统计张量中True 元素的个数,即可知道预测正确的个数。为了达到这个目的,我们先将布尔型转换为整形张量,再求和其中1 的个数,可以得到比较结果中True 元素的个数:

In [27]:out = tf.cast(out, dtype=tf.float32) # 布尔型转int 型
correct = tf.reduce_sum(out) # 统计True 的个数
Out[27]:<tf.Tensor: id=293, shape=(), dtype=float32, numpy=12.0>

除了比较相等的tf.equal(a, b)函数,其他的比较函数用法类似,如表格 5.1 所示:
在这里插入图片描述

填充与复制

填充

在需要补充长度的信号开始或结束处填充足够数量的特定数值,如0,使得填充后的长度满足系统要求。那么这种操作就叫做填充(Padding)。

填充操作可以通过tf.pad(x, paddings)函数实现,paddings 是包含了多个[𝐿𝑒𝑓𝑡 𝑃𝑎𝑑𝑑𝑖𝑛𝑔, 𝑅𝑖𝑔ℎ𝑡 𝑃𝑎𝑑𝑑𝑖𝑛𝑔]的嵌套方案List,如[[0,0], [2,1], [1,2]]表示第一个维度不填充,第二个维度左边(起始处)填充两个单元,右边(结束处)填充一个单元,第三个维度左边填充一个单元,右边填充两个单元。

In [28]:a = tf.constant([1,2,3,4,5,6])
b = tf.constant([7,8,1,6])
b = tf.pad(b, [[0,2]]) # 填充
b
Out[28]:<tf.Tensor: id=3, shape=(6,), dtype=int32, numpy=array([7, 8, 1, 6,0, 0])>
填充后句子张量形状一致,再将这2 句子Stack 在一起:
In [29]:tf.stack([a,b],axis=0) # 合并
Out[29]:<tf.Tensor: id=5, shape=(2, 6), dtype=int32, numpy=
array([[1, 2, 3, 4, 5, 6],
[7, 8, 1, 6, 0, 0]])>

复制

tf.tile 函数除了可以对长度为1 的维度进行复制若干份,还可以对任意长度的维度进行复制若干份,进行复制时会根据原来的数据次序重复复制。由于已经介绍过,此处仅作简单回顾。

通过 tf.tile 函数可以在任意维度将数据重复复制多份,如shape 为[4,32,32,3]的数据,复制方案multiples=[2,3,3,1],即通道数据不复制,高宽方向分别复制2 份,图片数再复制1 份:

In [32]:x = tf.random.normal([4,32,32,3])
tf.tile(x,[2,3,3,1]) # 数据复制
Out[32]:<tf.Tensor: id=25, shape=(8, 96, 96, 3), dtype=float32, numpy=
array([[[[ 1.20957184e+00, 2.82766962e+00, 1.65782201e+00],
[ 3.85402292e-01, 2.00732923e+00, -2.79068202e-01],
[-2.52583921e-01, 7.82584965e-01, 7.56870627e-01],...

数据限幅

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

In [33]:x = tf.range(9)
tf.maximum(x,2) # 下限幅2
Out[33]:<tf.Tensor: id=48, shape=(9,), dtype=int32, numpy=array([2, 2, 2, 3,4, 5, 6, 7, 8])>
In [34]:tf.minimum(x,7) # 上限幅7
Out[34]:<tf.Tensor: id=41, shape=(9,), dtype=int32, numpy=array([0, 1, 2, 3,4, 5, 6, 7, 7])>
那么ReLU 函数可以实现为:
def relu(x):
return tf.minimum(x,0.) # 下限幅为0 即可

高级操作

tf.gather

tf.gather 可以实现根据索引号收集数据的目的。

x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32) # 4 个班级,每个班级35 个学生,8 门科目,

tf.gather(x,[0,1],axis=0) # 在班级维度收集第1-2 号班级成绩册

tf.gather 非常适合索引没有规则的场合,其中索引号可以乱序排列,此时收集的数据也是对应顺序:

In [41]:a=tf.range(8)
a=tf.reshape(a,[4,2]) # 生成张量a
Out[41]:<tf.Tensor: id=115, shape=(4, 2), dtype=int32, numpy=
array([[0, 1],
[2, 3],
[4, 5],
[6, 7]])>
In [42]:tf.gather(a,[3,1,0,2],axis=0) # 收集第4,2,1,3 号元素
Out[42]:<tf.Tensor: id=119, shape=(4, 2), dtype=int32, numpy=
array([[6, 7],
[2, 3],
[0, 1],
[4, 5]])>

tf.gather_nd

通过 tf.gather_nd,可以通过指定每次采样的坐标来实现采样多个点的目的。

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

In [47]: # 根据多维度坐标收集数据
tf.gather_nd(x,[[1,1],[2,2],[3,3]])
Out[47]:<tf.Tensor: id=256, shape=(3, 8), dtype=int32, numpy=
array([[45, 34, 99, 17, 3, 1, 43, 86],
[11, 25, 84, 95, 97, 95, 69, 69],
[ 0, 89, 52, 29, 76, 7, 2, 98]])>

tf.boolean_mask

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

考虑在班级维度上进行采样,对这4 个班级的采样方案的掩码为
𝑚𝑎𝑠𝑘 = [𝑇𝑟𝑢𝑒, 𝐹𝑎𝑙𝑠𝑒, 𝐹𝑎𝑙𝑠𝑒, 𝑇𝑟𝑢𝑒]

即采样第1 和第4 个班级,通过tf.boolean_mask(x, mask, axis)可以在axis 轴上根据mask 方案进行采样,实现为:

In [49]: # 根据掩码方式采样班级
tf.boolean_mask(x,mask=[True, False,False,True],axis=0)
Out[49]:<tf.Tensor: id=288, shape=(2, 35, 8), dtype=int32, numpy=
array([[[43, 10, 93, 85, 75, 87, 28, 19],…
注意掩码的长度必须与对应维度的长度一致

tf.where

通过 tf.where(cond, a, b)操作可以根据cond 条件的真假从a 或b 中读取数据,条件判定规则如下:
在这里插入图片描述
其中 i 为张量的索引,返回张量大小与a,b 张量一致,当对应位置中𝑐𝑜𝑛𝑑𝑖为True,𝑜𝑖位置从𝑎𝑖中复制数据;当对应位置中𝑐𝑜𝑛𝑑𝑖为False,𝑜𝑖位置从𝑏𝑖中复制数据。考虑2 个全1、全0 的3x3 大小的张量a,b 中提取数据,其中cond 为True 的位置从a 中对应位置提取,cond 为False 的位置从b 对应位置提取:

In [53]:
a = tf.ones([3,3]) # 构造a 为全1
b = tf.zeros([3,3]) # 构造b 为全0
# 构造采样条件
cond =
tf.constant([[True,False,False],[False,True,False],[True,True,False]])
tf.where(cond,a,b) # 根据条件从a,b 中采样
Out[53]:<tf.Tensor: id=384, shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
[0., 1., 0.],
[1., 1., 0.]], dtype=float32)>

可以看到,返回的张量中为1 的位置来自张量a,返回的张量中为0 的位置来自张量b。
当 a=b=None 即a,b 参数不指定时,tf.where 会返回cond 张量中所有True 的元素的索引坐标。考虑如下cond 张量:

In [54]: cond # 构造cond
Out[54]:<tf.Tensor: id=383, shape=(3, 3), dtype=bool, numpy=
array([[ True, False, False],
[False, True, False],
[ True, True, False]])>

其中True 共出现4 次,每个True 位置处的索引分布为[0,0], [1,1], [2,0], [2,1],可以直接通过tf.where(cond)来获得这些索引坐标:

In [55]:tf.where(cond) # 获取cond 中为True 的元素索引
Out[55]:<tf.Tensor: id=387, shape=(4, 2), dtype=int64, numpy=
array([[0, 0],
[1, 1],
[2, 0],
[2, 1]], dtype=int64)>

scatter_nd

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

meshgrid

通过 tf.meshgrid 可以方便地生成二维网格采样点坐标,方便可视化等应用场合。考虑
2 个自变量x,y 的Sinc 函数表达式为:
在这里插入图片描述
在这里插入图片描述

通过在 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]:

In [63]:
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
Out[63]: (TensorShape([100, 100]), TensorShape([100, 100]))

Sinc 函数在TensorFlow 中实现如下:

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

通过matplotlib 即可绘制出函数在𝑥 ∈ [−8,8], 𝑦 ∈ [−8,8]区间的3D 曲面,如图 5.5 中所示:

fig = plt.figure()
ax = Axes3D(fig)
# 根据网格点绘制sinc 函数3D 曲面
ax.contour3D(x.numpy(), y.numpy(), z.numpy(), 50)
plt.show()
```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汀桦坞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值