基於PaddleX的駕駛員狀態識別和Paddle-Lite部署
項目簡介:
該項目使用PaddleX提供的圖像分類模型,在 kaggle 駕駛員狀態檢測數據集進行訓練;
訓練得到的模型能夠區分駕駛員正常駕駛、打電話、喝水等等不同動作,準確率爲0.979;
並使用PaddleLite進行模型的量化和壓縮;
該項目使用CPU環境或GPU環境運行,PaddleX會自動選擇合適的環境;
目錄:
- PaddleX工具簡介;
- 數據集簡介;
- 定義數據加載器;
- 定義並訓練模型;
- 評估模型性能;
- 使用PaddleLite進行模型壓縮;
- 總結
一、PaddleX 工具簡介:
PaddleX簡介:PaddleX是飛槳全流程開發工具,集飛槳核心框架、模型庫、工具及組件等深度學習開發所需全部能力於一身,打通深度學習開發全流程,並提供簡明易懂的Python API,方便用戶根據實際生產需求進行直接調用或二次開發,爲開發者提供飛槳全流程開發的最佳實踐。目前,該工具代碼已開源於GitHub,同時可訪問PaddleX在線使用文檔,快速查閱讀使用教程和API文檔說明。
PaddleX代碼GitHub鏈接:https://github.com/PaddlePaddle/PaddleX/tree/develop
PaddleX文檔鏈接:https://paddlex.readthedocs.io/zh_CN/latest/index.html
PaddleX官網鏈接:https://www.paddlepaddle.org.cn/paddle/paddlex
二、數據集簡介:
數據集地址:https://www.kaggle.com/c/state-farm-distracted-driver-detection
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ex1qr7ru-1590022154854)(https://storage.googleapis.com/kaggle-competitions/kaggle/5048/media/output_DEb8oT.gif)]
該數據集由kaggle提供,共包括十個類別:
'c0': 'normal driving',
'c1': 'texting-right',
'c2': 'talking on the phone-right',
'c3': 'texting-left',
'c4': 'talking on the phone-left',
'c5': 'operating the radio',
'c6': 'drinking',
'c7': 'reaching behind',
'c8': 'hair and makeup',
'c9': 'talking to passenger'
#解壓數據集
# !unzip /home/aistudio/data/data35503/imgs.zip -d /home/aistudio/work/imgs
# !cp /home/aistudio/data/data35503/lbls.csv /home/aistudio/work/
安裝paddleX和1.7.0版本的paddlepaddle(這是由於paddlex並不支持最新版本)
!pip install paddlex -i https://mirror.baidu.com/pypi/simple
!pip install paddlepaddle-gpu==1.7.0.post107 -i https://mirror.baidu.com/pypi/simple
import os
# 設置使用0號GPU卡(如無GPU,執行此代碼後仍然會使用CPU訓練模型)
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
os.chdir('/home/aistudio/work/')
# jupyter中使用paddlex需要設置matplotlib
import matplotlib
matplotlib.use('Agg')
import paddlex as pdx
三、定義數據加載器:
這裏主要是通過 pdx.datasets.ImageNet 類定義用於識別任務的數據加載器;
import paddlehub as hub
import paddle.fluid as fluid
import numpy as np
base = './data/'
datas = []
for i in range(10):
c_base = base+'train/c{}/'.format(i)
for im in os.listdir(c_base):
pt = os.path.join('train/c{}/'.format(i), im)
line = '{} {}'.format(pt, i)
# print(line)
datas.append(line)
np.random.seed(10)
np.random.shuffle(datas)
total_num = len(datas)
train_num = int(0.8*total_num)
test_num = int(0.1*total_num)
valid_num = total_num - train_num - test_num
print('train:', train_num)
print('valid:', valid_num)
print('test:', test_num)
with open(base+'train_list.txt', 'w') as f:
for v in datas[:train_num]:
f.write(v+'\n')
with open(base+'test_list.txt', 'w') as f:
for v in datas[-test_num:]:
f.write(v+'\n')
with open(base+'val_list.txt', 'w') as f:
for v in datas[train_num:-test_num]:
f.write(v+'\n')
train: 17939
valid: 2243
test: 2242
from paddlex.cls import transforms
train_transforms = transforms.Compose([
transforms.RandomCrop(crop_size=224),
transforms.RandomHorizontalFlip(),
transforms.Normalize()
])
eval_transforms = transforms.Compose([
transforms.ResizeByShort(short_size=256),
transforms.CenterCrop(crop_size=224),
transforms.Normalize()
])
train_dataset = pdx.datasets.ImageNet(
data_dir='data',
file_list='data/train_list.txt',
label_list='data/labels.txt',
transforms=train_transforms,
shuffle=True)
eval_dataset = pdx.datasets.ImageNet(
data_dir='data',
file_list='data/val_list.txt',
label_list='data/labels.txt',
transforms=eval_transforms)
2020-05-18 07:57:03 [INFO] Starting to read file list from dataset...
2020-05-18 07:57:03 [INFO] 17939 samples in file data/train_list.txt
2020-05-18 07:57:03 [INFO] Starting to read file list from dataset...
2020-05-18 07:57:03 [INFO] 2243 samples in file data/val_list.txt
num_classes = len(train_dataset.labels)
print(num_classes)
10
四、定義並訓練模型:
這裏使用 MobileNetv3 進行訓練;
MobileNetv3詳細介紹可以看我的這一篇博客:
https://blog.csdn.net/weixin_44936889/article/details/104243853
這裏簡單複述一下:
琦玉老師 和 龍捲(阿姨)小姐姐 告訴我一個道理——畫風越簡單,實力越強悍;
這篇論文只有四個詞,我只能說:不!簡!單!
MobileNet簡介:
爲了使深度學習神經網絡能夠用於移動和嵌入式設備,
MobileNet 提出了使用深度分離卷積減少參數的方法;
DW Conv:
即先將特徵層的每個channel分開,然後分別做卷積,這樣參數大約少了N倍(N是輸出特徵層的channel數);
PW Conv:
就是1×1卷積,用於融合不同channel的特徵;
(一)論文地址:
(二)核心思想:
- 使用了兩個黑科技:NAS 和 NetAdapt 互補搜索技術,其中 NAS 負責搜索網絡的模塊化結構,NetAdapt 負責微調每一層的 channel 數,從而在延遲和準確性中達到一個平衡;
- 提出了一個對於移動設備更適用的非線性函數 ;
- 提出了 和 兩個新的高效率網絡;
- 提出了一個新的高效分割(指像素級操作,如語義分割)的解碼器();
(三)Platform-Aware NAS for Block-wise Search:
3.1 MobileNetV3-Large:
對於有較大計算能力的平臺,作者提出了 MobileNetV3-Large,並使用了跟 MnanNet-A1 相似的基於 RNN 控制器和分解分層搜索空間的 NAS 搜索方法;
3.1 MobileNetV3-Small:
對於有計算能力受限制的平臺,作者提出了 MobileNetV3-Small;
這裏作者發現,原先的優化方法並不適用於小的網絡,因此作者提出了改進方法;
用於近似帕累托最優解的多目標獎勵函數定義如下:
其中 是第 個模型的索引, 是模型的準確率, 是模型的延遲, 是目標延遲;
作者在這裏將權重因數 改成了 ,最後得到了一個期望的種子模型(initial seed model);
(四)NetAdapt for Layer-wise Search:
第二個黑科技就是 NetAdapt 搜索方法,用於微調上一步生成的種子模型;
NetAdapt 的基本方法是循環迭代以下步驟:
- 生成一系列建議模型(proposals),每個建議模型代表了一種結構改進,滿足延遲至少比上一步的模型減小了 ,其中 , 是種子模型的延遲;
- 對於每一個建議模型,使用上一步的預訓練模型,刪除並隨機初始化改進後丟失的權重,繼續訓練 步來粗略估計建議模型的準確率,其中 ;
- 根據某種度量,選取最合適的建議模型,直到達到了目標延遲 ;
作者將度量方法改進爲最小化(原文是最大化,感覺是筆誤):
其中建議模型的提取方法爲:
- 減小 Expansion Layer 的大小;
- 同時減小 BottleNeck 模塊中的前後殘差項的 channel 數;
(五)Efficient Mobile Building Blocks:
作者在 BottleNet 的結構中加入了SE結構,並且放在了depthwise filter之後;
由於SE結構會消耗一定的計算時間,所以作者在含有SE的結構中,將 Expansion Layer 的 channel 數變爲原來的1/4;
其中 SE 模塊首先對卷積得到的特徵圖進行 Squeeze 操作,得到特徵圖每個 channel 上的全局特徵,
然後對全局特徵進行 Excitation 操作,學習各個 channel 間的關係,
從而得到不同channel的權重,最後乘以原來的特徵圖得到最終的帶有權重的特徵;
(六)Redesigning Expensive Layers:
作者在研究時發現,網絡開頭和結尾處的模塊比較耗費計算能力,因此作者提出了改進這些模塊的優化方法,從而在保證準確度不變的情況下減小延遲;
6.1 Last Stage:
在這裏作者刪掉了 Average pooling 前的一個逆瓶頸模塊(包含三個層,用於提取高維特徵),並在 Average pooling 之後加上了一個 1×1 卷積提取高維特徵;
這樣使用 Average pooling 將大小爲 7×7 的特徵圖降維到 1×1 大小,再用 1×1 卷積提取特徵,就減小了 7×7=49 倍的計算量,並且整體上減小了 11% 的運算時間;
6.2 Initial Set of Filters:
之前的 MobileNet 模型開頭使用的都是 32 組 3×3 大小的卷積核並使用 ReLU 或者 swish 函數作爲激活函數;
作者在這裏提出,可以使用 h-switch 函數作爲激勵函數,從而刪掉多餘的卷積核,使得初始的卷積核組數從 32 下降到了 16;
(七)hard switch 函數:
之前有論文提出,可以使用 函數替代 ReLU 函數,並且能夠提升準確率;
其中 switch 函數定義爲:
,其中 ;
由於 sigmaoid 函數比較複雜,在嵌入式設備和移動設備計算消耗較大,作者提出了兩個解決辦法:
7.1 h-swish 函數:
將 swish 中的 sigmoid 函數替換爲一個線性函數,將其稱爲 h-swish:
-
7.2 going deeper:
作者發現 swish 函數的作用主要是在網絡的較深層實現的,因此只需要在網絡的第一層和後半段使用 h-swish 函數;
(八)網絡結構:
8.1 MobileNetV3-Large:
8.2 MobileNetV3-Small:
(九)訓練細節:
使用了 Tensorflow 的 RMSPropOptimizer 優化器,並附加 0.9 的動量項;
初始化學習率爲 0.1,batch 大小爲 4096(每個 GPU 128);
每 3 個 epoch 學習率衰減 0.01;
使用了 0.8 的 dropout 和 1e-5 的 weight decay;
(十)實驗結果:
# 定義並訓練模型
model = pdx.cls.MobileNetV3_small_ssld(num_classes=num_classes)
model.train(num_epochs=2,
train_dataset=train_dataset,
train_batch_size=32,
log_interval_steps=20,
eval_dataset=eval_dataset,
lr_decay_epochs=[1],
save_interval_epochs=1,
learning_rate=0.01,
save_dir='output/mobilenetv3')
五、評估模型性能
save_dir = 'output/mobilenetv3/best_model'
model = pdx.load_model(save_dir)
model.evaluate(eval_dataset, batch_size=1, epoch_id=None, return_details=False)
2020-05-18 09:25:35 [INFO] Model[MobileNetV3_small_ssld] loaded.
2020-05-18 09:25:35 [INFO] Start to evaluating(total_samples=2243, total_steps=2243)...
100%|██████████| 2243/2243 [00:58<00:00, 38.38it/s]
OrderedDict([('acc1', 0.9790459206419974), ('acc5', 1.0)])
六、使用PaddleLite進行模型壓縮
PaddleLite 是 paddle 提供的模型壓縮和量化工具;
文檔地址:
https://paddle-lite.readthedocs.io/zh/latest/index.html#
簡介:
Paddle-Lite 框架是 PaddleMobile 新一代架構,重點支持移動端推理預測,特點爲高性能、多硬件、輕量級 。
支持PaddleFluid/TensorFlow/Caffe/ONNX模型的推理部署,目前已經支持 ARM CPU, Mali GPU, Adreno GPU, Huawei NPU 等多種硬件,
正在逐步增加 X86 CPU, Nvidia GPU 等多款硬件,相關硬件性能業內領先。
# 進行模型量化並保存量化模型
pdx.slim.export_quant_model(model, eval_dataset, save_dir='./quant_mobilenet')
print('done.')
# 加載量化後的模型並進行評估
quant_model = pdx.load_model('./quant_mobilenet')
load_model('./quant_mobilenet')
quant_model.evaluate(eval_dataset, batch_size=1, epoch_id=None, return_details=False)
七、總結:
在本項目中我們完成了以下任務:
-
使用PaddleX在駕駛員狀態識別數據集訓練了MobileNetv3模型;
-
使用PaddleLite實現了模型的量化;
關於作者:
北京理工大學 大二在讀
感興趣的方向爲:目標檢測、人臉識別、EEG識別等
也歡迎大家fork、評論交流