YOLOv5 項目教程
作者:elfin 資料來源:YOLOv5
1、前言
YOLOv5項目地址:ultralytics/yolov5
項目自發布以來,直到現在仍然在不斷改進模型、項目。作者的更新頻率很大,很多問題都能夠及時解決,當然問題也很多!到寫稿此時,項目的device參數仍然無法正常工作,查看源碼,作者的代碼寫的GPU設備控制比較復雜,修改源碼也沒有解決,可能我里解決就差一步了吧!在項目提交bug后,得到作者的及時回應,但是最后仍然沒有解決。難道使用GPU必須從GPU:0開始嗎?查看bug請點擊。
-
ONNX (Open Neural Network Exchange)
開發式神經網絡交換格式!
它是深度學習模型的一種標准,可使模型在不同框架間轉移
-
TensorRT
英偉達官方提供的GPU版深度學習模型加速平台,可以和Tensor Serving一起部署模型,實現模型的快速運算,其特點是性能高、資源占用少。
1.1 模型訓練
$ python train.py --batch-size 64 --data coco.yaml --weights yolov5l.pt --device 0,1
使用 --device 0,1指定多個GPU是沒有用的!會衰減計算速度。推薦多GPU使用數據並行的方法。
$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --weights yolov5x.pt
-
--nproc_per_node: 指定你的GPU節點數;
-
--batch-size:這里的批量是總的批量,實際每個GPU的的batch為 64/32.
這里的處理方式和keras是一樣的,采用數據並行可以提高我們的計算速度。
$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --cfg yolov5x.yaml --weights '' --device 2,3
數據並行運算的時候可以指定要使用的GPU: --device 2,3
$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --cfg yolov5x.yaml --weights '' --sync-bn
SyncBatchNorm多個GPU之間進行批量標准化!
下面介紹將模型轉為ONNX模型,使用TensorRT部署……
2、yolov5模型轉ONNX模型
2.1 環境准備
$ git clone https://github.com/ultralytics/yolov5 # clone repo
$ cd yolov5
$ pip install -r requirements.txt # base requirements
$ pip install onnx>=1.7.0 # for ONNX export
$ pip install coremltools==4.0 # for CoreML export
2.2 輸出已訓練的模型為ONNX模型
$ python models/export.py --weights yolov5x.pt --img 640 --batch 1 # export at 640x640 with batch size 1
export.py
輸出了三個模型,這里我們主要查看onnx模型!
使用https://netron.app/可以查看我們生成的ONNX模型結構。
文件中代碼:
print('\nStarting ONNX export with onnx %s...' % onnx.__version__)
f = opt.weights.replace('.pt', '.onnx') # filename
torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'],
output_names=['classes', 'boxes'] if y is None else ['output'])
# Checks
onnx_model = onnx.load(f) # load onnx model
onnx.checker.check_model(onnx_model) # check onnx model
# print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model
print('ONNX export success, saved as %s' % f)
except Exception as e:
print('ONNX export failure: %s' % e)
torch.onnx.export()模型轉換關鍵函數
關於torch.onnx的介紹見:https://pytorch.org/docs/stable/onnx.html
3、Test Time Augmentation (TTA)
添加命令行參數--augment
實現測試、預測時的性能增強!
3.1 Test Normally
$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
3.2 Test with TTA
$ python test.py --weights yolov5x.pt --data coco.yaml --img 832 --augment
3.3 Inference with TTA
$ python detect.py --weights yolov5s.pt --img 832 --source ./inference/images/ --augment
4、Model Ensembling模型嵌入
還記得決策樹嗎?而現在我們一般使用隨機森林、LightGBM等模型。Ensembling的思想在深度學習模型中同樣適應,這里我們對yolov5的不同模型進行融合,結果發現,預測顯著性明顯提升。
4.1 Test Normally
$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
4.2 Ensemble Test
$ python test.py --weights yolov5x.pt yolov5l.pt --data coco.yaml --img 640
4.3 Ensemble Inference
$ python detect.py --weights yolov5x.pt yolov5l.pt --img 640 --source ./inference/images/
這里測試結果為:齊達內是人的概率從0.8上漲到0.9!
5、Pruning/Sparsity Tutorial 剪枝/稀疏教程
5.1 Test Normally
$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.492 < -------- baseline mAP
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.676
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.534
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.318
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.541
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.633
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.376
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.616
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.670 < -------- baseline mAR
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.493
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.723
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.812
5.2 Test with 30% Pruned Model
我們現在用一個修剪過的模型重復上面的測試,設置修剪30%,這里我們需要修改test.py文件的torch_utils.prune(model, 0.3)
$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.462 < -------- lower mAP
Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.649
Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.505
Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.293
Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.507
Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.602
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.361
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.590
Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.643 < -------- lower mAR
Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.465
Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.691
Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.786
在結果中我們可以觀察到,我們的模型在剪枝后達到了30%的稀疏性,這意味着30%的模型權重參數nn.Conv2d層等於0。測試時間(在P100 GPU上)保持不變,而模型的AP和AR分數略有降低。
6、 Hyperparameter Evolution超參數調優
超參數對模型的影響是很大的,但是具體是如何影響的,通過嚴格的數學推導我們是做不到的。科學家在如何獲取最優參數的道路上做了很多嘗試,遺傳算法(GA)就是比較出名的存在。
6.1 初始化超參數
YOLOv5有大約25個用於各種訓練設置的超參數。這些是在/data目錄的yaml文件中定義的。更好的初始猜測將產生更好的最終結果,因此在演化之前正確初始化這些值是很重要的。如果有疑問,只需使用默認值,這是優化YOLOv5在COCO數據集的配置參數。
6.2 定義適應性函數
適應性是我們追求最大化的價值。在YOLOv5中,我們將默認適應度函數定義為度量的加權組合:mp@0.5貢獻了10%、mp@0.5:0.95占其余90%。您可以根據需要調整這些值,也可以使用默認的適應度定義。
def fitness(x):
# Returns fitness (for use with results.txt or evolve.txt)
w = [0.0, 0.0, 0.1, 0.9] # weights for [P, R, mAP@0.5, mAP@0.5:0.95]
return (x[:, :4] * w).sum(1)
6.3 遺傳算法演變
evolution是在一個我們尋求改進的基本場景下進行的。本例中的基本場景是使用預訓練的yolov5對COCO128進行10個時期的微調。基本場景訓練命令是:
$ python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache
為了進化特定於這個場景的超參數,從我們在第6.1節中定義的初始值開始,並最大化第6.2節中定義的適應度,增加--evolve
# Single-GPU
python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache --evolve
# Multi-GPU
for i in 0 1 2 3; do
nohup python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache --evolve --device $i > evolve_gpu_$i.log &
done
# Multi-GPU bash while (not recommended)
for i in 0 1 2 3; do
nohup "$(while true; do python train.py ... --evolve --device $i; done)" > evolve_gpu_$i.log &
done
主要的遺傳算子是交叉和變異。在這項工作中,使用了變異,以90%的概率和0.04的方差,根據所有前幾代最好的父母的組合來創造新的后代。結果記錄在yolov5中/evolve.txt,每一代都保存着最適合的后代於yolov5/runs/evolve/hyp_evolved.yaml中。
我們建議至少300代進化才能獲得最佳結果。請注意,進化通常是昂貴和耗時的,因為基本場景需要數百次訓練,可能需要數百或數千個小時的GPU訓練。
7、遷移學習凍結yolov5層
本指南解釋了如何在遷移學習時凍結YOLOv5層。遷移學習是一種有用的方法,可以快速地在新數據上重新訓練模型,而不必重新訓練整個網絡。相反,一部分初始權重被凍結在原位,其余權重用於計算損失,並由優化器更新。這需要比正常訓練更少的資源,並且允許更快的訓練時間,盡管它也可能導致最終訓練精度的降低。
Transfer Learning with Frozen Layers
7.1 凍結骨干網絡
# Freeze
freeze = [] # parameter names to freeze (full or partial)
for k, v in model.named_parameters():
v.requires_grad = True # train all layers
if any(x in k for x in freeze):
print('freezing %s' % k)
v.requires_grad = False
查看模型的參數名:
for k, v in model.named_parameters():
print(k)
# Output
model.0.conv.conv.weight
model.0.conv.bn.weight
model.0.conv.bn.bias
model.1.conv.weight
model.1.bn.weight
model.1.bn.bias
model.2.cv1.conv.weight
model.2.cv1.bn.weight
...
model.23.m.0.cv2.bn.weight
model.23.m.0.cv2.bn.bias
model.24.m.0.weight
model.24.m.0.bias
model.24.m.1.weight
model.24.m.1.bias
model.24.m.2.weight
model.24.m.2.bias
發現模型的backbone是0~9層,所以在freeze中添加這幾層的name。
freeze = ['model.%s.' % x for x in range(10)] # parameter names to freeze (full or partial)
7.2 凍結全部yolov5的layers
這個簡單,你可以將name全部放入freeze中,也可以設置所以層v.requires_grad = False
7.3 總結
凍結yolov5的backbone之后,GPU的占用率下降了20%,顯存占用下降了40%+。訓練其他數據即的性能指標下降了0.0021%,這種性能損失還是可以接受的。
既然訓練非backbone部分訓練時間更快,GPU占用、顯存占用更少,是不是可以先訓練非backbone部分,再一起訓練呢?
8、TensorRT模型加速
yolov5里面涉及的一些結構在TensorRT里面還沒有支持,這些結構要加速就只能自己用C++實現。使用C++編程對很多人來說都是非常麻煩的事情,這里借鑒TensorRTx項目進入這個領域。
TensorRTx旨在通過TensorRT網絡定義API實現流行的深度學習網絡。我們知道,TensorRT有內置的解析器,包括caffeparser、uffparser、onnxparser等,但當我們使用這些解析器時,常常會遇到一些“不支持的操作或層”問題,特別是一些最新的模型正在使用新型的層。
我們為什么不跳過所有的解析器呢?我們只需要使用TensorRT網絡定義API來構建整個網絡,並不復雜。
這個項目是為了熟悉TensorRT API,也為了分享和向社區學習。
所有模型首先在pytorch/mxnet/tensorflow中實現,並導出一個權重文件xxx.wts然后使用TensorRT進行權值加載、網絡定義和推理。一些pytorch實現可以在我的repo Pytorchx中找到,其余的來自熱門的開源實現。