再次擺在您面前的是偵測物體的任務。 優先考慮的是操作速度和可接受的精度。 您採用 YOLOv3 架構並進一步訓練它。 準確度(mAP75)大於0.95。 但運行率仍然較低。 廢話。
今天我們將繞過量化。 在切口下我們會看到 模型剪枝 - 修剪網路的冗餘部分以加速推理而不失去準確性。 切割地點、切割量、切割方式一目了然。 讓我們弄清楚如何手動執行此操作以及在哪裡可以自動執行此操作。 最後有一個關於 keras 的儲存庫。
介紹
在我之前的工作地點彼爾姆的Macroscop,我養成了一個習慣──總是監控演算法的執行時間。 並且始終透過充分性過濾器檢查網路運作時。 通常最先進的生產不會通過這個過濾器,這導致我進行修剪。
修剪是一個古老的話題,在
但讓我們把它拆開
生物學一瞥
我喜歡深度學習研究來自生物學的想法。 它們就像進化一樣,是可以信任的(你知道 ReLU 與
模型剪枝過程也接近生物學。 網路的反應可以與大腦的可塑性進行比較。 書中有幾個有趣的例子。
- 一名出生時只有一半的女性的大腦已經重新編程,以執行缺失的一半的功能。
- 那傢伙射掉了他腦中負責視覺的部分。 隨著時間的推移,大腦的其他部分接管了這些功能。 (我們不想重複)
同樣,您可以從模型中刪除一些弱卷積。 作為最後的手段,剩餘的捆綁包將有助於替換切割的捆綁包。
您喜歡遷移學習還是從頭開始學習?
選項一。 您在 Yolov3 上使用遷移學習。 視網膜、Mask-RCNN 或 U-Net。 但大多數時候我們不需要像 COCO 中那樣辨識 80 個物件類別。 在我的實踐中,一切都僅限於1-2年級。 人們可能會認為 80 個類別的架構在這裡是多餘的。 這個想法本身就顯示架構需要變得更小。 此外,我想在不失去現有預訓練權重的情況下做到這一點。
選項二。 也許你擁有大量的資料和運算資源,或者只是需要一個超級客製化的架構。 沒關係。 但你是從頭開始學習網路。 通常的過程是查看資料結構,選擇功率過大的架構,然後從再訓練中退出。 我看到 0.6 人輟學,卡爾。
在這兩種情況下,都可以減少網路。 有動力。 現在我們就來了解什麼是包皮環切修剪
通用演算法
我們決定刪除這些捆綁包。 看起來很簡單:
刪除任何卷積會對網路造成壓力,這通常會導致錯誤增加。 一方面,錯誤的增加表明我們刪除卷積的正確程度(例如,大幅增加表明我們做錯了什麼)。 但小幅增加是完全可以接受的,通常可以透過隨後使用小 LR 進行的輕微額外訓練來消除。 增加額外的訓練步驟:
現在我們需要弄清楚何時停止學習<->修剪循環。 當我們需要將網路減少到一定的規模和速度時(例如,對於行動裝置),這裡可能有一些奇特的選擇。 然而,最常見的選擇是繼續循環,直到誤差變得高於可接受的水平。 新增一個條件:
這樣,演算法就變得清晰了。 仍有待弄清楚如何確定刪除的捲積。
搜尋已刪除的包
我們需要刪除一些卷積。 衝向前去「射擊」任何人都是一個壞主意,儘管它會起作用。 但既然你有頭腦,你可以思考並嘗試選擇「弱」卷積來刪除。 有幾種選擇:
最小 L1 測量或 low_magnitude_pruning 。 小權值卷積對最終決策貢獻不大的想法- 考慮平均值和標準差的最小 L1 測量。 我們補充了對分佈性質的評估。
屏蔽卷積並排除那些對最終精度影響最小的捲積 。 更準確地確定無關緊要的捲積,但非常耗時和資源消耗。- 他人
每個選項都有生命權和自己的實施特徵。 這裡我們考慮具有最小 L1 度量的選項
YOLOv3 的手動流程
原始架構包含殘餘區塊。 但無論它們對於深度網路來說有多酷,它們都會在一定程度上阻礙我們。 困難在於您無法刪除這些層中具有不同索引的對帳:
因此,讓我們選擇可以自由刪除調節的圖層:
現在讓我們建立一個工作循環:
- 上傳啟用
- 計算出要削減多少
- 剪掉它
- 學習 10 個 epoch,LR=1e-4
- 測試
卸載卷積對於估計我們在某個步驟可以刪除多少部分很有用。 卸載範例:
我們發現幾乎所有地方都有 5% 的捲積具有非常低的 L1 範數,我們可以將其刪除。 在每一步中,都會重複這種卸載,並評估可以切割哪些層以及多少層。
整個過程分 4 個步驟完成(RTX 2060 Super 的數字在這裡和各處):
步驟 | MAP75 | 參數數量,百萬 | 網路大小,MB | 從初始值開始,% | 運轉時間,毫秒 | 包皮環切情況 |
---|---|---|---|---|---|---|
0 | 0.9656 | 60 | 241 | 100 | 180 | - |
1 | 0.9622 | 55 | 218 | 91 | 175 | 佔全部的 5% |
2 | 0.9625 | 50 | 197 | 83 | 168 | 佔全部的 5% |
3 | 0.9633 | 39 | 155 | 64 | 155 | 對於具有 15 多個卷積的層,為 400% |
4 | 0.9555 | 31 | 124 | 51 | 146 | 對於具有 10 多個卷積的層,為 100% |
步驟 2 中添加了一個積極的效果——批量大小 4 適合內存,這大大加快了額外訓練的過程。
在第 4 步,該過程被停止,因為即使長期額外訓練也沒有將 mAP75 提高到舊值。
結果,我們設法加快了推理速度 企業排放佔全球 15%,將尺寸減小 企業排放佔全球 35% 並且不完全丟失。
自動化實現更簡單的架構
對於更簡單的網路架構(沒有條件添加、連接和殘差塊),很可能專注於處理所有捲積層並自動化剪切卷積的過程。
我實現了這個選項
很簡單:您只需要一個損失函數、一個優化器和批次產生器:
import pruning
from keras.optimizers import Adam
from keras.utils import Sequence
train_batch_generator = BatchGenerator...
score_batch_generator = BatchGenerator...
opt = Adam(lr=1e-4)
pruner = pruning.Pruner("config.json", "categorical_crossentropy", opt)
pruner.prune(train_batch, valid_batch)
如果需要,您可以變更配置參數:
{
"input_model_path": "model.h5",
"output_model_path": "model_pruned.h5",
"finetuning_epochs": 10, # the number of epochs for train between pruning steps
"stop_loss": 0.1, # loss for stopping process
"pruning_percent_step": 0.05, # part of convs for delete on every pruning step
"pruning_standart_deviation_part": 0.2 # shift for limit pruning part
}
此外,也實施了基於標準差的限制。 目標是限制被刪除的部分,排除具有已經「足夠」的 L1 度量的捲積:
因此,我們允許您僅從與右側分佈類似的分佈中刪除弱卷積,而不影響從與左側分佈類似的分佈中刪除:
當分佈接近常態時,pruning_standart_deviation_part係數可以選自:
我建議假設為 2 西格瑪。 或者您可以忽略此功能,保留值 < 1.0。
輸出是整個測試的網路大小、損失和網路運行時間的圖表,標準化為 1.0。 例如,這裡網路大小幾乎減少了 2 倍,而品質沒有損失(具有 100k 權重的小型捲積網路):
運行速度會出現正常波動,並且幾乎保持不變。 對此有一個解釋:
- 卷積的數量從方便的(32、64、128)變為對於顯示卡來說不太方便的 - 27、51 等。 我在這裡可能是錯的,但很可能它會產生影響。
- 該架構並不廣泛,但具有一致性。 透過減少寬度,我們不會影響深度。 因此,我們減少了負載,但不改變速度。
因此,改進表現為運行期間 CUDA 負載減少 20-30%,而不是運行時間減少
結果
讓我們反思一下。 我們考慮了 2 個剪枝選項 - 用於 YOLOv3(當你必須用手操作時)和用於具有更簡單架構的網路。 可以看出,在這兩種情況下都可以在不損失精度的情況下實現網路規模的減少和加速。 結果:
- 減小尺寸
- 加速跑
- 減少 CUDA 負載
- 因此,環境友善(我們優化計算資源的未來使用。在某個地方,人們感到高興
格蕾塔·桑伯格 )
附錄
來源: www.habr.com