@w460461339
2019-02-20T18:12:47.000000Z
字数 3739
阅读 938
Tensorflow
RCNN分四步:
1、利用selective_search在图上找到候选框。
2、利用特征提取器对每个候选框提取特征。
3、对提取到的特征,送入SVM进行分类。
4、对提取到的特征,送入回归网络,进行位置精修。
在程序设计上,主要有这么几步:
1、获取原始特征提取器,用image-net上训练的也行。
2、在自己的数据集上对特征提取器进行fine_tune。
3、去除特征提取器最后的fc分类层。
4、利用Selective_search进行候选框选择。
5、对每个候选框,利用3中的特征提取器提取特征,得到【特征向量,label】这样的数据,以及【特征向量,ground_truth】的数据
6、利用5中得到的【特征向量,label】数据,训练SVM。
7、利用5中得到的【特征向量,ground_truth】数据,训练边框回归。
8、完事。
它的程序设计的很巧妙:
1、利用一个Solver类,来统一处理网络训练/预测过程中,共通的:
1.1 可视化参数,比如tensorboard的参数等。
1.2 持久化参数,比如模型的保存路径等。
1.3 训练参数:
1.3.1 学习率,都使用指数下降的学习率
1.3.2 移动平均,都使用移动平均
2、在Solver类中,通过:is_training,is_fineturn,is_Reg来控制状态:
2.1 初始特征提取器训练状态:is_training=True,is_fineturn=False,is_Reg=False
2.2 fine_tune训练状态:is_training=True,is_fineturn=True,is_Reg=False
2.3 回归网络训练状态:is_training=True,is_fineturn=False,is_Reg=True
3、在网络设计上,也采用上述的控制方案,对于基提取器,采用,is_training=True,is_fineturn=False,is_SVM=False来控制:
3.1 初始特征提取器训练状态:is_training=True,is_fineturn=False,is_SVM=False
3.2 Fine_tune训练状态:is_training=True,is_fineturn=True,is_SVM=False
此状态下,最后一层FC的输出个数不同。
3.3 SVM特征提取器状态:is_training=False,is_fineturn=False,is_SVM=True
此状态下,移除最后一层FC。(这里si_fineturn无所谓了)
其实这里的selective_search就是起到了anchors的作用。
我们用selective_search拿到碎片的时候,就是拿到了一个个的anchors。
我们计算gt和anchors的偏移量,然后让我们预测框的偏移量,尽可能的和【gt与anchors的偏移量】靠近,就完事了。
这里要求,selective_search每次的结果都要一样才行啊…
no_object_loss = tf.reduce_mean(tf.square((1-y_true[:,0])*y_pred[:,0]))
object_loss = tf.reduce_mean(tf.square(y_true[:,0]*(y_pred[:,0]-1)))
loss = (tf.reduce_mean(y_true[:,0] * ( tf.reduce_sum(tf.square(y_true[:,1:5]-y_pred[:,1:5])
,1) ) )
+ no_object_loss
+ object_loss
)
这里是我觉得问题最大的地方。y_pred是一个[None,5]的tensor,y_pred[None,0]表示是否是前景,y_pred[None,1:5]表示坐标参数。
上面它显然是想通过真实的前景概率*坐标的MSE+前景/背景的分类loss
来计算总的loss。
但是,问题出在他计算前景/背景的分类loss上,y_pred[None,0]没有经过sigmoid啊…输出并不是一个概率值,而且他的这部分
no_object_loss = tf.reduce_mean(tf.square((1-y_true[:,0])*y_pred[:,0]))
object_loss = tf.reduce_mean(tf.square(y_true[:,0]*(y_pred[:,0]-1)))
算得是个啥啊…根本看不懂啊…
self.global_step = tf.get_variable('global_step',[],initializer=tf.constant_initializer(0),trainable=False)
# learning_rate 在这里定义
self.learning_rate = tf.train.exponential_decay(
self.initial_learning_rate,
self.global_step,
self.decay_step,
self.decay_rate,
self.staircase,
name='learning_rate'
)
self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate)\
.minimize(self.net.total_loss,global_step=self.global_step)
3)滑动平均:
1、首先解释MovingAverage是干什么用的。
可以理解为,又做了一次momentum。
在得到某个参数v的更新之后的值vt+1后,我们在训练时直接用vt+1,
但是会维护一个影子变量v't+1,v't+1 = decay*v't+(1-decay)vt+1
当预测和测试时候,就用这个影子变量代替原变量。
2、那么下面就是具体的操作过程了:
2.1 通过tf.train.ExponentialMovingAverage获得一个滑动平均的操作符ema。
2.2 通过ema.apply指定哪些参数需要运用到滑动平均方法。
2.3 由于滑动平均需要在参数更新后使用,因此通过tf.control_dependencies([ops])指定顺序,
那么就会先运行self.optimizer,再运行self.average_op
2.4 另外,由于两者其实没有实质的依赖关系,因此通过tf.group将他们绑在一起,
那么当train_op执行的时候,会先执行self.optimizer,再执行self.average_op
(所以 tf.group其实是将没有关系的两个操作,让它们可以一起执行)
# 用滑动平均,帮助预测的时候更准
# 1、得到滑动平均操作符
self.ema = tf.train.ExponentialMovingAverage(decay=0.99)
# 2、选定要滑动平均的对象
self.average_op = self.ema.apply(tf.trainable_variables())
# 3、指定顺序,绑定操作符
with tf.control_dependencies([self.optimizer]):
self.train_op = tf.group(self.average_op)
'''
1、指定self.optimizer这个op,会在self.average_op之前运行。
2、将self.average_op和self.optimizer绑定成一个op。
因为两者没有依赖关系,我光运行self.average_op不会运行self.optimizer,所以要绑在一起执行。
'''
with tf.control_dependencies([self.optimizer]):
self.train_op = tf.group(self.average_op)
net = slim.dropout(net,keep_prob=keep_prob,is_training=is_training,scope='dropout9')