单阶段目标检测算法(SSD):原理、训练与实战
1. 单阶段目标检测算法(SSD)概述
SSD(Single Shot Detectors)是一种端到端的目标检测算法,与Faster R - CNN不同,它将多个组件统一封装在一个网络中,训练更简单,能实现实时目标检测,且在多个目标检测数据集上与Faster R - CNN有相近的准确率。
1.1 特征图离散化
SSD通过将输入图像离散化为不同大小的单元格来生成特征图,每个单元格类似于Faster R - CNN中的锚点,有一组默认的边界框。单元格数量越少,能检测的物体越大;单元格数量越多,能检测的物体越小。
例如,在中间的8×8特征图中,周围的边界框先验较小,适合定位小物体;而右边的4×4特征图中,边界框先验较大,适合定位大物体。在Liu等人的示例中,8×8特征图(特别是蓝色高亮的边界框先验)可用于定位图像中的猫,4×4特征图(红色高亮的边界框先验)可定位狗,因为狗比猫大得多,需要单元格更少的离散化特征图。
1.2 边界框预测
SSD不是预测原始的(x,y)坐标,而是预测每个类别标签的边界框偏移量(即增量)。对于一个由两组(x,y)坐标组成的边界框,每个特征图单元格有b个默认固定先验,总共有c个类别标签。如果特征图的空间维度为f = M × N,则每个特征图需要计算t = f × b×(4 + c)个值。
同时,对于每个预测的边界框,SSD会计算该区域内所有类别标签的概率,而不是只保留所有类别中概率最大的边界框。按类别计算并保留边界框的概率,能检测可能重叠的物体。
1.3 训练方法
训练SSD时,需要考虑MultiBox算法的损失函数,它包括两个部分:
1.
置信度损失
:使用分类交叉熵损失,用于衡量边界框的类别标签预测是否正确。
2.
位置损失
:与Faster R - CNN类似,使用平滑L1损失,让SSD在接近但不完全精确的定位上有更大的灵活性,即最终预测的边界框不一定要完美,“足够接近”即可。
1.4 超参数选择
- 默认边界框数量 :Liu等人的原始论文建议使用4个或6个默认边界框。增加尺度和宽高比的变化可能(但不总是)能检测更多物体,但会显著降低推理速度。
- 特征图数量 :可以在网络中添加额外的卷积块来增加深度,提高物体被正确检测和分类的可能性,但会降低网络运行速度。
1.5 难负样本挖掘
SSD框架采用难负样本挖掘来提高训练准确率。在训练过程中,与真实物体交并比(IoU)较低的单元格被视为负样本。为保证正负样本数量平衡,Liu等人建议保持负样本与正样本的比例约为3:1。大多数SSD实现会默认进行这种采样或提供可调整的参数。
1.6 优化器与预测
- 优化器 :原始论文使用随机梯度下降(SGD)优化器进行端到端训练,也可以使用RMSprop或Adam,特别是在微调现有目标检测模型时。
- 预测 :预测时,按类别使用非极大值抑制(NMS)来得到最终预测结果。训练后,SSD在512×512输入图像上约能达到22 FPS,在300×300图像上约能达到59 FPS。将VGG基础网络替换为计算效率更高的MobileNet,可获得更高的帧率,但准确率可能会略有下降。
1.7 SSD的局限性
SSD对小物体的检测效果通常不佳,主要原因是小物体可能不会出现在所有特征图上,物体在特征图上出现的次数越多,MultiBox算法越有可能检测到它。常见的解决方法是增大输入图像的大小,但这会降低SSD的运行速度,且不能完全解决小物体检测的问题。如果要检测相对于输入图像尺寸较小的物体,建议使用Faster R - CNN。
2. 从零开始训练SSD
2.1 车辆数据集
本次使用的车辆数据集来自Davis King的dlib库,由King手动标注,用于演示他的最大间隔目标检测算法。数据集中的每张图像都由安装在汽车仪表盘上的相机拍摄,所有可见的车辆前后视图都被标注。标注使用了dlib中的imglab工具,该工具允许将图像中可能“混淆”的区域标记为“忽略”,在使用dlib库训练HOG + 线性SVM检测器或CNN MMOD检测器(使用C++)时,dlib会在训练中排除这些标记区域。
然而,TensorFlow目标检测API(TFOD API)没有“忽略”图像区域的概念,这可能会影响目标检测性能。使用dlib车辆数据集的目标有两个:
1. 训练一个高质量的SSD目标检测器,用于定位图像中车辆的前后视图。
2. 展示SSD的难负样本挖掘与TFOD API缺乏“忽略”区域功能的结合如何使目标检测器产生混淆。
2.2 训练SSD的步骤
2.2.1 目录结构和配置
项目的目录结构如下:
| --- ssds_and_rcnn
|
|--- build_vehicle_records.py
|
|--- config
|
|
|--- __init__.py
|
|
|--- dlib_front_rear_config.py
|
|--- dlib_front_and_rear_vehicles_v1/
|
|
|--- image_metadata_stylesheet.xsl
|
|
|--- input_videos
...
|
|
|--- testing.xml
|
|
|--- training.xml
|
|
|--- youtube_frames
|
|--- predict.py
|
|--- predict_video.py
创建一个名为config的模块,将所有必要的基于Python的配置存储在dlib_front_rear_config.py中。build_vehicle_records.py用于将车辆数据集构建为TensorFlow记录格式。predict.py和predict_video.py与之前的使用方法相同。setup.sh脚本用于配置PYTHONPATH以访问TFOD API的导入和库。
dlib_front_and_rear_vehicles_v1目录包含车辆数据集,可以通过以下链接下载(http://pyimg.co/9gq7u),然后点击dlib_front_and_rear_vehicles_v1.tar文件。也可以使用wget和tar下载并解压文件:
$ wget http://dlib.net/files/data/dlib_front_and_rear_vehicles_v1.tar
$ tar -xvf dlib_front_and_rear_vehicles_v1.tar
在dlib_front_and_rear_vehicles_v1目录中,有两个XML文件training.xml和testing.xml,分别包含训练集和测试集的边界框注释和类别标签。需要创建记录和数据目录:
$ mkdir records experiments
$ mkdir experiments/training experiments/evaluation experiments/exported_model
dlib_front_rear_config.py的内容如下:
# import the necessary packages
import os
# initialize the base path for the front/rear vehicle dataset
BASE_PATH = "dlib_front_and_rear_vehicles_v1"
# build the path to the input training and testing XML files
TRAIN_XML = os.path.sep.join([BASE_PATH, "training.xml"])
TEST_XML = os.path.sep.join([BASE_PATH, "testing.xml"])
# build the path to the output training and testing record files,
# along with the class labels file
TRAIN_RECORD = os.path.sep.join([BASE_PATH,
"records/training.record"])
TEST_RECORD = os.path.sep.join([BASE_PATH,
"records/testing.record"])
CLASSES_FILE = os.path.sep.join([BASE_PATH,
"records/classes.pbtxt"])
# initialize the class labels dictionary
CLASSES = {"rear": 1, "front": 2}
2.2.2 构建车辆数据集
build_vehicle_records.py文件主要基于之前的build_lisa_records.py,主要修改是解析车辆数据集的XML文件,而不是LISA交通标志数据集提供的CSV文件。
可以使用BeautifulSoup库来解析XML文件,如果系统中没有安装该库,可以使用pip安装:
$ pip install beautifulsoup4
build_vehicle_records.py的代码如下:
# import the necessary packages
from config import dlib_front_rear_config as config
from pyimagesearch.utils.tfannotation import TFAnnotation
from bs4 import BeautifulSoup
from PIL import Image
import tensorflow as tf
import os
def main(_):
# open the classes output file
f = open(config.CLASSES_FILE, "w")
# loop over the classes
for (k, v) in config.CLASSES.items():
# construct the class information and write to file
item = ("item {\n"
"\tid: " + str(v) + "\n"
"\tname: '" + k + "'\n"
"}\n")
f.write(item)
# close the output classes file
f.close()
# initialize the data split files
datasets = [
("train", config.TRAIN_XML, config.TRAIN_RECORD),
("test", config.TEST_XML, config.TEST_RECORD)
]
# loop over the datasets
for (dType, inputPath, outputPath) in datasets:
# build the soup
print("[INFO] processing '{}'...".format(dType))
contents = open(inputPath).read()
soup = BeautifulSoup(contents, "html.parser")
# initialize the TensorFlow writer and initialize the total
# number of examples written to file
writer = tf.python_io.TFRecordWriter(outputPath)
total = 0
# loop over all image elements
for image in soup.find_all("image"):
# load the input image from disk as a TensorFlow object
p = os.path.sep.join([config.BASE_PATH, image["file"]])
encoded = tf.gfile.GFile(p, "rb").read()
encoded = bytes(encoded)
# load the image from disk again, this time as a PIL
# object
pilImage = Image.open(p)
(w, h) = pilImage.size[:2]
# parse the filename and encoding from the input path
filename = image["file"].split(os.path.sep)[-1]
encoding = filename[filename.rfind(".") + 1:]
# initialize the annotation object used to store
# information regarding the bounding box + labels
tfAnnot = TFAnnotation()
tfAnnot.image = encoded
tfAnnot.encoding = encoding
tfAnnot.filename = filename
tfAnnot.width = w
tfAnnot.height = h
# loop over all bounding boxes associated with the image
for box in image.find_all("box"):
# check to see if the bounding box should be ignored
if box.has_attr("ignore"):
continue
# extract the bounding box information + label,
# ensuring that all bounding box dimensions fit
# inside the image
startX = max(0, float(box["left"]))
startY = max(0, float(box["top"]))
endX = min(w, float(box["width"]) + startX)
endY = min(h, float(box["height"]) + startY)
label = box.find("label").text
# TensorFlow assumes all bounding boxes are in the
# range [0, 1] so we need to scale them
xMin = startX / w
xMax = endX / w
yMin = startY / h
yMax = endY / h
# due to errors in annotation, it may be possible
# that the minimum values are larger than the maximum
# values -- in this case, treat it as an error during
# annotation and ignore the bounding box
if xMin > xMax or yMin > yMax:
continue
# similarly, we could run into the opposite case
# where the max values are smaller than the minimum
# values
elif xMax < xMin or yMax < yMin:
continue
# update the bounding boxes + labels lists
tfAnnot.xMins.append(xMin)
tfAnnot.xMaxs.append(xMax)
tfAnnot.yMins.append(yMin)
tfAnnot.yMaxs.append(yMax)
tfAnnot.textLabels.append(label.encode("utf8"))
tfAnnot.classes.append(config.CLASSES[label])
tfAnnot.difficult.append(0)
# increment the total number of examples
total += 1
# encode the data point attributes using the TensorFlow
# helper functions
features = tf.train.Features(feature=tfAnnot.build())
example = tf.train.Example(features=features)
# add the example to the writer
writer.write(example.SerializeToString())
# close the writer and print diagnostic information to the
# user
writer.close()
print("[INFO] {} examples saved for '{}'".format(total,
dType))
# check to see if the main thread should be started
if __name__ == "__main__":
tf.app.run()
运行以下命令构建车辆数据集:
$ time python build_vehicle_records.py
运行结果如下:
[INFO] processing 'train'...
[INFO] 6133 examples saved for 'train'
[INFO] processing 'test'...
[INFO] 382 examples saved for 'test'
real 0m2.749s
user 0m2.411s
sys 0m1.163s
查看records目录的内容,可以看到训练和测试记录文件已成功创建:
$ ls dlib_front_and_rear_vehicles_v1/records/
classes.pbtxt testing.record training.record
2.3 流程总结
整个训练SSD的流程可以用以下mermaid流程图表示:
graph LR
A[下载数据集] --> B[配置目录结构]
B --> C[配置参数]
C --> D[构建数据集]
D --> E[训练SSD模型]
2.4 关键步骤说明
- 下载数据集 :从指定链接下载车辆数据集,并解压到指定目录。
- 配置目录结构 :创建必要的目录,如records、experiments等,确保项目结构与之前的训练方法一致。
- 配置参数 :在dlib_front_rear_config.py中设置数据集路径、训练和测试文件路径、类别标签等参数。
- 构建数据集 :使用build_vehicle_records.py脚本,将XML文件解析为TensorFlow记录格式。
- 训练SSD模型 :使用TFOD API进行模型训练,可参考之前训练Faster R - CNN的方法。
通过以上步骤,我们可以从零开始训练一个SSD模型,用于识别车辆的前后视图。在实际应用中,需要根据具体情况调整超参数,以获得更好的检测效果。
3. 训练过程中的注意事项与问题解决
3.1 边界框坐标问题
在处理车辆数据集的XML文件时,会遇到边界框坐标的问题。由于dlib库要求物体在训练时具有相似的边界框宽高比,当车辆出现在图像边界时,为了保持宽高比,边界框可能会超出图像的实际尺寸。而TFOD API没有这样的要求,因此需要对边界框坐标进行处理,确保其在图像尺寸范围内。具体代码如下:
startX = max(0, float(box["left"]))
startY = max(0, float(box["top"]))
endX = min(w, float(box["width"]) + startX)
endY = min(h, float(box["height"]) + startY)
同时,由于标注过程中可能存在错误,边界框的最小坐标值可能大于最大坐标值,或者最大坐标值小于最小坐标值。这种情况下,需要忽略这些无效的边界框,代码如下:
if xMin > xMax or yMin > yMax:
continue
elif xMax < xMin or yMax < yMin:
continue
3.2 忽略区域问题
dlib的imglab工具允许将图像中可能“混淆”的区域标记为“忽略”,但TFOD API没有这个功能。这些“忽略”区域包括车辆密集难以区分边界的区域,以及距离过远难以清晰识别的车辆区域。由于TFOD API无法处理这些区域,可能会影响目标检测性能。目前没有直接的解决办法,但在训练时需要注意这些区域可能带来的影响。
3.3 数据集构建问题
在构建车辆数据集时,使用了BeautifulSoup库来解析XML文件。如果系统中没有安装该库,可以使用以下命令进行安装:
$ pip install beautifulsoup4
同时,在运行build_vehicle_records.py脚本时,需要确保已经执行了setup.sh脚本,配置好PYTHONPATH以访问TFOD API的导入和库。
4. 模型评估与优化
4.1 模型评估指标
在训练完SSD模型后,需要对模型进行评估。常用的评估指标包括准确率、召回率、平均精度(mAP)等。准确率衡量模型预测正确的样本占总样本的比例;召回率衡量模型正确预测出的正样本占实际正样本的比例;平均精度是每个类别的精度的平均值,综合考虑了模型的准确性和召回率。
4.2 模型优化方法
- 调整超参数 :可以调整SSD的超参数,如默认边界框数量、特征图数量等。增加默认边界框数量和特征图数量可能会提高检测准确率,但会降低推理速度。需要根据具体需求进行权衡。
- 更换基础网络 :将VGG基础网络替换为计算效率更高的MobileNet,可以提高模型的推理速度,但可能会降低准确率。可以根据实际应用场景选择合适的基础网络。
- 数据增强 :对训练数据进行增强,如随机裁剪、翻转、旋转等,可以增加数据的多样性,提高模型的泛化能力。
4.3 优化流程
以下是模型优化的mermaid流程图:
graph LR
A[训练模型] --> B[评估模型]
B --> C{是否满足要求}
C -- 是 --> D[使用模型]
C -- 否 --> E[调整超参数]
E --> F[更换基础网络]
F --> G[数据增强]
G --> A
5. 总结与展望
5.1 总结
SSD是一种端到端的目标检测算法,具有训练简单、能实现实时目标检测的优点,在多个目标检测数据集上与Faster R - CNN有相近的准确率。但SSD对小物体的检测效果通常不佳,需要根据具体情况选择合适的算法。通过使用TFOD API,我们可以从零开始训练一个SSD模型,用于识别车辆的前后视图。在训练过程中,需要注意边界框坐标问题、忽略区域问题和数据集构建问题,并对模型进行评估和优化。
5.2 展望
随着深度学习技术的不断发展,目标检测算法也在不断进步。未来可能会出现更高效、更准确的目标检测算法,能够更好地解决小物体检测等问题。同时,目标检测技术在自动驾驶、智能安防等领域的应用也将越来越广泛,为人们的生活带来更多的便利。
5.3 关键要点回顾
为了方便大家回顾,以下是本文的关键要点总结列表:
1.
SSD算法原理
:通过特征图离散化、边界框预测等实现目标检测,训练时考虑置信度损失和位置损失。
2.
训练方法
:使用MultiBox算法的损失函数,采用难负样本挖掘提高训练准确率,可选择不同的优化器。
3.
数据集处理
:下载车辆数据集,配置目录结构和参数,使用BeautifulSoup解析XML文件构建数据集。
4.
训练注意事项
:处理边界框坐标问题,注意TFOD API缺乏忽略区域功能的影响。
5.
模型评估与优化
:使用准确率、召回率、mAP等指标评估模型,通过调整超参数、更换基础网络、数据增强等方法优化模型。
超级会员免费看
967

被折叠的 条评论
为什么被折叠?



