论文题目:Rich feature hierarchies for accurate object detection and semantic segmentation
论文地址:https://arxiv.org/abs/1311.2524
tensorflow rcnn代码地址:https://github.com/Liu-Yicheng/R-CNN
RCNN,即Region CNN。
目标检测,不仅要将图片中含有的物体判断出是什么类别,而且还要找到物体所在的位置。
一般分为以下步骤:
1、提取可能存在物体的区域框
2、用CNN提取特征
3、对区域框的物体进行分类、框回归
1、网络模型:AlexNet
2、目标检测过程
(1)用selective search方法,从输入图片中提取2000个类别独立的候选区域。(selective search 也就是将图像分割成很多小的区域,计算区域之间的相似度(颜色、纹理等等),进行融合,形成大一些的region,结合各尺度结果。)
(2)对每个区域使用CNN,得到一个固定长度的特征向量。
(3)对每个区域用SVM进行目标分类。
3、迁移学习、finetune
在ImageNet数据集上训练得到很多很好的物体通用的特征,将其放在PASCAL VOC数据集上训练识别某些物体类型的能力。
4、inputsize:227*227pixel,由CNN的网络输入大小决定,因此将不同尺度的候选框wrap到227*227。
5、NMS
NMS,non maximum suppression,即非极大值抑制。就是将检测出的候选框,按照置信度score进行排序,保留最高的那个候选框。用NMS可以去掉重复的候选框,可以大大提高计算速度,没有冗余计算,而且较为准确。
6、查全率(precison)和查准率(recall)
定义以下标记:
tp:正确的标记为正;预测为真,实际为真
fp:错误的标记为正;预测为假,实际为假
tn:正确的标记为负;预测为真,实际为假
fn:错误的标记为负;预测为假,实际为真
查准率:precision = tp / (tp+fp) ;宁愿漏掉,不可错杀;正确预测占所有预测的比例
查全率:recall = tp / (tp+fn) ;宁可错杀,不可漏掉;正确预测占所有正例的比例
它们是评价模型的不同标准。
7、评价指标mAP
mAP,mean Average precision,对所有类别的precison取平均。
IoU,Intersection over Union,即交并比,本文Iou>0.5,就标记为positive正样本。
8、HNM
HNM,hard negetive mining,硬样本挖掘,所谓硬样本hard example,就是容易让分类器混淆,不好判断的样本。由于训练数据太大,全部训练成本大,采取HNM方法,可以将硬样本运用到下次训练中。
9、pool5特征可视化
这是一种无参方法,中心思想就是,挑选一个特征出来(本文采用的是pool5)计算对于不同的候选区域,激活层activation的值,将这些值按从大到小排序,取出前n个,显示对应的候选区域,就可以知道提取的是什么特征。
10、提取特征主要是卷积层,而不是全连接层
在finetune前,fc6,fc7作用不大,几乎可以移除;finetune后,fc6,fc7的作用得到提升。
11、bbox回归
借鉴于DPM中的框回归,线性回归模型,用pool5预测box的位置。
12、网络结构
(1)用selective search 提取候选区域
(2)用CNN提取特征
(3)用SVM进行分类
(4)对每个类别用NMS舍掉一部分region,进行框回归
13、预训练
使用一个大型辅助数据集 ILSVRC 2012 进行有监督的预训练,解决训练集数据缺乏的问题。
学习率:0.1
14、用region做detection的CNN
train:随机梯度下降SGD
学习率:0.001
15、判断正/负样本
部分区域含有物体,如何判断postive/negative example?IoU>0.5,记为正样本。
16、与DPM比较
DPM,deformable parts model,用一系列既定的model对feature map做计算,得到每类的值。DPM框架如下:
17、object proposal transformation
采用图b的方式,wraping with context padding,对候选框进行变形,也就是补全部分背景区域的图像,填充了16个像素。经过CNN计算,得到4096d的特征。
18、用selective search替代滑动窗口
selective search,区域合并的选择性搜索算法
(1)比滑动窗口复杂度低
(2)适应不同尺度,采用图像分割以及一种层次算法
(3)多样化,采用color/texture/size多种策略进行merge
(4)速度快
19、使用不同的标准(阈值)定义正负样本,这是调参的结果。
20、用SVM替代softmax,softmax背景样本共存,svm背景样本独立,更hard
21、最后要再做一次框回归,因为提取特征前为了resize样本,padding了16个像素,提取出来的框偏大。
22、瓶颈:对每个候选区域计算feature,2000个区域,计算量太大。
二、对代码的解读
1、IOU计算
def IOU(ver1, vertice2):
'''
用于计算两个矩形框的IOU
:param ver1: 第一个矩形框
:param vertice2: 第二个矩形框
:return: 两个矩形框的IOU值
'''
vertice1 = [ver1[0], ver1[1], ver1[0]+ver1[2], ver1[1]+ver1[3]]
area_inter = if_intersection(vertice1[0], vertice1[2], vertice1[1], vertice1[3], vertice2[0], vertice2[2], vertice2[1], vertice2[3])
if area_inter:
area_1 = ver1[2] * ver1[3]
area_2 = vertice2[4] * vertice2[5]
iou = float(area_inter) / (area_1 + area_2 - area_inter)
return iou
return False
2、selective search计算
def selective_search(
im_orig, scale=1.0, sigma=0.8, min_size=50):
'''Selective Search
Parameters
----------
im_orig : ndarray
Input image
scale : int
Free parameter. Higher means larger clusters in felzenszwalb segmentation.
sigma : float
Width of Gaussian kernel for felzenszwalb segmentation.
min_size : int
Minimum component size for felzenszwalb segmentation.
Returns
-------
img : ndarray
image with region label
region label is stored in the 4th value of each pixel [r,g,b,(region)]
regions : array of dict
[
{
'rect': (left, top, right, bottom),
'labels': [...]
},
...
]
'''
assert im_orig.shape[2] == 3, "3ch image is expected"
# load image and get smallest regions
# region label is stored in the 4th value of each pixel [r,g,b,(region)]
img = _generate_segments(im_orig, scale, sigma, min_size)
if img is None:
return None, {}
imsize = img.shape[0] * img.shape[1]
R = _extract_regions(img)
# extract neighbouring information
neighbours = _extract_neighbours(R)
# calculate initial similarities
S = {}
for (ai, ar), (bi, br) in neighbours:
S[(ai, bi)] = _calc_sim(ar, br, imsize)
# hierarchal search
while S != {}:
# get highest similarity
# i, j = sorted(S.items(), cmp=lambda a, b: cmp(a[1], b[1]))[-1][0]
i, j = sorted(list(S.items()), key = lambda a: a[1])[-1][0]
# merge corresponding regions
t = max(R.keys()) + 1.0
R[t] = _merge_regions(R[i], R[j])
# mark similarities for regions to be removed
key_to_delete = []
for k, v in S.items():
if (i in k) or (j in k):
key_to_delete.append(k)
# remove old similarities of related regions
for k in key_to_delete:
del S[k]
# calculate similarity set with the new region
for k in filter(lambda a: a != (i, j), key_to_delete):
n = k[1] if k[0] in (i, j) else k[0]
S[(t, n)] = _calc_sim(R[t], R[n], imsize)
regions = []
for k, r in R.items():
regions.append({
'rect': (
r['min_x'], r['min_y'],
r['max_x'] - r['min_x'], r['max_y'] - r['min_y']),
'size': r['size'],
'labels': r['labels']
})
return img, regions
3、读取数据
def load_17flowers(self,save_name='17flowers.pkl'):
'''
在train_txt文件中以列为单位依次获取 图片地址、图片类别等信息
将图片的矩阵数据(img)与图片类别数据(lable)作为一个整体进行保存
'''
save_path = os.path.join(self.data, save_name)
if os.path.isfile(save_path):
self.flower17_data = pickle.load(open(save_path, 'rb'))
else:
with codecs.open(self.train_list, 'r', 'utf-8') as f:
lines = f.readlines()
for num, line in enumerate(lines):
context = line.strip().split(' ')
image_path = context[0]
index = int(context[1])
img = cv2.imread(image_path)
img = resize_image(img, self.image_size, self.image_size)
img = np.asarray(img, dtype='float32')
label = np.zeros(self.train_class_num)
label[index] = 1
self.flower17_data.append([img, label])
view_bar("Process train_image of %s" % image_path, num + 1, len(lines))
pickle.dump(self.flower17_data,open(save_path,'wb'))
4、网络模型AlexNet
(1)network:
def build_network(self, input, output, is_svm= False, scope='R-CNN',is_training=True, keep_prob=0.5):
with tf.variable_scope(scope):
with slim.arg_scope([slim.fully_connected, slim.conv2d],
activation_fn=nn_ops.relu,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.conv2d(input, 96, 11, stride=4, scope='conv_1')
net = slim.max_pool2d(net, 3, stride=2, scope='pool_2')
net = local_response_normalization(net)
net = slim.conv2d(net, 256, 5, scope='conv_3')
net = slim.max_pool2d(net, 3, stride=2, scope='pool_2')
net = local_response_normalization(net)
net = slim.conv2d(net, 384, 3, scope='conv_4')
net = slim.conv2d(net, 384, 3, scope='conv_5')
net = slim.conv2d(net, 256, 3, scope='conv_6')
net = slim.max_pool2d(net, 3, stride=2, scope='pool_7')
net = local_response_normalization(net)
net = slim.flatten(net, scope='flat_32')
net = slim.fully_connected(net, 4096, activation_fn=self.tanh(), scope='fc_8')
net = slim.dropout(net, keep_prob=keep_prob,is_training=is_training, scope='dropout9')
net = slim.fully_connected(net, 4096, activation_fn=self.tanh(), scope='fc_10')
if is_svm:
return net
net = slim.dropout(net, keep_prob=keep_prob, is_training=is_training, scope='dropout11')
net = slim.fully_connected(net, output, activation_fn=self.softmax(), scope='fc_11')
return net
(2)loss:分类问题用交叉熵损失函数
5、SVM:sklearn.svm.LinearSVC()
6、regression Net
(1)network
def build_network(self, input_image, output_num, is_training= True, scope='regression_box', keep_prob=0.5):
with tf.variable_scope(scope):
with slim.arg_scope([slim.fully_connected],
activation_fn=self.tanh(),
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.fully_connected(input_image, 4096, scope='fc_1')
net = slim.dropout(net, keep_prob=keep_prob, is_training=is_training, scope='dropout11')
net = slim.fully_connected(net, output_num, scope='fc_2')
return net
(2)loss:均方差
(3)激活函数activation:tf.tanh()
7、训练和预测
共三种任务:特征提取,SVM预测分类,Reg_Box预测框回归。不同任务,不同的网络,不同的训练data。