YOLOv5从入门到部署之:训练私有数据并修改模型

1,209次阅读
没有评论

共计 5576 个字符,预计需要花费 14 分钟才能阅读完成。

1 环境部署

YOLOv5 是基于 pytorch 实现的, 需要的运行环境已经在 requirements.txt 文件里面列出。

# base ----------------------------------------
Cythonmatplotlib>=3.2.2numpy>=1.18.5
opencv-python>=4.1.2pillowPyYAML>=5.3scipy>=1.4.
tensorboard>=2.2torch>=1.6.0torchvision>=0.7.0tqdm>=4.41.0

因此可以直接使用 pip 进行安装:

pip install -U -r requirements.txt
2 准备数据

YOLOv5 的数据集格式和之前的YOLOv3 一致。每张图片都有自己的目标的 txt 文件

例如:

datasets/score/images/train/00b5fefed.jpg     
# imagedatasets/score/lables/train/00b5fefed.txt       # lable

这里我们使用之前 kaggle 的小麦头检测的比赛为例,讲解数据准备的流程。

kaggle 的小麦头检测的数据集下载链接:
https://www.kaggle.com/c/global-wheat-detection

这里值得注意的是,Yolov5 的开源许可是 GPL-3.0 License 不满足 Kaggle 比赛的要求,因此不能在 Kaggle 比赛中使用,本文只是为了做个训练示范。

2.1、创建数据集的配置文件 dataset.yaml

训练 COCO 数据集或者是 VOC 数据集,可以直接使用已经配置好的 coco.yaml 和 voc.yaml 文件。
如果训练别的数据集,则需要模仿 coco.yaml 文件写一个自己的 wheat.yaml 文件 保存在 data 文件夹下。

# train and val datasets (image directory or *.txt file with image paths)  
train: ./datasets/wheat/images/train/  val: ./datasets/wheat/images/val/  
# number of classes  nc: 1  
# class names  names: ['wheat']

2.2、创建标注文件

如果是自己制作的数据集可以使用 LabelImg 工具, 需要注意的是选择 YOLO 格式生成标注文件。

YOLOv5 从入门到部署之:训练私有数据并修改模型

如果是已经有的数据集,像是 kaggle 的 wheat 数据集,则需要进行转换数据格式成 yolo 的格式。
我们先来看一下 yolo 的 lable 文件里面的格式 00b5fefed.txt

YOLOv5 从入门到部署之:训练私有数据并修改模型

数据排布是:<object-class> <x> <y> <width> <height>

其中,object-class 是类的索引,后面的 4 个值都是相对于整张图片的 比例 x 是 ROI 中心的 x 坐标,y 是 ROI 中心的 y 坐标,width 是 ROI 的宽,height 是 ROI 的高。

Wheat 数据集的 lable 是都在一个 train.csv 文件里面,并且 bbox 是绝对位置,这一点和 YOLO 不一样。

YOLOv5 从入门到部署之:训练私有数据并修改模型

因此,需要进行 Box 的转换:

x, y, w, h = bbox[0], bbox[1], bbox[2], bbox[3]  
x_center = (x+ w / 2)/width  
y_center = (y+ h / 2) /hight  
w = w/ width  
h = h / hight

转换好数据格式后需要保存到相应的文件夹格式

YOLOv5 从入门到部署之:训练私有数据并修改模型

至此数据准备阶段已经完成。

3 训练和检测

(1)训练 coco 数据集,命令如下:

python train.py --data  coco.yaml --cfg yolov5s.yaml --weights '' --batch-size 64                                
yolov5m.yaml  40                                        
yolov5l.yaml    24                                        
yolov5x.yaml  16

(2)训练自己的数据集:

如果想加快训练,可以使用官方在 coco 数据集上训练好的网络接着进行训练
官方的预训练模型链接:https://github.com/ultralytics/yolov5/releases/tag/v3.0

将下载好的模型 yolov5*.pt 保存在 weights 文件夹下。

python train.py –data wheat.yaml --cfg  yolov5s.yaml --weights yolov5s.pt --batch-size 16

训练完成是 yolov5 会保存最好的模型 best.pt 和最后一个模型 last.pt 在 weights 文件夹里。

(3)检测图片

用训练好的模型进行检测:

python detect.py --source inference/images/ --weights  best.pt

YOLOv5 从入门到部署之:训练私有数据并修改模型

4 YOLOv5-MobileNetv2
本文以 Yolov5 的 backbone 换成 mobilenetv2 为例,讲解如何替换其他网络结构。首先,需要 models/common.py里,实现 MobileNetv2 的 bottleneck 和 Pwconv。

1、Mobilenetv2 的 bottleneck: InvertedResidual

#mobilenet  Bottleneck  InvertedResidual  class BottleneckMOB(nn.Module):      
#c1:inp  
c2:oup s:stride  expand_ratio:t      
def __init__(self, c1, c2, s, expand_ratio):          
super(BottleneckMOB, self).__init__()          
self.s = s          hidden_dim = round(c1 * expand_ratio)          
self.use_res_connect = self.s == 1 and c1 == c2          
if expand_ratio == 1:              
self.conv = nn.Sequential(                  # dw                  
nn.Conv2d(hidden_dim, hidden_dim, 3, s, 1, groups=hidden_dim, bias=False),                  
nn.BatchNorm2d(hidden_dim),                  
nn.ReLU6(inplace=True),                  
# pw-linear                  
nn.Conv2d(hidden_dim, c2, 1, 1, 0, bias=False),                  
nn.BatchNorm2d(c2),              )          
else:              
self.conv = nn.Sequential(                  # pw                  
nn.Conv2d(c1, hidden_dim, 1, 1, 0, bias=False),                  
nn.BatchNorm2d(hidden_dim),                  
nn.ReLU6(inplace=True),                  # dw                  
nn.Conv2d(hidden_dim, hidden_dim, 3, s, 1, groups=hidden_dim, bias=False), 
nn.BatchNorm2d(hidden_dim), nn.ReLU6(inplace=True), # pw-linear                  
nn.Conv2d(hidden_dim, c2, 1, 1, 0, bias=False),                  
nn.BatchNorm2d(c2),              )  
    def forward(self, x):          
if self.use_res_connect:              
return x + self.conv(x)          
else:              
return self.conv(x)

2、Pointwise Convolution

class PW_Conv(nn.Module):      
def __init__(self, c1, c2):  # ch_in, ch_out          
super(PW_Conv, self).__init__()          
self.conv = nn.Conv2d(c1, c2, 1, 1, 0, bias=False)          
self.bn = nn.BatchNorm2d(c2)         
self.act = nn.ReLU6(inplace=True)  
    def forward(self, x):          
return self.act(self.bn(self.conv(x)))

接着需要在 yolov5 的读取模型配置文件的代码(models/yolo.py 的 parse_model 函数)进行修改,使得能够调用到上面的模块,只需修改下面这部分代码。

n = max(round(n * gd), 1) if n > 1 else n  # depth gain  
if m in [nn.Conv2d, Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, 
CrossConv, BottleneckCSP, C3, PW_Conv, BottleneckMOB]:      
c1, c2 = ch[f], args[0]

并且需要在 import 引用处加入 PW_Conv,BottleneckMOB 这两个模块。

from models.common import Conv, Bottleneck,SPP, DWConv, Focus, 
BottleneckCSP, Concat, NMS, autoShape, PW_Conv,BottleneckMOB

然后就是搭建我们的模型配置文件,我在 yolov5s.yaml 的基础上进行修改,将 yolov5s 的 backbone 替换成 mobilenetv2,重新建立了一个模型配置文件 yolov5-mobilenet.yaml。

# parameters  nc: 1  # number of classes  depth_multiple: 0.33 
 # model depth multiple  width_multiple: 0.50  # layer channel multiple  
# anchors  anchors:    - [116,90, 156,198, 373,326]  # P5/32    
- [30,61, 62,45, 59,119]  # P4/16    - [10,13, 16,30, 33,23]  # P3/8  
# YOLOv5 backbone: mobilenet v2  backbone:    # [from, number, module, args]    
[[-1, 1, nn.Conv2d, [32, 3, 2]],  # 0-P1/2   oup, k, s     640     
[-1, 1, BottleneckMOB, [16, 1, 1]],  # 1-P2/4   oup, s, t 320     
[-1, 2, BottleneckMOB, [24, 2, 6]],  #                    320     
[-1, 1, PW_Conv, [256]],  #4  output p3                   160     
[-1, 3, BottleneckMOB, [32, 2, 6]],  # 3-P3/8             160     
[-1, 4, BottleneckMOB, [64, 1, 6]],  # 5                  80     
[-1, 1, PW_Conv, [512]],  #7 output p4  6                 40     
[-1, 3, BottleneckMOB, [96, 2, 6]],  # 7                  80     
[-1, 3, BottleneckMOB, [160, 1, 6,]], #                   40     
[-1, 1, BottleneckMOB, [320, 1, 6,]], #                   40     
[-1, 1, nn.Conv2d, [1280, 1, 1]],     #                   40     
[-1, 1, SPP, [1024, [5, 9, 13]]],  #11     #              40    ]  
# YOLOv5 head  head:    [[-1, 3, BottleneckCSP, [1024, False]],  # 12             40  
   [-1, 1, Conv, [512, 1, 1]],                      #       40     
[-1, 1, nn.Upsample, [None, 2, 'nearest']],      #       40     
[[-1, 6], 1, Concat, [1]],  # cat backbone P4-7  #       80     
[-1, 3, BottleneckCSP, [512, False]],  # 16      #       80  
   [-1, 1, Conv, [256, 1, 1]],                      #       80     
[-1, 1, nn.Upsample, [None, 2, 'nearest']],      #       160     
[[-1, 3], 1, Concat, [1]],  # cat backbone P3-4          160    
 [-1, 3, BottleneckCSP, [256, False]],            #       160     
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]],  # 21 (P3/8-small)   #        160  
   [-2, 1, Conv, [256, 3, 2]],                     #       160     
[[-1, 17], 1, Concat, [1]],  # cat head P4      #       160     
[-1, 3, BottleneckCSP, [512, False]],           #       160     
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]],  # 25 (P4/16-medium)  #       160  
   [-2, 1, Conv, [512, 3, 2]],                     #       160     
[[-1, 13], 1, Concat, [1]],  # cat head P5-13   #      160     
[-1, 3, BottleneckCSP, [1024, False]],          #      160     
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]],  # 29 (P5/32-large)           160  
   [[21, 25, 29], 1, Detect, [nc, anchors]],  # Detect(P5, P4, P3)     
nc:number class, na:number of anchors    ]

到这我们就实现了将 yolov5 的 backbone 替换成了 mobilenetv2。在使用时只需要将网络结构配置参数—cfg 修改成 –cfg yolov5-mobilenet.yaml。

训练指令:

python train.py --data coco.yaml --cfg yolov5-mobilenet.yaml--weights '' --batch-size 64

5 Next

《YOLOv5 从入门到部署》系列将会 介绍:YOLOv5 的部署。

正文完
 0
一诺
版权声明:本站原创文章,由 一诺 于2021-02-17发表,共计5576字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码