1、用labelImg標數據
2、將數據轉換為tfrecord
錯誤記錄:
NotFoundError:無法創建NewWriteableFile?
解決方法:您需要在運行此腳本的運行環境文件夾中自己創建一個目錄
1、前期準備工作
第一步:先將SSD框架下載到本地,解壓出來;SSD源碼下載
第二步:在解壓出來的主目錄下依次創建tfrecords_、train_model、VOC2007文件夾,再將之前在SSD目標檢測(2):如何制作自己的數據集(詳細說明附源碼)中制作的三個文件夾Annotations、ImageSets、JPEGImages全都拖入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文件,依次點擊:run、Edit 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 syssys.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
總結
以上是生活随笔為你收集整理的ssd训练自己数据集的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。