ResNet網絡的訓練和預測
簡介 Introduction
圖像分類與CNN
圖像分類 是指將圖像信息中所反映的不同特征,把不同類別的目標區分開來的圖像處理方法,是計算機視覺中其他任務,比如目標檢測、語義分割、人臉識別等高層視覺任務的基礎。
ImageNet 大規模視覺識別挑戰賽(ILSVRC),常稱為 ImageNet 競賽,包括圖像分類、物體定位,以及物體檢測等任務,推動計算機視覺領域發展最重要的比賽之一。
在2012年的 ImageNet 競賽中,深度卷積網絡 AlexNet 橫空出世。以超出第二名10%以上的top-5准確率,勇奪 ImageNet2012 比賽的冠軍。從此,以 CNN(卷積神經網絡) 為代表的深度學習方法開始在計算機視覺領域的應用開始大放異彩,更多的更深的CNN網絡被提出,比如 ImageNet2014 比賽的冠軍 VGGNet, ImageNet2015 比賽的冠軍 ResNet。
ResNet
ResNet 是2015年ImageNet競賽的冠軍。目前,ResNet 相對對於傳統的機器學習分類算法而言,效果已經相當的出色,之后大量的檢測,分割,識別等任務也都在 ResNet 基礎上完成。
OneFlow-Benchmark 倉庫中,提供 ResNet50 v1.5 的 OneFlow 實現。在 ImageNet-2012 數據集上訓練90輪后,驗證集上的准確率能夠達到:77.318%(top1),93.622%(top5)。
更詳細的網絡參數對齊工作,見 OneFlow-Benchmark的cnns 部分
關於 ResNet50 v1.5 的說明:
ResNet50 v1.5 是原始 ResNet50 v1 的一個改進版本,相對於原始的模型,精度稍有提升 (~0.5% top1) 。
本文就以上面的 ResNet50 為例,一步步展現如何使用 OneFlow 進行 ResNet50 網絡的訓練和預測。
主要內容包括:
- 准備工作
- 項目安裝和准備工作
- 快速開始
- 預測/推理
- 訓練和驗證
- 評估
- 更詳細的說明
- 分布式訓練
- 混合精度訓練與預測
- 進階
- 參數對齊
- 數據集制作(ImageNet2012)
- OneFlow 模型轉 ONNX 模型
准備工作 Requirements
別擔心,使用 OneFlow 非常容易,只要准備好下面三步,即可開始 OneFlow 的圖像識別之旅。
- 安裝 OneFlow,安裝方式參考 OneFlow項目主頁
- 克隆/下載 OneFlow-Benchmark 倉庫。
git clone git@github.com:Oneflow-Inc/OneFlow-Benchmark.git
cd OneFlow-Benchmark/Classification/cnns
- 准備數據集(可選)
- 直接使用 synthetic 虛擬合成數據集
- 下載制作的 Imagenet(2012) 迷你數據集 解壓放入data目錄
- 或者:制作完整 OFRecord 格式的 ImageNet 數據集(見下文進階部分)
提供了通用腳本:train.sh 和 inference.sh,它們適用於此倉庫下所有cnn網絡模型的訓練、驗證、推理。可以通過設置參數使用不同的模型、數據集來訓練/推理。
關於模型的說明:
默認情況下,使用resnet50,也可以通過改動腳本中的--model參數指定其他模型,如:--model="resnet50",--model="vgg" 等。
關於數據集的說明:
1)為了使讀者快速上手,提供了 synthetic 虛擬合成數據,“合成數據”是指不通過磁盤加載數據,而是直接在內存中生成一些隨機數據,作為神經網絡的數據輸入源。
2)同時,提供了一個小的迷你示例數據集。直接下載解壓至 cnn 項目的 data 目錄,即可快速開始訓練。讀者可以在熟悉了流程后,參考數據集制作部分,制作完整的 Imagenet2012 數據集。
3)使用 OFRcord 格式的數據集可以提高數據加載效率(但這非必須,參考數據輸入,OneFlow 支持直接加載 numpy 數據)。
快速開始 Quick Start
開始 OneFlow 的圖像識別之旅吧!
首先,切換到目錄:
cd OneFlow-Benchmark/Classification/cnns
預訓練模型
resnet50
resnet50_v1.5_model (validation accuracy: 77.318% top1,93.622% top5 )
預測/推理
下載好預訓練模型后,解壓后放入當前目錄,然后執行:
sh inference.sh
此腳本將調用模型對這張金魚圖片進行分類:
若輸出下面的內容,則表示預測成功:
data/fish.jpg
0.87059885 goldfish, Carassius auratus
可見,模型判斷這張圖片有87.05%的概率是金魚 goldfish。
訓練和驗證(Train & Validation)
- 訓練同樣很簡單,只需執行:
sh train.sh
即可開始模型的訓練,將看到如下輸出:
Loading synthetic data.
Loading synthetic data.
Saving model to ./output/snapshots/model_save-20200723124215/snapshot_initial_model.
Init model on demand.
train: epoch 0, iter 10, loss: 7.197278, top_1: 0.000000, top_k: 0.000000, samples/s: 61.569
train: epoch 0, iter 20, loss: 6.177684, top_1: 0.000000, top_k: 0.000000, samples/s: 122.555
Saving model to ./output/snapshots/model_save-20200723124215/snapshot_epoch_0.
train: epoch 0, iter 30, loss: 3.988656, top_1: 0.525000, top_k: 0.812500, samples/s: 120.337
train: epoch 1, iter 10, loss: 1.185733, top_1: 1.000000, top_k: 1.000000, samples/s: 80.705
train: epoch 1, iter 20, loss: 1.042017, top_1: 1.000000, top_k: 1.000000, samples/s: 118.478
Saving model to ./output/snapshots/model_save-20200723124215/snapshot_epoch_1.
...
為了方便運行演示,默認使用synthetic虛擬合成數據集,使可以快速看到模型運行的效果
同樣,你也可以使用迷你示例數據集,下載解壓后放入 cnn 項目的 data 目錄即可,然后修改訓練腳本如下:
rm -rf core.*
rm -rf ./output/snapshots/*
DATA_ROOT=data/imagenet/ofrecord
python3 of_cnn_train_val.py \
--train_data_dir=$DATA_ROOT/train \
--num_examples=50 \
--train_data_part_num=1 \
--val_data_dir=$DATA_ROOT/validation \
--num_val_examples=50 \
--val_data_part_num=1 \
--num_nodes=1 \
--gpu_num_per_node=1 \
--model_update="momentum" \
--learning_rate=0.001 \
--loss_print_every_n_iter=1 \
--batch_size_per_device=16 \
--val_batch_size_per_device=10 \
--num_epoch=10 \
--model="resnet50"
運行此腳本,將在僅有50張金魚圖片的迷你 ImageNet 數據集上,訓練出一個分類模型,可以對金魚圖片進行分類。
不要着急,如果需要在完整的 ImageNet2012 數據集上進行訓練,請參考:OneFlow-Benchmark倉庫。
評估(Evaluate)
你可以使用自己訓練好的模型,或者提供的 resnet50_v1.5_model (解壓后放入當前目錄),對resnet50模型的精度進行評估。
只需運行:
sh evaluate.sh
即可獲得訓練好的模型在50000張驗證集上的准確率:
Time stamp: 2020-07-27-09:28:28
Restoring model from resnet_v15_of_best_model_val_top1_77318.
I0727 09:28:28.773988162 8411 ev_epoll_linux.c:82] Use of signals is disabled. Epoll engine will not be used
Loading data from /dataset/ImageNet/ofrecord/validation
validation: epoch 0, iter 195, top_1: 0.773277, top_k: 0.936058, samples/s: 1578.325
validation: epoch 0, iter 195, top_1: 0.773237, top_k: 0.936078, samples/s: 1692.303
validation: epoch 0, iter 195, top_1: 0.773297, top_k: 0.936018, samples/s: 1686.896
執行 sh evaluate.sh 前,確保准備了 ImageNet(2012) 的驗證集,驗證集制作方法請參考:OneFlow-Benchmark倉庫。
從3輪的評估結果來看,模型在 ImageNet(2012) 上已經達到了77.32+%的 top1 精度。
最后,恭喜你!完成了 Resnet 模型在 ImageNet 上完整的訓練/驗證、推理和評估!
更詳細的說明 Details
分布式訓練
簡單而易用的分布式,是 OneFlow 的主打特色之一。
OneFlow 框架從底層設計上,就原生支持高效的分布式訓練。尤其對於分布式的數據並行,用戶完全不用操心算法從單機單卡擴展到多機多卡時,數據如何划分以及同步的問題。也就是說,使用 OneFlow,用戶以單機單卡的視角寫好的代碼,自動具備多機多卡分布式數據並行的能力。
如何配置並運行分布式訓練?
還是以上面"快速開始"部分演示的代碼為例,在 train.sh 中,只要用 --num_nodes 指定節點(機器)個數,同時用 --node_ips 指定節點的 IP 地址,然后用 --gpu_num_per_node 指定每個節點上使用的卡數,就輕松地完成了分布式的配置。
例如,想要在2機8卡上進行分布式訓練,像下面這樣配置:
# train.sh
python3 of_cnn_train_val.py \
--num_nodes=2 \
--node_ips="192.168.1.1, 192.168.1.2"
--gpu_num_per_node=4 \
...
--model="resnet50"
然后分別在兩台機器上,同時執行:
./train.sh
程序啟動后,通過 watch -n 0.1 nvidia-smi 命令可以看到,兩台機器的 GPU 都開始了工作。一段時間后,會在 --node_ips 設置中的第一台機器的屏幕上,打印輸出。
混合精度訓練與預測
目前,OneFlow 已經原生支持 float16/float32 的混合精度訓練。訓練時,模型參數(權重)使用 float16 進行訓練,同時保留 float32 用作梯度更新和計算過程。由於參數的存儲減半,會帶來訓練速度的提升。
在 OneFlow 中開啟 float16/float32 的混合精度訓練模式,ResNet50 的訓練速度理論上能達到1.7倍的加速。
如何開啟 float16 / float32 混合精度訓練?
只需要在 train.sh 腳本中添加參數 --use_fp16=True 即可。
混合精度模型
為提供了一個在 ImageNet2012 完整訓練了90個 epoch 的混合精度模型,Top_1:77.33%
可以直接下載使用:resnet50_v15_fp16
進階 Advanced
參數對齊
OneFlow 的 ResNet50 實現,為了保證和英偉達的 Mxnet 版實現對齊,從 learning rate 學習率,優化器 Optimizer 的選擇,數據增強的圖像參數設定,到更細的每一層網絡的形態,bias,weight 初始化等都做了細致且幾乎完全一致的對齊工作。具體的參數對齊工作,請參考:OneFlow-Benchmark 倉庫
數據集制作
用於圖像分類數據集簡介
用於圖像分類的公開數據集有CIFAR,ImageNet 等等,這些數據集中,以 jpeg 的格式提供原始的圖片。
- CIFAR 是由Hinton 的學生 Alex Krizhevsky 和 Ilya Sutskever 整理的一個用於識別普適物體的小型數據集。包括CIFAR-10和CIFAR-100。
- ImageNet ImageNet 數據集,一般是指2010-2017年間大規模視覺識別競賽 (ILSVRC) 的所使用的數據集的統稱。ImageNet 數據從2010年來稍有變化,常用 ImageNet-2012 數據集包含1000個類別,其中訓練集包含1,281,167張圖片,每個類別數據732至1300張不等,驗證集包含50,000張圖片,平均每個類別50張圖片。
完整的 ImageNet(2012)制作過程,請參考 tools 目錄下的README說明
OneFlow 模型轉 ONNX 模型
簡介
ONNX (Open Neural Network Exchange) 是一種較為廣泛使用的神經網絡中間格式,通過 ONNX 格式,OneFlow 模型可以被許多部署框架(如 OpenVINO、ONNX Runtime 和移動端的 ncnn、tnn、TEngine 等)所使用。這一節介紹如何將訓練好的 ResNet50 v1.5 模型轉換為 ONNX 模型並驗證正確性。
快速上手
提供了完整代碼:resnet_to_onnx.py 幫你輕松完成模型的轉換和測試的工作
步驟一: 下載預訓練模型:resnet50_v1.5_model ,解壓后放入當前目錄
步驟二: 執行:python3 resnet_to_onnx.py
此代碼將完成 OneFlow 模型 -> ONNX 模型的轉化,然后使用 ONNX Runtime 加載轉換后的模型對單張圖片進行測試。測試圖片如下:
圖片來源:https://en.wikipedia.org/wiki/Tiger
輸出:
Convert to onnx success! >> onnx/model/resnet_v15_of_best_model_val_top1_77318.onnx
data/tiger.jpg
Are the results equal? Yes
Class: tiger, Panthera tigris; score: 0.8112028241157532
如何生成 ONNX 模型
上面的示例代碼,介紹了如何轉換 OneFlow 的 ResNet 模型至 ONNX 模型,並給出了一個利用 onnx runtime 進行預測的例子,同樣,可以利用下面的步驟來完成自己訓練的 ResNet 或其他模型的轉換。
步驟一:將模型權重保存到本地
首先指定待轉換的 OneFlow 模型路徑,然后指定轉換后的 ONNX 模型存放路徑,例如示例中:
#set up your model path
flow_weights_path = 'resnet_v15_of_best_model_val_top1_77318'
onnx_model_dir = 'onnx/model'
步驟二:新建一個用於推理的 job function
然后新建一個用於推理的 job function,它只包含網絡結構本身,不包含讀取 OFRecord 的算子,並且直接接受 numpy 數組形式的輸入。可參考 resnet\_to\_onnx.py 中的 InferenceNet。
步驟三:調用 flow.onnx.export方法
接下來代碼中會調用 oneflow_to_onnx() 方法,此方法包含了核心的模型轉換方法: flow.onnx.export()
flow.onnx.export 將從 OneFlow 網絡得到 ONNX 模型,它的第一個參數是上文所說的專用於推理的 job function,第二個參數是 OneFlow 模型路徑,第三個參數是(轉換后)ONNX 模型的存放路徑
onnx_model = oneflow_to_onnx(InferenceNet, flow_weights_path, onnx_model_dir, external_data=False)
驗證 ONNX 模型的正確性
生成 ONNX 模型之后可以使用 ONNX Runtime 運行 ONNX 模型,驗證 OneFlow 模型和 ONNX 模型能夠在相同的輸入下產生相同的結果。相應的代碼在 resnet_to_onnx.py 的 check_equality。