How to convert YOLOv5 model to ncnn model

ncnn

この記事では、YOLOv5のPyTorchモデルをncnnモデルに変換する方法について解説します。一度PyTorchモデル(.pt)をONNXモデルに変換し、ONNXモデルからncnnモデル(.paramと.bin)に変換します。またYOLOv5での注意点を説明します。

環境

セットアップ

こちらの記事では、ncnnをJetson Nanoにインストールしましたが、今回はホストPCで動作確認をするために、ホストPCにncnnをインストールします。

sudo apt update
sudo apt install -y cmake build-essential libprotobuf-dev protobuf-compiler libopencv-dev
git clone https://github.com/Tencent/ncnn
cd ncnn
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DNCNN_VULKAN=OFF -DNCNN_SYSTEM_GLSLANG=ON -DNCNN_BUILD_EXAMPLES=ON ..
make -j
make installCode language: Bash (bash)

なお詳細なインストール方法はこちらこちらの記事を参照して下さい。

動作確認

以下を実行します。レポジトリshaoshengsong/yolov5_62_export_ncnnはncnnのyolov5サンプルコードが参照する yolov5s_6.2.paramyolov5s_6.2.binを持っています。

git clone https://github.com/shaoshengsong/yolov5_62_export_ncnn
cd yolov5_62_export_ncnn
../examples/yolov5 bus.jpgCode language: Bash (bash)

以下の画像が表示されます。

bus.jpg with ncnn yolov5

PyTorchモデルからONNXモデルへの変換

PyTorchモデル(.pt)からONNXモデルへの変換は、先程クローンしたshaoshengsong/yolov5_62_export_ncnnレポジトリを使用します。YOLOv5オリジナルのレポジトリにもONNXに変換するスクリプトがありますが、出力結果が異なります。

$ pwd
yolov5_62_export_ncnnCode language: Bash (bash)

必要なパッケージをインストールします。

sudo apt install -y python3-pip
python3 -m pip install -U pip
python3 -m pip install -r requirements.txtCode language: Bash (bash)

YOLOv5のPyTorchモデルを取得し、ONNXモデルに変換します。

$ get https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5s.pt
$ python3 export.py --weights yolov5s.pt --img 640 --batch 1 --include onnx --simplify
$ ls yolov5s.onnx
yolov5s.onnxCode language: Bash (bash)

ONNXモデルからncnnモデルへの変換

ONNXモデルからncnnモデルへの変換は、onnx2ncnnを使用します。onnx2ncnnはncnnをビルド&インストールしたときにbuild/install/bin/onnx2ncnnに作成されます。

../install/bin/onnx2ncnn yolov5s.onnx yolov5s.param yolov5s.binCode language: Bash (bash)

さて、ncnnのYOLOv5のサンプルコードを見てみるとyolov5s_6.2.paramyolov5s_6.2.binを直接指定してモデルを読み込んでいることが分かります。今回作成したファイルはyolov5s.paramとyolov5s.binですのでファイル名を以下のように変更します。

diff --git a/examples/yolov5.cpp b/examples/yolov5.cpp
index 88f6db2..ef5b2d5 100644
--- a/examples/yolov5.cpp
+++ b/examples/yolov5.cpp
@@ -280,9 +280,9 @@ static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects)
     // original pretrained model from https://github.com/ultralytics/yolov5
     // the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
 #if YOLOV5_V62
-    if (yolov5.load_param("yolov5s_6.2.param"))
+    if (yolov5.load_param("yolov5s.param"))
         exit(-1);
-    if (yolov5.load_model("yolov5s_6.2.bin"))
+    if (yolov5.load_model("yolov5s.bin"))
         exit(-1);
 #elif YOLOV5_V60
     if (yolov5.load_param("yolov5s_6.0.param"))Code language: Diff (diff)

ビルドして再度実行します。

$ make -C ..
$ ../examples/yolov5 bus.jpg
find_blob_index_by_name 353 failed
Try
    ex.extract("output", out0);
    ex.extract("354", out1);
    ex.extract("366", out2);
Floating point exception (core dumped)Code language: Bash (bash)

先程と異なり、エラーが表示されbus.jpgが表示されませんでした。

ここでは簡単に修正方法について解説します。

まず上のエラーについてyolov5s.paramを調べてみます。

$ grep -w -e output -e 354 -e 366 yolov5s.param 
Permute          /model.24/Transpose      1 1 /model.24/Reshape_output_0 output 0=1
Permute          /model.24/Transpose_1    1 1 /model.24/Reshape_1_output_0 354 0=1
Permute          /model.24/Transpose_2    1 1 /model.24/Reshape_2_output_0 366 0=1Code language: Bash (bash)

output354366という数字が含まれていました。yolov5s.onnxhttps://netron.appで可視化してみます。

yolov5s.onnxはoutput354366という3つの出力があるようです。

一方、shaoshengsong/yolov5_62_export_ncnnにあるyolov5s_6.2.paramを調べてみます。

$ grep Permute yolov5s_6.2.param
Permute          Transpose_200            1 1 338 output 0=1
Permute          Transpose_203            1 1 352 353 0=1
Permute          Transpose_206            1 1 366 367 0=1Code language: Bash (bash)

表示は少し異なりますが、output353367が出力となっているようです。これはyolov5s.paramを調べた値(354366)と異なります。

ncnnのYOLOv5のサンプルコードを見てみると以下のコードが存在します。

$ grep -w -e 353 -e 367 ../../examples/yolov5.cpp 
        ex.extract("353", out);
        ex.extract("367", out);Code language: Bash (bash)

ここでoutputの出力を指定しているようです。yolov5.cppを以下のように修正します。

@@ -366,7 +366,7 @@ static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects)
         ncnn::Mat out;
 
 #if YOLOV5_V62
-        ex.extract("353", out);
+        ex.extract("354", out);
 #elif YOLOV5_V60
         ex.extract("376", out);
 #else
@@ -391,7 +391,7 @@ static int detect_yolov5(const cv::Mat& bgr, std::vector<Object>& objects)
     {
         ncnn::Mat out;
 #if YOLOV5_V62
-        ex.extract("367", out);
+        ex.extract("366", out);
 #elif YOLOV5_V60
         ex.extract("401", out);
 #elseCode language: Diff (diff)

ビルドして再度実行します。

$ make -C ..
$ ../examples/yolov5 bus.jpg
67 = 0.99632 at 656.76 509.26 152.24 x 0.00
67 = 0.99476 at 644.50 499.47 164.50 x 0.00
67 = 0.99158 at 809.00 464.90 0.00 x 0.00
67 = 0.98984 at 809.00 452.97 0.00 x 0.00
67 = 0.98339 at 195.63 596.97 222.76 x 0.00
67 = 0.98278 at 749.81 304.04 59.19 x 0.00
67 = 0.98148 at 809.00 294.05 0.00 x 0.00
67 = 0.97992 at 0.00 523.36 195.36 x 0.00
67 = 0.97671 at 726.73 307.79 82.27 x 0.00
67 = 0.97668 at 45.13 341.54 48.62 x 0.00
...Code language: Bash (bash)

エラーはでなくなりましたが、以下の画像が表示されます。

詳細はこちらの記事にありますが、Reshapeのパラメータを修正します。なおパラメータのフォーマットはこちらを参照してください。

$ grep ^Reshape yolov5s.param 
Reshape          /model.24/Reshape        1 1 /model.24/m.0/Conv_output_0 /model.24/Reshape_output_0 0=6400 1=85 2=3
Reshape          /model.24/Reshape_1      1 1 /model.24/m.1/Conv_output_0 /model.24/Reshape_1_output_0 0=1600 1=85 2=3
Reshape          /model.24/Reshape_2      1 1 /model.24/m.2/Conv_output_0 /model.24/Reshape_2_output_0 0=400 1=85 2=3
$ 
$ grep ^Reshape yolov5s_6.2.param 
Reshape          Reshape_199              1 1 326 338 0=-1 1=85 2=3
Reshape          Reshape_202              1 1 340 352 0=-1 1=85 2=3
Reshape          Reshape_205              1 1 354 366 0=-1 1=85 2=3Code language: Bash (bash)

yolov5s.param0=64000=16000=400となっています。

一方yolov5s_6.2.param0=-10=-10=-1となっています。

yolov5s.param0=-1に修正します。

@@ -165,11 +165,11 @@
 Convolution      /model.23/cv3/conv/Conv  1 1 /model.23/Concat_output_0 /model.23/cv3/conv/Conv_output_0 0=512 1=1 11=1 2=1 12=1 3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=262144
 Swish            /model.23/cv3/act/Mul    1 1 /model.23/cv3/conv/Conv_output_0 /model.23/cv3/act/Mul_output_0
 Convolution      /model.24/m.0/Conv       1 1 /model.17/cv3/act/Mul_output_0_splitncnn_0 /model.24/m.0/Conv_output_0 0=255 1=1 11=1 2=1 12=1 3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=32640
-Reshape          /model.24/Reshape        1 1 /model.24/m.0/Conv_output_0 /model.24/Reshape_output_0 0=6400 1=85 2=3
+Reshape          /model.24/Reshape        1 1 /model.24/m.0/Conv_output_0 /model.24/Reshape_output_0 0=-1 1=85 2=3
 Permute          /model.24/Transpose      1 1 /model.24/Reshape_output_0 output 0=1
 Convolution      /model.24/m.1/Conv       1 1 /model.20/cv3/act/Mul_output_0_splitncnn_0 /model.24/m.1/Conv_output_0 0=255 1=1 11=1 2=1 12=1 3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=65280
-Reshape          /model.24/Reshape_1      1 1 /model.24/m.1/Conv_output_0 /model.24/Reshape_1_output_0 0=1600 1=85 2=3
+Reshape          /model.24/Reshape_1      1 1 /model.24/m.1/Conv_output_0 /model.24/Reshape_1_output_0 0=-1 1=85 2=3
 Permute          /model.24/Transpose_1    1 1 /model.24/Reshape_1_output_0 354 0=1
 Convolution      /model.24/m.2/Conv       1 1 /model.23/cv3/act/Mul_output_0 /model.24/m.2/Conv_output_0 0=255 1=1 11=1 2=1 12=1 3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=130560
-Reshape          /model.24/Reshape_2      1 1 /model.24/m.2/Conv_output_0 /model.24/Reshape_2_output_0 0=400 1=85 2=3
+Reshape          /model.24/Reshape_2      1 1 /model.24/m.2/Conv_output_0 /model.24/Reshape_2_output_0 0=-1 1=85 2=3
 Permute          /model.24/Transpose_2    1 1 /model.24/Reshape_2_output_0 366 0=1Code language: Diff (diff)

yolov5s.paramの修正ですのでビルドは不要です。修正したら再度実行します。

../examples/yolov5 bus.jpgCode language: Bash (bash)

うまく表示されました!

自分のデータセットで学習した結果を使ってJetson Nano上でncnnモデルを使用しYOLOv5を実行した動画が以下になります。

ncnn YOLOv5 with own dataset on Jetson Nano

以上です。

参考

GitHub - Tencent/ncnn: ncnn is a high-performance neural network inference framework optimized for the mobile platform
ncnn is a high-performance neural network inference framework optimized for the mobile platform - GitHub - Tencent/ncnn: ncnn is a high-performance neural netwo...
how to build
ncnn is a high-performance neural network inference framework optimized for the mobile platform - Tencent/ncnn
Tutorial for compiling NCNN library - Zekun Wang's Blog | Data Science
Across the Great Wall, we can reach every corner in the world
详细记录u版YOLOv5目标检测ncnn实现
详细记录u版YOLOv5目标检测ncnn实现 允许在不修改内容前提下转载本文 0x0 u版YOLOv5众所周知,原版YOLO系列是 darknet 框架训练的,而广泛使用的是 YOLOv4 作者 AlexeyAB 的版本 AlexeyAB 首字母是a,于是也被叫做…
operation param weight table
ncnn is a high-performance neural network inference framework optimized for the mobile platform - Tencent/ncnn
Netron
Visualizer for neural network, deep learning and machine learning models.