cls_ohem()函数的作用就是返回人脸分类任务训练的loss值,其中只用loss值较大的前百分之七十参与反向传播。
import numpy as np
import tensorflow as tf
def cls_ohem(cls_prob, label):
num_keep_radio = 0.7
# label = tf.placeholder(tf.float32, shape=[config.BATCH_SIZE], name='label')
# cls_prob的shape是[384,2]
# label的shape是[384]
zeros = tf.zeros_like(label)
# 建立一个和label相同shape的全0数组
# label=-1 --> label=0net_factory
# pos -> 1, neg -> 0, others -> 0
# tf.less(x,y):x,y都是一个tensor,返回值是一个bool型的tensor,(逐元素返回是否x<y)
# tf.where(a,b,c)可以简单的认为a为真时,返回b,不然返回c
# 所以下面的操作是将正样本的label保持为1,负样本label为0,其他的的两个样本label值为0。
label_filter_invalid = tf.where(tf.less(label, 0), zeros, label)
# 返回cls_prob中元素的个数,384*2
num_cls_prob = tf.size(cls_prob)
# reshape成768行
cls_prob_reshape = tf.reshape(cls_prob, [num_cls_prob, -1])
# 将label_filter_invalid转化为int32类型
label_int = tf.cast(label_filter_invalid, tf.int32)
# 获取class_prob的行数,384
num_row = tf.to_int32(cls_prob.get_shape()[0])
# row = [0,2,4.....]
row = tf.range(num_row) * 2
# 因为conv4_1输出的的shape是[batch,2],每一张图片经过网络后,再经过softmax函数,输出的的是两个概率值
# 第一个值表示非人脸的概率,第二个值表示是人脸的概率,加起来的和为1,此时的label_int仍是[batch]
# 只是里面只有pos样本对应的label值是1,其余均为0,所以row + label_int表示的是cls_prob_reshape里面pos样本的索引
indices_ = row + label_int
# 使用tf.gather函数将pos样本提取出来,再使用tf.squeeze函数将shape变成(384,)
# 此时label_prob是里面有384个概率,是pos样本对应的概率和非pos样本对应的概率:pos样本为概率与非pos样本对应的概率
label_prob = tf.squeeze(tf.gather(cls_prob_reshape, indices_))
# 是一个二分类问题,使用的交叉熵损失函数。加上1e-10,是为了防止里面的label_prob值太小,输出为负无穷
loss = -tf.log(label_prob + 1e-10)
zeros = tf.zeros_like(label_prob, dtype=tf.float32)
ones = tf.ones_like(label_prob, dtype=tf.float32)
# set pos and neg to be 1, rest to be 0
# 建立一个valid_inds,里面将正样本和负样本的label设置为1,其余的为0
valid_inds = tf.where(label < zeros, zeros, ones)
# get the number of POS and NEG examples
# 获得正样本和负样本的数量
num_valid = tf.reduce_sum(valid_inds)
# 获取正样本和负样本数量的百分之70
keep_num = tf.cast(num_valid * num_keep_radio, dtype=tf.int32)
# FILTER OUT PART AND LANDMARK DATA
# 去除掉part样本和landmark样本
loss = loss * valid_inds
# 得到loss值中大小排前百分之七十的样本 为了找到输入的张量的最后的一个维度的最大的k个值和它的下标!
loss, _ = tf.nn.top_k(loss, k=keep_num)
#返回loss
with tf.Session() as sess:
# print(sess.run(indices_))
#print(sess.run(cls_prob_reshape))
print(sess.run(tf.gather(cls_prob_reshape, indices_)))
print(sess.run(label_prob))
print("Loss:%s"%(sess.run(tf.reduce_mean(loss))))
return tf.reduce_mean(loss)
#cls_prob1 = np.array([[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6],[0.5,0.6]])
cls_prob = tf.random_uniform([10,2],0,1,seed = 100)
with tf.Session() as sess:
print("cls_prob:%s"%(sess.run(cls_prob)))
#print(cls_prob1)
label = np.array([1,0,0,0,0,0,0,0,0,0])
cls_ohem(cls_prob, label)
注释:
tf.gather:用一个一维的索引数组,将张量中对应索引的向量提取出来
tf.squeeze()给定张量输入,此操作返回相同类型的张量,并删除所有尺寸为1的尺寸。 如果不想删除所有尺寸1尺寸,可以通过指定squeeze_dims来删除特定尺寸1尺寸。
如果不想删除所有大小是1的维度,可以通过squeeze_dims指定。
# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t)) ==> [2, 3]
Or, to remove specific size 1 dimensions:
# 't' is a tensor of shape [1, 2, 1, 3, 1, 1]
shape(squeeze(t, [2, 4])) ==> [1, 2, 3, 1]
import numpy as np
import tensorflow as tf
cl = tf.Variable([[1],
[4],
[5],
[1],
[7]])
t = tf.Variable([[1,2,3,5,6,1]])
label_prob1 = tf.squeeze(cl)
label_prob = tf.squeeze(t)
sess = tf.Session()
init = tf.global_variables_initializer()
sess.run(init)
print("label_prob1:%s"%(sess.run(label_prob1)))
print("label_prob1:%s"%(sess.run(label_prob)))
tf.nn.top_k(
input,
k=1,
sorted=True,
name=None
)
为了找到输入的张量的最后的一个维度的最大的k个值和它的下标!
如果输入的是一个向量,也就是rank=1,找到最大的k个数在这个向量,则输出最大的k个数字和最大的这k个数字的下标。如果输入的张量是一个更高rank的矩阵,那么我们只要找到每一行的最大的k个数字,以及他们的下标。如果两个元素相同,那么低一点的下标先出现。
参数:
input:输入的tensor,不能是array这些啊!要么输入1-D,要是更高维度必须保证最后的一个维度长度必须大于等于K
k:0-D的int32的数字张量。
sorted:如果sorted=True,那么选出来的k个数字就需要按照降序的顺序排序
name:可选项,也就是这个操作的名字
返回:
values:也就是每一行的最大的k个数字
indices:这里的下标是在输入的张量的最后一个维度的下标
import tensorflow as tf
import numpy as np
# 选出每一行的最大的前两个数字,返回的是最大的k个数字,同时返回的是最大的k个数字在最后的一个维度的下标
a = tf.constant(np.random.rand(4, 4))
b = tf.nn.top_k(a, k=3)
with tf.Session() as sess:
print(sess.run(a))
print(sess.run(b))