【论文源码实战】EdgeYOLO: 边缘设备友好的无锚框检测器
前言
这篇文章介绍了一种名为EdgeYOLO的高效、低复杂度且无锚点(anchor-free)的实时目标检测器,它基于最先进的YOLO框架,并能在边缘计算平台上实时运行。主要贡献包括:
-
设计了一个无锚点的目标检测器,能在边缘设备上实时运行,并在MS COCO2017数据集上达到50.6%的AP精度。
-
提出了一种更强大的数据增强方法,确保了训练数据的数量和有效性。
-
使用可重新参数化的结构来减少推理时间。
-
设计了一种损失函数,提高了对小目标的检测精度。
文章还详细介绍了EdgeYOLO的关键技术,包括增强的数据增强方法、轻量级解耦头(Lite-Decoupled Head)、分阶段损失函数(Staged Loss Function)以及针对小目标检测的优化。
总的来说,这篇文章提出了一种适用于边缘计算设备的高效实时目标检测器EdgeYOLO,通过一系列创新技术提高了小目标的检测精度,并在保持实时性的同时减少了模型的复杂度。未来的工作将进一步提高小目标的检测精度,并探索更高效的优化方法。
一、项目下载
git clone https://github.com/LSH9832/edgeyolo.git cd edgeyolo
二、环境配置
创建专属环境
conda create -n EdgeYOLO python=3.9
激活环境
conda activate EdgeYOLO
安装 Pytorch 环境
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple "torch-1.13.0+cu116-cp39-cp39-win_amd64.whl"pip install -i https://pypi.tuna.tsinghua.edu.cn/simple "torchvision-0.14.0+cu116-cp39-cp39-win_amd64.whl"
安装相关库
pip install -r requirements.txt
三、代码测试
python detect.py --weights ./weights/edgeyolo_coco.pth --source ./TestVideo/test.avi --fp16
四、使用自己的数据集进行训练
目前支持COCO、YOLO、VOC、VisDrone、DOTA五种格式的数据集
在这里我使用VOC格式的数据集。
数据库来源:gazebo中标准锥桶路障,纯手工标记,VOC格式,适用于需要在gazebo仿真环境中检验自动驾驶算法。
数据集划分
按照比例进行划分训练集、验证集、测试集。
# -*- coding:utf-8 -*-
# @author: 牧锦程
# @微信公众号: AI算法与电子竞赛
# @Email: m21z50c71@163.com
# @VX:fylaicaiimport os
import random
from tqdm import tqdmtrain_val_percent = 0.9 # 训练集和验证集的总比例
train_percent = 0.8 # 训练集占总比例的比例
Annotations_file_path = 'JPEGImages'
txt_save_path = 'ImageSets/Main'
total_xml = os.listdir(Annotations_file_path)if not os.path.exists(txt_save_path):os.makedirs(txt_save_path, exist_ok=True)num = len(total_xml)
list = range(num)
tv = int(num * train_val_percent)
tr = int(tv * train_percent)
train_val = random.sample(list, tv)
train = random.sample(train_val, tr)f_train_val = open(f'./{txt_save_path}/trainval.txt', 'w')
f_test = open(f'./{txt_save_path}/test.txt', 'w')
f_train = open(f'./{txt_save_path}/train.txt', 'w')
f_val = open(f'./{txt_save_path}/val.txt', 'w')for i in tqdm(list, unit="image", ncols=80, desc='划分数据'):name = total_xml[i].split(".")[0] + '\n'if i in train_val:f_train_val.write(name)if i in train:f_train.write(name)else:f_val.write(name)else:f_test.write(name)f_train_val.close()
f_train.close()
f_val.close()
f_test.close()
修改训练数据配置文件
./params/dataset/voc.yaml 中的相应参数
-
dataset_path:数据集的路径
-
names:类别
type: "voc"dataset_path: "F:/PersonalWebsite/PaperSourceCodeInterpretation/edgeyolo-main/VOCData"kwargs:suffix: "jpg"use_cache: truetrain:image_dir: "JPEGImages"anno_dir: "Annotations"label: "ImageSets/Main/train.txt"val:image_dir: "JPEGImages"anno_dir: "Annotations"label: "ImageSets/Main/val.txt"test:test_dir: "test"segmentaion_enabled: falsenames: ['barricade']
修改训练模型配置文件
./params/train/train_voc.yaml 中的相应参数
-
model_cfg:模型的配置文件
-
weights:预训练模型文件
-
use_cfg:强制使用model_cfg而不是weights来构建模型
-
output_dir:模型输出内容文件夹
-
save_checkpoint_for_each_epoch:是否保存每一轮的模型
-
log_file:日志文件
-
dataset_cfg:数据集文件
-
batch_size_per_gpu:batch_size
-
device:GPU设备
-
fp16:在训练时是否使用fp16
-
input_size:训练的输入尺寸
# models & weights------------------------------------------------------------------------------------------------------
model_cfg: "params/model/edgeyolo_tiny.yaml" # model structure config file
weights: "output/train/edgeyolo_tiny_voc/best.pth" # contains model_cfg, set null or a no-exist filename if not use it
use_cfg: false # force using model_cfg instead of cfg in weights to build model# output----------------------------------------------------------------------------------------------------------------
output_dir: "output/train/edgeyolo_tiny_voc" # all train output file will save in this dir
save_checkpoint_for_each_epoch: False # save models for each epoch (epoch_xxx.pth, not only best/last.pth)
log_file: "log.txt" # log file (in output_dir)# dataset & dataloader--------------------------------------------------------------------------------------------------
dataset_cfg: "params/dataset/voc.yaml" # dataset config
batch_size_per_gpu: 2 # batch size for each GPU
loader_num_workers: 4 # number data loader workers for each GPU
num_threads: 1 # pytorch threads number for each GPU# device & data type----------------------------------------------------------------------------------------------------
device: [0] # training device list
fp16: false # train with fp16 precision
cudnn_benchmark: false # it's useful when multiscale_range is set zero# train hyper-params----------------------------------------------------------------------------------------------------
optimizer: "SGD" # or Adam
max_epoch: 300 # or 400
close_mosaic_epochs: 15 # close data augmentation at last several epochs# learning rate---------------------------------------------------------------------------------------------------------
lr_per_img: 0.00015625 # total_lr = lr_per_img * batch_size_per_gpu * len(devices)
warmup_epochs: 5 # warm-up epochs at the beginning of training
warmup_lr_ratio: 0.0 # warm-up learning rate start from value warmup_lr_ratio * total_lr
final_lr_ratio: 0.05 # final_lr_per_img = final_lr_ratio * lr_per_img# training & dataset augmentation---------------------------------------------------------------------------------------
# [cls_loss, conf_loss, iou_loss]
loss_use: ["bce", "bce", "giou"] # bce: BCE loss. bcf: Balanced Focal loss. hyb: HR loss, iou, c/g/s iou is available
input_size: [640, 640] # image input size for model
multiscale_range: 5 # real_input_size = input_size + randint(-multiscale_range, multiscale_range) * 32
weight_decay: 0.0005 # optimizer weight decay
momentum: 0.9 # optimizer momentum
enhance_mosaic: false # use enhanced mosaic method
use_ema: true # use EMA method
enable_mixup: true # use mixup
mixup_scale: [0.5, 1.5] # mixup image scale
mosaic_scale: [0.1, 2.0] # mosaic image scale
flip_prob: 0.5 # flip image probability
mosaic_prob: 1 # mosaic probability
mixup_prob: 1 # mixup probability
degrees: 10 # maximum rotate degrees
hsv_gain: [0.0138, 0.664, 0.464] # hsv gain ratio# evaluate--------------------------------------------------------------------------------------------------------------
eval_at_start: false # evaluate loaded model before training
val_conf_thres: 0.001 # confidence threshold when doing evaluation
val_nms_thres: 0.65 # NMS IOU threshold when doing evaluation
eval_only: false # do not train, run evaluation program only for all weights in output_dir
obj_conf_enabled: true # use object confidence when doing inference
eval_interval: 1 # evaluate interval epochs# show------------------------------------------------------------------------------------------------------------------
print_interval: 100 # print result after every $print_interval iterations# others----------------------------------------------------------------------------------------------------------------
load_optimizer_params: true # load optimizer params when resume train, set false if there is an error.
train_backbone: true # set false if you only want to train yolo head
train_start_layers: 51 # if not train_backbone, train from this layer, see params/models/edgeyolo.yaml
force_start_epoch: -1 # set -1 to disable this option
模型训练
cfg:模型训练的配置文件
修改后直接运行 train.py 就行。
from edgeyolo import launch as train
import argparsedef make_parser():parser = argparse.ArgumentParser("EdgeYOLO train parser")parser.add_argument("-c", "--cfg", type=str, default="params/train/train_voc.yaml")# Not commendparser.add_argument("--default", action="store_true", help="use default train settings in edgeyolo/train/default.yaml")return parser.parse_args()if __name__ == '__main__':args = make_parser()train("DEFAULT" if args.default else args.cfg)
绘制训练过程图
-
all:绘制所有曲线或 (--lr:学习率, --ap:准确度, --loss:损失)
-
file:训练的输出文件夹路径
-
save:是否保存图片
-
format:保存图片的格式,支持多种格式同时保存【pdf png svg jpg eps】
-
no-show:不使用plt.show()显示曲线
parser = argparse.ArgumentParser()parser.add_argument("-ap", "--ap", action="store_true")
parser.add_argument("-loss", "--loss", action="store_true")
parser.add_argument("-lr", "--lr", action="store_true", default=True)
parser.add_argument("-a", "--all", action="store_true")parser.add_argument("-f", "--file", type=str, default='./output/train/edgeyolo_tiny_voc/log.txt')parser.add_argument("-s", "--save", action="store_true", default=True)
parser.add_argument("--format", type=str, default=DEFAULT_SUFFIX, nargs="+")
parser.add_argument("--no-show", action="store_true", default=True)
修改上述参数后,直接就运行 plot.py 函数就行。
根据loss图可以看出模型在50轮左右就已经被训练得很好了。
在这个绘图代码中,我遇到了一个问题
修改成下面的代码,就不会有误。
while epochs[idx-1] >= now_value:idx -= 1if abs(idx) == len(epochs):break
模型验证
修改后直接运行 evaluate.py 就行。
-
weights:权重文件, 也可是tensorrt模型
-
input-size:输入尺寸(高、宽)
-
dataset:数据集配置文件路径
-
device:gpu显卡
-
save:保存验证的模型文件
-
fp16:保存时是否使用fp16精度
-
trt:如果使用的是TensorRT模型需要加上此选项
parser = argparse.ArgumentParser("EdgeYOLO evaluate parser")
parser.add_argument("-w", "--weights", type=str, default="./output/train/edgeyolo_tiny_voc/best.pth", help="weights")
parser.add_argument("-b", "--batch", type=int, default=2, help="batch size for each device")
parser.add_argument("-i", "--input-size", type=int, nargs="+", default=[640, 640], help="image input size")parser.add_argument("--dataset", type=str, default="params/dataset/voc.yaml", help="dataset config")
parser.add_argument("--device", type=int, nargs="+", default=[0], help="eval device")parser.add_argument("--no-obj-conf", action="store_true", help="for debug only, do not use it")
parser.add_argument("--save", action="store_true", default=True, help="save deploy model without optimizer params")
parser.add_argument('--fp16', action='store_true', help='half precision')
parser.add_argument('--trt', action='store_true', help='is tensorrt model')
现在你就可以使用 detect.py 函数进行推理了。
五、总结
EdgeYOLO是一个为边缘计算设备优化的实时目标检测器,它以较低的计算成本实现了高效率和高精度的目标检测,特别是在小目标识别方面表现出色。模型设计简洁,易于在边缘设备上部署,并且通过开源协作,支持社区进行进一步的改进和定制。此外,它还提供了不同规模的模型以适应不同的计算环境需求。
关于edgeyolo模型的相关代码、论文PDF、预训练模型、使用方法、训练数据集等,我都已打包好,供需要的小伙伴交流研究,获取方式如下:
关注公众号,回复:edgeyolo,即可获取edgeyolo相关代码、论文、预训练模型、使用方法示例等
【论文源码实战】EdgeYOLO: 边缘设备友好的无锚框检测器
硬性的标准其实限制不了无限可能的我们,所以啊!少年们加油吧!