OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
How to schedule your network for Halide backend

前のチュートリアル: 効率向上のためにHalideバックエンドを有効にする方法
次のチュートリアル: OpenVINOを用いたOpenCVの利用

原著者Dmitry Kurtaev
互換性OpenCV >= 3.3

はじめに

Halideのコードは、使用するどのデバイスでも同じである。しかし満足のいく効率を得るには、計算を適切にスケジューリングする必要がある。このチュートリアルでは、OpenCVのディープラーニングモジュールにおいて、Halideバックエンドを用いてネットワークをスケジューリングする方法を説明する。

Halideのスケジューリングをより深く理解するために、http://halide-lang.org/tutorials のチュートリアルを読むとよい。

OpenCVでHalideを初めて扱う場合は、効率向上のためにHalideバックエンドを有効にする方法 から始めることを推奨する。

設定ファイル

テキスト形式の設定ファイルを書くことで、Halideパイプラインの計算をスケジューリングできる。これにより、層計算のループ順序のベクトル化、並列化、管理を容易に行える。最初の cv::dnn::Net::forward 呼び出しより前に、特定のデバイス向けのスケジューリング指示を記したファイルへのパスを cv::dnn::Net::setHalideScheduler に渡す。

スケジューリング設定ファイルはYAMLファイルとして表現され、各ノードはスケジューリングされる関数またはスケジューリング指示である。

relu1:
reorder: [x, c, y]
split: { y: 2, c: 8 }
parallel: [yo, co]
unroll: yi
vectorize: { x: 4 }
conv1_constant_exterior:
compute_at: { relu1: yi }

バッチ次元には変数 n、チャンネルには c、行には y、列には x を使うものとする。分割後の変数には、同じ接頭辞を持ち、外側変数には o、内側変数には i の接尾辞を付けた名前を使用する。例えば、範囲 [0, 10) の変数 x に対して指示 split: { x: 2 } を適用すると、範囲 [0, 5)xo と範囲 [0, 2)xi という新しい変数が得られる。同じスケジューリングノード内では変数名 x はもはや使用できなくなる。

スケジューリングの例は opencv_extra/testdata/dnn で見つけることができ、自分のネットワークのスケジューリングに利用できる。

層の融合

層の融合のおかげで、融合されたセットの最上位層だけをスケジューリングすればよい。なぜなら、すべての出力値に対して融合された数式を使用するからである。例えば、Convolution + Scale + ReLU の3つの層が順に並んでいる場合、

conv(x, y, c, n) = sum(...) + bias(c);
scale(x, y, c, n) = conv(x, y, c, n) * weights(c);
relu(x, y, c, n) = max(scale(x, y, c, n), 0);

融合された関数は次のようなものになる

relu(x, y, c, n) = max((sum(...) + bias(c)) * weights(c), 0);

したがって、relu という名前の関数だけをスケジューリングすればよい。

スケジューリングパターン

ネットワークがブロック構造で構築されている場合があり、これは一部の層が同一またはかなり類似していることを意味する。タイリングやベクトル化の係数の範囲内で異なる層に同じスケジューリングを適用したい場合は、スケジューリングファイルの先頭にある patterns セクションでスケジューリングパターンを定義する。また、パターンでは何らかのパラメトリック変数を使用できる。

# At the beginning of the file
patterns:
fully_connected:
split: { c: c_split }
fuse: { src: [x, y, co], dst: block }
parallel: block
vectorize: { ci: c_split }
# Somewhere below
fc8:
pattern: fully_connected
params: { c_split: 8 }

自動スケジューリング

DNNに層を自動的にスケジューリングさせることもできる。単に cv::dnn::Net::setHalideScheduler の呼び出しを省略すればよい。手動スケジューリングよりも効率的になる場合さえある。ただし特定の層を手動でスケジューリングする必要がある場合は、手動と自動のスケジューリング方式を混在させることもできる。スケジューリングファイルを書き、自動的にスケジューリングしたい層は省略すればよい。