TensorFlow實戰:Chapter-9下(DeepLabv3+在自己的資料集訓練)
基本配置
資料集處理
我的資料集是3分類問題,但因為資料集的保密協議,在後面的demo中我沒有放出原圖片,我會盡量將訓練細節寫出來。為了方便記錄,我又使用了CamVid資料集
(從這裡下載)測試了一下。
資料集處理分成三大步:
- 標註資料
- 製作指示檔案
- 將資料打包成TFRecord
相關資源:
對於CamVid
資料集,我將製作好的TFRecord上傳到了CSDN上,如果只是單單測試程式的話,可以直接下載使用。
同時,對應的用於生成CamVid
不同資料集的index檔案我也上傳到github了。
標註資料
這裡需要提前說明一個概念,我自己在這裡耽誤了很長時間:
ignore_label
如果你的資料集存在ignore_label
,注意不要把ignore_label
和background
混淆。 ignore_label
是沒有做標註的,不在預測範圍內的,ignore_label
是不參與計算loss的。我們在mask中將ignore_label
的灰度值標記為255
。
mask的灰度值設定
CamVid
有11個分類,我自己的資料集有3個分類。生成的mask必須為單通道(灰度圖),建議為png格式。
注意,在製作mask時,對所有object的灰度畫素值有要求。
對於所有object包括background
在內,在mask中要將灰度值標註為0~n,雖然產生的圖片很難看出區別,但是這對訓練是有效的。注意,不要把object的灰度值標註成10,20,100…(這樣程式碼會直接將灰度值和class匹配,這在調整權重等操作會有麻煩~)
再強調一下:我在製作個人的資料集上將background
標註為0,注意這是background
而不是ignore_label
.
最終,我在個人的資料集上關於影象的mask,設定如下:
- 將
background
的灰度值設定為0 - object1-2的灰度值設定為1,2
- 因為我的資料集中沒有
ignore_label
,所以沒有點設定為255(我的資料集和CamVid都是沒有的,如果存在ingore_label
,在視覺上是白色的)
對於CamVid
,原標註的資料集格式滿足要求。示例如下,可以看到這裡面是不存在ignore_label
(沒有白色),取一個圖片示例如下:
製作指引檔案
再製作TFRecord之前,需要有檔案指引將資料集分類成訓練/測試/驗證集,故我們需要建立指引檔案。
將上述生成的所有mask和原輸入圖片分在兩個資料夾下,我的設定如下:
/root/dataset/CamVid/image
:存放所有的輸入圖片,共有701張,這其中包括訓練/測試/驗證集的圖片/root/dataset/CamVid/mask
:存放所有的labeled圖片,共有701張,和輸入圖片是一一對應的
對於CamVid
資料集,建立了一個目錄/root/dataset/CamVid/index
,該目錄下包含三個.txt檔案:
train.txt
:所有訓練集的檔名稱trainval.txt
:所有驗證集的檔名稱val.txt
:所有測試集的檔名稱
對應的CamVid
資料集目錄如下:
#from /root/dataset/CamVid/
+ image
+ mask
+ index # 自己建立
- train.txt
- trainval.txt
- val.txt
+ tfrecord #自己建立
上述的三個.txt檔案,也是從CamVid上下載的,參照製作指令碼的要求。
詳細的來講,只存放圖片的名稱,對應train/val.txt檔案部分擷取(可以使用Sublime或其他文字編輯工具統一find然後刪除):
# train.txt
0001TP_006690
0001TP_006720
...
0016E5_08640
#共367行
# val.txt
0016E5_07959
...
0016E5_08157
0016E5_08159
# 共101行
對應的用於生成CamVid
不同資料集的index檔案我也上傳到github了。
將資料打包成TFRecord
對於CamVid
資料集,我將製作好的TFRecord上傳到了CSDN上,如果只是單單測試程式的話,可以直接下載使用。
標準的指令格式如下:
python ./build_voc2012_data.py \
--image_folder="${IMAGE_FOLDER}" \
--semantic_segmentation_folder="${SEMANTIC_SEG_FOLDER}" \
--list_folder="${LIST_FOLDER}" \
--image_format="jpg" \
--output_dir="${OUTPUT_DIR}"
${IMAGE_FOLDER}
:資料集中原輸入資料的檔案目錄地址${SEMANTIC_SEG_FOLDER}
:資料集中標籤的檔案目錄地址${LIST_FOLDER}
: 將資料集分類成訓練集、驗證集等的指示目錄檔案目錄image_format
:輸入圖片資料的格式,CamVid
的是png格式output_dir
:製作的TFRecord
存放的目錄地址(自己建立)
依據上面對於資料集的處理,對於CamVid
資料集,製作TFRecord,使用的指令如下:
# from /research/deeplab/dataset
python ./build_voc2012_data.py \
--image_folder="/root/dataset/CamVid/image" \
--semantic_segmentation_folder="/root/dataset/CamVid/mask" \
--list_folder="/root/dataset/CamVid/index" \
--image_format="png" \
--output_dir="/root/dataset/CamVid/tfrecord"
指定執行結果部分輸出如下:
#trainval.txt
>> Converting image 59/233 shard 0
>> Converting image 118/233 shard 1
>> Converting image 177/233 shard 2
>> Converting image 233/233 shard 3
#train.txt
>> Converting image 92/367 shard 0
>> Converting image 184/367 shard 1
>> Converting image 276/367 shard 2
>> Converting image 367/367 shard 3
#val.txt
>> Converting image 26/101 shard 0
>> Converting image 52/101 shard 1
>> Converting image 78/101 shard 2
>> Converting image 101/101 shard 3
可以在對應的/root/dataset/CamVid/tfrecord
目錄下檢視已經制作好的TFRecord檔案:
# from /root/dataset/CamVid/tfrecord
$: ls
train-00000-of-00004.tfrecord trainval-00002-of-00004.tfrecord
train-00001-of-00004.tfrecord trainval-00003-of-00004.tfrecord
train-00002-of-00004.tfrecord val-00000-of-00004.tfrecord
train-00003-of-00004.tfrecord val-00001-of-00004.tfrecord
trainval-00000-of-00004.tfrecord val-00002-of-00004.tfrecord
trainval-00001-of-00004.tfrecord val-00003-of-00004.tfrecord
這樣關於資料集的處理都完事了。
修改訓練指令碼
在DeepLabv3+模型的基礎上,主要需要修改以下幾個檔案
segmentation_dataset.py
檔案train_utils.py
segmentation_dataset.py
CamVid描述配置
# segmentation_dataset.py line 110
_CAMVID_INFORMATION = DatasetDescriptor(
splits_to_sizes={
'train': 367, # num of samples in images/training
'val': 101, # num of samples in images/validation
},
num_classes=12,
ignore_label=255,
)
因為CamVid共有11個classes,所以加上ignore_label
共12個。
個人資料集描述配置
對於我個人的資料集,一共有object1,object2,background三個類別,加上ignore_label
,所以num_classes=4.
_MYDATA_INFORMATION = DatasetDescriptor(
splits_to_sizes={
'train': 444, # num of samples in images/training
'val': 46, # num of samples in images/validation
},
num_classes=4,
ignore_label=255,
)
註冊資料集
_DATASETS_INFORMATION = {
'cityscapes': _CITYSCAPES_INFORMATION,
'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
'ade20k': _ADE20K_INFORMATION,
'mydata':_MYDATA_INFORMATION, #我自己的資料集
'camvid':_CAMVID_INFORMATION, #camvid示例
}
train_utils.py
對應的train_utils.py
中,先將109行關於exclude_list
的設定修改,作用是在使用預訓練權重時候,不載入該logit
層:
# Variables that will not be restored.
exclude_list = ['global_step','logits']
if not initialize_last_layer:
exclude_list.extend(last_layers)
資料不平衡
參考DeepLabv3+一作aquariusjay的解疑,如果你的問題是二分類或者和我的資料集類似,存在object之間imablance,這需要在train_utils.py的70行修改權重.
因為CamVid
資料集這個問題可以忽略,這裡重點說我個人資料集,因為是三分類問題,其中background
佔了非常大的比例,並且object2比object1要稍微少一點,最終的設定的權重比例為1:10:15
:
irgore_weight = 0
label0_weight =1 # 對應灰度值0,即background
label1_weight = 10 # 對應object1, mask 中灰度值1
label2_weight = 15 # 對應object2,.mask 中灰度值2
not_ignore_mask =
tf.to_float(tf.equal(scaled_labels, 0)) * label0_weight +
tf.to_float(tf.equal(scaled_labels, 1)) * label1_weight +
tf.to_float(tf.equal(scaled_labels, 2)) * label2_weight +
tf.to_float(tf.equal(scaled_labels, ignore_label)) * irgore_weight
tf.losses.softmax_cross_entropy(
one_hot_labels,
tf.reshape(logits, shape=[-1, num_classes]),
weights=not_ignore_mask,
scope=loss_scope)
在這個步驟,我曾把ignore_label
和background
混淆了,最後我在mask 中將background的標註值為0,對應的label0_weight=1,object1的標註值為1,對應的為label1_weight = 10,… 最後才訓練成功。
訓練和視覺化
如果想在DeepLab的基礎上fine-tune其他資料集, 可在train.py中修改輸入引數。有一些選項:
- 使用預訓練的所有權重,設定
initialize_last_layer=True
- 只使用網路的backbone,設定
initialize_last_layer=False
和last_layers_contain_logits_only=False
- 使用所有的預訓練權重,除了
logits
,因為如果是自己的資料集,對應的classes不同(這個我們前面已經設定不載入logits),可設定initialize_last_layer=False
和last_layers_contain_logits_only=True
最終,我的設定是:
initialize_last_layer=False
last_layers_contain_logits_only=True
初試訓練
注意:我在訓練CamVid
時,是沒有考慮類別之間的的平衡問題,所以沒有做imblance的修正,如果你是二分類或者存在嚴重的imblance情況,可以參考部分troubleshot中我遇到的問題。
依據repo上的訓練示例,注意如下幾個引數:
tf_initial_checkpoint
:預訓練的權重,因為CamVid和我的資料集都和CityScapes類似,所以我使用的是CityScapes的預訓練權重train_logdir
:訓練產生的檔案存放位置dataset_dir
:資料集的TFRecord檔案
我在CamVid上的訓練指令如下:
python deeplab/train.py \
--logtostderr \
--training_number_of_steps=300 \
--train_split="train" \
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--train_crop_size=513 \
--train_crop_size=513 \
--train_batch_size=2 \
--dataset="camvid" \
--tf_initial_checkpoint='/root/newP/official_tf/models-master/research/deeplab/backbone/deeplabv3_cityscapes_train/model.ckpt' \
--train_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
--dataset_dir='/root/dataset/CamVid/tfrecord'
這裡只設置了300個step,對於crop_size設定為513,只是為了測試訓練是否能夠正確執行。 考慮到我的計算資源顯示,我設定batchsize=2。
部分的訓練輸出如下:
INFO:tensorflow:Starting Session.
INFO:tensorflow:Saving checkpoint to path /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt
INFO:tensorflow:Starting Queues.
INFO:tensorflow:global_step/sec: 0
INFO:tensorflow:Recording summary at step 0.
INFO:tensorflow:global step 10: loss = 2.7773 (0.550 sec/step)
INFO:tensorflow:global step 20: loss = 2.6438 (0.531 sec/step)
INFO:tensorflow:global step 30: loss = 2.4824 (0.555 sec/step)
INFO:tensorflow:global step 40: loss = 2.4652 (0.564 sec/step)
#...
INFO:tensorflow:global step 300: loss = 1.9276 (0.534 sec/step)
INFO:tensorflow:Stopping Training.
INFO:tensorflow:Finished training! Saving model to disk.
視覺化和評估
deepLab提供了視覺化和評估工具,這裡對於剛才在CamVid上訓練的權重測試。
視覺化的結果
因為CamVid的圖片大小和CityScapes是不同的,這裡相關的引數設定為:
vis_split
:設定為測試集vis_crop_size
:設定360,480為圖片的大小dataset_dir
:設定為建立的TFRecordcolormap_type
:視覺化標註的顏色
最終,視覺化的指令如下:
# From tensorflow/models/research/
python deeplab/vis.py \
--logtostderr \
--vis_split="val" \
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--vis_crop_size=360 \
--vis_crop_size=480 \
--dataset="camvid" \
--colormap_type="pascal" \
--checkpoint_dir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
--vis_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/vis' \
--dataset_dir='/root/dataset/CamVid/tfrecord'
視覺化指令的輸出部分如下:
INFO:tensorflow:Restoring parameters from /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt-300
INFO:tensorflow:Visualizing batch 1 / 101
INFO:tensorflow:Visualizing batch 2 / 101
INFO:tensorflow:Visualizing batch 3 / 101
...
INFO:tensorflow:Visualizing batch 100 / 101
INFO:tensorflow:Visualizing batch 101 / 101
我截取了視覺化輸出圖:
可以看到模型是可以正確分類的,雖然結果不怎麼好。
評估結果
同樣的,eval的引數也需要做一些改變:
eval_split
:設定為測試集crop_size
:同樣設定為360和480dataset
:設定為camviddataset_dir
:設定為我們建立的資料集
我們評估的指令如下:
python deeplab/eval.py \
--logtostderr \
--eval_split="val" \
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--eval_crop_size=360 \
--eval_crop_size=480 \
--dataset="camvid" \
--checkpoint_dir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
--eval_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/eval' \
--dataset_dir='/root/dataset/CamVid/tfrecord'
評估指令的輸出部分如下:
INFO:tensorflow:Restoring parameters from /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt-300
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Starting evaluation at 2018-07-06-06:34:28
INFO:tensorflow:Evaluation [10/101]
INFO:tensorflow:Evaluation [20/101]
#...
INFO:tensorflow:Evaluation [101/101]
INFO:tensorflow:Finished evaluation at 2018-07-06-06:34:34
miou_1.0[0.149601415]
結果一般般,但是證明了我們訓練過程是沒有大問題~
進階訓練
在前面初始訓練後,針對CamVid
資料集,我做了進一步參考修改,簡單測試了一下新的結果。
我將前面儲存在train_logdir裡面的權重刪除了,一些引數的修改:
training_number_of_steps
設定為3000crop_size
縮小為321batch_size
可以提高到4
進階的訓練指令如下:
python deeplab/train.py \
--logtostderr \
--training_number_of_steps=3000 \
--train_split="train" \
--model_variant="xception_65" \
--atrous_rates=6 \
--atrous_rates=12 \
--atrous_rates=18 \
--output_stride=16 \
--decoder_output_stride=4 \
--train_crop_size=321 \
--train_crop_size=321 \
--train_batch_size=4 \
--dataset="camvid" \
--tf_initial_checkpoint='/root/newP/official_tf/models-master/research/deeplab/backbone/deeplabv3_cityscapes_train/model.ckpt' \
--train_logdir='/root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train' \
--dataset_dir='/root/dataset/CamVid/tfrecord'
進階訓練的輸出部分如下:
INFO:tensorflow:global step 2960: loss = 0.6281 (0.407 sec/step)
INFO:tensorflow:Saving checkpoint to path /root/newP/official_tf/models-master/research/deeplab/exp/camvid_train/train/model.ckpt
INFO:tensorflow:global_step/sec: 2.45499
INFO:tensorflow:Recording summary at step 2962.
INFO:tensorflow:global step 2970: loss = 0.8240 (0.439 sec/step)
INFO:tensorflow:global step 2980: loss = 0.9588 (0.385 sec/step)
INFO:tensorflow:global step 2990: loss = 0.8880 (0.412 sec/step)
INFO:tensorflow:global step 3000: loss = 0.8292 (0.392 sec/step)
進階訓練結果評估
重新使用eval.py做了驗證:
INFO:tensorflow:Evaluation [101/101]
INFO:tensorflow:Finished evaluation at 2018-07-06-07:03:30
miou_1.0[0.4015598]
可以看到,新的訓練結果有了顯著的提示。
進階訓練結果視覺化
重新使用vis.py,部分視覺化結果:
可以看到明顯比前面好多了。
部分troubleshot
一開始我是一直在DeepLabv3+上測試自己的資料集,故問題都是集中於自己的資料集上。
我主要犯了三個錯誤:
- 1:資料集的mask的生成
- 2:混淆了ignore_label和background
- 3:錯誤的設定各個object和background之間的權重
對於mask的生成,我在前面詳細的講了如何生成,這是非常重要的一步。,我犯的錯誤是將不同的object和background的畫素值設定為0,100,150.這導致後續的預測會有問題,無法解決imblance。
混淆了ignore_label和background
對於ignore_label
我曾經設定為0,對應的segmentation_dataset.py
檔案:
# segmentation_dataset.py
_CAMVID_INFORMATION = DatasetDescriptor(
splits_to_sizes={
'train': 367, # num of samples in images/training
'val': 101, # num of samples in images/validation
},
num_classes=3,
ignore_label=0,
)
這裡我錯誤的把ignore_label
設定為0,並且錯誤的將num_classes設定為3.
在train_utils.py
檔案中計算如下:
ignore_weight = 0
label0_weight =20
label1_weight = 20
not_ignore_mask =
tf.to_float(tf.equal(scaled_labels, 1)) * label0_weight
+ tf.to_float(tf.equal(scaled_labels, 2)) * label1_weight
+ tf.to_float(tf.equal(scaled_labels, ignore_label)) * ignore_weight
因為ignore_label
設定為0了,這會導致結果背景不做任何loss,因為我每次都是訓練了200個step,可以看到如下兩個圖,是有學習到資訊,但是在預測上是有存在問題的。
設定權重問題
預測是一種顏色
對應各個類別之間權重設定有問題,預測權重設定出現了問題:
## 輸出全藍色
irgore_weight = 0
label0_weight =10
label1_weight = 20
## 輸出全黑色
irgore_weight = 10
label0_weight =10
label1_weight = 10
## 輸出全綠色
irgore_weight = 0
label0_weight =20
label1_weight = 10
輸出結果為全藍色/黑色/綠色,對應的object2/background/object1設定過大,且沒有考慮background,這導致模型不計算object2/background/object1的loss,一股腦的預測藍色/黑色/綠色就能得到不錯的loss。
成功的訓練
我的資料集中大部分都是background,object1一般會比object2大一些,故最終測試的權重比例為1:10:15
,在實際過程中可通過畫素統計給出一個更為精確的值:
irgore_weight = 0
label0_weight =1 # background
label1_weight = 10 # object1
label2_weight = 15 #object2
not_ignore_mask =
tf.to_float(tf.equal(scaled_labels, 0)) * label0_weight +
tf.to_float(tf.equal(scaled_labels, 1)) * label1_weight +
tf.to_float(tf.equal(scaled_labels, 2)) * label2_weight +
tf.to_float(tf.equal(scaled_labels, ignore_label)) * irgore_weight
這是我在訓練200個step和4000個step之間模型的測試結果如下: