训练、测试都是调用了网络。那么网络是如何张开、恢复、调用,数据是如何在网络里面流动的呢?
network类基本包含了整个网络的架构设计,直接给出代码解析:
# --------------------------------------------------------
# Tensorflow Faster R-CNN
# Licensed under The MIT License [see LICENSE for details]
# Written by Xinlei Chen
# 南石北岸生2019.4.7
# https://mp.csdn.net/postedit
# --------------------------------------------------------
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf
import tensorflow.contrib.slim as slim
from tensorflow.contrib.slim import losses
from tensorflow.contrib.slim import arg_scope
import numpy as np
from layer_utils.snippets import generate_anchors_pre, generate_anchors_pre_tf
from layer_utils.proposal_layer import proposal_layer, proposal_layer_tf
from layer_utils.proposal_top_layer import proposal_top_layer, proposal_top_layer_tf
from layer_utils.anchor_target_layer import anchor_target_layer
from layer_utils.proposal_target_layer import proposal_target_layer
from utils.visualization import draw_bounding_boxes
from model.config import cfg
class Network(object):
def __init__(self):#构造方法,自动生成下列变量,用了self,可以不传入值得生成变量。
self._predictions = {}#
self._losses = {}
self._anchor_targets = {}
self._proposal_targets = {}
self._layers = {}
self._gt_image = None
self._act_summaries = []
self._score_summaries = {}
self._train_summaries = []
self._event_summaries = {}
self._variables_to_fix = {}
#最后还原图像的时候加上均值并且进行通道变换
def _add_gt_image(self):
# add back mean
image = self._image + cfg.PIXEL_MEANS
# BGR to RGB (opencv uses BGR)
resized = tf.image.resize_bilinear(image, tf.to_int32(self._im_info[:2] / self._im_info[2]))
self._gt_image = tf.reverse(resized, axis=[-1])
#以下都是tensorboard用到的summary
def _add_gt_image_summary(self):
# use a customized visualization function to visualize the boxes
if self._gt_image is None:
self._add_gt_image()
image = tf.py_func(draw_bounding_boxes,
[self._gt_image, self._gt_boxes, self._im_info],
tf.float32, name="gt_boxes")
return tf.summary.image('GROUND_TRUTH', image)
def _add_act_summary(self, tensor):
tf.summary.histogram('ACT/' + tensor.op.name + '/activations', tensor)
tf.summary.scalar('ACT/' + tensor.op.name + '/zero_fraction',
tf.nn.zero_fraction(tensor))
def _add_score_summary(self, key, tensor):
tf.summary.histogram('SCORE/' + tensor.op.name + '/' + key + '/scores', tensor)
def _add_train_summary(self, var):
tf.summary.histogram('TRAIN/' + var.op.name, var)
#rpn对block4的feature maps 利用18个1x1的卷积得到一个18通道的特征图,对于特征图上每一个点,都对应一个长度为18的向量
#这个向量对应这9个尺寸的anchor,每个anchor对应了前景和背景两类。
def _reshape_layer(self, bottom, num_dim, name):#num_dim是要强制转换的通道数。
#整体思路:首先进行通道顺序变换,然后强制将1x18通道形状转换为2x9的通道形状
input_shape = tf.shape(bottom)#读取数据维度
with tf.variable_scope(name) as scope:#打开变量域,定位这个变量
# change the channel to the caffe format
#将通道顺序变换
to_caffe = tf.transpose(bottom, [0, 3, 1, 2])
# then force it to have channel 2
#首先进行tf.concat,得到一个shape,按照这个shape进行reshape,
#
reshaped = tf.reshape(to_caffe,
tf.concat(axis=0, values=[[1, num_dim, -1], [input_shape[2]]]))
# then swap the channel back
#然后将通道顺序换回原来的通道。
to_tf = tf.transpose(reshaped, [0, 2, 3, 1])
return to_tf
#针对以上,给出1组示例,帮助理解。
#feature map 大小为38x38x24,24是因为我设置的anchor参数为[4,8,16,32]和[0.5,1,2]。每个anchor centre有3x4=12个anchor框,每个框有前景和背景2类得分值。所以是24.
#softmax用于将得分转换为概率
#key:tf.nn.softmax()
def _softmax_layer(self, bottom, name):
if name.startswith('rpn_cls_prob_reshape'):
input_shape = tf.shape(bottom)#输入的shape
bottom_reshaped = tf.reshape(bottom, [-1, input_shape[-1]])#重新排版一下得分矩阵以满足tf.nn.softmax的输入要求
reshaped_score = tf.nn.softmax(bottom_reshaped, name=name)#将得分矩阵转换为概率矩阵。
return tf.reshape(reshaped_score, input_shape)#转换回输入的数据组织顺序
return tf.nn.softmax(bottom, name=name)#如果不是rpn_cls_prob_reshape,即不是经过reshape的rpn_cls_prob,就直接满足tf.nn.softmax了,可以直接调用并返回结果
#测试的时候有两种模式,一种是top,一种是nms,都是对众多框的选测方法,nms快,top慢,但是文档解释说top更好,默认是nms。
def _proposal_top_layer(self, rpn_cls_prob, rpn_bbox_pred, name):
#这个函数跟proposal_layer()功能类似
with tf.variable_scope(name) as scope:
if cfg.USE_E2E_TF:#USE_E2E_TF是端端的tf模型,这个参数默认为True。相对的是非端端的tf模型,可以输入到多个tf实例模型里面运行。
#端到端的tf模型只在测试的前馈过程中测试过。这个参数最好别动,不然下面代码可能会出BUG
#proposal_top_layer的函数有两种,一种是非tf的,一种是tf的。这个版本的faster用的tf实现的。毕竟tf可以调用gpu,更快。
rois, rpn_scores = proposal_top_layer_tf(
rpn_cls_prob,
rpn_bbox_pred,
self._im_info,
self._feat_stride,
self._anchors,
self._num_anchors
)#选择前TEST.RPN_TOP_N个,这个参数默认是C.TRAIN.RPN_PRE_NMS_TOP_N = 12000
#需要注意的是,对于得到的框数m,m小于12000的话,会从5000个值里面随机选择m个框,所以会选到空白,导致丢失重要的框,不过这种情况很少发生
else:
#如果不是端端的tfmodel,就调用tf.py_func,将tensor转换为numpy array格式进行np的处理,然后输出numpy array,并转换为tensor后返回。
rois, rpn_scores = tf.py_func(proposal_top_layer,
[rpn_cls_prob, rpn_bbox_pred, self._im_info,
self._feat_stride, self._anchors, self._num_anchors],
[tf.float32, tf.float32], name="proposal_top")
rois.set_shape([cfg.TEST.RPN_TOP_N, 5])
rpn_scores.set_shape([cfg.TEST.RPN_TOP_N, 1])
#以上总之就是proposals挑选的两种方法nms、TOP中的TOP。TOP又分两种模式,端到端的tf处理或者调用np处理,增加tensor接口的灵活性,结果没差。
return rois, rpn_scores
#和上面对应,下面的是nms模式。函数名字的区别就是有无top。
#_proposal_layer从所有anchor框中选择出选择C.TRAIN.RPN_POST_NMS_TOP_N = 2000个作为rois供给fast rcnn部分,即rois, roi_scores = self._proposal_layer(rpn_cls_prob, rpn_bbox_pred, "rois")
def _proposal_layer(self, rpn_cls_prob, rpn_bbox_pred, name):
with tf.variable_scope(name) as scope:
if cfg.USE_E2E_TF:
rois, rpn_scores = proposal_layer_tf(
rpn_cls_prob,
rpn_bbox_pred,
self._im_in