【轉發】實現yolo3模型訓練自己的數據集總結


  原文鏈接實現yolo3模型訓練自己的數據集總結

  經過兩天的努力,借鑒網上眾多博客,在自己電腦上實現了使用yolo3模型訓練自己的數據集並進行測試圖片。本文主要是我根據下面參考文章一步步實施過程的總結,可能沒參考文章中那么詳細,但是會包含一些參考文章中沒提及的容易掉坑的小細節,建議讀者結合參考文章一起看,一步步走即可。首先貼出本文主要參考的文章以及代碼出處:

代碼:https://github.com/qqwweee/keras-yolo3

參考文章:https://blog.csdn.net/patrick_Lxc/article/details/80615433

一.下載項目源碼,進行快速測試

  從上面代碼鏈接處下載整個項目源碼。下載好后,首先根據github中指引進行快速測試。

  yolo web:https://pjreddie.com/darknet/yolo

  對應操作如下(命令行操作):

  1.  wget https://pjreddie.com/media/files/yolov3.weights                                  

  注釋:這里wget為linux命令,windows系統可以直接訪問后面鏈接來下載yolov3權重文件,也可以訪問yolo web去下載。

  2. python convert.py yolov3.cfg yolov3.weights model_data/yolo.h5

  注釋:執行convert.py文件,此為將darknet的yolo轉換為可以用於keras的h5文件,生成的h5被保存在model_data下。命令中的convert.py和yolov3.vfg克隆下來后已經有了,不需要單獨下載。

  3.用已經被訓練好的yolo.h5進行圖片識別測試。執行:python yolo.py

  執行后會讓你輸入一張圖片的路徑,因為我准備的圖片(網上隨便找的)放在yolo.py同級目錄,所以直接輸入圖片名稱,沒有加路徑。

  過程和結果如下圖所示:

  以上結果表明快速開始項目成功,接下來我們進行搭建自己的數據集,進行模型的訓練以及訓練后模型用於測試圖片識別。

二.准備自己的數據集

  可以按照上面參考文章里面做法下載VOC數據集,然后清空里面內容,保留文件目錄結構。也可以直接手動創建如下目錄結構:

  這里面用到的文件夾是Annotation、ImageSets和JPEGImages。注意:需要在VOC2007再創建一個上級目錄VOCdevkit。

  其中文件夾Annotation中主要存放xml文件,每一個xml對應一張圖像,並且每個xml中存放的是標記的各個目標的位置和類別信息,命名通常與對應的原始圖像一樣;而ImageSets我們只需要用到Main文件夾,這里面存放的是一些文本文件,通常為train.txt、test.txt等,該文本文件里面的內容是需要用來訓練或測試的圖像的名字;JPEGImages文件夾中放我們已按統一規則命名好的原始圖像。

  原始圖片就不解釋了,而與原始圖片一 一對應的xml文件,可以使用LabelImg工具,具體使用方法百度即可。工具可以從參考的博客中的附帶地址下載,也可以自己網上找,很容易。

  將自己的圖片以及xml按照要求放好后,在VOC2007的同級目錄下建立convert_to_txt.py文件,拷貝下面的代碼,然后運行該py文件。該代碼是讀取上面的xml文件中圖片名稱,並保存在ImageSets/Main目錄下的txt文件中。注意:此處txt中僅有圖片名稱。

代碼:

 1 import os
 2 import random
 3  
 4 trainval_percent = 0.1
 5 train_percent = 0.9
 6 xmlfilepath = 'Annotations'
 7 txtsavepath = 'ImageSets\Main'
 8 total_xml = os.listdir(xmlfilepath)
 9  
10 num = len(total_xml)
11 list = range(num)
12 tv = int(num * trainval_percent)
13 tr = int(tv * train_percent)
14 trainval = random.sample(list, tv)
15 train = random.sample(trainval, tr)
16  
17 ftrainval = open('ImageSets/Main/trainval.txt', 'w')
18 ftest = open('ImageSets/Main/test.txt', 'w')
19 ftrain = open('ImageSets/Main/train.txt', 'w')
20 fval = open('ImageSets/Main/val.txt', 'w')
21  
22 for i in list:
23     name = total_xml[i][:-4] + '\n'
24     if i in trainval:
25         ftrainval.write(name)
26         if i in train:
27             ftest.write(name)
28         else:
29             fval.write(name)
30     else:
31         ftrain.write(name)
32  
33 ftrainval.close()
34 ftrain.close()
35 fval.close()
36 ftest.close()

  然后,回到從github上下載的源碼所在目錄,執行其中的voc_annotation.py,會在當前目錄生成新的三個txt文件,手動去掉名稱中2007_部分。當然,可以自己進入voc_annotation.py修改代碼,使生成的txt文件名中不包含2007_。

  注意:一開始VOC2007,也可以叫VOC2008之類,這樣此處的txt就會成為2008_xxx.txt。此外,有一個很關鍵的地方需要注意,必須修改,不然此處生成的三個新的txt文件中僅僅比前面Main下的txt中多了圖片路徑而已,並不包含框box的信息,這樣的話在后面的訓練步驟,由於沒有框的信息,僅僅是圖片路徑和名稱信息,是訓練不好的,即使可以得到訓練后的h5文件,但是當用這樣的h5文件去執行類似前面所說的測試圖片識別,效果就是將整幅圖框住,而不是框住你所要識別的部分。

  故所要做的是:在執行voc_annotation.py之前,打開它,進行修改。將其中最上面的sets改為你自己的,比如2012改為我得2007,要和前面的目錄年份保持一致。還需要將最上面的classes中的內容,改為你自己xml文件中object屬性中name屬性的值。你有哪些name值,就改為哪些,不然其中讀取xml框信息的代碼就不會執行。

上面是我的xml中一個object截圖,這里的name實際上為你用LableIma工具畫框時候給那個框的命名值。

至此,自己數據集的准備工作就完成了。

三.修改一些文件,然后執行訓練

  首先是修改model_data下的文件,放入你的類別,coco,voc這兩個文件都需要修改。這里的命名會成為最終檢測圖片時候框的框上的名稱。

  其次是yolov3.cfg文件

  這一步事后我和同學討論了下,得出的結論是,從0開始訓練自己的模型,則不需要下面的修改步驟,而如果想用遷移學習思想,用已經預訓練好的權重接着訓練,則需要下面的修改步驟。

  DE里直接打開cfg文件,ctrl+f搜 yolo, 總共會搜出3個含有yolo的地方。

  每個地方都要改3處,filters:3*(5+len(classes));

                                      classes: len(classes) = 1,我只識別一種,所以為1

                                      random:原來是1,顯存小改為0   

  如果要用預訓練的權重接着訓練,則需要執行以下代碼:然后執行原train.py就可以了。原train.py中有加載預訓練權重的代碼,並凍結部分層數,在此基礎上進行訓練。可以修改凍結層數。

  python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5

這個在github和參考的文章中均提到。

如果不用預訓練的權重,上一步不用執行(執行也沒影響),但是下面的train.py需要修改,改為如下所示(代碼出處為上文提到的參考博客中):直接復制替換原train.py即可

  1 """
  2 Retrain the YOLO model for your own dataset.
  3 """
  4 import numpy as np
  5 import keras.backend as K
  6 from keras.layers import Input, Lambda
  7 from keras.models import Model
  8 from keras.callbacks import TensorBoard, ModelCheckpoint, EarlyStopping
  9  
 10 from yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss
 11 from yolo3.utils import get_random_data
 12  
 13  
 14 def _main():
 15     annotation_path = 'train.txt'
 16     log_dir = 'logs/000/'
 17     classes_path = 'model_data/voc_classes.txt'
 18     anchors_path = 'model_data/yolo_anchors.txt'
 19     class_names = get_classes(classes_path)
 20     anchors = get_anchors(anchors_path)
 21     input_shape = (416,416) # multiple of 32, hw
 22     model = create_model(input_shape, anchors, len(class_names) )
 23     train(model, annotation_path, input_shape, anchors, len(class_names), log_dir=log_dir)
 24  
 25 def train(model, annotation_path, input_shape, anchors, num_classes, log_dir='logs/'):
 26     model.compile(optimizer='adam', loss={
 27         'yolo_loss': lambda y_true, y_pred: y_pred})
 28     logging = TensorBoard(log_dir=log_dir)
 29     checkpoint = ModelCheckpoint(log_dir + "ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5",
 30         monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)
 31     batch_size = 10
 32     val_split = 0.1
 33     with open(annotation_path) as f:
 34         lines = f.readlines()
 35     np.random.shuffle(lines)
 36     num_val = int(len(lines)*val_split)
 37     num_train = len(lines) - num_val
 38     print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))
 39  
 40     model.fit_generator(data_generator_wrap(lines[:num_train], batch_size, input_shape, anchors, num_classes),
 41             steps_per_epoch=max(1, num_train//batch_size),
 42             validation_data=data_generator_wrap(lines[num_train:], batch_size, input_shape, anchors, num_classes),
 43             validation_steps=max(1, num_val//batch_size),
 44             epochs=500,
 45             initial_epoch=0)
 46     model.save_weights(log_dir + 'trained_weights.h5')
 47  
 48 def get_classes(classes_path):
 49     with open(classes_path) as f:
 50         class_names = f.readlines()
 51     class_names = [c.strip() for c in class_names]
 52     return class_names
 53  
 54 def get_anchors(anchors_path):
 55     with open(anchors_path) as f:
 56         anchors = f.readline()
 57     anchors = [float(x) for x in anchors.split(',')]
 58     return np.array(anchors).reshape(-1, 2)
 59  
 60 def create_model(input_shape, anchors, num_classes, load_pretrained=False, freeze_body=False,
 61             weights_path='model_data/yolo_weights.h5'):
 62     K.clear_session() # get a new session
 63     image_input = Input(shape=(None, None, 3))
 64     h, w = input_shape
 65     num_anchors = len(anchors)
 66     y_true = [Input(shape=(h//{0:32, 1:16, 2:8}[l], w//{0:32, 1:16, 2:8}[l], \
 67         num_anchors//3, num_classes+5)) for l in range(3)]
 68  
 69     model_body = yolo_body(image_input, num_anchors//3, num_classes)
 70     print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes))
 71  
 72     if load_pretrained:
 73         model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
 74         print('Load weights {}.'.format(weights_path))
 75         if freeze_body:
 76             # Do not freeze 3 output layers.
 77             num = len(model_body.layers)-3
 78             for i in range(num): model_body.layers[i].trainable = False
 79             print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers)))
 80  
 81     model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
 82         arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5})(
 83         [*model_body.output, *y_true])
 84     model = Model([model_body.input, *y_true], model_loss)
 85     return model
 86 def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
 87     n = len(annotation_lines)
 88     np.random.shuffle(annotation_lines)
 89     i = 0
 90     while True:
 91         image_data = []
 92         box_data = []
 93         for b in range(batch_size):
 94             i %= n
 95             image, box = get_random_data(annotation_lines[i], input_shape, random=True)
 96             image_data.append(image)
 97             box_data.append(box)
 98             i += 1
 99         image_data = np.array(image_data)
100         box_data = np.array(box_data)
101         y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
102         yield [image_data, *y_true], np.zeros(batch_size)
103  
104 def data_generator_wrap(annotation_lines, batch_size, input_shape, anchors, num_classes):
105     n = len(annotation_lines)
106     if n==0 or batch_size<=0: return None
107     return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes)
108  
109 if __name__ == '__main__':
110     _main()   

  我是在cpu版本tensorflow上跑的,故特別慢,有運算資源的就不說了,如果資源有限,建議少弄些圖片和xml文件,這樣最后的txt文件中數據就少,跑起來輕松點。其次可以修改train.py中的迭代次數epochs的值,該值原作者設置的為500;也可以修改batch_size = 10的大小。

  注意:我第一次訓練時候,確實在目錄下自動生成了logs文件夾,並在其中生成000文件夾,然后里面放的是自己訓練好的h5文件。但是后來我調試代碼,刪除該目錄,再次訓練時,報如下錯誤:

 

  此時只需要手動創建logs文件夾和其內的000文件夾即可。嫌名字不好,可以自己修改train.py文件,改里面的保存目錄。

  下面為成功測試截圖:

  四.用自己訓練的h5文件進行測試

  先修改yolo.py文件中的模型路徑,如下所示,改為自己訓練后生成的h5文件路徑。

  然后執行,測試過程和前面所講一樣,因為我得沒怎么訓練,就不貼出很挫的測試效果圖了。在最初沒有框信息的txt文件訓練后,執行測試很慢,因為訓練時候根本不知道要框什么,改為正常txt后,訓練后的模型進行測試,速度就會很快。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM