ssd訓練自己數據集

1、用labelImg標數據

2、將數據轉換爲tfrecord

錯誤記錄:

NotFoundError:無法創建NewWriteableFile 

解決方法:您需要在運行此腳本的運行環境文件夾中自己創建一個目錄

1、前期準備工作

第一步:先將SSD框架下載到本地,解壓出來;SSD源碼下載

第二步:在解壓出來的主目錄下依次創建tfrecords_train_modelVOC2007文件夾,再將之前在SSD目標檢測(2):如何製作自己的數據集(詳細說明附源碼)中製作的三個文件夾AnnotationsImageSetsJPEGImages全都拖入VOC2007文件夾內;
第2.5步:爲方便操作不易混淆,請在PyCharm裏建立工程;得到的截圖如下,截圖說明如下:

         1、請注意紅色框VOCxxx使用的是具體的名字,不過一般都是VOC2007

         2、目錄對應的從屬關係不要出錯

   3、tfrecords_文件夾是用來存儲.tfrecords文件(後面有程序可以直接生成)

   4、train_model文件夾是用來存儲模型的記錄與參數的

這裏寫圖片描述

2、生成.tfrecords文件的代碼微調說明

第三步:修改標籤項——打開datasets文件夾中pascalvoc_common.py文件,將自己的標籤項填入。我之前做的圖片標籤.xml文件中,就只有一個標籤項“watch”,所以要根據你自己數據集實際情況進行修改;

在這裏插入圖片描述

第四步:修改讀取個數、讀取方式——打開datasets文件夾中的pascalvoc_to_tfrecords.py文件,

  • 修改67行SAMPLES_PER_FILES的個數;
  • 修改83行讀取方式爲'rb'
  • 如果你的文件不是.jpg格式,也可以修改圖片的類型;這裏寫圖片描述

3、生成.tfrecords文件

第五步:生成.tfrecords文件——打開tf_convert_data.py文件,依次點擊:run、Edit Configuration,在Parameters中填入以下內容,再運行tf_convert_data.py文件,在面板中得到成功信息,可以在tfrecords_文件夾下看到生成的.tfrecords文件;
 

--dataset_name=pascalvoc
--dataset_dir=./VOC2007/
--output_name=voc_2007_train
--output_dir=./tfrecords_

這裏寫圖片描述

4、重新訓練模型的代碼微調說明

第六步:修改訓練數據shape——打開datasets文件夾中的pascalvoc_2007.py文件,

    根據自己訓練數據修改:NUM_CLASSES = 類別數;

說明:TRAIN_STATISTICS的數值我並沒有深入瞭解,大於新數據集該標籤的總數一般都不會報錯。我的數據集是由20張、每張包含一隻手錶的圖片組成,所以下圖的值我設定爲20,大於20也沒有報錯,如果你有更精確的想法,請留言告訴大家!
在這裏插入圖片描述

第七步:修改類別個數——打開nets文件夾中的ssd_vgg_300.py文件,

     根據自己訓練類別數修改96 和97行:等於類別數+1

這裏寫圖片描述

第八步:修改類別個數——打開eval_ssd_network.py文件,

        修改66行的類別個數:等於類別數+1

這裏寫圖片描述

第九步:修改訓練步數epoch——打開train_ssd_network.py文件

  • 修改27行的數據格式,改爲'NHWC'
  • 修改135行的類別個數:等於類別數+1
  • 修改154行訓練總步數,None會無限訓練下去;
  • 說明:60行、63行是關於模型保存的參數;

這裏寫圖片描述

5、加載vgg_16,重新訓練模型

第十步:下載vgg_16模型——下載地址請點擊,密碼:ge3x;下載完成解壓後存入checkpoint文件中;
最後一步:重新訓練模型——打開train_ssd_network.py文件,依次點擊:runEdit Configuration,在Parameters中填入以下內容,再運行train_ssd_network.py文件

--train_dir=./train_model/
--dataset_dir=./tfrecords_/
--dataset_name=pascalvoc_2007
--dataset_split_name=train
--model_name=ssd_300_vgg
--checkpoint_path=./checkpoints/vgg_16.ckpt
--checkpoint_model_scope=vgg_16
--checkpoint_exclude_scopes=ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box
--trainable_scopes=ssd_300_vgg/conv6,ssd_300_vgg/conv7,ssd_300_vgg/block8,ssd_300_vgg/block9,ssd_300_vgg/block10,ssd_300_vgg/block11,ssd_300_vgg/block4_box,ssd_300_vgg/block7_box,ssd_300_vgg/block8_box,ssd_300_vgg/block9_box,ssd_300_vgg/block10_box,ssd_300_vgg/block11_box
--save_summaries_secs=60
--save_interval_secs=100
--weight_decay=0.0005
--optimizer=adam
--learning_rate=0.001
--learning_rate_decay_factor=0.94
--batch_size=4
--gpu_memory_fraction=0.7

注意:上面是輸入參數:

    --save_interval_secs是訓練多少次保存參數的步長;
    --optimizer是優化器;
    --learning_rate是學習率;
    --learning_rate_decay_factor是學習率衰減因子;
    如果你的機器比較強大,可以適當增大--batch_size的數值,以及調高GPU的佔比--gpu_memory_fraction
    --model_name:我並沒有嘗試使用其他的模型做增量訓練,如果你有需要,也請留言聯繫我,我很樂意研究;

若得到下圖日誌,即說明模型開始訓練:
在這裏插入圖片描述

訓練結束可以在train_model文件夾下看到生成的參數文件;

在這裏插入圖片描述

到這裏,訓練終於結束了!!!

二、結果展示

這是我訓練的loss,我的數據集總共就20張圖片,進行4.8W次訓練用了將近一個小時,我的配置是GTX1060的單顯卡;

在這裏插入圖片描述

1、在日誌中,選取最後一次生成模型作爲測試模型進行測試;
2、在demo文件夾下放入測試圖片;
3、最後在notebooks文件夾下建立demo_test.py測試文件,代碼如下:
4、注意第48行,導入的新模型的名稱是否正確

# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/20; 15:19
# -*- python3.6
import os
import math
import random
import numpy as np
import tensorflow as tf
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from nets import ssd_vgg_300, ssd_common, np_methods
from preprocessing import ssd_vgg_preprocessing
from notebooks import visualization
import sys

sys.path.append('../')
slim = tf.contrib.slim
# TensorFlow session: grow memory when needed. TF, DO NOT USE ALL MY GPU MEMORY!!!
gpu_options = tf.GPUOptions(allow_growth=True)
config = tf.ConfigProto(log_device_placement=False, gpu_options=gpu_options)
isess = tf.InteractiveSession(config=config)


# 定義數據格式,設置佔位符
net_shape = (300, 300)
# 輸入圖像的通道排列形式,'NHWC'表示 [batch_size,height,width,channel]
data_format = 'NHWC'
# 預處理,以Tensorflow backend, 將輸入圖片大小改成 300x300,作爲下一步輸入
img_input = tf.placeholder(tf.uint8, shape=(None, None, 3))
# 數據預處理,將img_input輸入的圖像resize爲300大小,labels_pre,bboxes_pre,bbox_img待解析
image_pre, labels_pre, bboxes_pre, bbox_img = ssd_vgg_preprocessing.preprocess_for_eval(
    img_input, None, None, net_shape, data_format, resize=ssd_vgg_preprocessing.Resize.WARP_RESIZE)
# 拓展爲4維變量用於輸入
image_4d = tf.expand_dims(image_pre, 0)

# 定義SSD模型
# 是否複用,目前我們沒有在訓練所以爲None
reuse = True if 'ssd_net' in locals() else None
# 調出基於VGG神經網絡的SSD模型對象,注意這是一個自定義類對象
ssd_net = ssd_vgg_300.SSDNet()
# 得到預測類和預測座標的Tensor對象,這兩個就是神經網絡模型的計算流程
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
    predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse)

# 導入新訓練的模型參數
ckpt_filename = '../train_model/model.ckpt-xxx'   # 注意xxx代表的數字是否和文件夾下的一致
# ckpt_filename = '../checkpoints/VGG_VOC0712_SSD_300x300_ft_iter_120000.ckpt'
isess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(isess, ckpt_filename)

# 在網絡模型結構中,提取搜索網格的位置
# 根據模型超參數,得到每個特徵層(這裏用了6個特徵層,分別是4,7,8,9,10,11)的anchors_boxes
ssd_anchors = ssd_net.anchors(net_shape)
"""
每層的anchors_boxes包含4個arrayList,前兩個List分別是該特徵層下x,y座標軸對於原圖(300x300)大小的映射
第三,四個List爲anchor_box的長度和寬度,同樣是經過歸一化映射的,根據每個特徵層box數量的不同,這兩個List元素
個數會變化。其中,長寬的值根據超參數anchor_sizes和anchor_ratios制定。
"""


# 主流程函數
def process_image(img, select_threshold=0.6, nms_threshold=.01, net_shape=(300, 300)):
    # select_threshold:box閾值——每個像素的box分類預測數據的得分會與box閾值比較,高於一個box閾值則認爲這個box成功框到了一個對象
    # nms_threshold:重合度閾值——同一對象的兩個框的重合度高於該閾值,則運行下面去重函數

    # 執行SSD模型,得到4維輸入變量,分類預測,座標預測,rbbox_img參數爲最大檢測範圍,本文固定爲[0,0,1,1]即全圖
    rimg, rpredictions, rlocalisations, rbbox_img = isess.run([image_4d, predictions, localisations, bbox_img],
                                                              feed_dict={img_input: img})

    # ssd_bboxes_select()函數根據每個特徵層的分類預測分數,歸一化後的映射座標,
    # ancohor_box的大小,通過設定一個閾值計算得到每個特徵層檢測到的對象以及其分類和座標
    rclasses, rscores, rbboxes = np_methods.ssd_bboxes_select(
        rpredictions, rlocalisations, ssd_anchors,
        select_threshold=select_threshold, img_shape=net_shape, num_classes=21, decode=True)
    """
    這個函數做的事情比較多,這裏說的細緻一些:
    首先是輸入,輸入的數據爲每個特徵層(一共6個,見上文)的:
                                                rpredictions: 分類預測數據,
                                                rlocalisations: 座標預測數據,
                                                ssd_anchors: anchors_box數據
                                            其中:
                                               分類預測數據爲當前特徵層中每個像素的每個box的分類預測
                                               座標預測數據爲當前特徵層中每個像素的每個box的座標預測
                                               anchors_box數據爲當前特徵層中每個像素的每個box的修正數據

        函數根據座標預測數據和anchors_box數據,計算得到每個像素的每個box的中心和長寬,這個中心座標和長寬會根據一個算法進行些許的修正,
    從而得到一個更加準確的box座標;修正的算法會在後文中詳細解釋,如果只是爲了理解算法流程也可以不必深究這個,因爲這個修正算法屬於經驗算
    法,並沒有太多邏輯可循。
        修正完box和中心後,函數會計算每個像素的每個box的分類預測數據的得分,當這個分數高於一個閾值(這裏是0.5)則認爲這個box成功
    框到了一個對象,然後將這個box的座標數據,所屬分類和分類得分導出,從而得到:
        rclasses:所屬分類
        rscores:分類得分
        rbboxes:座標
        
        最後要注意的是,同一個目標可能會在不同的特徵層都被檢測到,並且他們的box座標會有些許不同,這裏並沒有去掉重複的目標,而是在下文
    中專門用了一個函數來去重
    """

    # 檢測有沒有超出檢測邊緣
    rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)
    rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)
    # 去重,將重複檢測到的目標去掉
    rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=nms_threshold)
    # 將box的座標重新映射到原圖上(上文所有的座標都進行了歸一化,所以要逆操作一次)
    rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)
    return rclasses, rscores, rbboxes


# 測試的文件夾
path = '../demo/'
image_names = sorted(os.listdir(path))
# 文件夾中的第幾張圖,-1代表最後一張
img = mpimg.imread(path + image_names[-1])
rclasses, rscores, rbboxes = process_image(img)

# visualization.bboxes_draw_on_img(img, rclasses, rscores, rbboxes, visualization.colors_plasma)
visualization.plt_bboxes(img, rclasses, rscores, rbboxes)

結果展示:這是我自己拍的照片,得到的識別效果還算勉強吧(請自動忽略我那性感的手毛!)

在這裏插入圖片描述

如果你的測試結果是下面這樣的:

導致的原因:

   1 訓練次數太少,loss過高——解決方法除了優化數據集外,就是增大訓練次數(要明白谷歌公佈的模型都是在大型集羣上訓練好多天的結果,我們就在GTX1060單顯卡上訓練4.8W次就想出非常好的結果?偶然的成功比失敗更可怕,而且想彎道超谷歌不太可能吧!)
   2 另外上面程序65行的select_threshold、 nms_threshold參數你也可以做調整;觀察下圖可以發現誤標框框的預測值都小於0.55,而唯一正確的框框預測值等於0.866。所以認真理解上面程序66、67行我寫的註釋,對你的問題會有幫助;
在這裏插入圖片描述

本博客用的測試數據集在這,只有20張標記圖片。並不包含最後訓練得到的模型

參考自https://blog.csdn.net/zzZ_CMing/article/details/81131523

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