RCNN
RCNN (Region-based Convolutional Neural Networks)是将CNN引入目标检测的开山之作, 大大提高了目标检测效果。
RCNN 算法介绍
R-CNN是一种最先进的视觉目标检测系统,它将自底向上的区域提议与卷积神经网络计算的丰富特征相结合。在发布时,R-CNN将PASCAL VOC 2012上之前的最佳检测性能相对提高了30%,平均平均精度从40.9%提高到53.3%。与之前的最佳结果不同,R-CNN在不使用上下文记录或特征类型集合的情况下实现了这一性能。
官方link:https://github.com/rbgirshick/rcnn
RCNN 分为以下几个步骤:
- 候选区域生成: 一张图像生成1K~2K个候选区域 (采用Selective Search 方法)
- 特征提取: 对每个候选区域,使用深度卷积网络提取特征 (CNN)
- 类别判断: 特征送入每一类的SVM 分类器,判别是否属于该类
- 位置精修: 使用回归器精细修正候选框位置
Selective Search
Selective Search方法主要有三个优势: 捕捉不同尺度(Capture All Scales)、多样化(Diversification)、快速计算(Fast to Compute)总结为:选择性搜索是用于目标检测的区域提议算法,它计算速度快,具有很高的召回率,基于颜色,纹理,大小和形状兼容计算相似区域的分层分组。
Selective Search 算法步骤:
- 计算所有邻近区域之间的相似性;
- 两个最相似的区域被组合在一起;
- 计算合并区域和相邻区域的相似度;
- 重复2、3过程,直到整个图像变为一个地区。
参考上一篇博客: https://www.cnblogs.com/Asp1rant/p/16644539.html
IOU
IOU是衡量两个Region区域重合度的指标,对于区域\(A\)和\(B\),IOU公式为:
\(IOU=(A∩B)/(A∪B)\)
AlexNet
RCNN的作者Alex设计了AlexNet模型来进行每个区域的分类预测,Alex模型如下图所示:
算法有如下特点:
- 用Relu作为激活函数
- 多GPU训练
- 局部响应归一化(Local Response Normalization,LRN)
- 重叠池化(Overlapping Pooling)
- 对于每个训练集,需要固定输入图片的大小为224 * 224
代码实现
代码参考:https://github.com/1297rohit/RCNN
由于代码较长,下面介绍一些重点部分:
Selective Search
Selective Search 在opencv库中有现成方法,可以参考上一篇博客。
cv2.setUseOptimized(True);
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
im = cv2.imread(os.path.join(path,"42850.jpg"))
ss.setBaseImage(im)
ss.switchToSelectiveSearchFast()
rects = ss.process()
imOut = im.copy()
for i, rect in (enumerate(rects)):
x, y, w, h = rect
cv2.rectangle(imOut, (x, y), (x+w, y+h), (0, 255, 0), 1, cv2.LINE_AA)=
plt.imshow(imOut)
AlexNet
下面是AlexNet的代码,不过原文没有用AlexNet作为模型。
AlexNet分为版本和版本2
# V1
def AlexNet_v1(im_height=224, im_width=224, class_num=1000):
# NHWC
input_image = tf.keras.layers.Input(shape=(im_height, im_width, 3), dtype="float32") # output(None, 224, 224, 3)
x = tf.keras.layers.ZeroPadding2D(((1, 2), (1, 2)))(input_image) # padding output(None, 227, 227, 3)
x = tf.keras.layers.Conv2D(48, kernel_size=11, strides=4, activation="relu")(x) # output(None, 55, 55, 48)
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 27, 27, 48)
x = tf.keras.layers.Conv2D(128, kernel_size=5, padding="same", activation="relu")(x) # output(None, 27, 27, 128)
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 13, 13, 128)
x = tf.keras.layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 192)
x = tf.keras.layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 192)
x = tf.keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 128)
x = tf.keras.layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 6, 6, 128)
x = tf.keras.layers.Flatten()(x) # output(None, 6*6*128=4608)
x = tf.keras.layers.Dropout(0.2)(x)
x = tf.keras.layers.Dense(2048, activation="relu")(x) # output(None, 2048)
x = tf.keras.layers.Dropout(0.2)(x)
x = tf.keras.layers.Dense(2048, activation="relu")(x) # output(None, 2048)
x = tf.keras.layers.Dense(class_num)(x) # output(None, 5)
predict = tf.keras.layers.Softmax()(x) # convert output to probability distribution
model = tf.keras.models.Model(inputs=input_image, outputs=predict)
return model
# V2
class AlexNet_v2(tf.keras.Model):
def __init__(self, class_num=1000):
super(AlexNet_v2, self).__init__()
self.features = tf.keras.Sequential([
tf.keras.layers.ZeroPadding2D(((1, 2), (1, 2))), # output(None, 227, 227, 3)
tf.keras.layers.Conv2D(48, kernel_size=11, strides=4, activation="relu"), # output(None, 55, 55, 48)
tf.keras.layers.MaxPool2D(pool_size=3, strides=2), # output(None, 27, 27, 48)
tf.keras.layers.Conv2D(128, kernel_size=5, padding="same", activation="relu"), # output(None, 27, 27, 128)
tf.keras.layers.MaxPool2D(pool_size=3, strides=2), # output(None, 13, 13, 128)
tf.keras.layers.Conv2D(192, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 192)
tf.keras.layers.Conv2D(192, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 192)
tf.keras.layers.Conv2D(128, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 128)
tf.keras.layers.MaxPool2D(pool_size=3, strides=2)]) # output(None, 6, 6, 128)
self.flatten = tf.keras.layers.Flatten()
self.classifier = tf.keras.Sequential([
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(1024, activation="relu"), # output(None, 2048)
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(128, activation="relu"), # output(None, 2048)
tf.keras.layers.Dense(class_num), # output(None, 5)
tf.keras.layers.Softmax()
])
def call(self, inputs, **kwargs):
x = self.features(inputs)
x = self.flatten(x)
x = self.classifier(x)
return x
Transfer VGG16
原文用tensorflow中的VGG16作为模型:
vggmodel = VGG16(weights='imagenet', include_top=True)
vggmodel.summary()
RCNN 的改进
RCNN首次提出于2014年,至今有多次被提出改进。
参考:https://zhuanlan.zhihu.com/p/23006190
Fast RCNN
Fast R-CNN 主要是在R-CNN和SPPNet的基础上进行改进的,有着以下几个优点:
- 与R-CNN、SPPNet相比,有着更高的准确率。
- 通过使用多任务损失,将模型训练由多阶段转变为单阶段训练。
- 训练时可以一次更新网络的所有层,不再需要分步更新参数。
- 不再需要硬盘来存储CNN提取的特征数据
Fast R-CNN 步骤:
- 使用 Selective Search 方法生成2K个图片候选区域。
- 对整张图片进行特征提取得到相应的特征图(这是对R-CNN的一大改进,参考了SPPNet),并将上一步生成的候选区域映射到特征图中。
- 使用ROI Pooling将所有的候选区域特征统一缩放到7*7大小,然后将这2K个特征向量展平,并连接到全连接层上,得到两个输出结果,一个是K+1类(类别数+背景类)的概率,还有一个是每个类的预测边框。
Faster RCNN
Faster R-CNN算是RCNN系列算法的最杰出产物,也是two-stage中最为经典的物体检测算法。
- 第一阶段生成图片中待检测物体的anchor矩形框(对背景和待检测物体进行二分类)
- 第二阶段对anchor框内待检测物体进行分类。
Faster RCNN可以看作 RPN+Fast RCNN,其中RPN使用CNN来生成候选区域,并且RPN网络可以认为是一个使用了注意力机制的候选区域选择器,具体的网络结构如下图所示:
具体算法步骤如下
- 输入图像到特征提取器中,得到整张图片的feature map。
- 使用RPN生成候选框,并投影到feature map上,得到每一个候选区域的特征矩阵。
- 将每一个特征矩阵经过ROI Pooling缩放到7*7大小,然后经过展平处理后通过全连接层获得预测的分类以及候选区域位置偏移信息。
和YOLO的联系
RCNN是YOLO的衍生和基础,关于RCNN和YOLO的具体联系可以参考:https://blog.csdn.net/a264672/article/details/122952162
后面我也会针对YOLO进行具体的学习