1. 程式人生 > 其它 >yolov5-pytorch訓練自己的資料集

yolov5-pytorch訓練自己的資料集

技術標籤:pytorch

程式碼下載

這個是github的下載地址

git clone https://github.com/ultralytics/yolov5

如果沒有下載成功,就用下面碼雲的下載地址。

git clone https://gitee.com/pursuit_zhangyu/yolov5-pytorch

環境安裝

建立一個python=3.8的虛擬環境

conda create -n yolov5 python=3.8
#啟用python3.8的虛擬環境
source activate yolov5

安裝依賴環境

conda install cudatoolkit=9.0 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/linux-64/

pip install -U -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple

因為requirements.txt裡面的依賴項太多,如果沒有安裝完全的話,缺什麼安裝什麼,具體如下:

pip install matplotlib>=3.2.2 -i https://mirrors.aliyun.com/pypi/simple
pip install numpy>=1.18.5 -i https://mirrors.aliyun.com/pypi/simple
pip install opencv-python>=4.1.2 -i https://mirrors.aliyun.com/pypi/simple
pip install Pillow -i https://mirrors.aliyun.com/pypi/simple
pip install PyYAML>=5.3 -i https://mirrors.aliyun.com/pypi/simple
pip install scipy>=1.4.1 -i https://mirrors.aliyun.com/pypi/simple
pip install tensorboard>=2.2 -i https://mirrors.aliyun.com/pypi/simple
pip install torch>=1.7.0  -i https://mirrors.aliyun.com/pypi/simple
pip install torchvision>=0.8.1 -i https://mirrors.aliyun.com/pypi/simple
pip install tqdm>=4.41.0 -i https://mirrors.aliyun.com/pypi/simple

pip install pandas -i https://mirrors.aliyun.com/pypi/simple
pip install seaborn -i https://mirrors.aliyun.com/pypi/simple
pip install pycocotools>=2.0 -i https://mirrors.aliyun.com/pypi/simple

測試一張圖片

python detect.py --source data/images --weights yolov5m.pt --conf 0.25

訓練自己的資料集

資料預處理

新建data my_lables_3.py,資料具體分成成訓練集:測試集=8:2,三個類別。

#coding=utf8
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
 
classes = ["person", "helmet_on", "helmet_off"] 
 
def convert(size, box):
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[1])/2.0 - 1
    y = (box[2] + box[3])/2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

def convert_annotation(file_name, label_path):
    in_file = open(file_name, encoding="utf-8")  
    out_file = open(label_path, 'w')
    # print("in_file:", in_file)
    tree=ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
 
    for obj in root.iter('object'):
        if obj.find("name") == None:
            continue
        cls = obj.find('name').text
        if cls not in classes:
            continue
        print("cls:", cls)
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = convert((w,h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

    in_file.close()
    out_file.close()


def convert_dataset(image_dir, xml_dir, train_txt_path, test_txt_path, out_label_path):
    print("inter convert")
    if not os.path.exists(out_label_path):
        os.makedirs(out_label_path)

    train_txt = open(train_txt_path, "w")
    test_txt = open(test_txt_path, "w")

    image_list = os.listdir(image_dir)

    for i, image_name in enumerate(image_list):
        if ".jpg" in image_name:
            print(image_name)

            label_name = image_name.replace(".jpg", ".txt")
            xml_name = image_name.replace(".jpg", ".xml")

            label_path = os.path.join(out_label_path, label_name)
            image_path = os.path.join(image_dir, image_name)
            xml_path = os.path.join(xml_dir, xml_name)
            
            if i % 5 != 0:
                train_txt.write(image_path + "\n")
            else:
                test_txt.write(image_path + "\n")

            convert_annotation(xml_path, label_path)

    train_txt.close()

if __name__ == "__main__":
    image_dir = "JPEGImages" #這個最好寫絕對地址
    xml_dir = "Annotations"
    out_label_path = "labels"

    train_txt_path = "train_3.txt"
    test_txt_path = "test_3.txt"

    convert_dataset(image_dir, xml_dir, train_txt_path, test_txt_path, out_label_path)

生成一個label資料夾,兩個檔案train_3.txt、test_3.txt,其中label是標籤資訊,train_3.txt是訓練圖片的地址,test_3.txt為測試圖片的地址

訓練

配置檔案修改

cd data 
cp coco.yaml helmet.yaml
vim helmet.yaml
# COCO 2017 dataset http://cocodataset.org
# Train command: python train.py --data coco.yaml
# Default dataset location is next to /yolov5:
#   /parent_folder
#     /coco
#     /yolov5


# download command/URL (optional)
#download: bash data/scripts/get_coco.sh

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /xx/train_3.txt  # 118287 images
val: /xx/test_3.txt  # 5000 images
test: /xx/test_3.txt  # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794

# number of classes  類別數量需要修改
nc: 3

# class names  類別名稱需要修改
names: [ 'person', 'helmet_on', 'helmet_off' ]

# Print classes
# with open('data/coco.yaml') as f:
#   d = yaml.load(f, Loader=yaml.FullLoader)  # dict
#   for i, x in enumerate(d['names']):
#     print(i, x)

修改網路結構

cd models
cp yolov5s.yaml helmets.yaml
vim helmets.yaml

我的資料集是三個類別

# parameters
nc: 3  # number of classes
depth_multiple: 0.33  # model depth multiple
width_multiple: 0.50  # layer channel multiple

# anchors
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Focus, [64, 3]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 9, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 1, SPP, [1024, [5, 9, 13]]],
   [-1, 3, C3, [1024, False]],  # 9
  ]

# YOLOv5 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [24, False]],  # 17 (P3/8-small)  #需要修改(3*(classnums+5))

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [24, False]],  # 20 (P4/16-medium) #需要修改(3*(classnums+5))

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [24, False]],  # 23 (P5/32-large)  #需要修改(3*(classnums+5))

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

因為圖片資料夾的名稱不時images,需要修改一下程式碼

vim utils/datasets.py

#sa,sb=os.sep+'images'+os.sep,os.sep+'labels'+os.sep#/images/,/labels/substrings
sa,sb=os.sep+'JPEGImages'+os.sep,os.sep+'labels'+os.sep#/images/,/labels/substrings

訓練

python train.py --data ./data/helmets.yaml --cfg ./models/helmets.yaml --batch-size 64 --device 0,1,2,3
後臺執行
nohup python train.py --data ./data/helmets.yaml --cfg ./models/helmets.yaml --batch-size 64 --device 0,1,2,3 &

開啟nohup.out可以檢視訓練情況

測試

python test.py --weights runs/train/exp3/weights/last.pt --data ./data/helmets.yaml --device 0,1,2,3

模型轉換onnx

安裝依賴環境

pip install onnx>=1.8.0 -i https://mirrors.aliyun.com/pypi/simple
pip install coremltools==4.0 -i https://mirrors.aliyun.com/pypi/simple

執行

python models/export.py --weights runs/train/exp3/weights/last.pt --img 640 --batch 1

結果

Starting ONNX export with onnx 1.8.1...
ONNX export success, saved as runs/train/exp3/weights/last.onnx