【PaddleDetection深度學習】中國交通標誌影象分類任務

語言: CN / TW / HK

highlight: a11y-dark theme: awesome-green


我正在參與掘金創作者訓練營第5期,點選瞭解活動詳情

1 引言

1.1 專案簡介

對於交通標誌識別系統,其核心作用就是可以準確並及時的識別道路交通標誌資訊獲取當前路況以及行車環境,從而起到提醒和輔助駕駛員對道路資訊的把控以及糾正錯誤交通行為的作用。

傳統的目標檢測演算法容易受到多種因素影響導致演算法實現困難、識別精度低、識別速率慢等問題。隨著深度學習的發展,人工智慧的檢測方法受到廣泛關注與認可,可以有效地解決誤檢率高、速度慢等問題。

為解決交通標誌識別問題,本專案使用飛槳場景應用開發套件PaddleDetecion中的PicoDet_LCNet模型進行訓練,預測,並完成整體流程。本專案包括環境安裝、資料準備、模型訓練、模型評估、模型預測、模型匯出、總結以及附錄等主要部分。

PP-PicoDet有著精度高(PicoDet-S僅1M引數量以內,416輸入COCO mAP達到30.6),速度快(PicoDet-S-320在SD865上可達150FPS),部署友好(支援Paddle Inference、Paddle Lite,支援快速匯出為ONNX格式,支援Python、C++、Android 部署)等特點。PicoDet具體的指標如下圖所示:

image.png

1.2 資料集介紹

該資料集源自中國交通標誌識別資料庫。里加資料科學俱樂部成員已經探索了它,以便對卷積神經網路進行一些訓練。

資料集由 58 個類別的 5998 張交通標誌影象組成。每個影象都是單個交通標誌的放大檢視。註釋提供影象屬性(檔名、寬度、高度)以及影象和類別中的交通標誌座標(例如,限速 5 公里/小時),如下圖所示。

image.png

image.png

該資料集由png圖片以及annotations.csv檔案構成。

annotations.csv構成:

csv file_name:包含交通標誌的影象的檔名 width:圖片寬度 height:圖片高度 x1:邊界矩形左上角X座標 y1:邊界矩形左上角Y座標 x2:邊界矩形右下角X座標 y2:邊界矩形右下角Y座標 category:交通標誌類別

2 環境安裝

2.1 克隆PaddleDetection

PaddleDetection作為成熟的目標檢測開發套件,提供了從資料準備、模型訓練、模型評估、模型匯出到模型部署的全流程。

使用如下命令完成克隆操作:

```linux

從github上克隆,若網速較慢也可以考慮從gitee上克隆

! git clone https://gitee.com/PaddlePaddle/PaddleDetection ```

2.2 安裝依賴庫

通過如下方式安裝PaddleDetection依賴,並設定環境變數

%cd ~/work/PaddleDetection/ !pip install -r requirements.txt %env PYTHONPATH=.:$PYTHONPATH %env CUDA_VISIBLE_DEVICES=0 通過如下命令驗證是否安裝成功

! python ppdet/modeling/tests/test_architectures.py

3 資料準備

該資料集源自中國交通標誌識別資料庫。里加資料科學俱樂部成員已經探索了它,以便對卷積神經網路進行一些培訓。

資料集由 58 個類別的 5998 張交通標誌影象組成。每個影象都是單個交通標誌的放大檢視。註釋提供影象屬性(檔名、寬度、高度)以及影象和類別中的交通標誌座標(例如,限速 5 公里/小時)

目前PaddleDetection支援:COCO VOC WiderFace, MOT四種資料格式。因此,先解壓資料集壓縮包,再按照VOC格式將csv檔案轉化為xml。

3.1 解壓資料集

``` %cd ~

解壓所掛載的資料集在同級目錄下

!unzip -oq data/data107275/archive(5).zip -d data/TrafficSignsVoc ```

3.2 資料集格式轉換

本專案採用VOC格式資料。VOC資料格式的目標檢測資料,是指每個影象檔案對應一個同名的xml檔案,xml檔案中標記物體框的座標和類別等資訊。

├── Annotations │ ├── 001_0001.xml │ ├── 001_0002.xml │ ... ├── pngImages │ ├── 001_0001.png │ ├── 001_0002.png │ ... ├── label_list.txt ├── train.txt └── valid.txt label_list.txt:

0 1 ... 57 train.txt/valid.txt:

./pngImages/014_0051.png ./Annotations/014_0051.xml ./pngImages/017_0031.png ./Annotations/017_0031.xml ./pngImages/015_0012.png ./Annotations/015_0012.xml ./pngImages/028_0025.png ./Annotations/028_0025.xml ... xml檔案中包含以下欄位:

  • filename,表示影象名稱。

    <filename>001_0008.png</filename> - size,表示影象尺寸。包括:影象寬度、影象高度、影象深度 <size> <width>118</width> <height>119</height> <depth>3</depth> </size> - object欄位,表示每個物體。包括

    • name: 目標物體類別名稱
    • pose: 關於目標物體姿態描述(非必須欄位)
    • truncated: 目標物體目標因為各種原因被截斷(非必須欄位)
    • occluded: 目標物體是否被遮擋(非必須欄位)
    • difficult: 目標物體是否是很難識別(非必須欄位)
    • bndbox: 物體位置座標,用左上角座標和右下角座標表示:xminyminxmaxymax

按照以上所述VOC格式,通過執行以下指令碼可以將csv檔案轉化為xml,並生成相應txt檔案

```python import os import numpy as np import codecs import pandas as pd import json from glob import glob import cv2 import shutil from sklearn.model_selection import train_test_split

from IPython import embed

1.標籤路徑

csv_file = "data/TrafficSignsVoc/annotations.csv" saved_path = "data/TrafficSignsVoc/VOC2007/" #VOC格式資料的儲存路徑 image_save_path = "./pngImages" image_raw_parh = "data/TrafficSignsVoc/images/"

2.建立需要的資料夾

if not os.path.exists(saved_path + "Annotations"): os.makedirs(saved_path + "Annotations") if not os.path.exists(saved_path + "pngImages/"): os.makedirs(saved_path + "pngImages/")

3.獲取待處理檔案

total_csv_annotations = {} annotations = pd.read_csv(csv_file,header=None).values for annotation in annotations: key = annotation[0].split(os.sep)[-1] value = np.array([annotation[1:]]) if key in total_csv_annotations.keys(): total_csv_annotations[key] = np.concatenate((total_csv_annotations[key],value),axis=0) else: total_csv_annotations[key] = value

4.讀取標註資訊,按VOC格式寫入xml

for filename,label in total_csv_annotations.items():

if filename == 'file_name':
    continue

height, width, channels = cv2.imread(image_raw_parh + filename).shape

with codecs.open(saved_path + "Annotations/"+filename.replace(".png",".xml"),"w","utf-8") as xml:
    xml.write('<annotation>\n')
    xml.write('\t<folder>' + 'pngImages' + '</folder>\n')
    xml.write('\t<filename>' + filename + '</filename>\n')
    xml.write('\t<size>\n')
    xml.write('\t\t<width>'+ str(width) + '</width>\n')
    xml.write('\t\t<height>'+ str(height) + '</height>\n')
    xml.write('\t\t<depth>' + str(channels) + '</depth>\n')
    xml.write('\t</size>\n')
    xml.write('\t<segmented>0</segmented>\n')

    if isinstance(label,float):
        xml.write('</annotation>')
        continue

    for label_detail in label:
        labels = label_detail

        xmin = int(labels[2])
        ymin = int(labels[3])
        xmax = int(labels[4])
        ymax = int(labels[5])
        label_ = labels[-1]
        if xmax <= xmin:
            pass
        elif ymax <= ymin:
            pass
        else:
            xml.write('\t<object>\n')
            xml.write('\t\t<name>'+label_+'</name>\n')
            xml.write('\t\t<pose>Unspecified</pose>\n')
            xml.write('\t\t<truncated>1</truncated>\n')
            xml.write('\t\t<difficult>0</difficult>\n')
            xml.write('\t\t<bndbox>\n')
            xml.write('\t\t\t<xmin>' + str(xmin) + '</xmin>\n')
            xml.write('\t\t\t<ymin>' + str(ymin) + '</ymin>\n')
            xml.write('\t\t\t<xmax>' + str(xmax) + '</xmax>\n')
            xml.write('\t\t\t<ymax>' + str(ymax) + '</ymax>\n')
            xml.write('\t\t</bndbox>\n')
            xml.write('\t</object>\n')
            print(filename,xmin,ymin,xmax,ymax,labels)
    xml.write('</annotation>')

6.建立txt檔案(可以不建立,後面用Paddlex劃分資料集時生成相應的txt檔案)

txtsavepath = saved_path ftrain = open(txtsavepath+'/train.txt', 'w') fval = open(txtsavepath+'/valid.txt', 'w') flabel = open(txtsavepath+'/label_list.txt', 'w')

total_files = glob(saved_path+"./Annotations/*.xml") total_files = [i.split("/")[-1].split(".xml")[0] for i in total_files]

將圖片複製到voc pngImages資料夾

for image in glob(image_raw_parh+"/*.png"): shutil.copy(image,saved_path+image_save_path)

train_files,val_files = train_test_split(total_files,test_size=0.15,random_state=42)

for file in train_files: ftrain.write("./pngImages/" + file + ".png" + " ./Annotations/" + file+ ".xml" + "\n")

val

for file in val_files: fval.write("./pngImages/" + file + ".png" + " ./Annotations/" + file+ ".xml" + "\n")

for i in range(58): flabel.write(str(i) + "\n")

ftrain.close() fval.close() flabel.close()

```

4 模型訓練

4.1 修改配置檔案

本專案使用的是work/PaddleDetection/configs/picodet/picodet_s_320_coco_lcnet.yml配置檔案,該配置檔案中涉及到的其他配置檔案以及修改的具體配置如下所示:

(1) work/PaddleDetection/configs/picodet/picodet_s_320_coco_lcnet.yml

  • 儲存訓練的輪數:snapshot_epoch: 10

(2) work/PaddleDetection/configs/picodet/base/picodet_320_reader.yml

  • 資料讀取程序數量,根據本地算力資源調整:worker_num: 6
  • 根據視訊記憶體大小調整:batch_size: 64

(3) work/PaddleDetection/configs/picodet/base/optimizer_300e.yml

  • 學習率:base_lr: 0.04
  • 訓練輪數:epoch: 300

(4) work/PaddleDetection/configs/datasets/voc.yml

  • 資料集包含的類別數:num_classes: 58
  • 圖片相對路徑:dataset_dir: dataset/VOC2007
  • anno_path: train.txt
  • label_list: label_list.txt
  • 資料格式metric: VOC

更多關於PaddleDetection的詳細資訊可參考30分鐘快速上手PaddleDetection以及官方說明文件

4.2 啟動訓練

通過指定visualDL視覺化工具,對loss變化曲線視覺化。僅需要指定 use_vdl 引數和 vdl_log_dir 引數即可。使用如下命令進行模型訓練。

```

選擇配置開始訓練。可以通過 -o 選項覆蓋配置檔案中的引數

!python tools/train.py -c configs/picodet/picodet_s_320_coco_lcnet.yml \ -o use_gpu=true \ -o pretrain_weights=https://paddledet.bj.bcebos.com/models/picodet_s_320_coco.pdparams \ --use_vdl=true \ --vdl_log_dir=vdl_dir/scalar \ --eval

指定配置檔案

設定或更改配置檔案裡的引數內容

預訓練權重

使用VisualDL記錄資料

指定VisualDL記錄資料的儲存路徑

邊訓練邊測試

```

4.3 訓練視覺化

上一步訓練過程中已開啟VisualDL視覺化工具,VisualDL記錄資料的儲存路徑為 work/PaddleDetection/vdl_dir/scalar,其訓練視覺化結果如下所示:

最終訓練結果 $mAP = 99.16%$,訓練效果很不錯,達到應用級別效果。

image.png

image.png

5 模型評估

``` !python tools/eval.py -c configs/picodet/picodet_s_320_coco_lcnet.yml \ -o weights=output/picodet_s_320_coco_lcnet/model_final.pdparams

指定模型配置檔案

載入訓練好的模型

```

6 模型預測

6.1 開始預測

載入訓練好的模型,置信度閾值設定為0.5,執行下行命令對驗證集或測試集圖片進行預測,此處挑選了一張驗證集圖片進行預測,並輸出預測後的結果到infer_output資料夾下。得到的預測結果如下所示:

image.png

``` !python3.7 tools/infer.py -c configs/picodet/picodet_s_320_coco_lcnet.yml \ --infer_img=/home/aistudio/work/PaddleDetection/dataset/VOC2007/pngImages/005_0047.png \ --output_dir=infer_output/ \ --draw_threshold=0.5 \ -o weights=output/picodet_s_320_coco_lcnet/model_final

指定模型配置檔案

測試圖片

結果輸出位置

置信度閾值

載入訓練好的模型

```

6.2 視覺化預測圖片

```python import cv2 import matplotlib.pyplot as plt import numpy as np

image = cv2.imread('infer_output/005_0047.png') plt.figure(figsize=(8,8)) plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) plt.show() ``` 執行結果如下圖所示:

image.png

7 模型匯出

將模型進行導成部署需要的模型格式。 執行下面命令,即可匯出模型。

預測模型會匯出到inference_model/目錄下,包括model.pdmodel、model.pdiparams、model.pdiparams.info和infer_cfg.yml四個檔案,分別表示模型的網路結構、模型權重、模型權重名稱和模型的配置檔案(包括資料預處理引數等)的流程配置檔案。

!python tools/export_model.py \ -c configs/picodet/picodet_s_320_coco_lcnet.yml \ -o weights=output/picodet_s_320_coco_lcnet/model_final.pdparams \ --output_dir=inference_model

8 總結

  • 🌟 經模型評估,對於經過300輪訓練的模型效果為:$mAP(0.50, 11point) = 99.16%$。
  • 🌟 PaddleDetecion中的PicoDet_LCNet模型上手方便,各種模型的配置檔案可根據自己需求去修改調整,可複用程度高,因此大大提高了效率。當然,PaddleDetection套件中還包含了很多種類的模型,提供多種主流目標檢測、例項分割、跟蹤、關鍵點檢測演算法,能夠滿足大大小小的開發需求,從而更好完成端到端全開發流程。
  • 🔥 注:本文使用的是飛槳深度學習開發套件,編譯器為AI Studio Notebook平臺。
  • 🔥 本文旨在為讀者提供深度學習流程思路參考,若需要執行時,請注意調整檔案路徑。