win10+torch+yolov3訓練自己數據集

1. 前情說明:

窮苦學生一枚,恰好最近在學習object detection,用到了yolov3模型,搗鼓了好幾天,看了各大論壇、貼吧、CSDN,知乎,博客園等好多大佬前輩們寫的文章(吐血.jpg),在這裏將自己的過程和結果寫出來,希望大家能少走點彎路。

2. 環境:

這個很重要!!!!!

  1. window 10
  2. pytorch 1.4.0
  3. opencv-python
  4. tqdm
  5. matplotlib
  6. pycocotools(這個很難裝!!,原作者壓根沒有考慮window環境下的coco tools,真的吐血,具體安裝教程可以參考我的博客:https://blog.csdn.net/weixin_45829462/article/details/104787103

本次使用的是ultralytics-yolov3
爲什麼使用這個版本,因爲這個版本工業應用的比較多,檢測速度也比較快,最重要的是,目前這個版本原作者一直在優化之中。
在這裏插入圖片描述

3. 步驟:

3.1 製作數據集:
在製作數據集需要用到一款標註工具,labellmge,安裝地址:github:https://github.com/tzutalin/labelImg,使用方法:
https://www.cnblogs.com/Terrypython/p/9577657.html
3.2 數據集:
博主使用的數據集是Voc行人數據集,是已經標註好的數據集,下載地址:
大家先將數據集下載到桌面,解壓,等待使用,數據中有兩個文件夾,分別是:
在這裏插入圖片描述
Annotations文件打開後如下:
在這裏插入圖片描述
JPEGImages打開後如下:
在這裏插入圖片描述
3.3 下載模型:
請大家將模型下載下來,地址:https://github.com/ultralytics/yolov3
因爲博主準備訓練關於檢測行人的模型,故命名爲yolov3-person,下載下來重要的源文件包都有:

在這裏插入圖片描述
3.4 裝載數據:
將數據集Annotations、JPEGImages複製到yolov3-person工程目錄下的data文件下;同時新建兩個文件夾,分別命名爲ImageSetslabels,最後我們將JPEGImages文件夾複製粘貼一下,並將文件夾重命名爲images,具體如下注:(data下加上原先的sample文件夾,一共是6個子文件夾):
在這裏插入圖片描述
3.5 建立標籤文件:

在根目錄下新建兩個文件夾,makeTxt.pyvoc_label.py,將以下代碼拷貝進py文件中,如下:

  • makeTxt.py:
import os
import random

trainval_percent = 0.1
train_percent = 0.9
xmlfilepath = 'data/Annotations'
txtsavepath = 'data/ImageSets'
total_xml = os.listdir(xmlfilepath)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

ftrainval = open('data/ImageSets/trainval.txt', 'w')
ftest = open('data/ImageSets/test.txt', 'w')
ftrain = open('data/ImageSets/train.txt', 'w')
fval = open('data/ImageSets/val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftest.write(name)
        else:
            fval.write(name)
    else:
        ftrain.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
  • voc_label.py:
import xml.etree.ElementTree as ET
import os
from os import listdir, getcwd

sets = ['train', 'test', 'val']

classes = ["person"]  # 我們只檢測person這個類別


def convert(size, box):#對圖片進行歸一化處理
    dw = 1. / size[0]
    dh = 1. / size[1]
    x = (box[0] + box[1]) / 2.0
    y = (box[2] + box[3]) / 2.0
    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(image_id):
    in_file = open('data/Annotations/%s.xml' % (image_id))
    out_file = open('data/labels/%s.txt' % (image_id), 'w')
    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'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        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')


wd = getcwd()
print(wd)
for image_set in sets:
    if not os.path.exists('data/labels/'):
        os.makedirs('data/labels/')
    image_ids = open('data/ImageSets/%s.txt' % (image_set)).read().strip().split()
    list_file = open('data/%s.txt' % (image_set), 'w')
    for image_id in image_ids:
        list_file.write('data/images/%s.jpg\n' % (image_id))
        convert_annotation(image_id)
    list_file.close()

注:classes=【‘person’】裏面的標籤並不是我們定的,請大家打開自己數據集的xml文件,類目頭標籤是是啥填啥:
在這裏插入圖片描述
3.6 運行標籤文件:

  • 運行makeTxt.py後,在ImagesSets文件夾裏面生成4個文件:
    在這裏插入圖片描述
  • 運行makeTxt.py後,在Iabels文件夾裏面生成文件名以及image中目標的位置信息:
    在這裏插入圖片描述
    3.7 配置文件:
    data文件下新建person.names文件,內容如下:
person

data文件下新建person.data文件,內容如下:

classes=1#只檢測一個類,原爲80個
train=data/train.txt
valid=data/test.txt
names=data/person.names#讀取類目信息
backup=backup/
eval=coco#配置標準與coco數據集一致

在這裏插入圖片描述
3.8 修改參數文件:
修改原yolov3-spp.cfg文件爲(可直接拷貝):

[net]
# Testing
# batch=1
# subdivisions=1
# Training
batch=64
subdivisions=16
width=608
height=608
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1



[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear


[yolo]
mask = 6,7,8
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1


[route]
layers = -4

[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky

[upsample]
stride=2

[route]
layers = -1, 61



[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky

[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky

[convolutional]
batch_normalize=1
filters=256
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=512
activation=leaky

[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear


[yolo]
mask = 3,4,5
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1



[route]
layers = -4

[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky

[upsample]
stride=2

[route]
layers = -1, 36



[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky

[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky

[convolutional]
batch_normalize=1
filters=128
size=1
stride=1
pad=1
activation=leaky

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=256
activation=leaky

[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear


[yolo]
mask = 0,1,2
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1
num=9
jitter=.3
ignore_thresh = .7
truth_thresh = 1
random=1

提一下,主要修改的是三個yolo層附近的參數,一共六處地方:

修改如下:
①filters=255改爲18:即3*(classes+5),單類的話classes=1
②classes=80改爲1,只檢測person一類
其他參數根據自己需要進行修改:
①如果想修改anchor的值,需要根據kmeans聚類跑自己的數據集得出結果,代碼地址:https://github.com/lars76/kmeans-anchor-boxes
②如果GPU顯存較小,可以設置將random=1設置爲0
③當單類是cls爲0

3.10 配置預權重:

預權重需要哪個根據自己修改的cfg文件進行選擇,以下附一張圖。但要提一點,如果不想在作者訓練好的權重再加以訓練,想要從頭開始訓練,選擇darknet53.conv.74
在這裏插入圖片描述
3.11 訓練模型:

使用pycharm中的Terminal,輸入如下命令:

python train.py --data data/person.data --cfg cfg/yolov3-spp.cfg --weights weights/darknet53.conv.74 --epochs 100 --batch-size 32

其中,–epochs 10指迭代了10次,batch-size 32指每次處理32張圖片。
①如果GPU顯存不夠的,會報出錯誤:
②batch-size 最好在32以上,並且爲2的指數倍,不然會不定時出現nan問題:
在這裏插入圖片描述
③數據集不要包括黑白圖片,請保持RBG通道一致,不然會報錯:
在這裏插入圖片描述
④,有條件的,請開啓多尺度訓練,提高模型精度,具體命令如下:

python train.py --data data/person.data --cfg cfg/yolov3-spp.cfg --weights weights/darknet53.conv.74 --epochs 100 --batch-size 32 --multi-scale

正常運行如下:
在這裏插入圖片描述
在這裏插入圖片描述(自己筆記本顯存不夠,通過Google Colab訓練)
訓練結束後得到模型best.ptlast.pt
在這裏插入圖片描述

4. 結果檢測:

在Terminal下輸入以下命令:

python detect.py --names data/person.names --cfg cfg/yolov3-spp.cfg --weights weights/best.pt

結果如下:
在這裏插入圖片描述
在這裏插入圖片描述
結果倒是出乎自己的預期

5. 模型評估與測試

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
模型仍有待優化,近期不定時更新該博文!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章