我想在 N × N 图像的自定义数据集上训练 SSD 检测器。所以我深入研究了Tensorflow对象检测API,发现了一个基于MobileNet v2的在COCO上预训练的SSD300x300模型。
查看用于训练的配置文件时:anchor_generator 字段如下所示:(在论文后面)
anchor_generator {
ssd_anchor_generator {
num_layers: 6
min_scale: 0.2
max_scale: 0.9
aspect_ratios: 1.0
aspect_ratios: 2.0
aspect_ratios: 0.5
aspect_ratios: 3.0
aspect_ratios: 0.33
}
}
在查看SSD锚点生成器原型时,我的假设是否正确:
base_anchor_height=base_anchor_width=1
?
如果是,我假设通过阅读多个网格锚点生成器得到的结果锚点(如果图像是 300x300 正方形)是: 大小范围从
0.2 * 300=60 * 60
像素到 0.9 * 300=270 * 270
像素(具有不同的长宽比)?
因此,如果有人想通过固定场来训练 NxN 图像:
fixed_shape_resizer {
height: N
width: N
}
他将使用相同的配置文件锚点,范围从
(0.2 * N, 0.2 * N)
像素到
(0.9 * N, 0.9 * N)
像素(具有不同的宽高比)?
我做了很多假设,因为代码很难掌握,而且似乎还没有文档。我对么?有没有一种简单的方法可以在不训练模型的情况下可视化所使用的锚点?
以下是一些可用于生成和可视化锚框坐标而无需训练模型的函数。我们在这里所做的只是调用训练/推理期间图中使用的相关操作。
首先,我们需要知道构成给定大小的输入图像的对象检测层的特征图的分辨率(形状)是多少。
import tensorflow as tf
from object_detection.anchor_generators.multiple_grid_anchor_generator import create_ssd_anchors
from object_detection.models.ssd_mobilenet_v2_feature_extractor_test import SsdMobilenetV2FeatureExtractorTest
def get_feature_map_shapes(image_height, image_width):
"""
:param image_height: height in pixels
:param image_width: width in pixels
:returns: list of tuples containing feature map resolutions
"""
feature_extractor = SsdMobilenetV2FeatureExtractorTest()._create_feature_extractor(
depth_multiplier=1,
pad_to_multiple=1,
)
image_batch_tensor = tf.zeros([1, image_height, image_width, 1])
return [tuple(feature_map.get_shape().as_list()[1:3])
for feature_map in feature_extractor.extract_features(image_batch_tensor)]
这将返回特征图形状的列表,例如
[(19,19), (10,10), (5,5), (3,3), (2,2), (1,1)]
,您可以将其传递给第二个函数,该函数返回锚框的坐标。
def get_feature_map_anchor_boxes(feature_map_shape_list, **anchor_kwargs):
"""
:param feature_map_shape_list: list of tuples containing feature map resolutions
:returns: dict with feature map shape tuple as key and list of [ymin, xmin, ymax, xmax] box co-ordinates
"""
anchor_generator = create_ssd_anchors(**anchor_kwargs)
anchor_box_lists = anchor_generator.generate(feature_map_shape_list)
feature_map_boxes = {}
with tf.Session() as sess:
for shape, box_list in zip(feature_map_shape_list, anchor_box_lists):
feature_map_boxes[shape] = sess.run(box_list.data['boxes'])
return feature_map_boxes
在您的示例中,您可以这样称呼它:
boxes = get_feature_map_boxes(
min_scale=0.2,
max_scale=0.9,
feature_map_shape_list=get_feature_map_shapes(300, 300)
)
您无需指定宽高比,因为您的配置中的宽高比与
create_ssd_anchors
的默认值相同。
最后,我们将锚框绘制在反映给定层分辨率的网格上。请注意,模型中的锚框和预测框的坐标在 0 和 1 之间标准化。
def draw_boxes(boxes, figsize, nrows, ncols, grid=(0,0)):
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize)
for ax, box in zip(axes.flat, boxes):
ymin, xmin, ymax, xmax = box
ax.add_patch(patches.Rectangle((xmin, ymin), xmax-xmin, ymax-ymin,
fill=False, edgecolor='red', lw=2))
# add gridlines to represent feature map cells
ax.set_xticks(np.linspace(0, 1, grid[0] + 1), minor=True)
ax.set_yticks(np.linspace(0, 1, grid[1] + 1), minor=True)
ax.grid(True, which='minor', axis='both')
fig.tight_layout()
return fig
如果我们以具有 3x3 特征图的第四层为例
draw_boxes(feature_map_boxes[(3,3)], figsize=(12,16), nrows=9, ncols=6, grid=(3,3))
在上图中,每一行代表 3x3 特征图中的不同单元格,而每一列代表特定的长宽比。
您最初的假设是正确的,例如,最高层中宽高比为 1.0 的锚框(具有最低分辨率的特征图)将具有等于输入图像大小的 0.9 的高度/宽度,而最低层中的锚框将具有高度/宽度等于输入图像尺寸的 0.2。中间层的锚尺寸在这些限制之间线性插值。
但是,关于 TensorFlow 锚点生成,有一些微妙之处值得注意:
interpolated_scale_aspect_ratio
参数来修改(或删除),或者在你的配置中也同样。reduce_boxes_in_lowest_layer
布尔参数覆盖。base_anchor_height = base_anchor_width = 1
。但是,如果您的输入图像不是正方形并且在预处理过程中被重新整形,那么纵横比 1.0 的“正方形”锚点实际上不会针对锚定原始图像中正方形的对象进行优化(当然,它可以学习在训练期间预测这些形状)。完整要点可以在这里找到。