精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

飞桨PaddlePaddle發表於2019-05-07

PaddleSlim 是一個無論是新手還是經驗者都可以很方便用來優化模型以進行部署的模型壓縮庫:在普通的模型訓練上,只需要兩行 python 程式碼,構造一個 Compressor 物件,即可呼叫。PaddleSlim 實現了目前主流的網路量化、剪枝、蒸餾三種壓縮策略,並可快速配置多種壓縮策略組合使用。針對體積已經很小的 MobileNet 模型,在模型效果不損失的前提下實現 70% 以上的體積壓縮。

專案地址:https://github.com/PaddlePaddle/models/tree/v1.4/PaddleSlim

深度學習技術已經在網際網路的諸多方向產生影響,關於深度學習和神經網路的討論越來越多。深度學習技術近幾年在計算機視覺、語音識別、自然語言處理等領域大放異彩,各種網際網路產品都爭相應用深度學習技術,我們的生活中也越來越多的 AI 時代新能力,例如人臉識別、智慧翻譯、語音助手等。

根據《2019 年移動市場報告》,2018 年,使用者支出已經突破了 1010 億美元。使用者越來越習慣於在手機上完成各種事項,平均每天在移動裝置上花費的時間已經達到 3 小時。隨著移動裝置被廣泛使用,在移動網際網路產品應用深度學習和神經網路技術已經成為必然趨勢。而移動端裝置通常記憶體少、運算能力也比較弱小,並且移動端的 CPU 需要將功耗指標維持在很低的水平,當前主流的模型很難直接部署到移動裝置中。在這種情況下,PaddleSlim 應運而生,實現了目前主流的網路量化、剪枝、蒸餾三種壓縮策略,並可快速配置多種壓縮策略組合使用,在多種壓縮策略上達到了業績領先的效果。

PaddleSlim架構圖

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

如圖所示,PaddleSlim 從上到下為 API 依賴關係。蒸餾、量化和剪下模組都依賴底層的基礎框架。最上層為使用者介面,在 Python 指令碼中呼叫模型壓縮功能時,只需要構造一個 Compressor 物件即可。我們將每個壓縮演算法稱為壓縮策略,在迭代訓練模型的過程中呼叫使用者註冊的壓縮策略完成模型壓縮。模型壓縮工具封裝好了模型訓練邏輯,使用者只需要提供訓練模型需要的網路結構、資料、優化策略(optimizer)等。

PaddleSlim 特點

介面簡單

  • 以配置檔案方式集中管理可配引數,方便實驗管理

  • 在普通模型訓練指令碼上,新增極少程式碼即可完成模型壓縮

效果好

  • 對於冗餘資訊較少的 MobileNetv1 模型,卷積核剪下策略依然可縮減模型大小,並保持儘量少的精度損失。

  • 蒸餾壓縮策略可明顯提升原始模型的精度。

  • 量化訓練與蒸餾的組合使用,可同時做到縮減模型大小和提升模型精度。

功能更強更靈活

  • 剪下壓縮過程自動化

  • 剪下壓縮策略支援更多網路結構

  • 蒸餾支援多種方式,使用者可自定義組合 loss

  • 支援快速配置多種壓縮策略組合使用

PaddleSlim 蒸餾、剪下、量化簡要介紹

蒸餾

模型蒸餾是將複雜網路中的有用資訊提取出來,遷移到一個更小的網路中去,PaddleSlim 提供傳統的蒸餾方法和基於 FSP 的蒸餾方法。使用者可以在配置檔案中,用 FSP_loss, L2_loss 和 softmax_with_cross_entropy_loss

這三種 loss 組合 teacher net 和 student net 的任意一層。

卷積核剪下

該策略通過減少指定卷積層中卷積核的數量,達到縮減模型大小和計算複雜度的目的。根據選取剪下比例的策略的不同,PaddleSlim 提供以下兩個方式:

  • uniform pruning: 每層剪下一樣比例的卷積核。在剪下一個卷積核之前,按 l1_norm 對 filter 從高到低排序,越靠後的 filter 越不重要,優先剪掉靠後的 filter.

  • sensitive pruning: 根據每層敏感度,剪下掉不同比例的卷積核數量。

兩種剪下方式都需要載入預訓練模型。

量化

PaddleSlim 為開發者提供在訓練過程中對量化進行建模以確定量化引數的 Quantization Aware Training 量化模式,提供更高的預測精度。現階段的量化訓練主要針對卷積層(包括二維卷積和 Depthwise 卷積)以及全連線層進行量化。卷積層和全連線層在 PaddlePaddle 框架中對應運算元包括 conv2d、depthwise_conv2d 和 mul 等。量化訓練會對所有的 conv2d、depthwise_conv2d 和 mul 進行量化操作,且要求它們的輸入中必須包括啟用和引數兩部分。

PaddleSlim 使用示例

該示例參考 PaddlePaddle/models/fluid/PaddleCV/image_classification 下程式碼,分別實現了以下策略:

  • 蒸餾:用 ResNet50 對 MobileNetv1 的在 ImageNet 1000 資料上的蒸餾訓練。

  • 剪下:對預訓練好的 MobileNetv1 進行剪下

  • 量化:對預訓練好的 MobileNetv1 進行 int8 量化訓練

  • 蒸餾量化組合:先用 ResNet50 對 MobileNetv1 進行蒸餾,再對蒸餾後得到的模型進行 int8 量化訓練。

  • 剪下量化組合:先用 Uniform 剪下策略對 MobileNetv1 進行剪下,再對剪下後的模型進行 int8 量化訓練

本示例完整程式碼連結:https://github.com/PaddlePaddle/models/tree/develop/fluid/PaddleSlim

使用方式:克隆 PaddlePaddle/models 到本地,並進入 models/fluid/PaddleSlim 路徑。

檔案結構

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

本示例中的五個壓縮策略使用相同的訓練資料和壓縮 Python 指令碼 compress.py,每種策略對應獨立的配置檔案。

1. 資料準備

1.1 訓練資料準備

參考 models/fluid/PaddleCV/image_classification 下的資料準備教程準備訓練資料,並放入 PaddleSlim/data 路徑下。

1.2 預訓練模型準備

指令碼 run.sh 會自動從 models/fluid/PaddleCV/image_classification 下載 ResNet50 和 MobileNetv1 的預訓練模型,並放入 PaddleSlim/pretrain 路徑下。

2. 壓縮指令碼介紹

在 compress.py 中定義了執行壓縮任務需要的所有模型相關的資訊,這裡對幾個關鍵的步驟進行簡要介紹:

2.1 目標網路的定義

compress.py 的以下程式碼片段定義了 train program, 這裡 train program 只有前向計算操作。

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

然後,通過 clone 方法得到 eval_program, 用來在壓縮過程中評估模型精度,如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

定義完目標網路結構,需要對其初始化,並根據需要載入預訓練模型。

2.2 定義 feed_list 和 fetch_list

對於 train program, 定義 train_feed_list 用於指定從 train data reader 中取的資料 feed 給哪些 variable。定義 train_fetch_list 用於指定在訓練時,需要在 log 中展示的結果。如果需要在訓練過程中在 log 中列印 accuracy 信心,則將 ('acc_top1', acc_top1.name) 新增到 train_fetch_list 中即可。

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

注意: 在 train_fetch_list 裡必須有 loss 這一項。

對於 eval program. 同上定義 eval_feed_list 和 train_fetch_list:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

2.3 定義 teacher 網路

以下程式碼片段定義了 teacher 網路,並對其進行了初始化操作。

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

需要注意的是:

  • teacher 網路只有一個輸入,直接 clone 在 train program(fluid.default_main_program) 中定義的 image 變數即可。

  • teacher 網路的輸出只需要到 predict 即可,不用加 loss 和 accuracy 等操作。

  • teacher 網路需要初始化並載入預訓練模型。

注意: ResNet50 和 MobileNetv1 的 fc layer 的 weight parameter 的名稱都為『fc_1.weight』,所以需要到 PaddleSlim/models/resnet.py 中修改一下 ResNet fc layer 的名稱, 同時,修改 ResNet50 pretrain model 中響應 weight 的檔名,使其與 resnet.py 中的名稱保持一致。

3. 執行壓縮策略示例

所有示例的執行命令都放在 run.sh 檔案中,使用者可以修改 run.sh 後,執行不同的壓縮策略示例。

3.1 蒸餾

在該示例中,用預訓練好的 ResNet50 模型監督訓練 MobileNetv1 模型。修改 run.sh, 執行以下命令,執行蒸餾壓縮示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例在評估資料集上的準確率結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

3.2 Uniform 剪下

在該示例中,將 MobileNetv1 模型剪掉 50% 的 FLOPS. 修改 run.sh, 執行以下命令,執行 Uniform 卷積核剪下模型壓縮示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例在評估資料集上的準確率結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

3.3 敏感度剪下

在該示例中,將 MobileNetv1 模型剪掉 50% 的 FLOPS. 修改 run.sh, 執行以下命令,執行敏感度卷積核剪下壓縮示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例在評估資料集上的準確率結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

3.4 int8 量化訓練

修改 run.sh, 執行以下命令,執行 int8 量化訓練示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

3.5 蒸餾後 int8 量化

本示例先用 ResNet50 模型對 MobileNetv1 蒸餾訓練 120 個 epochs,然後再對 MobileNetv1 模型進行動態 int8 量化訓練。修改 run.sh, 執行以下命令,執行蒸餾與 int8 量化訓練結合的模型壓縮示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

3.6 剪下後 int8 量化

本示例先將預訓練好的 MobileNetv1 模型剪掉 50% FLOPS, 讓後再對其進行動態 int8 量化訓練。修改 run.sh, 執行以下命令,執行剪下與 int8 量化訓練結合的模型壓縮示例:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

該示例結果如下:

精度無損,體積壓縮70%以上,百度PaddleSlim為你的模型瘦身

相關文章