我的PaddlePaddle学习之路笔记九——使用SSD进行目标检测¶
在计算机视觉领域,目标检测是一项基础且关键的任务,它能够定位并识别图像中的多个目标。本文将详细介绍如何使用PaddlePaddle实现SSD(Single Shot Multibox Detector)目标检测模型,包括数据准备、模型构建、训练、评估和预测。
目录¶
-
数据准备
- VOC数据集介绍
- 数据下载与预处理
- 生成图像列表 -
数据预处理
- 图像尺寸调整
- 标注信息解析
- 数据增强(如镜像、裁剪) -
SSD神经网络
- SSD原理
- 网络结构设计(基于VGG16)
- 损失函数与后处理 -
训练模型
- 训练流程
- 优化器设置
- 训练日志与模型保存 -
评估模型
- 测试集验证
- mAP指标计算 -
预测与可视化
- 单张图像预测
- 结果可视化
- 保存预测结果
1. 数据准备¶
VOC数据集介绍¶
本文使用PASCAL VOC 2007+2012数据集,包含20个目标类别(如人、动物、交通工具等)。数据集结构如下:
VOCdevkit/
├── VOC2007/
│ ├── Annotations/ # XML标注文件
│ ├── JPEGImages/ # 图像文件
│ └── ImageSets/ # 训练/测试图像列表
└── VOC2012/ # 类似结构
数据下载与预处理¶
- 下载数据集:
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar
wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
- 解压并合并数据集:
tar -xvf VOCtrainval_06-Nov-2007.tar
tar -xvf VOCtest_06-Nov-2007.tar
tar -xvf VOCtrainval_11-May-2012.tar
生成图像列表¶
编写prepare_voc_data.py生成训练/测试图像列表:
def prepare_filelist(devkit_dir, years, output_dir):
trainval_list = []
test_list = []
for year in years:
trainval, test = walk_dir(devkit_dir, year)
trainval_list.extend(trainval)
test_list.extend(test)
random.shuffle(trainval_list)
# 保存训练/测试列表
with open(os.path.join(output_dir, 'trainval.txt'), 'w') as ftrainval:
for item in trainval_list:
ftrainval.write(item[0] + ' ' + item[1] + '\n')
with open(os.path.join(output_dir, 'test.txt'), 'w') as ftest:
for item in test_list:
ftest.write(item[0] + ' ' + item[1] + '\n')
# 调用示例
prepare_filelist('VOCdevkit', ['2007', '2012'], '.')
2. 数据预处理¶
图像尺寸调整与归一化¶
- 统一图像尺寸:将图像调整为
300x300,并减去ImageNet均值(104, 117, 124)。 - 数据增强:随机水平翻转、亮度调整等。
标注信息解析¶
解析XML标注文件,提取目标类别、边界框(xmin, ymin, xmax, ymax)和难度标签:
def parse_xml(label_path):
root = xml.etree.ElementTree.parse(label_path).getroot()
bbox_labels = []
for obj in root.findall('object'):
label = label_list.index(obj.find('name').text)
difficult = float(obj.find('difficult').text)
bbox = obj.find('bndbox')
xmin = float(bbox.find('xmin').text) / img_width
ymin = float(bbox.find('ymin').text) / img_height
xmax = float(bbox.find('xmax').text) / img_width
ymax = float(bbox.find('ymax').text) / img_height
bbox_labels.append([label, xmin, ymin, xmax, ymax, difficult])
return bbox_labels
3. SSD神经网络¶
SSD原理¶
SSD是一种单阶段目标检测算法,通过在多个特征图上生成不同尺度的候选框(Prior Box),直接预测边界框坐标和类别概率,实现端到端训练。其核心特点:
- 多尺度特征图:在不同分辨率的特征图上生成候选框(如conv4_3, conv7, conv8_2等)。
- 先验框(Prior Box):每个特征图单元格生成多个预设宽高比的候选框。
- 回归与分类:共享卷积层进行边界框回归和类别预测。
网络结构¶
基于VGG16构建SSD:
1. 基础网络:冻结VGG16前10层卷积,将全连接层(fc6, fc7)转换为卷积层。
2. 特征图生成:在conv4_3、conv7、conv8_2等层后添加卷积层生成候选框。
3. 先验框生成:根据特征图尺寸和预设宽高比生成候选框。
代码片段:SSD网络构建¶
def vgg_ssd_net(mode='train'):
# 基础VGG16特征提取
vgg = paddle.layer.img_conv(...) # 卷积层堆叠
# 添加SSD特有卷积层
conv7 = paddle.layer.img_conv(...) # 类似VGG16的fc6/fc7转换
# 多尺度候选框生成
mbox_loc = paddle.layer.img_conv(...) # 边界框回归
mbox_conf = paddle.layer.img_conv(...) # 类别分类
# 损失函数与后处理
if mode == 'train':
loss = paddle.layer.multibox_loss(...) # 多框损失
return loss, mbox_loc, mbox_conf
4. 训练模型¶
训练流程¶
- 优化器设置:使用Momentum优化器,学习率
0.001,动量0.9,L2正则化。 - 训练参数:批量大小
32,迭代轮次200,保存模型每10轮。 - 训练日志:记录训练损失和验证集mAP。
代码片段:训练主函数¶
def train():
# 初始化训练器
optimizer = paddle.optimizer.Momentum(
momentum=0.9, learning_rate=0.001,
regularization=paddle.optimizer.L2Regularization(0.0005)
)
# 构建网络并获取损失函数
cost, _, _ = vgg_ssd_net(mode='train')
parameters = paddle.parameters.create(cost)
# 加载预训练模型(可选)
if init_model_path:
parameters.init_from_tar(gzip.open(init_model_path))
# 创建训练器
trainer = paddle.trainer.SGD(cost=cost, parameters=parameters,
update_equation=optimizer)
# 训练主循环
trainer.train(
reader=train_reader,
event_handler=event_handler,
num_passes=200,
batch_size=32
)
5. 评估模型¶
测试集验证¶
使用PaddlePaddle的test接口在验证集上评估:
def evaluate():
# 加载训练好的模型
cost, _, _ = vgg_ssd_net(mode='eval')
parameters = paddle.parameters.Parameters.from_tar(gzip.open(model_path))
# 测试集读取
test_reader = paddle.batch(test_data, batch_size=32)
# 计算mAP
result = trainer.test(reader=test_reader, metrics=['detection_map'])
print("Test mAP: ", result.metrics['detection_map'])
6. 预测与可视化¶
单张图像预测¶
加载模型并对单张图像进行预测:
def predict(image_path):
# 读取图像
img = Image.open(image_path).resize((300, 300))
# 预处理
img = np.array(img, dtype='float32') - [104, 117, 124]
# 加载模型
inferer = paddle.inference.Inference(output_layer=det_out, parameters=params)
result = inferer.infer(input=[img])
# 解析结果(类别、边界框、得分)
return parse_detection_result(result)
结果可视化¶
使用OpenCV在图像上绘制边界框:
def visualize(img_path, result):
img = cv2.imread(img_path)
for box in result:
label, score, xmin, ymin, xmax, ymax = box
xmin, ymin, xmax, ymax = int(xmin), int(ymin), int(xmax), int(ymax)
cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)
cv2.putText(img, f"{label}:{score:.2f}", (xmin, ymin-5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
cv2.imwrite('result.jpg', img)
项目代码¶
- GitHub仓库:https://github.com/yeyupiaoling/LearnPaddle
- 核心文件:
data_preprocess.py:数据下载与预处理ssd_model.py:SSD网络定义train.py:模型训练infer.py:图像预测
参考资料¶
通过以上步骤,可实现一个基于PaddlePaddle的SSD目标检测系统,涵盖数据准备、模型构建、训练到预测的全流程。训练完成后,模型可高效检测图像中的多个目标,适用于实时检测场景。