[关闭]
@w460461339 2020-03-01T13:27:01.000000Z 字数 4893 阅读 1701

YOLOV3训练-COCO

MachineLearning


0、步骤归纳

模型参考:https://github.com/eriklindernoren/PyTorch-YOLOv3
钢筋数据集下载:链接:https://pan.baidu.com/s/11qJu30XHunamQjo9zRTQNA 密码:p09l

0.0 数据准备

首先,这里的yolov3模型是通过coco进行训练的,它的数据和label的格式都是按照yolov3来的。

那么,为了尽可能的复用他们的结果,我们需要将我们自己的数据也变成对应的coco的格式。

比如,coco的格式为 [cls,cx,cy,dw,dh],其中cx,cy,dw,dh都是经过归一化的。(除以长or宽)

其次,不要忘记生成自己的配置文件:如类别文件等等。

最后,也需要注意划分训练(训练模型),验证(计算mAP),和测试(肉眼看效果)。

一般争取复用已有模型的dataset和dataloader。
另外需要搞清楚dataset在数据预处理阶段都做了什么,这个在之后的evaluate阶段很有用。

data-augmentation应该可以放在两个地方:

1、直接生成对应的数据和label文件。(更直观)
2、在训练时,通过预处理的方式加载。(省硬盘)
0.1 模型准备

对于darknet版本的yolov3,它的模型定义在coco.cfg中。

对于大多数任务而言,不需要修改backbone,只需要修改最终预测的类别数量即可。

那么对于yolov3而言,我们知道他会在3个scale上进行输出,找到这三个scale即可。

下面是其中一个scale。这里我们需要注意到,convolutional才是最后一个有训练参数的输出层,而下面的yolo层中是没有训练参数的,它定义的是如何计算loss的方法。而这两层都会与classes数量有关。

yolo层中,很明显,classes数量,按照自己的需求来。
convolutional层中,filters=3*(classes数量+bbox坐标+前背景flag)=3*(1+4+1)=18

  1. [convolutional]
  2. size=1
  3. stride=1
  4. pad=1
  5. filters=18
  6. activation=linear
  7. [yolo]
  8. mask = 3,4,5
  9. anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
  10. classes=1
  11. num=9
  12. jitter=.3
  13. ignore_thresh = .7
  14. truth_thresh = 1
  15. random=1

当然,如果是其他定义模型的方法,也做相同的操作。

方法:

1、使用tensorboardX
2、使用pytorchviz
0.2 训练准备

比如对于darknet53的yolov3而言,一般是复用74层以前的backbone,然后重新训练后面的分类器。

那么在优化的时候,前74层是freeze还是用小的lr?小的lr要多小呢?

  1. # 加载模型,并用自定义函数weights_init_normal进行初始化
  2. model = Darknet(opt.model_def).to(device)
  3. model.apply(weights_init_normal)
  4. # 加载预训练模型
  5. if opt.pretrained_weights:
  6. print('pre train')
  7. if opt.pretrained_weights.endswith(".pth"):
  8. model.load_state_dict(torch.load(opt.pretrained_weights))
  9. else:
  10. model.load_darknet_weights(opt.pretrained_weights)
  11. # 将backbone和classifier层分开
  12. pretrained_parameters = []
  13. classifier_parameters = []
  14. for name,layer in model.named_parameters():
  15. print(name,layer.shape)
  16. layer_num = name.split('.')[1]
  17. if int(layer_num)<74:
  18. pretrained_parameters.append(layer)
  19. else:
  20. classifier_parameters.append(layer)
  21. # 对不同的层采用不同的学习率
  22. optimizer = torch.optim.Adam([
  23. {'params':pretrained_parameters,'lr':1e-4},
  24. {'params':classifier_parameters,'lr':1e-3}
  25. ])

finetune的效果如何验证??

0.3 验证准备

1、数据准备

网上的代码基本都是按照coco数据集来做的。

这里看一下coco数据集的格式。

目录结构:

1、有一个文件里面包含所有图片的名字。
2、图片名字进行一定的修改就拿到对应label文件的名字。即,图片和label是以文件的形式进行一一对应的。

重要的是label文件的形式:

image_1e1ga3km31o691pjhu21oqm138u9.png-26.7kB

1、首先,一个对象一行。
2、每一行以 label_id center_x center_y box_w box_h的方式标记,中间以空格区分
3、所有的center_x center_y box_w box_h都经过归一化处理,放锁到0~1之间。
4、具体放缩方式为,x相关的值,除以整张图片的w;y相关的值,除以整张图片的h。

另外,图片的绝对路径也要放在一个文件中。

总结一下,利用别人再coco上训练好的yolov3来finetune,要准备以下几个文件。

4)label文件夹,里面有存放有和图片同名的txt文件,比如1.txt,内容为:

0 0.475 0.475 0.90 0.90
3 0.44  0.332 0.896 0.96

表示这里面有一个cat,位置为(c_x/img_w,c_y/img_h,w/img_w,h/img_h);还有一个trunk,位置在...

2、模型定义

2.1 模型简析

模型定义放在config/yolov3.cfg里面,截取片段如下:

  1. [net]
  2. # Testing
  3. #batch=1
  4. #subdivisions=1
  5. # Training
  6. batch=16
  7. subdivisions=1
  8. width=416
  9. height=416
  10. channels=3
  11. momentum=0.9
  12. decay=0.0005
  13. angle=0
  14. saturation = 1.5
  15. exposure = 1.5
  16. hue=.1
  17. learning_rate=0.001
  18. burn_in=1000
  19. max_batches = 500200
  20. policy=steps
  21. steps=400000,450000
  22. scales=.1,.1
  23. [convolutional]
  24. batch_normalize=1
  25. filters=32
  26. size=3
  27. stride=1
  28. pad=1
  29. activation=leaky
  30. # Downsample
  31. [convolutional]
  32. batch_normalize=1
  33. filters=64
  34. size=3
  35. stride=2
  36. pad=1
  37. activation=leaky
  38. ....
  39. [upsample]
  40. stride=2

[net]下面是模型超参数的定义(很奇怪居然没有classes的)

之后是按照顺序对模型进行定义;DarkNet里面有趣的一点是,他并没有使用maxpool来进行降采样,而是采用了stride=2的卷积,来进行降采样。

要注意,maxpoo实现几何倍数的降采样,通常也是通过stride来实现的。而maxpool和conv进行比较,同样是stride,前者无疑降低了网络的表现能力(少了需要学习参数),而conv进行stride,不仅降采样了,还增加了网络的表现力。

另外看一下这里的upsample,会发现这个upsample是不可训练的哦.

2.1 修改模型

比如我们想要训练一个6类的检测器,怎么改;

回想yolov3的结构,最终会有3个不同尺度的输出;截取其中一个尺度的输出片段如下

  1. [convolutional]
  2. size=1
  3. stride=1
  4. pad=1
  5. filters=18
  6. activation=linear
  7. [yolo]
  8. mask = 3,4,5
  9. anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
  10. classes=1
  11. num=9
  12. jitter=.3
  13. ignore_thresh = .7
  14. truth_thresh = 1
  15. random=1

[convolutional]这一层已经是模型的最后一层,[yolo]这一层其实是定义了如何解析[convlutional]这一层数据的方式,所以对于我们的6类检测器,需要修改:

1、[yolo]中的classes,变成6
2、[convlutional]中的filter变成3*(6+5)=33;3表示3个anchors,6表示6个类别,5=4+1,4表示坐标,1表示前背景。

3、关于parameters(),和named_parameters()

首先注意,torch里面有几种对象:

1、nn.Module 模块层,相对高级别的层;比如Conv2D层,就不需要你在手动去实现卷积。
2、nn.Parameters(一种特殊的tensor,默认可以进行梯度计算)

named_parameters和parameters都是打印nn.Parameters类别的东西;如果自定义的module中,没有使用到类似的nn.Parameters,那么就不会出现在named_parameters()中。

4、finetune

https://blog.csdn.net/dz4543/article/details/90049377

finetune中关于怎么修改模型的,已经放在前面了哈。

关于加载预训练模型,我们一般只加载backbone,然后让分类层重新训练会比较好。

对于yolov3的Dark53而言,74层之后就都是分类用的了,是需要重新训练的。

可以选在下一个darknet53.conv.74,也可以自己从yolov3.weights里面拿。

另外,你会发现如果自己的classes设置的小于80(前提是cfg文档改对了),你也可以正常加载它的weights,但是这个weights是不能用的;因为在程序中,它加载weights的方式是以偏移量的形式进行加载的。所以只要的网络总大小小于它的网络参数,你随便写一个参数也是能够把它的参数加载进来的。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注