易 AI - 使用 TensorFlow Object Detection API 训练自定义目标检测模型

原文:https://makeoptim.com/deep-learning/yiai-object-detection

前言

本文将介绍目标检测(Object Detection)的概念,并通过案例讲解如何使用 TensorFlow Object Detection API 来训练自定义的目标检测器,包括:数据集采集和制作TensorFlow Object Detection API 安装以及模型的训练

案例效果如下图所示:

image

目标检测

image

如上图所示,图像分类解决的问题是图中的物体是什么,而目标检测能识别图片中有哪些物体以及物体的位置(坐标)

位置

目标检测的位置信息一般有两种格式:

  • 极坐标(xmin, ymin, xmax, ymax):
    • xmin,ymin:x,y 坐标的最小值
    • xmin,ymin:x,y 坐标的最大值
  • 中心点:(x_center, y_center, w, h)
    • x_center, y_center:目标检测框的中心点坐标
    • w,h:目标检测框的宽、高

注:图片左上角为原点(0,0)

发展史

image

传统方法(候选区域+手工特征提取+分类器)

HOG+SVM、DPM

Region Proposal+CNN(Two-stage)

R-CNN, SPP-NET, Fast R-CNN, Faster R-CNN

端到端(One-stage)

YOLO、SSD

TensorFlow Object Detection API

TensorFlow Object Detection API 是一个构建在 TensorFlow 之上的开源框架,可以轻松构建、训练和部署对象检测模型。另外,TensorFlow Object Detection API 还提供了 Model Zoo 方便我们选择切换预训练模型

安装依赖项

使用以下命令检查是否安装成功。

$ conda --version
conda 4.9.2
$ protoc --version
libprotoc 3.17.1

安装 API

TensorFlow Object Detection API 官方的安装步骤较为繁琐,笔者写了一个脚本直接一键安装。

执行 git clone https://github.com/CatchZeng/object-detection-api.git 下载仓库,然后到该仓库(下文简称 oda 仓库)目录下,执行以下命令,如果看到如下输出,表示安装成功。

$ conda create -n  od python=3.8.5 && conda activate od && make install
......
----------------------------------------------------------------------
Ran 24 tests in 21.869s

OK (skipped=1)

注:如果你不想用 conda,可以在自己的 python 环境上直接使用 make install 安装即可,比如在 colab 中使用。

注:由于 cudaDNNtoolkit 更新可能没有 TensorFlow 快。因此,如果你的机器有 GPU,安装完成后,需要将 TensorFlow 降回到 cudaDNNtoolkit 支持的版本这样才能支持 GPU 训练,以 2.4.1 为例:

$ pip install --upgrade tf-models-official==2.4.0
$ pip install --upgrade tensorflow==2.4.1

注:如果安装失败,可以参考官方文档的详细步骤安装。

工程创建

注:!!! 从这里开始,请确保在 conda od 的环境下执行。

$ conda activate od
$ conda env list
# conda environments:
#
od                    *  /Users/catchzeng/.conda/envs/od
tensorflow               /Users/catchzeng/.conda/envs/tensorflow
base                     /Users/catchzeng/miniconda3

oda 仓库目录下,执行以下命令,创建工程目录结构。

注:SAVE_DIR 为保存项目的目录,NAME 为项目的名称。

$ make workspace-box SAVE_DIR=workspace NAME=test
└── workspace
    └── test
        ├── Makefile
        ├── annotations:存放标注好的数据集数据(val.record、train.record、label_map.pbtxt)
        ├── convert_quant_lite.py:量化 tflite 模型脚本
        ├── export_tflite_graph_tf2.py:导出 tflite 模型脚本
        ├── exported-models:存放训练完之后导出的模型
        ├── exporter_main_v2.py:导出模型脚本
        ├── images:数据集图片和 xml 标注
        │   ├── test:手动验证图片
        │   ├── train:训练集图片和 xml 标注
        │   └── val:验证集图片和 xml 标注
        ├── model_main_tf2.py:训练模型脚本
        ├── models:自定义模型
        ├── pre-trained-models:TensorFlow Model Zoo 提供的预训练模型
        └── test_images.py:手动验证图片脚本

数据集

图片

笔者喜欢喝茶,这次就用茶杯(cup)、茶壶(teapot)、加湿器(humidifier) 来做案例吧。

image

将收集的图片,放入工程目录的 images 的三个子目录下。

image

注:本案例只是为了验证如何训练目标识别模型,因此数据集采集得比较少,实际项目中记得尽量采集多点数据集

标注

收集完图片后,需要对训练验证集图片进行标注。标注工具,选用较为常用的 LabelImg

根据 installation 的说明安装好 LabelImg,然后执行 labelImg 选择 trainval 文件夹进行标注。

image

标注完成后,会生成图片对应的 xml 标注文件,如下所示:

workspace/test/images
├── test
│   ├── 15.jpg
│   └── 16.jpg
├── train
│   ├── 1.jpg
│   ├── 1.xml
│   ├── 10.jpg
│   ├── 10.xml
│   ├── 2.jpg
│   ├── 2.xml
│   ├── 3.jpg
│   ├── 3.xml
│   ├── 4.jpg
│   ├── 4.xml
│   ├── 5.jpg
│   ├── 5.xml
│   ├── 6.jpg
│   ├── 6.xml
│   ├── 7.jpg
│   ├── 7.xml
│   ├── 8.jpg
│   ├── 8.xml
│   ├── 9.jpg
│   └── 9.xml
└── val
    ├── 11.jpg
    ├── 11.xml
    ├── 12.jpg
    ├── 12.xml
    ├── 13.jpg
    ├── 13.xml
    ├── 14.jpg
    └── 14.xml

创建 TFRecord

TensorFlow Object Detection API 只支持 TFRecord 格式的数据集,因此,需要把标注好的数据集进行转换。

cd 到工程目录(cd workspace/test),然后执行 make gen-tfrecord,将在 annotations 文件夹下生成 label_map.pbtxtTFRecord 格式的数据集。

$ make gen-tfrecord
python gen_label_map.py
unsorted:  ['cup', 'teapot', 'humidifier']
sorted:  ['cup', 'humidifier', 'teapot']
item {
  id: 1
  name: 'cup'
}

item {
  id: 2
  name: 'humidifier'
}

item {
  id: 3
  name: 'teapot'
}

python generate_tfrecord.py \
        -x images/train \
        -l annotations/label_map.pbtxt \
        -o annotations/train.record
Successfully created the TFRecord file: annotations/train.record
python generate_tfrecord.py \
        -x images/val \
        -l annotations/label_map.pbtxt \
        -o annotations/val.record
Successfully created the TFRecord file: annotations/val.record
annotations
├── label_map.pbtxt
├── train.record
└── val.record
# label_map.pbtxt
item {
  id: 1
  name: 'cup'
}

item {
  id: 2
  name: 'humidifier'
}

item {
  id: 3
  name: 'teapot'
}

模型训练

注:!!! 从这里开始,请确保已经 cd 到工程目录(cd workspace/test)。

下载预训练模型

Model Zoo 中选择合适的模型下载解压并放到 workspace/test/pre-trained-models 中。

如果你选择的是 SSD MobileNet V2 FPNLite 320x320 可以执行如下命令,自动下载并解压

$ make dl-model

目录结构如下:

└── test
    └── pre-trained-models
        └── ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8
            ├── checkpoint
            ├── pipeline.config
            └── saved_model

配置训练 Pipeline

models 目录创建对应的模型文件夹,比如:ssd_mobilenet_v2_fpnlite_320x320,并拷贝 pre-trained-models/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8/pipeline.config

└── test
    ├── models
    │   └── ssd_mobilenet_v2_fpnlite_320x320
    │       └── pipeline.config
    └── pre-trained-models

其中,pipeline.config 如下几处需要根据项目修改

model {
  ssd {
    num_classes: 3 # 修改为需要识别的目标个数,示例项目为 3 种
    ......
}
train_config {
  batch_size: 8 # 这里需要根据自己的配置,调整大小,这里设置为 8
  ......
  optimizer {
    momentum_optimizer {
      learning_rate {
        cosine_decay_learning_rate {
          learning_rate_base: 0.07999999821186066
          total_steps: 10000 # 修改为想要训练的总步数
          warmup_learning_rate: 0.026666000485420227
          warmup_steps: 1000
        }
      }
      momentum_optimizer_value: 0.8999999761581421
    }
    use_moving_average: false
  }
  fine_tune_checkpoint: "pre-trained-models/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8/checkpoint/ckpt-0" # 修改为预制模型的路径
  num_steps: 10000 # 修改为想要训练的总步数
  startup_delay_steps: 0.0
  replicas_to_aggregate: 8
  max_number_of_boxes: 100
  unpad_groundtruth_tensors: false
  fine_tune_checkpoint_type: "detection" # 这里需要修改为 detection,因为我们是做目标检测
  fine_tune_checkpoint_version: V2
}
train_input_reader {
  label_map_path: "annotations/label_map.pbtxt" # 修改为标注的路径
  tf_record_input_reader {
    input_path: "annotations/train.record" # 修改为训练集的路径
  }
}
eval_config {
  metrics_set: "coco_detection_metrics"
  use_moving_averages: false
}
eval_input_reader {
  label_map_path: "annotations/label_map.pbtxt" # 修改为标注的路径
  shuffle: false
  num_epochs: 1
  tf_record_input_reader {
    input_path: "annotations/val.record" # 修改为验证集的路径
  }
}

训练模型

$ make train

注:如遇以下问题

ValueError: numpy.ndarray size changed, may indicate binary incompatibility. Expected 88 from C header, got 80 from PyObject

执行以下命令,重新安装下 numpy 即可。

pip uninstall numpy
pip install numpy

模型导出与转换

  • 普通模型

    $ make export
    
  • TFLite 模型

    $ make export-lite
    
  • 转换 TFLite 模型

    $ make convert-lite
    
  • 转换量化版 TFLite 模型

    $ make convert-quant-lite
    

注:以上命令,大家可以加 -640 表示使用 SSD MobileNet V2 FPNLite 640x640 的模型,比如:make train -> make train-640

测试模型

执行 make export 导出模型后,将测试图片放到 images/test 文件夹下,然后执行 python test_images.py 即可输出标记好目标的图片到 images/test_annotated

image

小结

本文通过案例将目标检测的整个流程都过了一遍,希望能帮助大家快速掌握训练自定义目标检测器的能力。

案例的代码和数据集都已经放在了 https://github.com/CatchZeng/object-detection-api,有需要的同学可以自行获取。

后面的文章将会为大家带来,目标检测的原理常用的目标检测网络,以及目标分割。本篇就到这了,咱们下一篇见。

参考链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339