26、从零开始:Faster R - CNN 训练与 Single Shot Detectors 解析

从零开始:Faster R - CNN 训练与 Single Shot Detectors 解析

1. Faster R - CNN 训练与应用

在训练 Faster R - CNN 时,可能会遇到一些问题,常见的有:
- 忘记导出 PYTHONPATH
- 配置文件中的文件路径存在拼写错误
- 生成记录文件时出现问题,例如无效的边界框

1.1 导出冻结模型图

当模型训练完成后,可使用 export_inference_graph.py 来创建一个能导入到自己脚本中的 TensorFlow 模型。具体操作步骤如下:
1. 切换到 models/research 目录:

$ cd ~/models/research
  1. 执行 export_inference_graph.py 脚本:
$ python object_detection/export_inference_graph.py \
--input_type image_tensor \
--pipeline_config_path ~ssds_and_rcnn/lisa/experiments/training/faster_rcnn_lisa.config \
--trained_checkpoint_prefix ~ssds_and_rcnn/lisa/experiments/training/model.ckpt-50000 \
--output ~/ssds_and_rcnn/lisa/experiments/exported_model

执行上述命令后,模型检查点在第 50000 步时的结果会被导出到 exported_model 目录。可以通过以下命令查看该目录下 TensorFlow 生成的文件:

$ ls lisa/experiments/exported_model/
checkpoint
frozen_inference_graph.pb
model.ckpt.data-00000-of-00001
model.ckpt.index
model.ckpt.meta
saved_model

务必保留 exported_model 目录及其内部的文件,后续会在 Python 脚本中导入这些文件。

1.2 在图像和视频上应用 Faster R - CNN

训练并评估了 Faster R - CNN 的准确性后,接下来要将其应用到输入图像上。打开 predict.py 并插入以下代码:

# import the necessary packages
from object_detection.utils import label_map_util
import tensorflow as tf
import numpy as np
import argparse
import imutils
import cv2

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", required=True,
                help="base path for frozen checkpoint detection graph")
ap.add_argument("-l", "--labels", required=True,
                help="labels file")
ap.add_argument("-i", "--image", required=True,
                help="path to input image")
ap.add_argument("-n", "--num-classes", type=int, required=True,
                help="# of class labels")
ap.add_argument("-c", "--min-confidence", type=float, default=0.5,
                help="minimum probability used to filter weak detections")
args = vars(ap.parse_args())

上述代码中,第 2 - 8 行导入了所需的 Python 库,其中 TensorFlow label_map_util 是一个辅助函数,可方便地从磁盘加载类标签文件。第 10 - 21 行解析命令行参数,共需要四个必需的命令行参数和一个可选参数:
| 参数 | 说明 |
| ---- | ---- |
| --model | 指定冻结检查点检测图的路径(即 TensorFlow 模型本身) |
| --labels | 提供 classes.pbtxt 文件的路径,因为标签未内置在 TensorFlow 模型中 |
| --image | 控制要应用目标检测的输入图像的路径 |
| --num-classes | 手动指定类别的数量,因为 TensorFlow 无法从 --labels 文件中自动确定 |
| --min-confidence (可选) | 用于过滤弱检测的最小概率,默认值为 0.5 |

解析完命令行参数后,就可以从磁盘加载模型:

# initialize a set of colors for our class labels
COLORS = np.random.uniform(0, 255, size=(args["num_classes"], 3))

# initialize the model
model = tf.Graph()

# create a context manager that makes this model the default one for
# execution
with model.as_default():
    # initialize the graph definition
    graphDef = tf.GraphDef()

    # load the graph from disk
    with tf.gfile.GFile(args["model"], "rb") as f:
        serializedGraph = f.read()
        graphDef.ParseFromString(serializedGraph)
        tf.import_graph_def(graphDef, name="")

第 24 行随机初始化了一组用于每个边界框的 RGB 颜色,第 27 行初始化了要从磁盘加载的模型,第 31 - 39 行使用 TensorFlow 的辅助工具加载序列化的 TensorFlow 网络。

接着,从磁盘加载类标签:

# load the class labels from disk
labelMap = label_map_util.load_labelmap(args["labels"])
categories = label_map_util.convert_label_map_to_categories(
    labelMap, max_num_classes=args["num_classes"],
    use_display_name=True)
categoryIdx = label_map_util.create_category_index(categories)

第 42 行从磁盘加载原始的 pbtxt 文件,然后使用 convert_label_map_to_categories 函数和 --numclasses 参数构建类别集合,第 46 行创建了从类标签的整数 ID 到人类可读类标签的映射。

为了预测输入图像的边界框,需要创建一个 TensorFlow 会话,并获取网络中图像、边界框、概率和类别张量的引用:

# create a session to perform inference
with model.as_default():
    with tf.Session(graph=model) as sess:
        # grab a reference to the input image tensor and the boxes
        # tensor
        imageTensor = model.get_tensor_by_name("image_tensor:0")
        boxesTensor = model.get_tensor_by_name("detection_boxes:0")

        # for each bounding box we would like to know the score
        # (i.e., probability) and class label
        scoresTensor = model.get_tensor_by_name("detection_scores:0")
        classesTensor = model.get_tensor_by_name("detection_classes:0")
        numDetections = model.get_tensor_by_name("num_detections:0")

这些引用能让我们在图像通过网络后访问其关联的值。

随后,从磁盘加载图像并为检测做准备:

# load the image from disk
image = cv2.imread(args["image"])
(H, W) = image.shape[:2]

# check to see if we should resize along the width
if W > H and W > 1000:
    image = imutils.resize(image, width=1000)
# otherwise, check to see if we should resize along the
# height
elif H > W and H > 1000:
    image = imutils.resize(image, height=1000)

# prepare the image for detection
(H, W) = image.shape[:2]
output = image.copy()
image = cv2.cvtColor(image.copy(), cv2.COLOR_BGR2RGB)
image = np.expand_dims(image, axis=0)

这段代码先从磁盘加载图像,获取其尺寸并根据情况进行缩放,最后为检测做准备。

获取边界框、概率和类标签只需调用会话的 .run 方法:

# perform inference and compute the bounding boxes,
# probabilities, and class labels
(boxes, scores, labels, N) = sess.run(
    [boxesTensor, scoresTensor, classesTensor, numDetections],
    feed_dict={imageTensor: image})

# squeeze the lists into a single dimension
boxes = np.squeeze(boxes)
scores = np.squeeze(scores)
labels = np.squeeze(labels)

这里将边界框、分数(即概率)、类标签和检测数量的张量列表传递给 sess.run 方法, feed_dict 指示 TensorFlow 将 imageTensor 设置为我们的图像并进行前向传播,得到边界框、分数和类标签。由于 boxes scores labels 都是多维数组,使用 np.squeeze 方法将它们压缩为一维数组,以便于循环遍历。

最后,循环遍历检测结果,在输出图像上绘制它们并显示结果:

# loop over the bounding box predictions
for (box, score, label) in zip(boxes, scores, labels):
    # if the predicted probability is less than the minimum
    # confidence, ignore it
    if score < args["min_confidence"]:
        continue

    # scale the bounding box from the range [0, 1] to [W, H]
    (startY, startX, endY, endX) = box
    startX = int(startX * W)
    startY = int(startY * H)
    endX = int(endX * W)
    endY = int(endY * H)

    # draw the prediction on the output image
    label = categoryIdx[label]
    idx = int(label["id"]) - 1
    label = "{}: {:.2f}".format(label["name"], score)
    cv2.rectangle(output, (startX, startY), (endX, endY),
                  COLORS[idx], 2)
    y = startY - 10 if startY - 10 > 10 else startY + 10
    cv2.putText(output, label, (startX, y),
                cv2.FONT_HERSHEY_SIMPLEX, 0.3, COLORS[idx], 1)

# show the output image
cv2.imshow("Output", output)
cv2.waitKey(0)

要执行 predict.py 脚本,在终端中执行以下命令,并确保提供有效的输入图像路径:

$ python predict.py \
--model lisa/experiments/exported_model/frozen_inference_graph.pb \
--labels lisa/records/classes.pbtxt \
--image path/to/input/image.png \
--num-classes 3

以下是 Faster R - CNN 应用于图像的流程图:

graph LR
    A[开始] --> B[导出冻结模型图]
    B --> C[解析命令行参数]
    C --> D[加载模型和类标签]
    D --> E[加载图像并准备检测]
    E --> F[执行推理]
    F --> G[绘制检测结果并显示]
    G --> H[结束]
2. 理解 Single Shot Detectors (SSDs)

在之前使用 Faster R - CNN 框架检测图像中的道路标志时,发现了一些问题,主要包括:
- 框架复杂,包含多个活动部件。
- 推理速度约为 7 - 10 FPS,对于基于深度学习的目标检测器来说还算合理,但无法实现真正的实时性能。

为了解决这些问题,接下来将介绍 Single Shot Detector (SSD) 框架。SSD 目标检测器是完全端到端的,没有复杂的活动部件,并且能够实现超实时性能。

2.1 动机

Girchick 等人在 R - CNN 系列论文中的工作使基于深度学习的目标检测成为现实,但 R - CNN 也存在一些问题:
- 训练阶段多 :在训练实际的分类器识别图像中的对象之前,需要先训练区域建议网络(RPN)来生成建议的边界框。虽然后来通过端到端训练整个 R - CNN 架构缓解了这个问题,但在此之前,这引入了繁琐的预训练过程。
- 训练时间长 :(Faster) R - CNN 由多个组件组成,包括区域建议网络、ROI 池化模块和最终的分类器。这些组件虽然构成了一个框架,但它们会减慢整个训练过程。
- 推理时间慢 :无法实现基于深度学习的实时目标检测。

从 SSD 的名称可以看出它是如何解决这些问题的:
- Single Shot :意味着在推理时,定位和检测在网络的单次前向传播中完成,网络只需“看”一次图像就能得到最终预测结果。与 R - CNN 不同,SSD 不需要从原始图像中重新获取像素或从特征图中切片,而是继续向前传播特征图,并以新颖的方式连接特征图,从而可以检测不同大小和尺度的对象。SSD 速度的根本提升在于消除了边界框建议以及像素或特征的子采样。
- Multibox :指的是 Szegedy 等人用于边界框回归的原始 Multibox 算法。该算法使 SSD 能够定位不同类别的对象,即使它们的边界框重叠。在许多目标检测算法中,不同类别的重叠对象通常会被抑制为一个具有最高置信度的边界框。
- Detector :表示不仅要定位图像中一组对象的 (x, y) 坐标,还要返回它们的类标签。

2.2 架构

和 Faster R - CNN 一样,SSD 也从一个基础网络开始。这个网络通常是在大型数据集(如 ImageNet)上预训练的,以便学习丰富的判别特征。同样会使用这个网络进行迁移学习,将输入图像传播到指定的层,获取特征图,然后进入目标检测层。

在 Liu 等人的工作中使用了 VGG16,因为在发表时,VGG 比其他流行的网络架构在迁移学习中能提供更好的准确性和结果。如今,为了获得更高的准确性,可以使用更深的 ResNet 或 DenseNet 架构;为了提高速度,可以使用 SqueezeNet 或 MobileNet。为了解释 SSD 框架,这里以 VGG 作为基础网络。

SSD 架构的工作流程如下:
1. 利用 VGG 层直到 conv_6,然后分离所有其他层,包括全连接层。
2. 在架构中添加一组新的 CONV 层,这些层使 SSD 框架成为可能。从图中可以看出,这些层也是 CONV 层,这意味着网络是全卷积的,可以接受任意大小的输入图像,不再受 VGG 的 224×224 输入要求的限制。

SSD 架构有两个重要组件:
- 像标准 CNN 一样,在更深的层中逐渐减小体积大小。
- 每个 CONV 层都连接到最终的检测层。

每个特征图连接到最终检测层这一点很重要,它使网络能够在不同尺度上检测和定位图像中的对象,并且这种尺度定位是在前向传播中完成的,不需要对特征图进行重采样,使得 SSD 能够以完全前馈的方式运行,这就是 SSD 如此快速和高效的原因。

以下是 SSD 架构的简单流程图:

graph LR
    A[输入图像] --> B[基础网络(VGG)]
    B --> C[获取特征图]
    C --> D[添加新的 CONV 层]
    D --> E[最终检测层]
    E --> F[输出检测结果]
2.3 MultiBox、Priors 和 Fixed Priors

SSD 框架使用了 Szegedy 等人的 MultiBox 算法的改进版本来进行边界框建议。该算法受到 Szegedy 之前在 Inception 网络工作的启发,使用了一系列 1×1 内核来帮助减少维度(在体积宽度和高度方面),以及一系列 3×3 内核进行更丰富的特征学习。

MultiBox 算法从 priors 开始,类似于但不完全等同于 Faster R - CNN 框架中的锚点。priors 是固定大小的边界框,其尺寸是根据数据集中每个类别的真实边界框的尺寸和位置预先计算的。之所以称为“prior”,是因为依赖于贝叶斯统计推断,更具体地说是对象位置在图像中出现的先验概率分布。选择的 priors 与真实对象的交并比(IoU)大于 50%。事实证明,这种计算 priors 的方法比从输入图像中随机选择坐标更好,但问题是现在需要预训练 MultiBox 预测器,这破坏了端到端训练完整的基于深度学习的目标检测器的目标。

幸运的是,有一个解决方案:fixed priors,它涉及到与 Faster R - CNN 类似的锚点选择技术。

为了直观理解 fixed priors 的概念,假设有一个原始输入图像和真实边界框。目标是生成将输入图像离散化为单元格的特征图,这个过程会在图像通过 CNN 时自然发生,因为输出体积的空间维度会逐渐变小。

特征图中的每个单元格,类似于锚点,都有一组小的默认边界框(通常是四个),它们具有不同的宽高比。通过将输入图像离散化为不同大小的特征图,能够检测图像中不同尺度的对象。

总结

通过上述内容,我们学习了如何使用 TensorFlow 目标检测 API 训练 Faster R - CNN + ResNet - 101 架构来检测图像中的交通标志。经过 50000 步的训练,最终获得了高达 98% 的 mAP@0.5 IoU。通常,网络训练的最少步数为 20000 步,根据数据集的不同,使用 50000 - 200000 步可以获得更高的准确性。

在使用 TFOD API 训练 Faster R - CNN 后,创建了一个 Python 脚本将网络应用于自己的输入图像,这个脚本也可以用于其他项目。

同时,为了解决 Faster R - CNN 存在的框架复杂和推理速度慢的问题,介绍了 SSD 框架。SSD 框架具有端到端、无复杂活动部件和超实时性能的优点,是一种优秀的目标检测解决方案。

以下是 Faster R - CNN 和 SSD 的对比表格:
| 特性 | Faster R - CNN | SSD |
| ---- | ---- | ---- |
| 框架复杂度 | 高,包含多个组件 | 低,端到端架构 |
| 训练时间 | 长,多个组件影响 | 相对较短 |
| 推理速度 | 约 7 - 10 FPS | 可达到 59 + FPS |
| 实时性能 | 难以实现 | 可以实现超实时 |

Delphi 12.3 作为一款面向 Windows 平台的集成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件组件包的形式提供,开发者将其集成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能集成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的集体贡献,通过定期更新以适配新版开发环境 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑用户体验的优化,从而提升整体开发效率软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值