RCNN系列算法总结在这里
yolo v1
这篇文章写的已经很好了,拿来做个笔记。
核心步骤
1.算法首先把输入图像划分成S * S的格子,然后对每个格子都预测B个bounding boxes,每个bounding box都包含5个预测值:x,y,w,h和confidence。x,y就是bounding box的中心坐标,与grid cell对齐(即相对于当前grid cell的偏移值),使得范围变成0到1;w和h进行归一化(分别除以图像的w和h,这样最后的w和h就在0到1范围)。
2.另外每个格子都预测C个假定类别的概率。在本文中作者取S=7,B=2,C=20(因为PASCAL VOC有20个类别),所以最后有7730个tensor。
3.每个bounding box都对应一个confidence score,如果grid cell里面没有object,confidence就是0,如果有,则confidence score等于预测的box和ground truth的IOU值。所以如何判断一个grid cell中是否包含object呢?答案是:如果一个object的ground truth的中心点坐标在一个grid cell中,那么这个grid cell就是包含这个object,也就是说这个object的预测就由该grid cell负责。 每个grid cell都预测C个类别概率,表示一个grid cell在包含object的条件下属于某个类别的概率。
4.每个bounding box的confidence和每个类别的score相乘,得到每个bounding box属于哪一类的confidence score。也就是说最后会得到20*(772)=20*98的score矩阵,括号里面是bounding box的数量,20代表类别。接下来的操作都是20个类别轮流进行:在某个类别中(即矩阵的某一行),将得分少于阈值(0.2)的设置为0,然后再按得分从高到低排序。最后再用NMS算法去掉重复率较大的bounding box(NMS:针对某一类别,选择得分最大的bounding box,然后计算它和其它bounding box的IOU值,如果IOU大于0.5,说明重复率较大,该得分设为0,如果不大于0.5,则不改;这样一轮后,再选择剩下的score里面最大的那个bounding box,然后计算该bounding box和其它bounding box的IOU,重复以上过程直到最后)。最后每个bounding box的20个score取最大的score,如果这个score大于0,那么这个bounding box就是这个socre对应的类别(矩阵的行),如果小于0,说明这个bounding box里面没有物体,跳过即可。
5.网络结构:输入为448 * 448 * 3,输出为7 * 7 * 30。
6.损失函数:
损失函数方面,把localization error(bounding box的坐标误差)和classificaton error整合在一起。但是如果二者的权值一致,容易导致模型不稳定,训练发散。因为很多grid cell是不包含物体的,这样的话很多grid cell的confidence score为0。所以采用设置不同权重方式来解决,一方面提高localization error的权重,另一方面降低没有object的box的confidence loss权值,loss权重分别是5和0.5。而对于包含object的box的confidence loss权值还是原来的1。详见下面的原文解释和loos function函数。
在loss function中,前面两行表示localization error(即坐标误差),第一行是box中心坐标(x,y)的预测,第二行为宽和高的预测。这里注意用宽和高的开根号代替原来的宽和高,这样做主要是因为相同的宽和高误差对于小的目标精度影响比大的目标要大。举个例子,原来w=10,h=20,预测出来w=8,h=22,跟原来w=3,h=5,预测出来w1,h=7相比,其实前者的误差要比后者小,但是如果不加开根号,那么损失都是一样:4+4=8,但是加上根号后,变成0.15和0.7。
第三、四行表示bounding box的confidence损失,就像前面所说的,分成grid cell包含与不包含object两种情况。这里注意下因为每个grid cell包含两个bounding box,所以只有当ground truth 和该网格中的某个bounding box的IOU值最大的时候,才计算这项。 第五行表示预测类别的误差,注意前面的系数只有在grid cell包含object的时候才为1。
yolo v2
主要参考这篇文章,做一个小结。
主要改进
1.Batch Normalization
CNN在训练过程中网络每层输入的分布一直在改变, 会使训练过程难度加大,但可以通过normalize每层的输入解决这个问题。新的YOLO网络在每一个卷积层后添加batch normalization,通过这一方法,mAP获得了2%的提升。batch normalization 也有助于规范化模型,可以在舍弃dropout优化后依然不会过拟合。
2.High Resolution Classifier
对于YOLOv2,作者首先对分类网络(自定义的darknet)进行了fine tune,分辨率改成448 * 448,在ImageNet数据集上训练10轮(10 epochs),训练后的网络就可以适应高分辨率的输入了。然后,作者对检测网络部分(也就是后半部分)也进行fine tune。这样通过提升输入的分辨率,mAP获得了4%的提升。
3.Convolutional With Anchor Boxes
之前的YOLO利用全连接层的数据完成边框的预测,导致丢失较多的空间信息,定位不准。作者在这一版本中借鉴了Faster R-CNN中的anchor思想,回顾一下,anchor是RPN网络中的一个关键步骤,说的是在卷积特征图上进行滑窗操作,每一个中心可以预测9种不同大小的建议框。
YOLOv1是利用全连接层直接预测bounding box的坐标。
YOLOv2则借鉴了Faster R-CNN的思想,引入anchor。
YOLOv2做了以下改变:
(1)删掉全连接层和最后一个pooling层,使得最后的卷积层可以有更高分辨率的特征;
(2)缩减网络,用416 * 416大小的输入代替原来448 * 448。这样做是希望希望得到的特征图都有奇数大小的宽和高,奇数大小的宽和高会使得每个特征图在划分cell的时候就只有一个中心cell。因为大的目标一般会占据图像的中心,所以希望用一个中心cell去预测,而不是4个中心cell。网络最终将416 * 416的输入下采样32倍变为13 * 13大小的feature map输出,查看.cfg文件可以看到有8个pooling层。
YOLOv1中将输入图像分成7 * 7的网格,每个网格预测2个bounding box,一共只有7 * 7 * 2=98个box。
YOLOv2中引入anchor boxes,输出feature map大小为13 * 13,每个cell有5个anchor box预测得到5个bounding box,一共有13 * 13 * 5 = 845个box。增加box数量是为了提高目标的定位准确率。
4.Dimension Clusters
和以前的精选boxes维度不同,作者使用了K-means聚类方法类训练bounding boxes,可以自动找到更好的boxes宽高维度。传统的K-means聚类方法使用的是欧氏距离函数,也就意味着较大的boxes会比较小的boxes产生更多的error,聚类结果可能会偏离。为此,作者采用的评判标准是IOU得分(也就是boxes之间的交集除以并集),这样的话,error就和box的尺度无关了。最终的距离函数为:
5.Direct location prediction
6.Fine-Grained Features(细粒度特征)
这里添加了一个直通层(passthrough layer),即就是源码中的reorg layer,将前面一层的2626的特征图和本层1313的特征图进行连接,与ResNet网络的shortcut类似,以前面更高分辨率的特征图为输入,然后将其连接到后面的低分辨率特征图上。在13*13的特征图上做预测,虽然对于大目标已经足够了,但对小目标不一定足够好,这里合并前面大一点的特征图可以有效的检测小目标。具体操作:对于26 * 26 * 512的特征图,经passthrough层处理之后就变成了13 * 13 * 2048的新特征图(特征图大小变为1/4,而通道数变为以前的4倍),然后与后面的13 * 13 * 1024特征图连接在一起形成13 * 13 * 3072的特征图,最后在该特征图上卷积做预测。
7.Multi-Scale Training
原来的YOLO网络使用固定的448 * 448的图片作为输入,现在加入anchor boxes后,输入变成了416 * 416。目前的网络只用到了卷积层和池化层,那么就可以进行动态调整(意思是可检测任意大小图片)。作者希望YOLOv2具有不同尺寸图片的鲁棒性,因此在训练的时候也考虑了这一点。
不同于固定输入网络的图片尺寸的方法,作者在几次迭代后就会微调网络。没经过10次训练(10 epoch),就会随机选择新的图片尺寸。YOLO网络使用的降采样参数为32,那么就使用32的倍数进行尺度池化{320,352,…,608}。最终最小的尺寸为320 * 320,最大的尺寸为608 * 608。接着按照输入尺寸调整网络进行训练。
这种机制使得网络可以更好地预测不同尺寸的图片,意味着同一个网络可以进行不同分辨率的检测任务,在小尺寸图片上YOLOv2运行更快,在速度和精度上达到了平衡。
yolo v3
读到这篇文章,觉得写得很好,拿来做个笔记,借博主图一用。
1.backbone
darknet19和darknet53如下所示,v2使用19,v3使用53,此外keras版的yolo v3提供yolo v3-tiny,使用的网络就是darknet19。
整个v3结构里面,是没有池化层和全连接层的。前向传播过程中,张量的尺寸变换是通过改变卷积核的步长来实现的,而v2里面是通过maxpooling来进行的。
2.anchor
使用k=9的anchor,tiny版的使用k=6的anchor,anchor的值是由kmeans聚类得来,k=9时coco2014上的值为:10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326。yolo v3最大的改进就是运用了多层特征融合进行检测,输入为416 * 416 * 3,有三层输出分别为13 * 13 * 255 ,26 * 26* 255 ,52 * 52 * 255,三层输出分别对应3个anchor,输出特征图越小,感受野越大,则他们分别对应116,90, 156,198, 373,326和30,61, 62,45, 59,119和10,13, 16,30, 33,23。
3.其他
其他和yolo v2很像,就不多说了。
最后放一个很久之前写的一个Keras版yolo v3的pyqt调用界面代码,放在keras-yolo3目录下就可以直接使用了。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
import cv2
from PyQt5 import QtCore, QtGui,QtWidgets
import os
from yolo import YOLO
import threading
import numpy as np
import time
from PIL import Image
class Ui_MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Ui_MainWindow, self).__init__(parent)
# self.face_recong = face.Recognition()
self.timer_camera = QtCore.QTimer()
self.cap = cv2.VideoCapture()
self.CAM_NUM = 0
self.set_ui()
self.slot_init()
self.__flag_work = 0
self.x =0
def set_ui(self):
self.__layout_main = QtWidgets.QHBoxLayout()
self.__layout_fun_button = QtWidgets.QVBoxLayout()
self.__layout_data_show = QtWidgets.QVBoxLayout()
self.button_open_camera = QtWidgets.QPushButton(u'打开相机')
self.button_close = QtWidgets.QPushButton(u'退出')
self.button_open_camera.setMinimumHeight(50)
self.button_close.setMinimumHeight(50)
self.button_close.move(10,100)
# 信息显示
self.label_show_camera = QtWidgets.QLabel()
self.label_move = QtWidgets.QLabel()
self.label_move.setFixedSize(200, 200)
self.label_show_camera.setFixedSize(641, 481)
self.label_show_camera.setAutoFillBackground(False)
self.__layout_fun_button.addWidget(self.button_open_camera)
self.__layout_fun_button.addWidget(self.button_close)
self.__layout_fun_button.addWidget(self.label_move)
self.__layout_main.addLayout(self.__layout_fun_button)
self.__layout_main.addWidget(self.label_show_camera)
self.setLayout(self.__layout_main)
self.label_move.raise_()
self.setWindowTitle(u'yolo3目标检测')
def slot_init(self):
self.button_open_camera.clicked.connect(self.button_open_camera_click)
self.timer_camera.timeout.connect(self.show_camera)
self.button_close.clicked.connect(self.close)
def button_open_camera_click(self):
if self.timer_camera.isActive() == False:
flag = self.cap.open(self.CAM_NUM)
if flag == False:
msg = QtWidgets.QMessageBox.warning(self, u"Warning", u"请检测相机与电脑是否连接正确", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
self.timer_camera.start(30)
self.button_open_camera.setText(u'关闭相机')
else:
self.timer_camera.stop()
self.cap.release()
self.label_show_camera.clear()
self.button_open_camera.setText(u'打开相机')
def show_camera(self):
flag, self.image = self.cap.read()
# face = self.face_detect.align(self.image)
# if face:
# pass
self.image = cv2.flip(self.image,1)
show = cv2.resize(self.image, (640, 480))
show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
image = Image.fromarray(show)
image = yolo.detect_image(image)
result = np.asarray(image)
img = QtGui.QImage(result[:],result.shape[1], result.shape[0],result.shape[1] * 3, QtGui.QImage.Format_RGB888)
self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(img))
def cv2_to_qimage(self,cv_img):
height, width, bytesPerComponent = cv_img.shape
bgra = np.zeros([height, width, 4], dtype=np.uint8)
bgra[:, :, 0:3] = cv_img
return QtGui.QImage(bgra.data, width, height, QtGui.QImage.Format_RGB32)
def closeEvent(self, event):
ok = QtWidgets.QPushButton()
cacel = QtWidgets.QPushButton()
msg = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Warning, u"关闭", u"是否关闭!")
msg.addButton(ok,QtWidgets.QMessageBox.ActionRole)
msg.addButton(cacel, QtWidgets.QMessageBox.RejectRole)
ok.setText(u'确定')
cacel.setText(u'取消')
# msg.setDetailedText('sdfsdff')
if msg.exec_() == QtWidgets.QMessageBox.RejectRole:
event.ignore()
else:
# self.socket_client.send_command(self.socket_client.current_user_command)
if self.cap.isOpened():
self.cap.release()
if self.timer_camera.isActive():
self.timer_camera.stop()
event.accept()
if __name__ == '__main__':
yolo=YOLO()
app = QtWidgets.QApplication(sys.argv)
ui = Ui_MainWindow()
ui.show()
sys.exit(app.exec_())
实际上keras这个版本的yolo v3很慢,建议使用pytorch版本的,快了快一倍。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:目标检测yolo v1-v3总结 - Python技术站