教女朋友學會用win10+yolov3+python訓練自己的模型

times:2020/3/23
操作系統:win10
環境:python 3.6
因爲我之前把所有內容寫在一篇文章裏非常的亂,所以本文主線是訓練自己的 yolo.h5 去識別圖像中的人,所有小細節的操作,我都在文中添加了鏈接,新手的話需要注意看一下。
// 有任何的問題都可以直接評論,還有資料的話直接留言郵箱,說明問題//
//也可以評論下加下微信詢問//
大家一起加油學習yolo,之後我會再出一篇詳細介紹yolo代碼的文章
新創建的小小交流羣:977947271 想要資料的也可以在羣裏要哦
2020/5/30:
近日每天都有十幾個很明顯的小號加羣,而且有的大號進羣竟然打廣告和賣不良物品,經我和羣友討論後,加羣需要1元錢,但是進羣后我會將錢退還給大家,只是爲了過濾一些不好的人。

如果你是 yolo 小白,或者環境配置等一直報錯,請先參閱上一篇博文:keras-yolov3目標檢測詳解——適合新手 (環境配置、用官方權重識別自己的圖片)

本文目的:

前面有篇文章說的是利用官方的權重直接識別自己的圖片,我也展示了識別的效果。

今天我介紹一下如何創建自己的數據集去訓練屬於自己的 model

前提準備:

1、配置好環境的 python、anaconda 或 pycharm

2、labelimg 軟件:下載方法: labelimg的下載與使用

3、準備一些圖片,創建訓練需要的 VOC 文件

(1) 官方的VOC2007下載鏈接:voc2007下載鏈接,可以從這裏找需要的圖片,或者一些有基礎的朋友可以寫爬蟲去爬一些圖片

(2) voc2007百度網盤下載鏈接:

鏈接:https://pan.baidu.com/s/18wqRTZDSz5NQEtvq0u0a1g 
提取碼:hexy 

(3) 可以自己準備圖片,不過最好準備多一點
.
.

正式訓練步驟:

一、準備自己的voc2007數據集

先用 pycharm 或 spyder 打開 keras-yolo3 文件夾,用 pycharm 或 spyder 是爲了看文件夾更方便,直接在 anaconda 裏運行也是可以的

1、打開文件夾

先按照這篇文章的步驟操作:keras-yolov3目標檢測詳解——適合新手

完成後打開的文件夾應該是這樣的:
在這裏插入圖片描述
2、新建voc2007數據集(存放自己的圖片及標註信息)

新建的文件夾:如下

在這裏插入圖片描述
ImageSets 文件夾下還有個名爲 Main 的小文件夾
在這裏插入圖片描述

VOCdevkit{
			VOC2007{	Annotations
						ImageSets{main}
						JPEGImages	}				
										}
雖然表達的很醜,但是上面有圖,應該還是可以看明白的
注意:文件夾的名稱必須和上面展示的一樣,這是 yolo 默認的
	  不然還需要改代碼纔行				

3、用labelimg軟件對自己的圖片進行信息標註

----labelimg 的使用方法:labelimg 下載和標註 xlm 文件

想要訓練自己的模型就要學會 labelimg 的使用,實在不想學的…就評論一下郵箱,我直接把我標註好的 VOC2007 文件夾打包發給你們吧

(1)需要訓練的圖片放在 JPEGImages 裏面:
在這裏插入圖片描述
在這裏插入圖片描述
(2)labelimg 標註的 xlm 文件放在 Annotations 裏面:
在這裏插入圖片描述
在這裏插入圖片描述

4、在 VOC2007 裏新建一個 py 文件

我這裏取名 voc.py
在這裏插入圖片描述
voc.py 的代碼:

import os
import random

trainval_percent = 0.2 #測試集佔0.2
train_percent = 0.8    #訓練集佔0.8
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
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('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/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.py 文件

運行成功的話 mian 文件夾裏會多了四個 txt 文件

在這裏插入圖片描述
完成以上所有步驟,VOC 的處理就完成了

.

二、進行訓練前的最後準備

1、修改 voc_annotation.py 文件並運行

更改這裏的 classes 的數量,你voc2007裏標註了哪幾種,你就留哪幾種就行

在這裏插入圖片描述
比如我的 voc 中只標註了 “person”,那我只留下“person”,然後再運行一下就行
在這裏插入圖片描述
運行完成後會多出這幾個 txt 文件
在這裏插入圖片描述
2、修改 model_data

將 coco_classes.txt 和 voc_classes.txt 中也只留下VOC2007 中所標註的那個類型
在這裏插入圖片描述
比如我標註的只有 “person”

那我只留下“person”
在這裏插入圖片描述
在這裏插入圖片描述
3、修改 yolo3.cfg

大概在 610、696 和 783 行的位置,把 classes 的數值都改爲 1

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
4、添加官方權重

按照上篇博文步驟進行的朋友應該下載好了 yolov3.weights 文件並轉爲了 yolo.h5 文件

附上上篇博文的鏈接(裏面有下載鏈接和轉化方法):keras-yolov3目標檢測詳解——適合新手

yolo.h5 改名爲 yolo_weights.h5
在這裏插入圖片描述
在這裏插入圖片描述
5、新建 logs 文件夾存放訓練的 權重文件

在這裏插入圖片描述
6、開始訓練

keras-yolo3-master 文件夾下新建 一個名爲 trainyolo.py 的文件

爲什麼不用 源文件中的train.py呢??(因爲我運行的時候一直出現庫的報錯…建議按照我的方法來)

trainyolo.py 的代碼(直接複製即可):

import numpy as np
import tensorflow as tf
import keras.backend as K
from keras.layers import Input, Lambda
from keras.models import Model
from keras.optimizers import Adam
from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping
from yolo3.model import yolo_body
from yolo3.model import yolo_loss
from keras.backend.tensorflow_backend import set_session
from yolo3.utils import get_random_data

def get_classes(classes_path):
    '''loads the classes'''
    with open(classes_path) as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names

def get_anchors(anchors_path):
    '''loads the anchors from a file'''
    with open(anchors_path) as f:
        anchors = f.readline()
    anchors = [float(x) for x in anchors.split(',')]
    return np.array(anchors).reshape(-1, 2)

def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
    '''data generator for fit_generator'''
    n = len(annotation_lines)
    i = 0
    while True:
        image_data = []
        box_data = []
        for b in range(batch_size):
            if i==0:
                np.random.shuffle(annotation_lines)
            image, box = get_random_data(annotation_lines[i], input_shape, random=True)
            image_data.append(image)
            box_data.append(box)
            i = (i+1) % n
        image_data = np.array(image_data)
        box_data = np.array(box_data)
        y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
        yield [image_data, *y_true], np.zeros(batch_size)

def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):

    assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'
    
    num_layers = len(anchors)//3 
    anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]

    true_boxes = np.array(true_boxes, dtype='float32')
    input_shape = np.array(input_shape, dtype='int32') # 416,416
    boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
    boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
    true_boxes[..., 0:2] = boxes_xy/input_shape[:]
    true_boxes[..., 2:4] = boxes_wh/input_shape[:]

    m = true_boxes.shape[0]
    grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]
    y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes),
        dtype='float32') for l in range(num_layers)]
    anchors = np.expand_dims(anchors, 0)
    anchor_maxes = anchors / 2.
    anchor_mins = -anchor_maxes
    valid_mask = boxes_wh[..., 0]>0

    for b in range(m):
        wh = boxes_wh[b, valid_mask[b]]
        if len(wh)==0: continue
        wh = np.expand_dims(wh, -2)
        box_maxes = wh / 2.
        box_mins = -box_maxes

        intersect_mins = np.maximum(box_mins, anchor_mins)
        intersect_maxes = np.minimum(box_maxes, anchor_maxes)
        intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
        intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
        box_area = wh[..., 0] * wh[..., 1]
        anchor_area = anchors[..., 0] * anchors[..., 1]
        iou = intersect_area / (box_area + anchor_area - intersect_area)
        best_anchor = np.argmax(iou, axis=-1)

        for t, n in enumerate(best_anchor):
            for l in range(num_layers):
                if n in anchor_mask[l]:
                    i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32')
                    j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32')
                    k = anchor_mask[l].index(n)
                    c = true_boxes[b,t, 4].astype('int32')
                    y_true[l][b, j, i, k, 0:4] = true_boxes[b,t, 0:4]
                    y_true[l][b, j, i, k, 4] = 1
                    y_true[l][b, j, i, k, 5+c] = 1

    return y_true


config = tf.ConfigProto()
config.gpu_options.allocator_type = 'BFC'
config.gpu_options.per_process_gpu_memory_fraction = 0.7
config.gpu_options.allow_growth = True
set_session(tf.Session(config=config)) 

if __name__ == "__main__":
    annotation_path = '2007_train.txt'
    classes_path = 'model_data/voc_classes.txt'    
    anchors_path = 'model_data/yolo_anchors.txt'
    weights_path = 'model_data/yolo_weights.h5'
    class_names = get_classes(classes_path)
    anchors = get_anchors(anchors_path)
    num_classes = len(class_names)
    num_anchors = len(anchors)
    log_dir = 'logs/'
    input_shape = (416,416)

    K.clear_session()

    image_input = Input(shape=(None, None, 3))
    h, w = input_shape

    print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
    model_body = yolo_body(image_input, num_anchors//3, num_classes)
    
    print('Load weights {}.'.format(weights_path))
    model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
    
    y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], \
        num_anchors//3, num_classes+5)) for l in range(3)]

    loss_input = [*model_body.output, *y_true]
    model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
        arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(loss_input)

    model = Model([model_body.input, *y_true], model_loss)

    freeze_layers = 249
    for i in range(freeze_layers): model_body.layers[i].trainable = False
    print('Freeze the first {} layers of total {} layers.'.format(freeze_layers, len(model_body.layers)))

    logging = TensorBoard(log_dir=log_dir)
    checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
        monitor='val_loss', save_weights_only=True, save_best_only=False, period=2)
    reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2, verbose=1)
    early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=6, verbose=1)

    val_split = 0.2
    with open(annotation_path) as f:
        lines = f.readlines()
    np.random.seed(10101)
    np.random.shuffle(lines)
    np.random.seed(None)
    num_val = int(len(lines)*val_split)
    num_train = len(lines) - num_val
    
    if True:
        model.compile(optimizer=Adam(lr=1e-3), loss={
            'yolo_loss': lambda y_true, y_pred: y_pred})

        batch_size = 1
        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
        model.fit_generator(data_generator(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                steps_per_epoch=max(1, num_train//batch_size),
                validation_data=data_generator(lines[num_train:], batch_size, input_shape, anchors, num_classes),
                validation_steps=max(1, num_val//batch_size),
                epochs=50,
                initial_epoch=0,
                callbacks=[logging, checkpoint])
        model.save_weights(log_dir + 'trained_weights_stage_1.h5')

    for i in range(freeze_layers): model_body.layers[i].trainable = True

    if True:
        model.compile(optimizer=Adam(lr=1e-4), loss={
            'yolo_loss': lambda y_true, y_pred: y_pred})

        batch_size = 1
        print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
        model.fit_generator(data_generator(lines[:num_train], batch_size, input_shape, anchors, num_classes),
                steps_per_epoch=max(1, num_train//batch_size),
                validation_data=data_generator(lines[num_train:], batch_size, input_shape, anchors, num_classes),
                validation_steps=max(1, num_val//batch_size),
                epochs=100,
                initial_epoch=50,
                callbacks=[logging, checkpoint])
        model.save_weights(log_dir + 'last1.h5')

然後運行trainyolo.py 的代碼
在這裏插入圖片描述
成功開始訓練,訓練時間比較長,需要耐心等待

.
.

訓練完成:

因爲我的電腦配置比較差,所以訓練了很長很長時間,這是我前幾天訓練的結果
在這裏插入圖片描述
訓練好的權重都放在 logs 文件夾下的 000 文件夾裏:
在這裏插入圖片描述
按理說訓練會經過兩個階段,且自動從一堆 loss 中選則出 loss最低的文件(應該是 earlystop函數的作用):

應該就是下面的框選的這兩個 h5文件,都可以使用
在這裏插入圖片描述

使用的方法就和前面那篇博文操作一樣了(用這個h5權重模型去識別自己圖片),下面給大家展示一下我訓練的模型的結果吧
在這裏插入圖片描述
在這裏插入圖片描述
對比使用官方的權重文件來說,我的模型可以僅僅識別出圖片中的人體,而且識別效果還不賴,感覺還不錯吧 哈哈哈

總結:

以上過程皆爲剛剛我親自操作,全程沒有任何問題

如果有朋友對這篇文章的任何內容感到不明白的請及時評論提出問題,我怕時間一久我也會忘,想要資料的直接評論你們的聯繫方式就行,我有時間會回覆的。

這個暫時告一段落,接下來我會在 matlab 中找一些算法去實現同樣的功能,有興趣的朋友可以評論一下一起探討。

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