使用deeplabv3+訓練自己資料集(遷移學習)
阿新 • • 發佈:2020-09-27
# 概述
在前邊一篇文章,我們講了如何復現論文程式碼,使用pascal voc 2012資料集進行訓練和驗證,具體內容可以參考[《deeplab v3+在pascal_voc 2012資料集上進行訓練》](https://www.vcjmhg.top/train-deeplabv3-puls-with-pascal-voc-2012),在本篇文章,我們主要講述,如何對deeplab v3+進行遷移學習,也即如何使用deeplab v3+演算法來訓練個人的資料集。
# 1. 資料集準備
首先在開始之前我們先對資料集做一個簡單的說明,由於`deeplabv3+`使用的`TensorFlow`框架,並且為了提高訓練的速度,因此在訓練開始前,需要轉換成`tfrecorde`型別的檔案來進行訓練,因此,我們直接仿照pascal voc 2012資料集的資料結構來製作資料集,這樣我們在訓練所需圖片準備完成之後可以直接複用轉換`tfrecorde`的指令碼。
## 1.1 標註圖片,獲取json檔案
古人有句話:兵馬未動糧草先行,而對深度學習來說,糧草毫無疑問指的是訓練的資料,畢竟我們最終的模型都是依靠資料來餵養出來的:dog:!因此選擇一個趁手的標註工具很重要,此處我推薦使用`labelme`,標註起來相當方便。
下邊我簡單說一下`lableme`的**安裝方法**(此處建議使用Anconda來實現環境隔離)。
1. 安裝Ancodna環境,
執行如下命令:
```shell
conda create --name=labelme python=2.7(這一步python=*選擇自己的Python版本)
activate labelme
```
2. 安裝軟體與依賴
```sh
conda install pyqt
pip install labelme
```
3. 啟動與使用
```sh
activate labelme
labelme
```
啟動完成之後可以看到如下介面:
![image-20200927143131553](http://img.vcjmhg.top/20200927143139.png)
標註的時候,將物體用線條框起來即可,例如:
![img](http://img.vcjmhg.top/20200927143313)
## 1.2 轉換json,獲取png圖片
在影象標註完成之後,在我們對應設定的資料夾下有許多json,這些json檔案記錄了所標註圖片的位置以及圖片內容等資訊,根據這些資訊我們可以轉換成訓練所需要的mask圖(此處是png格式的圖片)。
雖然`labelme`中包含`labelme_json_to_dataset`來幫助我們將json圖片轉成png圖片,但是該命令有一個巨大的缺點就是無法實現批量轉換,因此需要我們自己寫一個批量轉換的指令碼來輔助轉換。
一個簡單的轉換指令碼如下:
```python
import os
#path = 'C:/Users/tj/Desktop/dd' # path為labelme標註後的.json檔案存放的路徑
path = 'C:\\Users\\Administrator\\Desktop\\第五次資料集擴充\\labels'
json_file = os.listdir(path)
for file in json_file:
if(file.split('.')[1]=='json'):
os.system("labelme_json_to_dataset %s" % (path + '/' + file)) #
# C:/soft/ev4/venv/Scripts/labelme_json_to_dataset.exe 為labelme_json_to_dataset.exe的路徑 path + '/' + file 為讀取.json路徑
print(path + '/' + file)
```
通過該指令碼每一個json檔案都會生成一個以其名字命名的資料夾。
![image-20200927151507055](http://img.vcjmhg.top/20200927151508.png)
進入該檔案我們可以看到有如下四個檔案:
```
img.png
lable.png
label_names.txt
label_viz.png
```
其中第二個檔案使我們所需要的用於訓練的檔案,因此我們需要將該檔案整合重新命名成其原來json檔案的檔名(主要原因是保證和原圖的檔名保持一致,便於後續訓練)。
從資料夾中提取圖片並重命名,我也簡單寫了一個指令碼,可以用於參考,具體內容如下:
```python
import os
path = 'c:\\Users\\Administrator\\Desktop\\temp\\'
output='c:\\Users\\Administrator\\Desktop\\output\\'
fileDirs=os.listdir(path)
for fileDir in fileDirs:
file=path+fileDir+"\\label.png"
if(os.path.exists(file)):
# 輸出的檔案直接以上層資料夾命名
end= len(fileDir);
fileName=fileDir[:end-5]
os.rename(file,output+fileName+".png")
```
此處處理完成我們便會的到一系列的mask圖片,此時我們便可以著手資料集的製作。
## 1.3 製作資料集
正如前邊所說,我們在製作資料集的時候仿照的是pascal voc 2012的資料集,因此需要建立預期類似資料夾結構。
1. 我們首先在`models/research/deeplab/datasets`資料夾下為自己的訓練集建立一個目錄,目錄名稱即自己的訓練集名稱。執行如下命令:
```sh
cd ~/models/research/deeplab/datasets
mkdir mydataset
cd mydataset
```
2. 建立與voc資料集類似的資料夾
```sh
# 存放mask檔案
mkdir SegmentationClassRaw
# 存放原圖
mkdir JPEGImages
# 存放資料集描述檔案
mkdir Segmentation
# 存放預訓練權重,如不需要預訓練權重可不建立
mkdir tf_initial_checkpoint
# 訓練權重儲存目錄
mkdir train_logs
# 評估以及測試結果的生成目錄
mkdir vis
# 存放tfrecorde
```
3. 將訓練資料放到指定資料夾中:
1. SegmentationClassRaw:存放mask檔案,也就是前邊我們所轉換提取的png圖片
2. JPEGImages:存放訓練集、驗證集以及測試集的原始圖片
3. Segmentation:存放資料集描述檔案,包含三個檔案`train.txt`、t`rainval.txt`、`val.txt`
1. train.txt:記錄訓練集的圖片名稱
2. trainval.txt:該檔案中所記錄的內容,後續既會被當做訓練集來訓練,後續也會被當做驗證集來做驗證
3. val.txt用以記錄驗證集的圖片名稱
4. 轉換成tfrecorde檔案。
在`dataset`目錄下,執行如下命令:
```python
python3 "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}"
```
執行成功後,會在tfrecorde目錄下出現如下檔案,證明轉換成功:
![image-20200927164917463](http://img.vcjmhg.top/20200927164918.png)
# 程式碼修改
### 在`models/research/deeplab/datasets`目錄下:
* 在`remove_gt_colormap.py`修改的內容如下:
51行左右,
```python
old_raw_pic=np.array(Image.open(filename))
#原來畫素比為0:1:2:3乘以50之後變成0:50:100:150
raw_pic=old_raw_pic*50
return raw_pic
```
* 在`data_generator.py`中修改的內容:
104行左右
```python
# has changed 增加資料集種類,以及訓練驗證集合的數量,修改物體類別3+1+1
_MYDATASET = DatasetDescriptor(
splits_to_sizes={
'train':392,
'trainval':98,
'val':5,
},
num_classes=5, # classes+label+ignore_label
ignore_label=255,
)
#has changed
_DATASETS_INFORMATION = {
'cityscapes': _CITYSCAPES_INFORMATION,
'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
'ade20k': _ADE20K_INFORMATION,
'mydataset':_MYDATASET,
}
```
### 在`models/research/deeplab/utils`下
* 在`get_dataset_colormap.py`檔案中
在第41行左右,增加訓練種類
```python
# has changed
_MYDATASET='mydataset'
```
在388行左右,直接使用pascal的colormap
```python
#has changed
elif dataset == _MYDATASET:
return create_pascal_label_colormap()
```
* 在`train_utils.py`中修改的內容
153行左右,進行訓練權重的修改。具體修改參考https://blog.csdn.net/jairana/article/details/83900226
```python
# has changed
ignore_weight = 0
label0_weight = 1 # 對應background,mask中灰度值0
label1_weight = 10 # 對應a,mask中灰度值1
label2_weight = 10 # 對應b,mask中灰度值2
label3_weight = 10 # 對應c,mask中灰度值為3
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, 3)) * label3_weight + \
tf.to_float(tf.equal(scaled_labels, ignore_label)) * ignore_weight
tf.losses.softmax_cross_entropy(
train_labels,
tf.reshape(logits, shape=[-1, num_classes]),
weights=not_ignore_mask,
scope=loss_scope)
# end change
```
228行,排除列表中增加logits
```python
exclude_list = ['global_step','logits']
```
### 在目錄`models/research/deeplab/deprecated`下
* `segmentation_dataset.py`檔案中
在90行,增加資料類別
```python
#has changed
_MYDATASET= DatasetDescriptor(
splits_to_sizes={
'train':392,
'trainval':98,
'val':5,
},
num_classes=5,
ignore_label=255,#background、ignore_label、ignore_label,即label數+2
)
```
在128行左右,註冊新資料集
```python
_DATASETS_INFORMATION = {
'cityscapes': _CITYSCAPES_INFORMATION,
'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
'ade20k': _ADE20K_INFORMATION,
# has changed
'mydataset':_MYDATASET
}
```
### 在`models/research/deeplab/train.py`目錄下
158行左右,修改兩個引數(使用所有的預訓練權重,除了logits,因為如果是自己的資料集,對應的classes不同(這個我們前面已經設定不載入logits),可設定initialize_last_layer=False和last_layers_contain_logits_only=True),可參考https://blog.csdn.net/u011974639/article/details/80948990
```python
# has changed
flags.DEFINE_boolean('initialize_last_layer',False,
'Initialize the last layer.')
flags.DEFINE_boolean('last_layers_contain_logits_only', True,
'Only consider logits as last layers or not.')
```
# 訓練與驗證
## 訓練
執行如下命令開始進行訓練:
```java
python train.py \
--logtostderr \
--training_number_of_steps=5000 \
--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,513" \
--train_batch_size=12 \
--dataset="mydataset" \
--tf_initial_checkpoint='init_models/deeplabv3_pascal_train_aug/model.ckpt' \
--train_logdir='datasets/mydataset/train_logs' \
--dataset_dir='datasets/mydataset/tfrecord'
```
## 驗證
```java
python 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="1217,1921" \
--checkpoint_dir='models/research/deeplab/datasets/mydataset/train_logs' \
--eval_logdir='datasets/mydataset/eval' \
--dataset_dir='datasets/mydataset/tfrecord' \
--max_number_of_evaluations=1
```
# 遇到的如果問題與解決方案
1. 無法找到slim。
解決方法:進入`models/research`目錄下執行
```sh
export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim:`pwd`/deeplab\
```
2. 資料格式不支援,檢查是否註冊了自己的數