飞道的博客

Faster RCNN网络源码解读(Ⅱ) --- Faster RCNN源码使用

317人阅读  评论(0)

目录

一、源码链接

二、环境配置

三、文件结构 

四、预训练权重下载地址 

五、训练集 

六、训练方法及注意事项 

七、大概看一下训练过程(train_mobilenetv2.py) 


一、源码链接

Faster R-CNN源码链接https://pan.baidu.com/s/1SQjyLXD47H11ke05OXYSsQ?pwd=gt4f 

二、环境配置

* Python3.6/3.7/3.8
* Pytorch1.7.1(注意:必须是1.6.0或以上,因为使用官方提供的混合精度训练1.6.0后才支持)
* pycocotools(Linux:`pip install pycocotools`; Windows:`pip install pycocotools-windows`(不需要额外安装vs))
* Ubuntu或Centos(不建议Windows)
* 最好使用GPU训练
* 详细环境配置见`requirements.txt`

三、文件结构 

  ├── backbone: 特征提取网络,可以根据自己的要求选择
  ├── network_files: Faster R-CNN网络(包括Fast R-CNN以及RPN等模块)
  ├── train_utils: 训练验证相关模块(包括cocotools)
  ├── my_dataset.py: 自定义dataset用于读取VOC数据集
  ├── train_mobilenet.py: 以MobileNetV2做为backbone进行训练
  ├── train_resnet50_fpn.py: 以resnet50+FPN做为backbone进行训练
  ├── train_multi_GPU.py: 针对使用多GPU的用户使用
  ├── predict.py: 简易的预测脚本,使用训练好的权重进行预测测试
  ├── validation.py: 利用训练好的权重验证/测试数据的COCO指标,并生成record_mAP.txt文件
  └── pascal_voc_classes.json: pascal_voc标签文件

四、预训练权重下载地址 

## 预训练权重下载地址(下载后放入backbone文件夹中):
* MobileNetV2 weights(下载后重命名为`mobilenet_v2.pth`,然后放到`bakcbone`文件夹下): https://download.pytorch.org/models/mobilenet_v2-b0353104.pth
* Resnet50 weights(下载后重命名为`resnet50.pth`,然后放到`bakcbone`文件夹下): https://download.pytorch.org/models/resnet50-0676ba61.pth
* ResNet50+FPN weights: https://download.pytorch.org/models/fasterrcnn_resnet50_fpn_coco-258fb6c6.pth
* 注意,下载的预训练权重记得要重命名,比如在train_resnet50_fpn.py中读取的是`fasterrcnn_resnet50_fpn_coco.pth`文件,
  不是`fasterrcnn_resnet50_fpn_coco-258fb6c6.pth`,然后放到当前项目根目录下即可。

五、训练集 

## 数据集,本例程使用的是PASCAL VOC2012数据集
* Pascal VOC2012 train/val数据集下载地址:http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar
* 如果不了解数据集或者想使用自己的数据集进行训练,请参考我的bilibili:https://b23.tv/F1kSCK
* 使用ResNet50+FPN以及迁移学习在VOC2012数据集上得到的权重: 链接:https://pan.baidu.com/s/1ifilndFRtAV5RDZINSHj5w 提取码:dsz8

六、训练方法及注意事项 

## 训练方法
* 确保提前准备好数据集
* 确保提前下载好对应预训练模型权重
* 若要训练mobilenetv2+fasterrcnn,直接使用train_mobilenet.py训练脚本
* 若要训练resnet50+fpn+fasterrcnn,直接使用train_resnet50_fpn.py训练脚本
* 若要使用多GPU训练,使用`python -m torch.distributed.launch --nproc_per_node=8 --use_env train_multi_GPU.py`指令,`nproc_per_node`参数为使用GPU数量
* 如果想指定使用哪些GPU设备可在指令前加上`CUDA_VISIBLE_DEVICES=0,3`(例如我只要使用设备中的第1块和第4块GPU设备)
* `CUDA_VISIBLE_DEVICES=0,3 python -m torch.distributed.launch --nproc_per_node=2 --use_env train_multi_GPU.py`

## 注意事项
* 在使用训练脚本时,注意要将`--data-path`(VOC_root)设置为自己存放`VOCdevkit`文件夹所在的**根目录**
* 由于带有FPN结构的Faster RCNN很吃显存,如果GPU的显存不够(如果batch_size小于8的话)建议在create_model函数中使用默认的norm_layer,
  即不传递norm_layer变量,默认去使用FrozenBatchNorm2d(即不会去更新参数的bn层),使用中发现效果也很好。
* 训练过程中保存的`results.txt`是每个epoch在验证集上的COCO指标,前12个值是COCO指标,后面两个值是训练平均损失以及学习率
* 在使用预测脚本时,要将`train_weights`设置为你自己生成的权重路径。
* 使用validation文件时,注意确保你的验证集或者测试集中必须包含每个类别的目标,并且使用时只需要修改`--num-classes`、`--data-path`和`--weights-path`即可,其他代码尽量不要改动

七、大概看一下训练过程(train_mobilenetv2.py) 


   
  1. def main():
  2. device = torch.device( "cuda:0" if torch.cuda.is_available() else "cpu")
  3. print( "Using {} device training.". format(device. type))
  4. # 用来保存coco_info的文件
  5. results_file = "results{}.txt". format(datetime.datetime.now().strftime( "%Y%m%d-%H%M%S"))
  6. # 检查保存权重文件夹是否存在,不存在则创建
  7. if not os.path.exists( "save_weights"):
  8. os.makedirs( "save_weights")
  9. data_transform = {
  10. "train": transforms.Compose([transforms.ToTensor(),
  11. transforms.RandomHorizontalFlip( 0.5)]),
  12. "val": transforms.Compose([transforms.ToTensor()])
  13. }
  14. VOC_root = "./" # VOCdevkit
  15. aspect_ratio_group_factor = 3
  16. batch_size = 8
  17. amp = False # 是否使用混合精度训练,需要GPU支持
  18. # check voc root
  19. if os.path.exists(os.path.join(VOC_root, "VOCdevkit")) is False:
  20. raise FileNotFoundError( "VOCdevkit dose not in path:'{}'.". format(VOC_root))
  21. # load train data set
  22. # VOCdevkit -> VOC2012 -> ImageSets -> Main -> train.txt
  23. train_dataset = VOCDataSet(VOC_root, "2012", data_transform[ "train"], "train.txt")
  24. train_sampler = None
  25. # 是否按图片相似高宽比采样图片组成batch
  26. # 使用的话能够减小训练时所需GPU显存,默认使用
  27. if aspect_ratio_group_factor >= 0:
  28. train_sampler = torch.utils.data.RandomSampler(train_dataset)
  29. # 统计所有图像高宽比例在bins区间中的位置索引
  30. group_ids = create_aspect_ratio_groups(train_dataset, k=aspect_ratio_group_factor)
  31. # 每个batch图片从同一高宽比例区间中取
  32. train_batch_sampler = GroupedBatchSampler(train_sampler, group_ids, batch_size)
  33. nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # number of workers
  34. print( 'Using %g dataloader workers' % nw)
  35. # 注意这里的collate_fn是自定义的,因为读取的数据包括image和targets,不能直接使用默认的方法合成batch
  36. if train_sampler:
  37. # 如果按照图片高宽比采样图片,dataloader中需要使用batch_sampler
  38. train_data_loader = torch.utils.data.DataLoader(train_dataset,
  39. batch_sampler=train_batch_sampler,
  40. pin_memory= True,
  41. num_workers=nw,
  42. collate_fn=train_dataset.collate_fn)
  43. else:
  44. train_data_loader = torch.utils.data.DataLoader(train_dataset,
  45. batch_size=batch_size,
  46. shuffle= True,
  47. pin_memory= True,
  48. num_workers=nw,
  49. collate_fn=train_dataset.collate_fn)
  50. # load validation data set
  51. # VOCdevkit -> VOC2012 -> ImageSets -> Main -> val.txt
  52. val_dataset = VOCDataSet(VOC_root, "2012", data_transform[ "val"], "val.txt")
  53. val_data_loader = torch.utils.data.DataLoader(val_dataset,
  54. batch_size= 1,
  55. shuffle= False,
  56. pin_memory= True,
  57. num_workers=nw,
  58. collate_fn=val_dataset.collate_fn)
  59. # create model num_classes equal background + 20 classes
  60. model = create_model(num_classes= 21)
  61. # print(model)
  62. model.to(device)
  63. scaler = torch.cuda.amp.GradScaler() if amp else None
  64. train_loss = []
  65. learning_rate = []
  66. val_map = []
  67. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  68. # first frozen backbone and train 5 epochs #
  69. # 首先冻结前置特征提取网络权重(backbone),训练rpn以及最终预测网络部分 #
  70. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  71. for param in model.backbone.parameters():
  72. param.requires_grad = False
  73. # define optimizer
  74. params = [p for p in model.parameters() if p.requires_grad]
  75. optimizer = torch.optim.SGD(params, lr= 0.005,
  76. momentum= 0.9, weight_decay= 0.0005)
  77. init_epochs = 5
  78. for epoch in range(init_epochs):
  79. # train for one epoch, printing every 10 iterations
  80. mean_loss, lr = utils.train_one_epoch(model, optimizer, train_data_loader,
  81. device, epoch, print_freq= 50,
  82. warmup= True, scaler=scaler)
  83. train_loss.append(mean_loss.item())
  84. learning_rate.append(lr)
  85. # evaluate on the test dataset
  86. coco_info = utils.evaluate(model, val_data_loader, device=device)
  87. # write into txt
  88. with open(results_file, "a") as f:
  89. # 写入的数据包括coco指标还有loss和learning rate
  90. result_info = [ f"{i:.4f}" for i in coco_info + [mean_loss.item()]] + [ f"{lr:.6f}"]
  91. txt = "epoch:{} {}". format(epoch, ' '.join(result_info))
  92. f.write(txt + "\n")
  93. val_map.append(coco_info[ 1]) # pascal mAP
  94. torch.save(model.state_dict(), "./save_weights/pretrain.pth")
  95. # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  96. # second unfrozen backbone and train all network #
  97. # 解冻前置特征提取网络权重(backbone),接着训练整个网络权重 #
  98. # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  99. # 冻结backbone部分底层权重
  100. for name, parameter in model.backbone.named_parameters():
  101. split_name = name.split( ".")[ 0]
  102. if split_name in [ "0", "1", "2", "3"]:
  103. parameter.requires_grad = False
  104. else:
  105. parameter.requires_grad = True
  106. # define optimizer
  107. params = [p for p in model.parameters() if p.requires_grad]
  108. optimizer = torch.optim.SGD(params, lr= 0.005,
  109. momentum= 0.9, weight_decay= 0.0005)
  110. # learning rate scheduler
  111. lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
  112. step_size= 3,
  113. gamma= 0.33)
  114. num_epochs = 20
  115. for epoch in range(init_epochs, num_epochs+init_epochs, 1):
  116. # train for one epoch, printing every 50 iterations
  117. mean_loss, lr = utils.train_one_epoch(model, optimizer, train_data_loader,
  118. device, epoch, print_freq= 50,
  119. warmup= True, scaler=scaler)
  120. train_loss.append(mean_loss.item())
  121. learning_rate.append(lr)
  122. # update the learning rate
  123. lr_scheduler.step()
  124. # evaluate on the test dataset
  125. coco_info = utils.evaluate(model, val_data_loader, device=device)
  126. # write into txt
  127. with open(results_file, "a") as f:
  128. # 写入的数据包括coco指标还有loss和learning rate
  129. result_info = [ f"{i:.4f}" for i in coco_info + [mean_loss.item()]] + [ f"{lr:.6f}"]
  130. txt = "epoch:{} {}". format(epoch, ' '.join(result_info))
  131. f.write(txt + "\n")
  132. val_map.append(coco_info[ 1]) # pascal mAP
  133. # save weights
  134. # 仅保存最后5个epoch的权重
  135. if epoch in range(num_epochs+init_epochs)[- 5:]:
  136. save_files = {
  137. 'model': model.state_dict(),
  138. 'optimizer': optimizer.state_dict(),
  139. 'lr_scheduler': lr_scheduler.state_dict(),
  140. 'epoch': epoch}
  141. torch.save(save_files, "./save_weights/mobile-model-{}.pth". format(epoch))
  142. # plot loss and lr curve
  143. if len(train_loss) != 0 and len(learning_rate) != 0:
  144. from plot_curve import plot_loss_and_lr
  145. plot_loss_and_lr(train_loss, learning_rate)
  146. # plot mAP curve
  147. if len(val_map) != 0:
  148. from plot_curve import plot_map
  149. plot_map(val_map)

        先判断是否有可用的GPU,若没有则使用CPU进行训练。

        data_transform定义了图像预处理的函数。

        VOC_root指定了VOC数据集的根目录。

        VOCDataSet定义我们的数据集(通过mydataset.py文件定义我们自己的数据集)

        DataLoader进行数据载入。

        训练过程首先冻结前置特征提取网络权重(backbone),训练rpn以及最终预测网络部分。随后解冻前置特征提取网络权重(backbone),接着训练整个网络权重。

 


转载:https://blog.csdn.net/qq_41694024/article/details/128490710
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场