深度學(xué)習(xí)快速上手——基于 MegEngine 的 LeNet 快速訓(xùn)練與部署
|本文轉(zhuǎn)載自知乎??@嘿呀嘿???個人 blog。
1. 前言
Megengine?是曠視科技開發(fā)的一款訓(xùn)練推理一體化的深度學(xué)習(xí)框架,類似于 pytorch,tensorflow。
使用 Megengine 可以快速實現(xiàn)常見的深度學(xué)習(xí)模型,本文將使用 Megengine 實現(xiàn)手寫數(shù)字識別,以完成深度學(xué)習(xí)的兩大步驟:訓(xùn)練和預(yù)測。通過本文,讀者對深度學(xué)習(xí)的最基本流程和 Megengine 框架的使用方法有大致了解。
2. 環(huán)境安裝
在命令行輸入下列語句即可安裝 Megengine ,建議使用 python 版本為 3.5 到 3.8
安裝完成后可以在命令行測試是否安裝成功。
3. 訓(xùn)練
本部分訓(xùn)練代碼來自 Megengine 官方教程,需要詳細(xì)了解細(xì)節(jié)請前往?MegEngine 快速上手
3.1 數(shù)據(jù)集準(zhǔn)備
3.1.1 下載數(shù)據(jù)集
深度學(xué)習(xí)的第一步為準(zhǔn)備數(shù)據(jù)集,通常會為數(shù)據(jù)集寫一個接口來訪問數(shù)據(jù)集,并對數(shù)據(jù)進(jìn)行預(yù)處理。
Megengine 中已經(jīng)實現(xiàn)了 MNIST 數(shù)據(jù)集的接口,我們可以通過以下代碼直接獲取。如果想要制作或使用其他數(shù)據(jù)集,可以點擊這里進(jìn)行學(xué)習(xí)。
3.1.2 數(shù)據(jù)加載及預(yù)處理
上面使用 MNIST()完成數(shù)據(jù)集的加載和 Dataset 的構(gòu)建,接下來將對數(shù)據(jù)進(jìn)行加載,修改數(shù)據(jù)。使用 DataLoader、Sampler 和 Transform 實現(xiàn)。
DataLoader
功能: 構(gòu)建可迭代的數(shù)據(jù)裝載器,非常靈活地從數(shù)據(jù)集連續(xù)獲取小批量數(shù)據(jù)?參數(shù):
dataset?– 需要從中分批加載的數(shù)據(jù)集。
sampler?(Optional) – 定義從數(shù)據(jù)集中采樣數(shù)據(jù)的策略。
transform?(Optional) – 定義抽樣批次的轉(zhuǎn)換策略。對數(shù)據(jù)需要作的變換 默認(rèn):None
...
RandomSampler
功能:創(chuàng)建一個列表,包含所有數(shù)據(jù)的索引,可實現(xiàn)數(shù)據(jù)的隨機取樣?參數(shù):
dataset?– 待采樣的目標(biāo)數(shù)據(jù)集。
batch_size?– 使用 batch 方法時指定 batch 大小。
drop_last?– 如果 batch 大小不能整除數(shù)據(jù)集大小時,為 True 則放棄最后一個不完整的batch; 為 False 則最后一個batch可能比較小。默認(rèn):False
...
3.2 模型
接下來定義網(wǎng)絡(luò)結(jié)構(gòu),LeNet 的網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示。 [圖片上傳失敗...(image-41aaca-1660811354056)] 定義網(wǎng)絡(luò)結(jié)構(gòu)主要為兩步:定義網(wǎng)絡(luò)子模塊和連接網(wǎng)絡(luò)子模塊。如下代碼所示,使用?init?方法創(chuàng)建子模塊,forward()方法連接子模塊。
class?LeNet?繼承自?Module?的類,想要了解更多關(guān)于構(gòu)建模型的細(xì)節(jié),可以參考參考 module 定義模型結(jié)構(gòu)
3.3 訓(xùn)練準(zhǔn)備
GradManager?負(fù)責(zé)計算梯度,管理需要優(yōu)化的參數(shù),用attach()方法添加,加入的參數(shù)才會被計算保存梯度值。對梯度計算了解更多請點擊?Autodiff 基本原理與使用
optimizer?選擇使用的優(yōu)化方法,這里使用 Optimizer.SGD(隨機梯度下降法)作為優(yōu)化器,對優(yōu)化器了解更多請點擊使用 optimizer 優(yōu)化參數(shù)
3.4 訓(xùn)練迭代
接下來進(jìn)入程序的主邏輯,開始訓(xùn)練模型。使用兩個嵌套循環(huán),一個大循環(huán)為一個 epoch,遍歷一次數(shù)據(jù)集,計算一次準(zhǔn)確度。
每個小循環(huán)為一個 batch,將一批數(shù)據(jù)傳入模型中,進(jìn)行前向計算得到預(yù)測概率,使用交叉熵(cross_entropy)來計算 loss, 接著調(diào)用 GradManager.backward 方法進(jìn)行反向計算并且記錄每個 tensor 的梯度信息。然后使用 Optimizer.step 方法更新模型中的參數(shù)。由于每次更新參數(shù)后不自動清除梯度,所以還需要調(diào)用 clear_grad 方法。
3.5 保存模型
常用的神經(jīng)網(wǎng)絡(luò)都具有非常大數(shù)量級的參數(shù),每次訓(xùn)練需要花費很長時間,為了能夠訓(xùn)練中斷后能夠按照上次訓(xùn)練的成果接著訓(xùn)練,我們可以每10個 epoch 保存一次模型(或更多)。保存模型有幾種方法,如表所示。方法詳細(xì)介紹請點擊保存與加載模型。https://www.megengine.org.cn/doc/stable/zh/user-guide/model-development/serialization/index.html#save-load-entire-model

我們選擇保存加載檢查點,既可以用于恢復(fù)訓(xùn)練也可以推理。保存時調(diào)用 megengine.save()方法,參數(shù)如下:
然后就可以愉快的進(jìn)行訓(xùn)練了,觀察訓(xùn)練結(jié)果,當(dāng) loss 下降到一定地步,準(zhǔn)確率滿足要求后,終止訓(xùn)練.
如果訓(xùn)練發(fā)生中斷,可以調(diào)用 load()方法和 optimizer.load_state_dict()方法,對模型的加載,重新開始訓(xùn)練。代碼如下:
4. 推理
上面幾個章節(jié)已經(jīng)完成深度學(xué)習(xí)大部分內(nèi)容,已經(jīng)能夠產(chǎn)生一個需要的算法模型。這個算法對準(zhǔn)備好的數(shù)據(jù)集有比較好的擬合效果,但是我們的最終目的是用模型進(jìn)行推理,即能夠?qū)π碌臄?shù)據(jù)進(jìn)行預(yù)測。這將是下面介紹的內(nèi)容。
首先有一種很簡單的方法,使用 python 加載模型并設(shè)定 model.eval(),代碼如下所示,這樣就可以簡單調(diào)用訓(xùn)練好的模型用以實際。
不過在實際部署中,還需要考慮部署環(huán)境,推理速度等因素,所以從訓(xùn)練好模型到部署落地還有很長的路。Megengine 由于其設(shè)計特點——訓(xùn)練推理一體化,可以方便地將訓(xùn)練模型部署。這將是下一章介紹的內(nèi)容,下一章將使用 C++ 調(diào)用 Megengine lite,進(jìn)行高效部署。
參考文獻(xiàn)
[1]:?MegEngine 快速上手?[2]: Yann LeCun, Corinna Cortes, and CJ Burges. Mnist handwritten digit database. ATT Labs [Online]. Available:?http://yann.lecun.com/exdb/mnist, 2010. [3]: Yann LeCun, Léon Bottou, Yoshua Bengio, and Patrick Haffner. Gradient-based learning applied to document recognition. Proceedings of the IEEE, 86(11):2278–2324, 1998.
附錄:train.py
C++ 推理
前半部分我們完成了深度學(xué)習(xí)的訓(xùn)練,得到了LeNet訓(xùn)練權(quán)重文件,后面我們將使用訓(xùn)練權(quán)重文件導(dǎo)出靜態(tài)圖模型。并使用C++調(diào)用模型完成實際部署的模擬。
準(zhǔn)備工作
在上一章中,我們提到有四種保存模型的方法,如下表所示,為了訓(xùn)練方便起見,保存了checkpoint文件。但實際部署中我們經(jīng)常使用靜態(tài)圖模型,所以我們首先要完成靜態(tài)圖導(dǎo)出。

到處靜態(tài)圖在megengine中有較完整的教程,請參考導(dǎo)出序列化模型文件(Dump)。主要分為三步:
將循環(huán)內(nèi)的前向計算、反向傳播和參數(shù)優(yōu)化代碼提取成單獨的函數(shù),如下面例子中的 train_func()
將網(wǎng)絡(luò)所需輸入作為訓(xùn)練函數(shù)的參數(shù),并返回任意你需要的結(jié)果(如輸出結(jié)果、損失函數(shù)值等);
用 jit 模塊中的 trace 裝飾器來裝飾這個函數(shù),將其中的代碼變?yōu)殪o態(tài)圖代碼。
在上一章最后的附錄train.py中有dump靜態(tài)圖的方法,代碼如下:
調(diào)用dump_mge方法即可完成靜態(tài)圖導(dǎo)出。
inference代碼
代碼的主邏輯為:
創(chuàng)建Network
使用load_model()載入模型
使用stb預(yù)處理圖片(加載和resize),然后歸一化,載入進(jìn)input tensor
使用network->forward()和network->wait()完成推理邏輯。
獲取模型輸出tensor,并對其進(jìn)行處理。
推理代碼為:
推理的代碼已經(jīng)編寫完成,還需要對其進(jìn)行編譯,根據(jù)我們部署的平臺,選擇編譯方式,比如安卓,可以選擇交叉編譯。這里我們選擇部署在本機上。
可以使用g++進(jìn)行編譯,編譯時需要連接MegEngine Lite庫文件,并且準(zhǔn)備好stb頭文件。
配置環(huán)境
由于使用C++調(diào)用MegEngine Lite接口,所以我們首先需要編譯出MegEngine Lite的庫。?安裝MegEngine:從源代碼編譯MegEngine。請參考編譯MegEngine Lite。
clone MegEngine工程,進(jìn)入根目錄
安裝MegEngine所需的依賴
使用cmake進(jìn)行編譯工程得到c++推理所需的庫文件
編譯完成后,需要的庫文件所在地址為:
MegEngine/build_dir/host/MGE_WITH_CUDA_OFF/MGE_INFERENCE_ONLY_ON/Release/install/lite/
這里為了在g++編譯時添加庫文件方便,可以將庫文件地址設(shè)為環(huán)境變量
安裝stb: stb是一個輕量化的圖片加載庫,可以替代opencv完成圖片的解碼。想要使用它,只需要將對應(yīng)的頭文件包含到項目內(nèi),不像opencv需要編譯產(chǎn)生鏈接庫。
這里為了調(diào)用方便直接將stb的項目下載下來:
想要使用圖片加載函數(shù)stbi_load(),只需在cpp文件中define STB_IMAGE_IMPLEMENTATION并且include stb_image.h頭文件
動態(tài)鏈接編譯
最后使用g++或者clang完成對inference.cpp的編譯。
-I選項添加編譯時頭文件搜索路徑
-l添加動態(tài)鏈接庫
-L添加動態(tài)鏈接庫搜索路徑
編譯后會在本目錄下會得到inference二進(jìn)制文件
執(zhí)行二進(jìn)制文件
準(zhǔn)備好一張手寫數(shù)字圖片,將圖片與模型放到同一目錄,執(zhí)行編譯好的文件即可得到推理結(jié)果。
以上就完成了LeNet神經(jīng)網(wǎng)絡(luò)的部署。
更多 MegEngine 信息獲取,您可以查看:
文檔:https://www.megengine.org.cn/doc/stable/zh/?
深度學(xué)習(xí)框架?MegEngine 官網(wǎng):https://www.megengine.org.cn/
GitHub 項目:https://github.com/MegEngine,或加入 MegEngine 用戶交流 QQ 群:1029741705