8.张量排序,填充与复制
I. 张量排序
1.tf.sort/argsort()
通过设置参数direction为DESCENDING则为降序排列,默认为升序排列
tf.sort(values,axis=-1,direction=‘ASCENDING’):对某一维度排序,
tf.argsort(values,axis=-1,direction=‘ASCENDING’):排序索引,得到排序后的索引
- 一维排序
In [46]: a=tf.random.shuffle(tf.range(5)) #打乱
In [49]: a
Out[49]: <tf.Tensor: id=116, shape=(5,), dtype=int32, numpy=array([2, 1, 4, 0, 3])>
In [47]: tf.sort(a,direction='DESCENDING') #降序
Out[47]: <tf.Tensor: id=124, shape=(5,), dtype=int32, numpy=array([4, 3, 2, 1, 0])>
In [51]: tf.argsort(a,direction='DESCENDING')
Out[51]: <tf.Tensor: id=144, shape=(5,), dtype=int32, numpy=array([2, 4, 0, 1, 3])>
In [53]: idx =tf.argsort(a,direction='DESCENDING')
In [54]: tf.gather(a,idx) #利用index收集最值,还原出来
Out[54]: <tf.Tensor: id=155, shape=(5,), dtype=int32, numpy=array([4, 3, 2, 1, 0])>
- 多维排序
In [56]: a=tf.random.uniform([3,3],maxval=10,dtype=tf.int32)
In [57]: a
Out[57]:
<tf.Tensor: id=159, shape=(3, 3), dtype=int32, numpy=
array([[0, 6, 6],
[2, 3, 0],
[9, 0, 5]])>
In [58]: tf.sort(a)
Out[58]:
<tf.Tensor: id=170, shape=(3, 3), dtype=int32, numpy=
array([[0, 6, 6],
[0, 2, 3],
[0, 5, 9]])>
In [59]: tf.sort(a,direction='DESCENDING')
Out[59]:
<tf.Tensor: id=178, shape=(3, 3), dtype=int32, numpy=
array([[6, 6, 0],
[3, 2, 0],
[9, 5, 0]])>
In [60]: idx = tf.argsort(a)
In [61]: idx
Out[61]:
<tf.Tensor: id=189, shape=(3, 3), dtype=int32, numpy=
array([[0, 1, 2],
[2, 0, 1],
[1, 2, 0]])>
In [62]: tf.gather(a,idx) #多维排序,还原会出错
Out[62]:
<tf.Tensor: id=191, shape=(3, 3, 3), dtype=int32, numpy=
array([[[0, 6, 6],
[2, 3, 0],
[9, 0, 5]],
[[9, 0, 5],
[0, 6, 6],
[2, 3, 0]],
[[2, 3, 0],
[9, 0, 5],
[0, 6, 6]]])>
2.Top_k之tf.math.top_k()最大的前k个元素
tf.math.top_k(input, k=1, sorted=True)
参数:
input:张量
k:沿维度查找的个数
sort:为True表示k个元素按照值降序排列
返回值:两个对象
values:沿每个维度的k个值
indices:values所对应的索引
In [63]: a=tf.constant([[4,6,8],[9,4,7],[4,5,1]])
In [64]: a
Out[64]:
<tf.Tensor: id=192, shape=(3, 3), dtype=int32, numpy=
array([[4, 6, 8],
[9, 4, 7],
[4, 5, 1]])>
In [66]: res=tf.math.top_k(a,2)
In [69]: res
Out[69]:
TopKV2(values=<tf.Tensor: id=194, shape=(3, 2), dtype=int32, numpy=
array([[8, 6],
[9, 7],
[5, 4]])>, indices=<tf.Tensor: id=195, shape=(3, 2), dtype=int32, numpy=
array([[2, 1],
[0, 2],
[1, 0]])>)
In [67]: res.indices #索引
Out[67]:
<tf.Tensor: id=195, shape=(3, 2), dtype=int32, numpy=
array([[2, 1],
[0, 2],
[1, 0]])>
In [68]: res.values #值
Out[68]:
<tf.Tensor: id=194, shape=(3, 2), dtype=int32, numpy=
array([[8, 6],
[9, 7],
[5, 4]])>
3.top-k accuracy
- Prob(预测概率):[0.1, 0.2, 0.3, 0.4] 则按照索引来预测的结果最有可能是3
- Label:[2] (真实值)
- Only consider top-1 prediction: [3] 当我们按照top-1来预测时结果是3,则预测对的个数是0,预测的次数是1,故它的准确度是0/1 = 0%
- Only consider top-2 prediction:[3, 2] ;考虑不仅仅是最有可能的预测值,而且还是次最有可能的预测值,则前两个概率最大的是0.4,0.3对应的值是3,2,里面还有正确值2,则它预测对的个数是1,预测的次数是1,故它的准确率是1/1 = 100%;top-2 accuracy:只需要保证前2个有一个预测为正确就好了
- Only consider top-3 prediction:[3,2,1],类似上例,它预测对的个数是1,预测的次数是1,故它的准确率是1/1 = 100%
注意:这就是top-k accuracy的定义,可以看出top-1 accuracy是要求最严格的,因为我们要去概率最大的那个是预测对的,但是实际上top-1可能没有这么表现好。例如:我们在imagenet上面,有1000个类别,每个类的物种是非常复杂的,我们做预测时会发现不太容易,有时只有70%的top-1 accuracy,或者80%的top-1 accuracy
那么为了很好的检测一个算法的性能,我们还会考虑另一个指标,Top-5 accuracy,也就是说1000个类别中,如果前5个预测最高的可能性之中有一个预测对了,那么我们就可以认为它预测对了。虽然没有top-1那么精准,但是top-5也能从另一个方面反映模型是否比较好,是否优于目前的。
计算top-5 accuracy
In [29]: prob= tf.constant([[0.1,0.2,0.7],[0.2,0.7,0.1]]) #第一个预测值是2,第二个预测值是1
In [30]: target=tf.constant([2,0]) #两个的真实值
In [32]: k_b=tf.math.top_k(prob,3).indices
In [33]: k_b
Out[33]:
<tf.Tensor: id=40, shape=(2, 3), dtype=int32, numpy=
array([[2, 1, 0],
[1, 0, 2]])> #预测值
In [34]: k_b = tf.transpose(k_b,[1,0]) #转置
In [35]: k_b
Out[35]:
<tf.Tensor: id=42, shape=(3, 2), dtype=int32, numpy=
array([[2, 1], #top-1
[1, 0], #top-2
[0, 2]])> #top-3
In [36]: target=tf.broadcast_to(target,[3,2])
In [37]: target
Out[37]:
<tf.Tensor: id=44, shape=(3, 2), dtype=int32, numpy=
array([[2, 0],
[2, 0],
[2, 0]])>
II.填充与复制
1.tf.pad() 填充
tf.pad(a, [ [1, 2], [3, 4] ]) 在矩阵中,[1, 2]表示在行上填充,1指在前面填充一行,2指在后面填充2行;[3, 4]表示在列上填充,3指在列前面填充3列,4指在列上填充4列
In [70]: a=tf.reshape(tf.range(9),[3,3])
In [71]: a
Out[71]:
<tf.Tensor: id=201, shape=(3, 3), dtype=int32, numpy=
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])>
In [73]: tf.pad(a,[[0,0],[0,0]]) #0表示未填充任何数据
Out[73]:
<tf.Tensor: id=203, shape=(3, 3), dtype=int32, numpy=
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])>
In [74]: tf.pad(a,[[0,0],[1,2]]) #只填充列
Out[74]:
<tf.Tensor: id=205, shape=(3, 6), dtype=int32, numpy=
array([[0, 0, 1, 2, 0, 0],
[0, 3, 4, 5, 0, 0],
[0, 6, 7, 8, 0, 0]])>
In [75]: tf.pad(a,[[1,1],[0,0]]) #只填充行
Out[75]:
<tf.Tensor: id=207, shape=(5, 3), dtype=int32, numpy=
array([[0, 0, 0],
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 0, 0]])>
In [78]: tf.pad(a,[[1,1],[2,0]]) #行列都填充
Out[78]:
<tf.Tensor: id=211, shape=(5, 5), dtype=int32, numpy=
array([[0, 0, 0, 0, 0],
[0, 0, 0, 1, 2],
[0, 0, 3, 4, 5],
[0, 0, 6, 7, 8],
[0, 0, 0, 0, 0]])>
应用:图片,句子的填充,为了等长需要进行填充成定长
Image padding
In [79]: a=tf.random.normal([4,28,28,3])
In [83]: b=tf.pad(a,[[0,0],[2,2],[2,2],[0,0]]) #在第二,三维度前后各填充2,
In [84]: b.shape
Out[84]: TensorShape([4, 32, 32, 3])
In [80]: c=tf.pad(a,[[1,1],[2,2],[3,3],[4,4]]) #一般padding为行和列,多维度,在对应的维度上增加相应的列数
In [81]: c.shape
Out[81]: TensorShape([6, 32, 34, 11])
2.tf.tile() 数据复制
tf.tile()在内存上真实的复制, tf.broadcast_to()隐性复制
In [87]: a
Out[87]:
<tf.Tensor: id=228, shape=(3, 3), dtype=int32, numpy=
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])>
In [88]: tf.tile(a,[1,2]) # 1(当前的维度复制一次保持不变)表示第一个维度
#复制的次数,2表示第二个维度复制的次数
Out[88]:
<tf.Tensor: id=230, shape=(3, 6), dtype=int32, numpy=
array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])>
In [89]: tf.tile(a,[2,1])
Out[89]:
<tf.Tensor: id=232, shape=(6, 3), dtype=int32, numpy=
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])>
In [90]: tf.tile(a,[2,2])
Out[90]:
<tf.Tensor: id=234, shape=(6, 6), dtype=int32, numpy=
array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8],
[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])>
3.tf.tile() VS tf.broadcast_to()
将a.shape为[3, 3]扩展成[2, 3, 3]
- 可以通过:tf.expand_dims()和tf.tile()来完成这样的操作
- 也可通过:tf.broadcast_to()来完成这个操作
- 两者效果都是一样的,只是tf.broadcast_to不需要占用内存,故运行时性能更强
- tf.broadcast_to不需要显式调用,在计算中会有广播机构存在
In [91]: aa=tf.expand_dims(a,axis=0) #在0维度前面增加一个维度
In [92]: aa
Out[92]:
<tf.Tensor: id=236, shape=(1, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]])>
In [93]: tf.tile(aa,[2,1,1])
Out[93]:
<tf.Tensor: id=238, shape=(2, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]],
[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]])>
In [94]: tf.broadcast_to(aa,[2,3,3])
Out[94]:
<tf.Tensor: id=240, shape=(2, 3, 3), dtype=int32, numpy=
array([[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]],
[[0, 1, 2],
[3, 4, 5],
[6, 7, 8]]])>