赞
踩
本文主要介绍MTCNN中PNet的网络结构,训练方式和BoundingBox的处理方式。PNet的网络结构是一个全卷积的神经网络结构,如下图所:
输入是一个12*12大小的图片,所以训练前需要把生成的训练数据(通过生成bounding box,然后把该bounding box 剪切成12*12大小的图片),转换成12*12*3的结构。
PNet是一个全卷积网络,所以Input可以是任意大小的图片,用来传入我们要Inference的图片,但是这个时候Pnet的输出的就不是1*1大小的特征图了,而是一个W*H的特征图,每个特征图上的网格对应于我们上面所说的(2个分类信息,4个回归框信息,10个人脸轮廓点信息)。W和H大小的计算,可以根据卷积神经网络W2=(W1-F+2P)/S+1, H2=(H1-F+2P)/S+1的方式递归计算出来,当然对于TensorFlow可以直接在程序中打印出最后Tensor的维度。相应的TensorFlow代码如下:
- 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'):
- net = slim.conv2d(inputs, 10, 3, stride=1,scope='conv1')
- net = slim.max_pool2d(net, kernel_size=[2,2], stride=2, scope='pool1', padding='SAME')
-
- net = slim.conv2d(net,num_outputs=16,kernel_size=[3,3],stride=1,scope='conv2')
- net = slim.conv2d(net,num_outputs=32,kernel_size=[3,3],stride=1,scope='conv3')
-
- conv4_1 = slim.conv2d(net,num_outputs=2,kernel_size=[1,1],stride=1,scope='conv4_1',activation_fn=tf.nn.softmax)
-
- bbox_pred = slim.conv2d(net,num_outputs=4,kernel_size=[1,1],stride=1,scope='conv4_2',activation_fn=None)
-
- landmark_pred = slim.conv2d(net,num_outputs=10,kernel_size=[1,1],stride=1,scope='conv4_3',activation_fn=None)
上述代码使用了slim API,可参见:
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/slim
使用Slim开发TensorFlow程序,增加了程序的易读性和可维护性,简化了hyper parameter的调优,使得开发的模型变得通用,集成了计算机视觉里面的一些常用模型(比如vgg19,alexnet),并且容易扩展复杂的模型。Slim API主要包含如下组件:
针对上面的PNet模型,介绍下相应的slimAPI:
采用TensorFlow默认API定义一个卷积操作一般采用如下代码:
- input = ...
- with tf.name_scope('conv1_1') as scope:
- kernel = tf.Variable(tf.truncated_normal([3, 3, 64, 128], dtype=tf.float32,
- stddev=1e-1), name='weights')
- conv = tf.nn.conv2d(input, kernel, [1, 1, 1, 1], padding='SAME')
- biases = tf.Variable(tf.constant(0.0, shape=[128], dtype=tf.float32),
- trainable=True, name='biases')
- bias = tf.nn.bias_add(conv, biases)
- conv1 = tf.nn.relu(bias, name=scope)
上述代码表示,输入为height*width*64,卷积大小为3*3*64,卷积核个数为128,stride为1*1,偏置项为bias,激活函数为relu。
采用slim接口可以简化为如下代码,简化了开发:
- input = ...
- net = slim.conv2d(input, 128, [3, 3], scope='conv1_1')
对TensorFlow的每个操作需要具体指定其中的参数,如下所示:
- padding = 'SAME'
- initializer = tf.truncated_normal_initializer(stddev=0.01)
- regularizer = slim.l2_regularizer(0.0005)
- net = slim.conv2d(inputs, 64, [11, 11], 4,
- padding=padding,
- weights_initializer=initializer,
- weights_regularizer=regularizer,
- scope='conv1')
- net = slim.conv2d(net, 128, [11, 11],
- padding='VALID',
- weights_initializer=initializer,
- weights_regularizer=regularizer,
- scope='conv2')
- net = slim.conv2d(net, 256, [11, 11],
- padding=padding,
- weights_initializer=initializer,
- weights_regularizer=regularizer,
- scope='conv3')
这样会有大量的重复参数设定,可以采用arg_scope组件,想用scope内的操作,使用相同的参数设定。从而简化参数的设定流程,简化开发,如下所示:
- with slim.arg_scope([slim.conv2d], padding='SAME',
- weights_initializer=tf.truncated_normal_initializer(stddev=0.01)
- weights_regularizer=slim.l2_regularizer(0.0005)):
- net = slim.conv2d(inputs, 64, [11, 11], scope='conv1')
- net = slim.conv2d(net, 128, [11, 11], padding='VALID', scope='conv2')
- net = slim.conv2d(net, 256, [11, 11], scope='conv3')
上述代码中,对二维卷积conv2d操作使用相同的weight初始化和正则化参数。
PNet的训练数据主要由4部分组成,包括正label数据(IOU>0.65,面部轮廓特值为0),负label数据(IOU<0.4,面部轮廓特值为0,回归框值为0),中间数据(0.4<IOU<0.65,面部轮廓特值为0),面部轮廓数据(回归框值为0)。把训练数据输入到网络中后,依据网络输出,计算loss值,如下所示:
- #分类loss值
- cls_prob = tf.squeeze(conv4_1,[1,2],name='cls_prob')
- cls_loss = cls_ohem(cls_prob,label)
- #回归框loss值
- bbox_pred = tf.squeeze(bbox_pred,[1,2],name='bbox_pred')
- bbox_loss = bbox_ohem(bbox_pred,bbox_target,label)
- #面部轮廓loss值
- landmark_pred = tf.squeeze(landmark_pred,[1,2],name="landmark_pred")
- landmark_loss = landmark_ohem(landmark_pred,landmark_target,label)
- #L2loss值
- L2_loss = tf.add_n(slim.losses.get_regularization_losses())
-
- #total loss
- total_loss=radio_cls_loss*cls_loss_op + radio_bbox_loss*bbox_loss_op + radio_landmark_loss*landmark_loss_op + L2_loss_op
-
- #优化操作
- optimizer = tf.train.MomentumOptimizer(lr, 0.9)
- train_op = optimizer.minimize(loss, global_step)
由于RNet是一个全卷积网络,所以当作Inference的时候输入数据可以是任意大小的图片。这样网络最后的输出就不是一个1*1大小的特征图了,而是一个H*W大小的特征网格。该特征网格每个网格的坐标表示对应一个回归框的位置信息,如下图所示:
其中右面是PNet网络生成的特征图,左边是原始图片中对应的回归框坐标。原始图片中回归框坐标需要经过反向运算,计算方式如下,其中cellSize=12,是因为12*12的图片进去后变成1*1,;stride=2是因为几层卷积中只有一个stride为2
- stride=2 (max pool, stride=2)
- cellSize=12 (one cell size equals 12)
- scale为图片的缩放比例
- x1= (stride*1)/scale,
- y1= (stride*1)/scale,
- x2= ((stride*1)+cellSize)/scale
- y2= ((stride*1)+cellSize)/scale
根据(x1, y1), (x2, y2)及该cell对应的reg(回归框的值),即可算出回归款的具体坐标。回归框的坐标的计算方式如下所示:
- stride = 2
- cellsize = 12
-
- t_index = np.where(cls_map > threshold)
-
- # find nothing
- if t_index[0].size == 0:
- return np.array([])
-
- #offset
- dx1, dy1, dx2, dy2 = [reg[t_index[0], t_index[1], i] for i in range(4)]
-
- reg = np.array([dx1, dy1, dx2, dy2])
- score = cls_map[t_index[0], t_index[1]]
- boundingbox = np.vstack([np.round((stride * t_index[1]) / scale),
- np.round((stride * t_index[0]) / scale),
- np.round((stride * t_index[1] + cellsize) / scale),
- np.round((stride * t_index[0] + cellsize) / scale),
- score,
- reg])
由上述步骤,可以看到一个原始图片会产生大量的回归框,那么到底要把那个回归框让RNet继续训练呢?这里采用非极大值抑制方法(NMS),该算法的主要思想是:将所有框的得分排序,选中最高分及其对应的框;遍历其余的框,如果和当前最高分框的重叠面积(IOU)大于一定阈值,我们就将框删除;从未处理的框中继续选一个得分最高的,重复上述过程。示例代码如下:
- x1 = dets[:, 0]
- y1 = dets[:, 1]
- x2 = dets[:, 2]
- y2 = dets[:, 3]
- scores = dets[:, 4]
-
- areas = (x2 - x1 + 1) * (y2 - y1 + 1)
- order = scores.argsort()[::-1]
-
- keep = []
- while order.size > 0:
- i = order[0]
- keep.append(i)
- xx1 = np.maximum(x1[i], x1[order[1:]])
- yy1 = np.maximum(y1[i], y1[order[1:]])
- xx2 = np.minimum(x2[i], x2[order[1:]])
- yy2 = np.minimum(y2[i], y2[order[1:]])
-
- w = np.maximum(0.0, xx2 - xx1 + 1)
- h = np.maximum(0.0, yy2 - yy1 + 1)
- inter = w * h
- if mode == "Union":
- ovr = inter / (areas[i] + areas[order[1:]] - inter)
- elif mode == "Minimum":
- ovr = inter / np.minimum(areas[i], areas[order[1:]])
- #keep
- inds = np.where(ovr <= thresh)[0]
- order = order[inds + 1]
这样我们就找到了要RNet继续训练(Refine)的回归框数据了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。