tensorflow基础2
1.合并与分割
合并张量是指将多个张量在某个维度上合并为一个张量。以某学校某年级成绩册数据为例,设张量A的shape = [4,35,8]。表示1~4号班级,每个班级35名学生的8门科目成绩。张量B的shape = [6,35,8]。表示5~10号班级的乘积。我们通过合并两个张量就可以得到这个学校这个年级的成绩册。这就是合并张量的意义所在。
1.1 拼接 tf.concat(tensors, axis)
拼接操作不会产生新的维度,仅仅在现有维度上进行合并。注意除了拼接维度,其他维度必须相同,否则不合法。
import tensorflow as tf
a = tf.random.normal([4,35,8])
b = tf.random.normal([6,35,8])
c = tf.concat([a,b],axis=0)
print(c.shape)
#(10, 35, 8)
1.2 堆叠 tf.stack(tensors, axis)
堆叠:如果合并数据时希望创建一个新的维度,则需要使用tf.stack操作。考虑张量A保存了某个班级成绩A.shape=[35,8],张量B保存了另一个班级的成绩,B.shape=[35,8]。我们堆叠两个张量就可以得到张量C,C.shape=[2,35,8]。表示两个班级的成绩。
import tensorflow as tf
a = tf.random.normal([35,8])
b = tf.random.normal([35,8])
c = tf.stack([a,b],axis=0)#axis表示新增的维度位置
print(c.shape)
#(2, 35, 8)
1.3 分割tf.split(x,num_or_size_splits,axis)
分割就是合并的逆操作,返回的是分割后的张量的列表。
import tensorflow as tf
a = tf.random.normal([10,35,8])
b = tf.split(a,num_or_size_splits=10,axis=0)#分为10个张量
c = tf.split(a,[2,2,6],axis=0)#分成2,2,6三个张量
2.数据统计
在神经网络的计算过程中,经常需要统计数据的各种属性,如最值、最值位置、均值、范数等信息。由于张量通常较大,直观观察数据很难获得有用信息,通过获取这些张量的统计信息可以较轻松地推测张量数值的分布。
2.1向量范数
向量范数(vector norm):是表征向量“长度”的一种度量方法,它可以推广到张量上。在神经网络中,常用来表示张量的权值大小,梯度大小等。常用的向量范数有:
L1范数,定义为向量x的所有元素绝对值之和:
L2范数, 定义为向量x的所有元素的平方和,再开根号:
∞ - 范数,定义为向量x的所有元素绝对值的最大值:
import tensorflow as tf
import numpy as np
a = tf.ones([2,2])
print(a)
print(tf.norm(a,ord=1))#L1范数
print(tf.norm(a,ord=2))#L2范数
print(tf.norm(a,ord=np.inf))#∞-范数
"""打印结果:
tf.Tensor(
[[1. 1.]
[1. 1.]], shape=(2, 2), dtype=float32)
tf.Tensor(4.0, shape=(), dtype=float32)
tf.Tensor(2.0, shape=(), dtype=float32)
tf.Tensor(1.0, shape=(), dtype=float32)
"""
2.2最值、均值、和
最大值、最小值tf.redunce_max(),tf.reduce_min()
import tensorflow as tf
a = tf.random.normal([4,10])
b = tf.reduce_max(a,axis=1)#求索引1维度的最大值
c = tf.reduce_min(a,axis=1)#求索引1维度的最小值
print(b)
print(c)
"""打印结果:一维的张量,四个元素就是每一维度的最大值
tf.Tensor([0.2986992 0.6242858 1.6938254 0.70986885], shape=(4,), dtype=float32)
tf.Tensor([-1.4581685 -3.1063654 -1.5195384 -2.1331983], shape=(4,), dtype=float32)
"""
均值与和:tf.reduce_mean(),tf.reduce_sum()
import tensorflow as tf
a = tf.random.normal([4,10])
b = tf.reduce_mean(a,axis=1)
c = tf.reduce_sum(a,axis=1)
print(b)
print(c)
"""打印结果:
tf.Tensor([ 0.06940475 -0.5237468 -0.26578826 -0.4367209 ], shape=(4,), dtype=float32)
tf.Tensor([ 0.6940475 -5.237468 -2.6578827 -4.367209 ], shape=(4,), dtype=float32)
"""
以上的方法,在不设置axis时对所有元素进行操作。
举个例子:在求解误差函数时计算样本平均误差
import tensorflow as tf
import numpy as np
from tensorflow.keras import losses
out = tf.random.normal([4,10])#模拟神经网络预测结果 4个数据分10类
y = tf.constant([1,2,2,0])#模拟正确解
y = tf.one_hot(y,depth=10)
loss = losses.mse(y, out)
loss = tf.reduce_mean(loss)
print(loss)
#tf.Tensor(1.086748, shape=(), dtype=float32)
通常我们还需要求最大值或最小值的索引,如下所示
import tensorflow as tf
a = tf.random.normal([4,10])
b = tf.argmax(a,axis=1)#求索引1维度的最大值索引
c = tf.argmin(a,axis=1)#求索引1维度的最小值索引
print(b)
print(c)
"""
tf.Tensor([7 6 8 1], shape=(4,), dtype=int64)
tf.Tensor([0 7 2 0], shape=(4,), dtype=int64)
"""
3.张量比较
一般我们为了计算分类任务的准确率等指标,需要将预测结果和真是标签比较,统计比较结果中正确的数量来计算准确率。举例如下:
import tensorflow as tf
out = tf.random.normal([100,10])
#100个样本的预测结果
pred = tf.argmax(out, axis = 1)
#张量pred是一维的,有一百个元素,每个元素就是预测正确解的索引
y = tf.random.uniform([100],dtype=tf.int64,maxval = 10)
#生成100个正确解索引
out = tf.equal(pred,y)
#比较两个张量,对应元素相等,则该位置是Ture,否则该位置是Fals
out = tf.cast(out, dtype=tf.int32)
#将bool类型转换为int型
correct = tf.reduce_sum(out)
#统计True的个数
accuracy = correct/out.y.shape
#正确率就是正确数量除以总数量
除了比较相等的tf.equal(a,b)函数,其他比较函数用法类似:
4.填充与复制
4.1 填充
对于图片数据的宽和高、序列信号的长度,维度长度可能各不相同。为了方便网络的并行计算,需要将不同长度的数据扩张为相同长度,之前我们介绍了通过复制的方式可以增加数据的长度,但是重复复制数据会破坏原有的数据结构,并不适合此处。通常的做法是,在需要补充长度的数据开始或结束处填充足够数量的特定数值,这些特定数值一般代表了无效意义,例如0,使得填充后的长度满足系统要求。那么这种操作就叫作填充。
tf.pad(x,paddings) :paddings是填充策略的List包含了多个[left padding, right padding].如[[0,0],[2,1],[1,2]]表示第一个维度不填充,第二个维度左边填充两个单元,右边填充一个单元,第三个维度左边填充1个单元,右边填充2个单元。
举个例子:有一批图片数据[4,28,28,1], 将28 * 28的图片填充但32*32
import tensorflow as tf
x = tf.random.normal([4,28,28,1])
x = tf.pad(x, [[0,0],[2,2],[2,2],[0,0]])
print(x.shape)
#(4, 32, 32, 1)
4.2 复制
tf.title()可以对任意长度的维度进行复制。
import tensorflow as tf
x = tf.random.normal([4,28,28,1])
a = tf.tile(x,[2,1,1,3])
print(a.shape)
#(8, 28, 28, 3)
5.数据限幅
tf.maximum(x,a):把张量x的数据元素限制最大为a。
tf.minimum(x,a):把张量x的数据元素限制最小为a。
tf.clip_by_value(x,a,b):把张量x限制为最大b,最小a。
import tensorflow as tf
x = tf.range(-8,8)
x = tf.reshape(x,[4,4])
a = tf.maximum(x,0)
b = tf.minimum(x,0)
c = tf.clip_by_value(x,-2,2)
print(x)
print(a)
print(b)
print(c)
"""
tf.Tensor(
[[-8 -7 -6 -5]
[-4 -3 -2 -1]
[ 0 1 2 3]
[ 4 5 6 7]], shape=(4, 4), dtype=int32)
tf.Tensor(
[[0 0 0 0]
[0 0 0 0]
[0 1 2 3]
[4 5 6 7]], shape=(4, 4), dtype=int32)
tf.Tensor(
[[-8 -7 -6 -5]
[-4 -3 -2 -1]
[ 0 0 0 0]
[ 0 0 0 0]], shape=(4, 4), dtype=int32)
tf.Tensor(
[[-2 -2 -2 -2]
[-2 -2 -2 -1]
[ 0 1 2 2]
[ 2 2 2 2]], shape=(4, 4), dtype=int32)
"""
6. 进阶操作
6.1 tf.gather
功能:根据索引号收集数据。
考虑班级成绩册的例子,假设共有4个班级,每个班级35个学生,8门科目,保存成绩册的张量shape为[4,35,8]。
现在需要收集1~2班的成绩册,给定索引号[0,1],并指定班级的维度axis=0,代码如下:
import tensorflow as tf
a = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
b = tf.gather(a,[0,1],axis=0)
c = a[0:2]
print(b)
print(c)
#结果太长,但是可以发现b与c是一样的,也就是我们可以通过切片简单的得到
但是如果索引不是连续的,切片就很麻烦,比如我们要抽查所有班级的第1、4、9、12、13、27号学生的成绩数据,tf.gather可以很简单的实现:
import tensorflow as tf
a = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
b = tf.gather(a,[0,3,8,11,12,26],axis = 1)
print(a)
"""
tf.Tensor(
[[[17 8 39 ... 40 75 77]
[32 8 17 ... 41 77 21]
[17 24 80 ... 5 75 50]
...
[44 14 34 ... 67 38 49]
[73 87 93 ... 11 6 28]
[19 92 17 ... 53 54 36]]
[[31 61 3 ... 32 22 90]
[29 18 17 ... 52 85 90]
[24 15 73 ... 99 13 62]
...
[51 70 58 ... 78 5 68]
[27 75 39 ... 82 40 77]
[ 7 43 58 ... 68 7 3]]
[[15 5 94 ... 20 24 7]
[73 67 35 ... 7 46 97]
[49 53 73 ... 26 59 85]
...
[24 81 33 ... 61 46 38]
[ 1 54 63 ... 89 81 61]
[58 34 37 ... 43 80 12]]
[[75 58 3 ... 73 72 96]
[36 79 75 ... 22 70 63]
[70 23 76 ... 91 24 21]
...
[59 6 74 ... 76 63 10]
[54 84 43 ... 11 61 41]
[61 23 3 ... 62 48 76]]], shape=(4, 35, 8), dtype=int32)
"""
接下来让问题更复杂一些,我们需要抽查第[2,3,]班级的第[3,4,6,27]号同学的成绩,我们可以组合多个tf.gather来实现。
import tensorflow as tf
a = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
b = tf.gather(a,[1,2],axis = 0)
c = tf.gather(b,[2,3,5,26],axis=1)
print(c)
"""
tf.Tensor(
[[[40 57 22 51 17 82 33 28]
[86 4 28 25 48 88 0 16]
[86 95 87 72 35 39 76 17]
[72 40 82 48 23 53 42 30]]
[[ 8 46 42 65 10 17 93 38]
[95 70 84 43 43 89 44 52]
[92 8 28 94 25 99 37 90]
[85 7 35 28 38 13 70 63]]], shape=(2, 4, 8), dtype=int32)
Process finished with exit code 0
tf.Tensor(
[[[40 57 22 51 17 82 33 28]
[86 4 28 25 48 88 0 16]
[86 95 87 72 35 39 76 17]
[72 40 82 48 23 53 42 30]]
[[ 8 46 42 65 10 17 93 38]
[95 70 84 43 43 89 44 52]
[92 8 28 94 25 99 37 90]
[85 7 35 28 38 13 70 63]]], shape=(2, 4, 8), dtype=int32)
"""
我们进一步深化问题,我们将抽查第2个班级的第二个同学的所有科目成绩,第三个班级的第三个同学的所有科目成绩,第四个班级的第四个同学的所有科目成绩。那怎么实现呢?我们可以结合切片和堆叠操作实现。
import tensorflow as tf
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
a = tf.stack([x[1,1],x[2,2],x[3,3]],axis=0)
print(a)
"""
tf.Tensor(
[[90 4 69 81 47 50 92 57]
[34 82 62 48 69 20 43 91]
[86 76 17 38 71 46 67 77]], shape=(3, 8), dtype=int32)
"""
上述方式我们道德到了正确结果,但是它的问题在于手动串行方式地执行采样,计算效率极低。我们介绍更好的方式tf.gather_nd
6.2 tf.gather_nd
功能:可以通过指定每次采样点的多为坐标来实现采样多个点的目的。
上述问题我们可以如下解决:
import tensorflow as tf
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
a = tf.gather_nd(x,[[1,1],[2,2],[3,3]])
print(a)
"""
tf.Tensor(
[[20 90 36 8 39 95 78 53]
[34 71 58 70 57 58 76 27]
[98 55 57 13 25 27 12 85]], shape=(3, 8), dtype=int32)
"""
6.3 tf.boolean_mask
功能:一种掩码采样方案。
我们对1班级班级采样如下和
import tensorflow as tf
x = tf.random.uniform([4,35,8],maxval=100,dtype=tf.int32)
a = tf.boolean_mask(x,[True,False,False,True],axis =0)
print(a)
#太长了此处不表
结果太长了,我们将班级数量减少到2个,学生3个,shape=[2,3,8]。
下面举例,采样1班级12号学生,2班级23号学生:
import tensorflow as tf
x = tf.random.uniform([2,3,8],maxval=100,dtype=tf.int32)
a = tf.boolean_mask(x,[[True,True,False],[False,True,True]],axis =0)
print(a)
"""
tf.Tensor(
[[37 0 36 43 20 25 73 77]
[98 75 51 74 55 87 80 76]
[72 40 6 40 10 54 29 0]
[35 44 30 73 11 75 83 25]], shape=(4, 8), dtype=int32)
"""
6.4 tf.where
tf.where(cond, a, b):cond是一个bool类型的张量,a,b,cond的shape是一样的。当cond对应元素取值True,复制a该位置的值,当cond对应元素取False时,复制b该位置的值。最后返回一个与a,b,cond同型的张量,其元素来自a或b。
import tensorflow as tf
a = tf.ones([3,3])#全1张量
b = tf.zeros([3,3])#全0张量
cond = tf.constant([[True,False,False],[False,True,False], [True,True,False]])
c = tf.where(cond,a,b)
print(c)
"""
tf.Tensor(
[[1. 0. 0.]
[0. 1. 0.]
[1. 1. 0.]], shape=(3, 3), dtype=float32)
"""
当a和b参数不指定时,tf.where会返回cond张量中所有True的元素的索引坐标。
import tensorflow as tf
#a = tf.ones([3,3])#全1张量
#b = tf.zeros([3,3])#全0张量
cond = tf.constant([[True,False,False],[False,True,False], [True,True,False]])
c = tf.where(cond)
print(c)
"""
tf.Tensor(
[[0 0]
[1 1]
[2 0]
[2 1]], shape=(4, 2), dtype=int64)
"""
那这有什么用呢?我们考虑这样一个场景,我们要提取张量中所有的正数数据和索引。
import tensorflow as tf
x = tf.random.normal([3,3])
mask = x>0 #mask是与x同型的张量,元素大于0位置为True否则为False
indices = tf.where(mask)
a = tf.gather_nd(x,indices)
b = tf.boolean_mask(x,mask)
print(x)
print(a)
print(b)
"""我们可以发现
a和b是相同的
[[ 1.4371932 0.07402689 -0.6966804 ]
[-1.5648468 -0.8848997 1.3896387 ]
[-1.4169993 -0.10606533 -0.9919792 ]], shape=(3, 3), dtype=float32)
tf.Tensor([1.4371932 0.07402689 1.3896387 ], shape=(3,), dtype=float32)
tf.Tensor([1.4371932 0.07402689 1.3896387 ], shape=(3,), dtype=float32)
"""
6.5 tf.scatter_nd
通过tf.scatter_nd(indices, updates, shape)函数可以高效地刷新张量的部分数据,但是这个函数只能在全0的白板张量上面执行刷新操作,因此可能需要结合其他操作来实现有张量的数据刷新功能。
举例:如下图,将updates的数据按索引indices插入到shape张量中。其实shape是个形状,因为只能在全0的白板张量上插入。
import tensorflow as tf
indices = tf.constant([[4],[3],[1],[7]])
updates = tf.constant([4.4,3.3,1.1,7.7])
x = tf.scatter_nd(indices,updates,[8])
print(x)
#tf.Tensor([0. 1.1 0. 3.3 4.4 0. 0. 7.7], shape=(8,), dtype=float32)
下面考虑3维张量的例子,shape=[4,4,4]
import tensorflow as tf
indices = tf.constant([[1],[3]])
updates = tf.constant([[[5,5,5,5],[6,6,6,6],[7,7,7,7],[8,8,8,8]],
[[1,1,1,1],[2,2,2,2],[3,3,3,3],[4,4,4,4]]])
x = tf.scatter_nd(indices,updates,[4,4,4])
print(x)
"""
tf.Tensor(
[[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
[[5 5 5 5]
[6 6 6 6]
[7 7 7 7]
[8 8 8 8]]
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
[[1 1 1 1]
[2 2 2 2]
[3 3 3 3]
[4 4 4 4]]], shape=(4, 4, 4), dtype=int32)
"""
6.6 tf.meshgrid
通过tf.meshgrid函数可以方便地生成二维网格的采样坐标,方便可视化等应用场合。
举例:我们要画出三维图像Sinc函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2YhOJvdD-1647417873731)(tensorflow基础2.assets/p7.png)]
绘制x,y均在[-8,8]。可以通过如下方式获得坐标采样点
def sinc(x,y):
return tf.math.sin(x**2+y**2)/(x**2+y**2)
points = []
for x in range(-8,8,100):
for y in range(-8,8,100):
z = sinc(float(x),float(y))
points.append([x,y,z])
通过tf.meshgrid(x,y)我们可以轻松实现:
import tensorflow as tf
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
x = tf.linspace(-8.,8,100)
y = tf.linspace(-8.,8,100)
x,y = tf.meshgrid(x,y)
z = tf.sqrt(x**2+y**2)#书上这样写,但是这不是求平方根了吗 我觉得错了自己调整下
z = tf.sin(z)/z
fig = plt.figure()
ax = Axes3D(fig)
ax.contour3D(x.numpy(),y.numpy(),z.numpy())
print(x.numpy().shape)
plt.show()