1、用labelImg標(biāo)數(shù)據(jù)
2、將數(shù)據(jù)轉(zhuǎn)換為tfrecord
錯(cuò)誤記錄:
NotFoundError:無(wú)法創(chuàng)建NewWriteableFile?
解決方法:您需要在運(yùn)行此腳本的運(yùn)行環(huán)境文件夾中自己創(chuàng)建一個(gè)目錄
1、前期準(zhǔn)備工作
第一步:先將SSD框架下載到本地,解壓出來(lái);SSD源碼下載
第二步:在解壓出來(lái)的主目錄下依次創(chuàng)建tfrecords_、train_model、VOC2007文件夾,再將之前在SSD目標(biāo)檢測(cè)(2):如何制作自己的數(shù)據(jù)集(詳細(xì)說明附源碼)中制作的三個(gè)文件夾Annotations、ImageSets、JPEGImages全都拖入VOC2007文件夾內(nèi);
第2.5步:為方便操作不易混淆,請(qǐng)?jiān)赑yCharm里建立工程;得到的截圖如下,截圖說明如下:
? ? ? ? ?1、請(qǐng)注意紅色框VOCxxx使用的是具體的名字,不過一般都是VOC2007;
? ? ? ? ?2、目錄對(duì)應(yīng)的從屬關(guān)系不要出錯(cuò)
? ?3、tfrecords_文件夾是用來(lái)存儲(chǔ).tfrecords文件(后面有程序可以直接生成)
? ?4、train_model文件夾是用來(lái)存儲(chǔ)模型的記錄與參數(shù)的
2、生成.tfrecords文件的代碼微調(diào)說明
第三步:修改標(biāo)簽項(xiàng)——打開datasets文件夾中pascalvoc_common.py文件,將自己的標(biāo)簽項(xiàng)填入。我之前做的圖片標(biāo)簽.xml文件中,就只有一個(gè)標(biāo)簽項(xiàng)“watch”,所以要根據(jù)你自己數(shù)據(jù)集實(shí)際情況進(jìn)行修改;
第四步:修改讀取個(gè)數(shù)、讀取方式——打開datasets文件夾中的pascalvoc_to_tfrecords.py文件,
- 修改67行SAMPLES_PER_FILES的個(gè)數(shù);
- 修改83行讀取方式為'rb';
- 如果你的文件不是.jpg格式,也可以修改圖片的類型;
3、生成.tfrecords文件
第五步:生成.tfrecords文件——打開tf_convert_data.py文件,依次點(diǎn)擊:run、Edit Configuration,在Parameters中填入以下內(nèi)容,再運(yùn)行tf_convert_data.py文件,在面板中得到成功信息,可以在tfrecords_文件夾下看到生成的.tfrecords文件;
?
--dataset_name=pascalvoc
--dataset_dir=./VOC2007/
--output_name=voc_2007_train
--output_dir=./tfrecords_
4、重新訓(xùn)練模型的代碼微調(diào)說明
第六步:修改訓(xùn)練數(shù)據(jù)shape——打開datasets文件夾中的pascalvoc_2007.py文件,
? ? 根據(jù)自己訓(xùn)練數(shù)據(jù)修改:NUM_CLASSES = 類別數(shù);
說明:TRAIN_STATISTICS的數(shù)值我并沒有深入了解,大于新數(shù)據(jù)集該標(biāo)簽的總數(shù)一般都不會(huì)報(bào)錯(cuò)。我的數(shù)據(jù)集是由20張、每張包含一只手表的圖片組成,所以下圖的值我設(shè)定為20,大于20也沒有報(bào)錯(cuò),如果你有更精確的想法,請(qǐng)留言告訴大家!
第七步:修改類別個(gè)數(shù)——打開nets文件夾中的ssd_vgg_300.py文件,
? ? ?根據(jù)自己訓(xùn)練類別數(shù)修改96 和97行:等于類別數(shù)+1
第八步:修改類別個(gè)數(shù)——打開eval_ssd_network.py文件,
? ? ? ??修改66行的類別個(gè)數(shù):等于類別數(shù)+1;
第九步:修改訓(xùn)練步數(shù)epoch——打開train_ssd_network.py文件
- 修改27行的數(shù)據(jù)格式,改為'NHWC';
- 修改135行的類別個(gè)數(shù):等于類別數(shù)+1;
- 修改154行訓(xùn)練總步數(shù),None會(huì)無(wú)限訓(xùn)練下去;
- 說明:60行、63行是關(guān)于模型保存的參數(shù);
5、加載vgg_16,重新訓(xùn)練模型
第十步:下載vgg_16模型——下載地址請(qǐng)點(diǎn)擊,密碼:ge3x;下載完成解壓后存入checkpoint文件中;
最后一步:重新訓(xùn)練模型——打開train_ssd_network.py文件,依次點(diǎn)擊:run、Edit Configuration,在Parameters中填入以下內(nèi)容,再運(yùn)行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
注意:上面是輸入?yún)?shù):
? ? --save_interval_secs是訓(xùn)練多少次保存參數(shù)的步長(zhǎng);
? ? --optimizer是優(yōu)化器;
? ? --learning_rate是學(xué)習(xí)率;
? ? --learning_rate_decay_factor是學(xué)習(xí)率衰減因子;
? ? 如果你的機(jī)器比較強(qiáng)大,可以適當(dāng)增大--batch_size的數(shù)值,以及調(diào)高GPU的占比--gpu_memory_fraction
? ? --model_name:我并沒有嘗試使用其他的模型做增量訓(xùn)練,如果你有需要,也請(qǐng)留言聯(lián)系我,我很樂意研究;
若得到下圖日志,即說明模型開始訓(xùn)練:
訓(xùn)練結(jié)束可以在train_model文件夾下看到生成的參數(shù)文件;
到這里,訓(xùn)練終于結(jié)束了!!!
二、結(jié)果展示
這是我訓(xùn)練的loss,我的數(shù)據(jù)集總共就20張圖片,進(jìn)行4.8W次訓(xùn)練用了將近一個(gè)小時(shí),我的配置是GTX1060的單顯卡;
1、在日志中,選取最后一次生成模型作為測(cè)試模型進(jìn)行測(cè)試;
2、在demo文件夾下放入測(cè)試圖片;
3、最后在notebooks文件夾下建立demo_test.py測(cè)試文件,代碼如下:
4、注意第48行,導(dǎo)入的新模型的名稱是否正確;
# -*- 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)# 定義數(shù)據(jù)格式,設(shè)置占位符
net_shape = (300, 300)
# 輸入圖像的通道排列形式,'NHWC'表示 [batch_size,height,width,channel]
data_format = 'NHWC'
# 預(yù)處理,以Tensorflow backend, 將輸入圖片大小改成 300x300,作為下一步輸入
img_input = tf.placeholder(tf.uint8, shape=(None, None, 3))
# 數(shù)據(jù)預(yù)處理,將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模型
# 是否復(fù)用,目前我們沒有在訓(xùn)練所以為None
reuse = True if 'ssd_net' in locals() else None
# 調(diào)出基于VGG神經(jīng)網(wǎng)絡(luò)的SSD模型對(duì)象,注意這是一個(gè)自定義類對(duì)象
ssd_net = ssd_vgg_300.SSDNet()
# 得到預(yù)測(cè)類和預(yù)測(cè)坐標(biāo)的Tensor對(duì)象,這兩個(gè)就是神經(jīng)網(wǎng)絡(luò)模型的計(jì)算流程
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse)# 導(dǎo)入新訓(xùn)練的模型參數(shù)
ckpt_filename = '../train_model/model.ckpt-xxx' # 注意xxx代表的數(shù)字是否和文件夾下的一致
# 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)# 在網(wǎng)絡(luò)模型結(jié)構(gòu)中,提取搜索網(wǎng)格的位置
# 根據(jù)模型超參數(shù),得到每個(gè)特征層(這里用了6個(gè)特征層,分別是4,7,8,9,10,11)的anchors_boxes
ssd_anchors = ssd_net.anchors(net_shape)
"""
每層的anchors_boxes包含4個(gè)arrayList,前兩個(gè)List分別是該特征層下x,y坐標(biāo)軸對(duì)于原圖(300x300)大小的映射
第三,四個(gè)List為anchor_box的長(zhǎng)度和寬度,同樣是經(jīng)過歸一化映射的,根據(jù)每個(gè)特征層box數(shù)量的不同,這兩個(gè)List元素
個(gè)數(shù)會(huì)變化。其中,長(zhǎng)寬的值根據(jù)超參數(shù)anchor_sizes和anchor_ratios制定。
"""# 主流程函數(shù)
def process_image(img, select_threshold=0.6, nms_threshold=.01, net_shape=(300, 300)):# select_threshold:box閾值——每個(gè)像素的box分類預(yù)測(cè)數(shù)據(jù)的得分會(huì)與box閾值比較,高于一個(gè)box閾值則認(rèn)為這個(gè)box成功框到了一個(gè)對(duì)象# nms_threshold:重合度閾值——同一對(duì)象的兩個(gè)框的重合度高于該閾值,則運(yùn)行下面去重函數(shù)# 執(zhí)行SSD模型,得到4維輸入變量,分類預(yù)測(cè),坐標(biāo)預(yù)測(cè),rbbox_img參數(shù)為最大檢測(cè)范圍,本文固定為[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()函數(shù)根據(jù)每個(gè)特征層的分類預(yù)測(cè)分?jǐn)?shù),歸一化后的映射坐標(biāo),# ancohor_box的大小,通過設(shè)定一個(gè)閾值計(jì)算得到每個(gè)特征層檢測(cè)到的對(duì)象以及其分類和坐標(biāo)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)"""這個(gè)函數(shù)做的事情比較多,這里說的細(xì)致一些:首先是輸入,輸入的數(shù)據(jù)為每個(gè)特征層(一共6個(gè),見上文)的:rpredictions: 分類預(yù)測(cè)數(shù)據(jù),rlocalisations: 坐標(biāo)預(yù)測(cè)數(shù)據(jù),ssd_anchors: anchors_box數(shù)據(jù)其中:分類預(yù)測(cè)數(shù)據(jù)為當(dāng)前特征層中每個(gè)像素的每個(gè)box的分類預(yù)測(cè)坐標(biāo)預(yù)測(cè)數(shù)據(jù)為當(dāng)前特征層中每個(gè)像素的每個(gè)box的坐標(biāo)預(yù)測(cè)anchors_box數(shù)據(jù)為當(dāng)前特征層中每個(gè)像素的每個(gè)box的修正數(shù)據(jù)函數(shù)根據(jù)坐標(biāo)預(yù)測(cè)數(shù)據(jù)和anchors_box數(shù)據(jù),計(jì)算得到每個(gè)像素的每個(gè)box的中心和長(zhǎng)寬,這個(gè)中心坐標(biāo)和長(zhǎng)寬會(huì)根據(jù)一個(gè)算法進(jìn)行些許的修正,從而得到一個(gè)更加準(zhǔn)確的box坐標(biāo);修正的算法會(huì)在后文中詳細(xì)解釋,如果只是為了理解算法流程也可以不必深究這個(gè),因?yàn)檫@個(gè)修正算法屬于經(jīng)驗(yàn)算法,并沒有太多邏輯可循。修正完box和中心后,函數(shù)會(huì)計(jì)算每個(gè)像素的每個(gè)box的分類預(yù)測(cè)數(shù)據(jù)的得分,當(dāng)這個(gè)分?jǐn)?shù)高于一個(gè)閾值(這里是0.5)則認(rèn)為這個(gè)box成功框到了一個(gè)對(duì)象,然后將這個(gè)box的坐標(biāo)數(shù)據(jù),所屬分類和分類得分導(dǎo)出,從而得到:rclasses:所屬分類rscores:分類得分rbboxes:坐標(biāo)最后要注意的是,同一個(gè)目標(biāo)可能會(huì)在不同的特征層都被檢測(cè)到,并且他們的box坐標(biāo)會(huì)有些許不同,這里并沒有去掉重復(fù)的目標(biāo),而是在下文中專門用了一個(gè)函數(shù)來(lái)去重"""# 檢測(cè)有沒有超出檢測(cè)邊緣rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)# 去重,將重復(fù)檢測(cè)到的目標(biāo)去掉rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=nms_threshold)# 將box的坐標(biāo)重新映射到原圖上(上文所有的坐標(biāo)都進(jìn)行了歸一化,所以要逆操作一次)rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)return rclasses, rscores, rbboxes# 測(cè)試的文件夾
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)
結(jié)果展示:這是我自己拍的照片,得到的識(shí)別效果還算勉強(qiáng)吧(請(qǐng)自動(dòng)忽略我那性感的手毛!)
如果你的測(cè)試結(jié)果是下面這樣的:
導(dǎo)致的原因:
? ?1 訓(xùn)練次數(shù)太少,loss過高——解決方法除了優(yōu)化數(shù)據(jù)集外,就是增大訓(xùn)練次數(shù)(要明白谷歌公布的模型都是在大型集群上訓(xùn)練好多天的結(jié)果,我們就在GTX1060單顯卡上訓(xùn)練4.8W次就想出非常好的結(jié)果?偶然的成功比失敗更可怕,而且想彎道超谷歌不太可能吧!)
? ?2 另外上面程序65行的select_threshold、 nms_threshold參數(shù)你也可以做調(diào)整;觀察下圖可以發(fā)現(xiàn)誤標(biāo)框框的預(yù)測(cè)值都小于0.55,而唯一正確的框框預(yù)測(cè)值等于0.866。所以認(rèn)真理解上面程序66、67行我寫的注釋,對(duì)你的問題會(huì)有幫助;
本博客用的測(cè)試數(shù)據(jù)集在這,只有20張標(biāo)記圖片。并不包含最后訓(xùn)練得到的模型
參考自https://blog.csdn.net/zzZ_CMing/article/details/81131523
總結(jié)
以上是生活随笔為你收集整理的ssd训练自己数据集的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。