読者です 読者をやめる 読者になる 読者になる

catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

ChainerのモデルデータをOpenCVで使うための準備

できれば一通りの目論見が達成してから投稿したかったのですが、まとまった時間をとれそうにないのでいったん投稿です。かたりぃなです。

何をしたいの?

ChainerなどのDeep-Learningフレームワークを使えば色々な学習モデルを構築できそうです。 次のステップとして、その学習したモデルを使ってアプリだったりサービスだったりに組み込むことを考えています。 このとき、サーバがあればPythonをインストールしてChainer走らせて完成になるわけですが、できればクライアント側で処理したいところです。 クライアント環境にPythonを導入してもいい(そういうアプリの配布方法もある)のですが、私自身Pythonはあんまり得意ではないので、妙なところで転びそうです。 できれば慣れたC++でやりたい。C++でchainerの学習済みモデルを取り込みたい

というわけで、今回のゴールは「C++でchainerの学習済みモデルを取り込む」です。 まずは「実現はできそう」というところは見えてきたので、ゴール手前までやります。

どうやるの?

方法ですが、まずOpenCVのdnnで学習済みモデルを取り込めないか考えました。 結論としては「CaffeとTorchはOpencv.dnnの関数を使えば済むけどchainerはインポータを書く必要がある」です。 インポータを書くためにはChainerの出力フォーマットを知る必要があります。chainerは現バージョンでは2種類提供されていて

でした。 numpyを使ってしまうとまたPythonの環境に戻されてしまうので、c++からOpenCVのhdfモジュールを使ってchainerが出力したhdf5を読み込む実験をしてみます。

使うライブラリ一覧

  • OpenCV3.1 dev
    • contrib
      • hdf5モジュール
      • dnnモジュール
  • libhdf

ビルドツール

ライブラリの簡単な説明

OpenCVは有名な画像処理ライブラリです。少し前まではltseezという会社がメンテナンスしていたのですが、Intelに買収されたらしい。 政治的な話はおいといて、構成です。 一般的な機能(枯れた技術)を含むものはopencv本家のリポジトリにあります。 これに対して最新技術であったり、特許なんかの理由で本家に含められないものはopencv-contribリポジトリに含められています。 というわけで、両方それぞれダウンロードしてきます。

libhdfは階層型のデータ構造を扱うためのフォーマットらしく、データサイエンスの分野でよく使われるらしいです。 このあたりにライブラリやツールがあるのでダウンロードしておきます。 The HDF Group - Information, Support, and Software

OpenCVはNuGetでインストールじゃだめなの?

NuGetだとうまくいきませんでした。 NuGetで提供されているものをためそうとしましたが、少し古いVisualStudioでビルドされているようで2015ではそのまま使うことはできなさそうです。(ディレクトリを覗いてみると2012,13用っぽいディレクトリができているのがわかります) このあたりのパッケージ定義をいじってツールセットもそれにあったものを選択してあげればいけそうな気はしますが、そうしてまで使うのは本末転倒(バージョン管理でラクできない)ので手元で全部バージョン管理します。 またNuGetでもOpenCV3.1まではリリースされているのですが、contribがどうなってるのかちょっと不安です。

cmakeの注意点

今回はver3.7を使います。 起動後にopencvやhdfのディレクトリを指定してconfigureしたときにコンパイラのバージョン問い合わせダイアログが表示されます。 ここでターゲット(x86/x64, visualstudioのバージョン)が固定されてしまいます。変更方法はわかりませんでした。 x86/64の切り替えだけなら以前はのcmakeで生成されたソリューションをvisual studioで開いてから切り替えられるようになっていた気がしたのですが。 まあ実験用としてはどちらかに固定しておけば問題ないです。 名前がわかりにくく表示されているので間違えないようにしましょう。visual studio 2015の64bitなら「VisualStudio 14 2015 Win64」です。

hdfライブラリのビルド

hdf5はさきほどのライブラリをビルドします。 ビルドツールはCmakeで。 cmakeの基本的な使い方はほかの親切なサイトに譲るとして、ここではトラブった内容と解決方法を書き留めます。

hdfライブラリのビルド後のイベントが失敗する

setlocalうんたらとかいうスクリプトがビルド後のイベントに設定されているのですが、インストールプロジェクトでインストールを試みるとここで失敗します。 原因としてはデフォルト設定ではインストール先が"c:\programfiles\HDF~"になっているためで、ここにアクセスするには管理者権限が必要です。

解決策をスマートな順に列挙します。

  1. cmakeのinstallprefix変数に適当なディレクトリを設定する
  2. visual studioを管理者モードで起動する

最後のはやっつけ仕事的ですね。 installprefix指定したところにincludeとlib,binと一通り吐き出してくれるので、これが正攻法だと思います。 configureしたらgenerateを忘れずに。

OpenCV

最後にOpenCVのビルドです。

  • opencv-contribを取りんでconfigure
  • hdfのパスを指定する
  • with-msmfをenable
  • with-python(2,3ともに外す)

msmfはMicrosoft Media Foundationの略です。レガシーになりつつあるDirect Showではなく今後はこちらが推奨とのこと。

with-pythonを外した理由は、chainerをインストールしたときにhdfライブラリがくっついていたらしく、有効にしたままだとこっちを参照してしまいました。 依存ライブラリのパスを変えてあげればいいのかもしれませんが、依存関係で面倒ごとにならないように最初から切っておきます。

OpenCVPythonから叩くことは当面は無いので。2.7系3.5系それぞれ外すのを忘れずに。

OpenCV-contrib

windows環境だとopencvのビルドで多数の警告が出ることがあります。 警告はW4819(文字コードunicode形式で保存してください)ですが、とりあえずvisual studioの警告抑制で4819を指定しておきます。

opencv,hdfを使うプロジェクトを作る

visual studioを起動して適当なプロジェクトを作ります。 win32コンソールでいいんじゃないでしょうか。

ライブラリの参照パスの設定

参照プロジェクトとしてopencvを指定すればいいんですが、もういい加減面倒になってきました。dllをコピーして済ませます。 ライブラリディレクトリとインクルードディレクトリの指定をして、インポートライブラリもすぐ使うものだけ指定します。 libhdfとopencv_coreがあればいいかと。

hdfを読む実験(途中)

libhdfは私が欲しい機能と違う気がしてきたので、コードの途中までです。(投げやり) ちなみにこのコードのままではcv::Matの型やサイズを指定していないので実行時エラーになります。

 const std::string  hdf5_file_path("testmodel.hdf");
    auto hdf5_inst = cv::hdf::open(hdf5_file_path);

    const std::string layer_name = "predictor/conv1";
    if (hdf5_inst->hlexists(layer_name) ) {
        auto mat = cv::Mat();
        hdf5_inst->dsread(mat, "predictor/conv1");
    }

何が違う?

このhdfライブラリはhlexistsで目的のノードが存在していることを確認してからdsreadすれば、目的のデータは読めそうです。 しかしこういったインターフェースは私が欲しいものではありません。

私が欲しい機能は 「データ構造が変わってもプログラムを変更せずに動作確認したい」 です。

対するhdfの機能は「データの階層構造は固定されたものである」という大前提があります。 これは「chainerで層を増やして学習したものをOpenCVで試したい」といったときにプログラムを書き換える手間が発生します。

hdfそのままだと扱いづらい。どうしよう?

hdfの構造なんてそういうものと言われればそれまでかもしれませんが、ちょっと納得いきません。 私が欲しいのはboost::ptreeみたいなパーサです。 libhdfのツールにdumphdfというのがあってxml出力できるようなので試してみましたが、xmlにするとファイルサイズが大変なことになってしまったのでちょっと工夫が必要そうです。 (以前作った3層のCNNで10MByte超)

感想と今後の展望

xmlとhdfのハイブリッド構成ができればいいのかもしれません。 xml側にネットの構造だけ記述されていて、実際のwとbはhdfから読んでくるとか。 色々工夫する余地はありそうです。 では今回はこれくらいで。