0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

M5UnitV2 & ncnnAdvent Calendar 2023

Day 12

ncnnでのクラス分類(shufflenetv2)推論の解説

Last updated at Posted at 2023-12-11

概要

ncnnのexampleに含まれている、shufflenetv2でのクラス分類のプログラムを読み解いていきます。ncnnでの画像処理の流れの理解を深めてみましょう。

exampleのビルド

まずは、shufflenetv2でのクラス分類のサンプルプログラムをビルドして、動かしてみます。

$ cd /opt/external/ncnn/example/
$ cmake .
$ make
$ wget https://upload.wikimedia.org/wikipedia/commons/4/43/Cute_dog.jpg
$ ./shufflenetv2 Cute_dog.jpg 
232 = 0.278603
219 = 0.163010
239 = 0.093620

入力画像のカテゴリインデックスは232であり、ImageNet 2012 1000 Categoryのファイル(ncnn/example/synset_words.txt)を参照すると、カテゴリは" collie(Dog)"であることがわかります。

shufflenetv2でのクラス分類

ncnnフレームワークでの、shufflenetv2でのをクラス分類推論の処理の流れを紹介します。
ソースコードはncnn/examples/shufflenetv2.cppファイルにあります。

プログラムは主に2つの関数、すなわちdetect_shufflenetv2()とprint_topk()に分かれています。 detect_shufflenetv2()は画像分類のネットワークを実行するために使われ、print_topk()は最初のN個の分類結果を出力するために使われます。

コードの流れは以下のように要約されます。

detect_shufflenetv2()関数の処理

ncnn::Netクラスが主にモデルの読み込みと推論に使用されており、主な流れは以下の通りです。

学習モデルのパラメータ(.param)とバイナリ(.bin)のファイルを読み込みます。

    ncnn::Net shufflenetv2;
    shufflenetv2.load_param("shufflenet_v2_x0.5.param")
    shufflenetv2.load_model("shufflenet_v2_x0.5.bin")

入力画像を cv::Mat フォーマットから ncnn::Mat フォーマットに変換し,同時にリサイズと正規化処理を行います.

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR, bgr.cols, bgr.rows, 224, 224);
    const float norm_vals[3] = {1 / 255.f, 1 / 255.f, 1 / 255.f};
    in.substract_mean_normalize(0, norm_vals);   

ncnn::Extractor オブジェクトを作成し,入力と出力を設定します.更に、分類出力を得るために推論計算を行います.

    ncnn::Extractor ex = shufflenetv2.create_extractor();
    ex.input("data", in);
    ex.extract("fc", out);

出力に対してソフトマックス演算を行います。

    {
        ncnn::Layer* softmax = ncnn::create_layer("Softmax");
        ncnn::ParamDict pd;
        softmax->load_param(pd);
        softmax->forward_inplace(out, shufflenetv2.opt);
        delete softmax;
    }

出力をベクトル型データに変換して cls_scores に格納します。

    out = out.reshape(out.w * out.h * out.c);
    cls_scores.resize(out.w);
    for (int j = 0; j < out.w; j++){
        cls_scores[j] = out[j];
    }

print_topk()関数の処理

print_topk関数は、cls_scoreの上位k個のカテゴリとその得点を出力します。

型の要素を持つ配列をvectorの構造体で定義します。

    int size = cls_scores.size();
    std::vector<std::pair<float, int> > vec;
    vec.resize(size);

分類モデルの出力 cls_scores を繰り返し処理し、 型の要素にインデックス値を代入してベクトル vec にします。

     for (int i = 0; i < size; i++) {
        vec[i] = std::make_pair(cls_scores[i], i);
    }

std::partial_sort() 関数を用いて、ベクトル vec をスコアの昇順に部分的にソートします。

   std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(),
                     std::greater<std::pair<float, int> >());

ソートされたベクトルvecを繰り返し処理し、上位k個の要素のインデックスと得点を出力します。

     for (int i = 0; i < topk; i++)    {
        float score = vec[i].first;
        int index = vec[i].second;
        fprintf(stderr, "%d = %f\n", index, score);
    }

main()関数の処理

cv::imread 関数を呼び出して画像の読み込み処理を完了します。

    cv::Mat m = cv::imread(imagepath, 1);

次にdetect_shufflenetv2 関数と print_topk 関数を呼び出して,shufflenetv2 ネットワーク推論と,出力操作の値の確率の画像分類結果を行います。

    std::vector<float> cls_scores;
    detect_shufflenetv2(m, cls_scores);
    print_topk(cls_scores, 3);

参考

この記事を作成するにあたり、以下のウェブサイトを参考にしました。

NCNN 模型推理详解及实战

ncnn/example

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?