おことわり
結論から言うと,この記事では OpenPose を Caffe CPU 版で動かすことはできていません. いくつか解決の糸口だけは示していますが,次回以降の記事で本格的に取り扱います.
概要
まずはこちらをご覧いただきたい. hirax.net::ディープラーニングによる姿勢推定openposeで「男子の夢な(衣服の下を透視する)妄撮カメラ」を作ってみよう!?[1] (※NSFW)
……お分かりいただけただろうか. こういうの見るとワクワクしちゃうじゃないですか.自分も試したいって思っちゃうじゃないですか. というわけでいそいそと OpenPose を使ってみようと思ったのですが…
しがない一介の貧乏学生としては,手元にあるのはインターンで稼いだお金をつぎ込んで購入した dynabook と古い Let’s Note ぐらいで,まともな GPU を積んだ PC は持っていません. System Requirements を見てみると, NVIDIA の GPU が必須条件として書いてあります. しかし, OpenPose が使用しているフレームワークである Caffe には CPU 版もあるため,おそらく GPU を使用しない方法もあるはずです……!と思いきや…
残念ながら試みは失敗に終わった。OpenPoseは、Caffe上に独自のライブラリを構築しており、そこで明示的にCUDAの機能を利用している。
どうも一筋縄にはいかないようです. そうこうしている間に熱が冷めてしまいそうになったので,ひとまずは OpenPose の前身である ZheC/Realtime_Multi-Person_Pose_Estimation を CPU 版 Caffe で動かしてお茶を濁そうと思います. OpenPose については現在鋭意チャレンジ中ですので,続報をお待ちいただきたく.
というか現実的にはこんな記事読んでないで Jetson とかグラボとか,ちゃんとしたPCを買ったほうがいいと思います.
なお,わたしの機械学習に関する知識は,各種用語の意味が少し分かったりMNISTを動かしてみたりしたぐらいの超付け焼き刃で,技術的な興味と下心だけでこれをしているためいろいろと間違ったことを書いていると思います. まさかりの数だけ強くなれると信じているので,もしまさかりポイントを見つけた場合はご指摘よろしくお願いします.
Caffe CPU版ビルドの前に: MATLABのCaffe対応状況
R2017a から Neural Network Toolbox にアドオンを入れることで Caffe モデルの読み込みを行えるようになりました[2]. ただし,これには若干落とし穴があります.
それは,直列モデルしか読み込めないという仕様です. したがって VGG16[3] みたいなものはおっけーなのですが, GoogLeNet や今回扱うネットワーク[4]のような,分岐して再度結合するようなネットワークは読み込めません.
具体的には,今回は Caffe モデルの Concat レイヤの読み込みができず, MATLAB 公式実装の Caffe モデル読み込みでの試行は断念しました. 特に MATLAB を使う意味はないのですが,上記の情報があったので気になって試してみたという報告でした.
以降では, Caffe に標準でついてくる MATLAB インタフェース matcaffe を使って, MATLAB から Caffe を叩いてみます.
Arch LinuxでのCaffe CPU版のビルド + matcaffeインストール
AUR には大変ありがたいことに CPU 版の PKGBUILD が公開されています.
今回は matcaffe を使いたいので, PKGBUILD を編集して, matcaffe のビルドとパッケージ化を行います.
$ mkdir -p ~/abs/caffe-cpu  | 
prepare()
prepare() {
    # ... 略 ...
    # mexの実行に必要.MATLABのインストールディレクトリは各自の環境に合わせて変更する
    sed -i 's/.*MATLAB_DIR[[:space:]]\:=[[:space:]].*/MATLAB_DIR := \/usr\/local\/MATLAB\/R2016b/' Makefile.config
    # ... 略 ...
}build()
build() {
    cd "${_srcname}-${pkgver}"
    msg2 "Building target 'all'..."
    make all -j9
    msg2 "Building target 'pycaffe'..."
    make pycaffe -j9
    msg2 "Building target 'matcaffe'..."
    make matcaffe -j9
    msg2 "Building target 'docs'..."
    make docs
    msg2 "Building target 'distribute'..."
    make distribute
}package()
package() {
    # directories creation
    # ... 略 ...
    mkdir -p "${pkgdir}/opt/matcaffe/"{+caffe,demo,hdf5creation}
    mkdir -p "${pkgdir}/opt/matcaffe/+caffe/"{imagenet,private,+test}
    # ... 略 ...
    # matcaffe
    cd "${srcdir}/${_srcname}-${pkgver}/matlab"
    install -D -m644 CMakeLists.txt "${pkgdir}/opt/matcaffe"
    for _dir in +caffe demo hdf5creation
    do
        cd "${srcdir}/${_srcname}-${pkgver}/matlab/${_dir}"
        for _file in *
        do
            [ -d "$_file" ] && continue # skip directories
            _mode="$(stat --format '%a' "$_file")"
            install -D -m"$_mode" "$_file" "${pkgdir}/opt/matcaffe/${_dir}"
        done
    done
    for _dir in imagenet private +test
    do
        cd "${srcdir}/${_srcname}-${pkgver}/matlab/+caffe/${_dir}"
        for _file in *
        do
            _mode="$(stat --format '%a' "$_file")"
            install -D -m"$_mode" "$_file" "${pkgdir}/opt/matcaffe/+caffe/${_dir}"
        done
    done
    # ... 略 ...
}この変更を適用してパッケージをビルドしてインストールすると,/opt/matcaffe/ 以下に matcaffe のファイルがインストールされます.
ZheC/Realtime_Multi-Person_Pose_Estimation を動かす
動かしてみるのに必要なファイル類は testing ディレクトリに入っています.
まずは学習済みモデルのデータをダウンロードします.
$ cd testing  | 
そのままのmファイルでは動かないため,config.m を編集して CPU 版 Caffe で動かせるようにします.
diff --git a/testing/config.m b/testing/config.m
index fa0361b..2247782 100755
--- a/testing/config.m
+++ b/testing/config.m
@@ -18,7 +18,7 @@ param.click = 1;
 % large memory
 % CPU mode or GPU mode
-param.use_gpu = 1;
+param.use_gpu = 0;
 param.test_mode = 3;
 param.vis = 1;
@@ -41,7 +41,7 @@ param.merge = 'avg';
 % path of your caffe
-caffepath = '/home/zhecao/caffe/matlab';
+caffepath = '/opt/matcaffe';
 %COCO parameter
 if id == 1
@@ -87,6 +87,6 @@ end
 disp(caffepath);
 addpath(caffepath);
-caffe.set_mode_gpu();
-caffe.set_device(GPUdeviceNumber);
+caffe.set_mode_cpu();
+%caffe.set_device(GPUdeviceNumber);
 caffe.reset_all();ボーンのモデルとして COCO と MPI が選択できますが,ここでは特徴点数の少ない MPI を選ぶことにします.
demo.m の7行目を mode = 2; に変更して実行してみると,以下のような結果が現れます.
CPU 版の場合,通常 GPU のメモリに展開されるネットワークが通常のメモリ上に展開されるため,実行中メモリが逼迫した状況になることが容易に想像できます. ブラウザなどメモリを食うものは予め閉じておいたほうがよさそうです. それでもうまく行かない場合は,MATLAB を非デスクトップモードで起動すると良いかもしれません.
$ matlab -nodesktop  | 
OpenPose on Caffe CPU への試み
本家の開発ペースが速いため,ここの内容はすぐに時代遅れになりそうですが,書いておきます.
これが本題のようなものですが,では OpenPose を動かすにはどうすればよいでしょうか. まず,[5]によると, OpenPose のビルド時に CPU 版フラグを指定しても以下のメッセージが出るとのこと.
NVCC src/openpose/pose/bodyPartConnectorBase.cu  | 
(.*)RenderGpu.cu は可視化関係のものでしょうから,本質的には関係なさそうです.
問題は bodyPartConnectorBase.cu, nmsBase.cu, resizeAndMergeBase.cu でしょうか.
ここで, NMS は Non-Maximum Supression という画像認識でよく使われるらしいエッジ検出アルゴリズムで, OpenPose のネットワークでは出力前の後処理として使っているようです. これに関しては MATLAB で簡単に実装できそうなので,その手前の層で出力させて処理を肩代わりすればよさそうです. というかもしかしたら ZheC/Realtime_Multi-Person_Pose_Estimation に実装されているかも.
bodyPartConnector は,該当するソースコードを見る限り,ネットワークが出力するヒートマップからボーンを算出するアルゴリズムのような気がします.
同様のアルゴリズムは ZheC/Realtime_Multi-Person_Pose_Estimation に C++/MATLAB 実装されているので,ひとまず棚上げできそうです.
resizeAndMerge は,prototxtファイルを注意深く眺めていると,ImResize という見慣れないタイプのレイヤの実装に相当していることがわかりました.
なるほど,[5]で触れられているように,Caffe 上にアドオン的に後処理を実装しているようです(先述のNMSもそうですね).
以上をまとめると,ネットワーク定義ファイルと学習済みモデルを持ってくれば,なんとか CPU 版の Caffe で OpenPose を動かせそうな気がしてきます. 次回はその試行錯誤の過程をまとめたいと思います.
hirax.net::ディープラーニングによる姿勢推定openposeで「男子の夢な(衣服の下を透視する)妄撮カメラ」を作ってみよう!? ↩
Import pretrained convolutional neural network models from Caffe - MATLAB importCaffeNetwork - MathWorks ↩
Zhe Cao, Tomas Simon, Shih-En Wei, Yaser Sheikh. Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields. arXiv:1611.08050 [cs.CV] ↩