この記事では、YOLOv5のPyTorchモデルをncnnモデルに変換する方法について解説します。一度PyTorchモデル(.pt)をONNXモデルに変換し、ONNXモデルからncnnモデル(.paramと.bin)に変換します。またYOLOv5での注意点を説明します。
環境
- ホストPC: Ubuntu20.04
- YOLOv5: https://github.com/ultralytics/yolov5/tree/v6.2
- version v6.2
- ncnn: https://github.com/Tencent/ncnn
セットアップ
こちらの記事では、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 install
Code language: Bash (bash)
なお詳細なインストール方法はこちらとこちらの記事を参照して下さい。
動作確認
以下を実行します。レポジトリshaoshengsong/yolov5_62_export_ncnn
はncnnのyolov5サンプルコードが参照する yolov5s_6.2.param
とyolov5s_6.2.bin
を持っています。
git clone https://github.com/shaoshengsong/yolov5_62_export_ncnn
cd yolov5_62_export_ncnn
../examples/yolov5 bus.jpg
Code language: Bash (bash)
以下の画像が表示されます。

PyTorchモデルからONNXモデルへの変換
PyTorchモデル(.pt)からONNXモデルへの変換は、先程クローンしたshaoshengsong/yolov5_62_export_ncnnレポジトリを使用します。YOLOv5オリジナルのレポジトリにもONNXに変換するスクリプトがありますが、出力結果が異なります。
$ pwd
yolov5_62_export_ncnn
Code language: Bash (bash)
必要なパッケージをインストールします。
sudo apt install -y python3-pip
python3 -m pip install -U pip
python3 -m pip install -r requirements.txt
Code 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.onnx
Code language: Bash (bash)
ONNXモデルからncnnモデルへの変換
ONNXモデルからncnnモデルへの変換は、onnx2ncnn
を使用します。onnx2ncnnはncnnをビルド&インストールしたときにbuild/install/bin/onnx2ncnn
に作成されます。
../install/bin/onnx2ncnn yolov5s.onnx yolov5s.param yolov5s.bin
Code language: Bash (bash)
さて、ncnnのYOLOv5のサンプルコードを見てみるとyolov5s_6.2.param
とyolov5s_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=1
Code language: Bash (bash)
output
、354
、366
という数字が含まれていました。yolov5s.onnx
をhttps://netron.appで可視化してみます。



yolov5s.onnxはoutput
、354
、366
という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=1
Code language: Bash (bash)
表示は少し異なりますが、output
、353
、367
が出力となっているようです。これはyolov5s.param
を調べた値(354
、366
)と異なります。
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);
#else
Code 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=3
Code language: Bash (bash)
yolov5s.param
は0=6400
、0=1600
、0=400
となっています。
一方yolov5s_6.2.param
は0=-1
、0=-1
、0=-1
となっています。
yolov5s.param
を0=-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=1
Code language: Diff (diff)
yolov5s.param
の修正ですのでビルドは不要です。修正したら再度実行します。
../examples/yolov5 bus.jpg
Code language: Bash (bash)

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