MTCNN源码详细解读(1)

您所在的位置:网站首页 wap网络和net网络有什么不同 MTCNN源码详细解读(1)

MTCNN源码详细解读(1)

2023-08-10 19:22| 来源: 网络整理| 查看: 265

代码地址 https://github.com/AITTSMD/MTCNN-Tensorflow 这里我就不在进行MTCNN的介绍了。分析的再清楚都不如从源码的实现去分析。 Talk is cheap, just show me the code。 MTCNN主要分为三个网络 PNet RNet ONet 其中PNet是个全卷积网络 这是和RNet ONet最大的区别 由于篇幅有限 分成多篇进行分析 MTCNN源码详细解读(2)- PNet的训练和数据集的构建 MTCNN源码详细解读(3)- RNet的训练和数据集的构建

def P_Net(inputs,label=None,bbox_target=None,landmark_target=None,training=True): #define common param # 为相同的卷积操作 设置一样的初始化参数和激活函数prelu with slim.arg_scope([slim.conv2d], activation_fn=prelu, weights_initializer=slim.xavier_initializer(), biases_initializer=tf.zeros_initializer(), weights_regularizer=slim.l2_regularizer(0.0005), padding='valid'): # PNet 训练输入时(batch_size, 12, 12, 3) # (batch_size, 10, 10, 10) net = slim.conv2d(inputs, 10, 3, stride=1,scope='conv1') # (batch_size, 5, 5, 10) net = slim.max_pool2d(net, kernel_size=[2,2], stride=2, scope='pool1', padding='SAME') # (batch_size, 3, 3, 16) net = slim.conv2d(net,num_outputs=16,kernel_size=[3,3],stride=1,scope='conv2') # (batch_size, 1, 1, 32) net = slim.conv2d(net,num_outputs=32,kernel_size=[3,3],stride=1,scope='conv3') #batch*H*W*2 # 用 1 * 1卷积核来做输出 # 这里是类别输出 虽然是二分类 但是作者用2的维度来表示 第一位表示不是人脸置信度 第二位表示是人脸的置信度 conv4_1 = slim.conv2d(net,num_outputs=2,kernel_size=[1,1],stride=1,scope='conv4_1',activation_fn=tf.nn.softmax) #batch*H*W*4 # 这里就是输出坐标的偏移 4个值 bbox_pred = slim.conv2d(net,num_outputs=4,kernel_size=[1,1],stride=1,scope='conv4_2',activation_fn=None) #batch*H*W*10 # 这里是landmark五个点的坐标就是10个值 回归值 landmark_pred = slim.conv2d(net,num_outputs=10,kernel_size=[1,1],stride=1,scope='conv4_3',activation_fn=None) if training: #batch*2 # (batch, 1, 1, 2) 去掉dim=[1, 2]两个维度 # 下面也是同理 cls_prob = tf.squeeze(conv4_1,[1,2],name='cls_prob') # 计算分类损失 cls_loss = cls_ohem(cls_prob,label) #batch bbox_pred = tf.squeeze(bbox_pred,[1,2],name='bbox_pred') # 计算坐标损失 bbox_loss = bbox_ohem(bbox_pred,bbox_target,label) #batch*10 landmark_pred = tf.squeeze(landmark_pred,[1,2],name="landmark_pred") # 计算landMark损失 landmark_loss = landmark_ohem(landmark_pred,landmark_target,label) accuracy = cal_accuracy(cls_prob,label) L2_loss = tf.add_n(slim.losses.get_regularization_losses()) return cls_loss,bbox_loss,landmark_loss,L2_loss,accuracy

网络结构看上去简单清晰 下面分析下三个损失函数 1 分类损失cls_ohem 常用的交叉熵损失

def cls_ohem(cls_prob, label): # 构建一个和label shape一致的0数组 # (batch, ) zeros = tf.zeros_like(label) #label=-1 --> label=0net_factory # 对于label小于0的过滤掉 label {0, 1}的保留 # 这里先简单说下 PNet总共有三种label 0-negative 1-positive -1-part -2-landmark 后面在数据集构建的时候会详细说明 # 对于分类损失只需要计算 label为 0, 1的图片 label_filter_invalid = tf.where(tf.less(label,0), zeros, label) # (batch_size, 2) --> size: batch_size * 2 num_cls_prob = tf.size(cls_prob) # reshape 后 (batch_size * 2, 1) 为什么这么做呢因为这里对二分类用了2个输出表示 所有每个位置的值度需要计算损失 # 如果用1个值来表示就没必要这么麻烦 cls_prob_reshape = tf.reshape(cls_prob,[num_cls_prob,-1]) # 将上面的label转成int label_int = tf.cast(label_filter_invalid,tf.int32) # cls_prob shape 还是 (batch_size, 2) 所有 num_row就是batch num_row = tf.to_int32(cls_prob.get_shape()[0]) # 这里对num_row * 2 因为有两个值表示置信度 第一个位置不是人脸的 第二个位置是人脸的 # 这里详细分析下为什么乘2 # 假设batch_size=5 row = [0, 2, 4, 6, 8] 假设我们的label经过过滤后[1, 0, 0, 0, 1] # 相加变成 [1, 2, 4, 6, 9] 也就是说如果第i张图片label为1 就把第i张图片输出第二个位置的置信度值取出来 对于0的不变就是第一个位置置信度 # 有可能会有人说那过滤掉的label也不是0嘛 后label为0的没区分开来 这里不用担心 坐着下面会做mask 这是个常用手段 不需要参与计算的位置都mask掉 row = tf.range(num_row)*2 indices_ = row + label_int # 从 (batch_size *2, 1)中取出对应位置的label进行损失计算 label_prob = tf.squeeze(tf.gather(cls_prob_reshape, indices_)) # 计算负的log损失 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) # 这里就是添加mask 对于label小于0的mask掉 # 下面就是简单的求和 valid_inds = tf.where(label


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3