※この記事はPyTorch Advent Calendar 2020の17日目の記事です。
とてつもなくどうでも良いですが、12月17日は僕の誕生日です。
はじめに
qiitaの検索で、「pytorch」で検索すると、2020年12月15日の0時時点で2,212件もヒットしますが、「libtorch」で検索すると48件しかヒットしませんでした。libtorchってそれくらいの知名度?なので、そもそも「初めて聞いたよ」って人もいれば、「知っているけど使ったことない」という人もいると思います。
この記事では、そんなマイナー(?)なライブラリ「libtorch」について書きたいと思います。
- 最初に、libtorchについて本当にざっくりと説明します
- 次に、libtorchはどうやったら使えるか?を本当にざっくりと説明します
- 次に、実際にlibtorchを使って思ったいくつかの小ネタについて書いてみようと思います
- 最後に、libtorchを使ったプログラムの参考URLを紹介します
【注意】本記事はLinux(Ubuntu)を前提としています。Windowsの場合はまた違う可能性があるのでご注意ください
【注意】一応、それぞれ色々調べて検証もして問題ないと思っていますが、もしかしたら間違いとかあるかもしれないです。
その時はご指導いただけますと幸いですm(_ _)m
libtorchって何?
libtorchについて知らない人に、一言でざっくりとした説明をすると・・・
libtorchはC++でPyTorchが使えるライブラリだよ
です。なぜPyTorchじゃなくてlibtorchを使うのか、それはだいたい下記の2つの理由だと思います。
- 1. PyTorchで作ったモデルをC++のアプリケーションで使う
- 2. PyTorchを使ったPythonのモジュールをC++を使って高速化する
libtorchはどうやったら使えるの?
libtorchはどうやったら使えるのか?大きく分けると下記の3通りの方法があると思います
- 方法その1: libtorchを公式からDL/インストールして使う
- 方法その2: pipやcondaでpytorchをインストールして、同梱されているlibtorchを使う
- 方法その3: 頑張ってソースからビルドする
qiitaの記事を見ていると、殆どがその1の「libtorchを公式からインストールして使う」という方法が多い気がしています。もしかしたらバージョンが古いと含まれていないのかもしれません。個人的にはPytorchに付属しているものを使ったほうが楽だと思うので、僕はその2の方法をおすすめします。その3については、環境によってはpipでPyTorchをインストールできないこともあるので、そんなときはコードからビルドしましょう。
# 共有ライブラリがおいているディレクトリ. libtorch.soがあることがわかると思います!
$ ls /usr/local/lib/python3.6/dist-packages/torch/lib
libc10.so libcaffe2_detectron_ops_gpu.so libcaffe2_nvrtc.so libshm.so libtorch.so libtorch_cuda.so libtorch_python.so libuv.so.1
libc10_cuda.so libcaffe2_module_test_dynamic.so libcaffe2_observers.so libtensorpipe.so libtorch_cpu.so libtorch_global_deps.so libuv.so libuv.so.1.0.0
# ヘッダファイルがおいているディレクトリ
$ ls /usr/local/lib/python3.6/dist-packages/torch/include/
ATen TH THC THCUNN c10 c10d caffe2 pybind11 torch
# CMakeの設定がおいているディレクトリ
$ ls /usr/local/lib/python3.6/dist-packages/torch/share/cmake/
ATen Caffe2 Gloo Torch
なお、上記「その1」と「その2」は、「libtorchのインストール方法」が違うだけで、Make設定やコードの書き方はどちらも同じです。よって、ここでは説明を省略し、先人様たちの記事に解説をお願いしようと思いますm(_ _)m
また、後述のサンプルPGも参考にしていただければと思います。
libtorchの小ネタ
libtorchを使ってみて思ったいくつかの小ネタについて書きます。
オススメlibtorchバージョン
libtorchのバージョンは1.5以上がオススメです。libtorchを初めて使ったときはたしか1.3か1.4かを使っていたのですが、Tensorのスライスがうまくできなかったりと、とても苦労した記憶があります。しかし、v1.5で色々と仕様変更があったらしく[参考]、今は(1.3とか1.4に比べると)大分使いやすくなったと思います。よっぽどの理由がない限り、libtorchは1.5以上、CMake使う人は(後述の理由により)1.6以上を使うことをおすすめします。
torch.utils.cmake_prefix_path
PyTorch1.6(多分)から、PytorchのAPIにtorch.utils.cmake_prefix_path
が追加されました[参考1] [参考2]。
torch.utils.cmake_prefix_path
を使えば、libtorchのCMAKE_PREFIX_PATHを取得することができます。
これを下記のようにCMakeLists.txt
で使えば、わざわざCMAKE_PREFIX_PATH
を環境変数でセットするといったことをせずに、libtorchを使うことができます。
if (NOT PYTHON_EXECUTABLE)
set(PYTHON_EXECUTABLE "python3")
endif()
# Pytorchのライブラリのcmake_prefix_pathを取得
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import torch; print(torch.utils.cmake_prefix_path, end='')" OUTPUT_VARIABLE pytorch_path)
list(APPEND CMAKE_PREFIX_PATH "${pytorch_path}") # CMAKE_PREFIX_PATHに追加
# libtorch find
find_package(Torch REQUIRED)
PyTorchで学習したモデルはどうやって使うの??
PyTorchで学習したモデルをtorchscriptに変換してから使うのが一般的だと思います。
ただ、公式ドキュメントを確認してみた感じでは、C++でスクラッチでネットワークを構築することもできるようです。
やろうと思えば「モデルの学習」もlibtorchでできるっぽいです[参考]
(まあほとんどの人はやらないと思いますが、、、)。
よっぽどのことがない限り、libtorchでモデルを使うのなら前者の「torchscriptに変換して使う」ほうが良いと思います。
(というか、C++で推論したいのであればtorchscriptを使うより、TensorRTを使ったほういいです)
libtorchのAPIリファレンス
libtorchのAPIリファレンスはこちらです。僕だけかもしれないですが、ググってlibtorchのAPIリファレンスにたどり着くのが難しいですOTL。
OpenCVのcv::Matとtorch::Tensorの相互変換
cv::Matとtorch::Tensorを相互に変換するときは、CPUの場合はmemcpy
、GPUの場合はcudaMemcpy
を使ってデータをまるっとコピーすればいいです。このとき、ポイントになるは、下記の4つです。
- cv::Matと、torch::Tensorのデータの型をあわせる
- チャンネル数と画像サイズをあわせる
- cv::Matとtorch::Tensorの並び順に注意
- cudaMemcpyAsyncを使うときは要注意
こちらについては、やり方の一例が後述のサンプルにあるのでそちらを参考にしてください。
libtorchを使ったプログラムの参考リンク
ukyoda作成 サンプルPG
URL: https://github.com/ukyoda/libtorch_tutorial
libtorchを使ってサンプルPGを作成しました。torchvisionにあるモデルをtorchscriptに変換+libtorchで画像分類ができます。
下記に書いているtrt_poseではsetup.pyを使ってビルドしていますが、こちらはCMakeを使っています。
trt_pose
URL: https://github.com/NVIDIA-AI-IOT/trt_pose
NVIDIA社が公開している骨格推定のプログラム「trt_pose」では、後処理でlibtorchを使っています。
pybind11と組み合わせてPythonのモジュールとして実装しています。
最後に
libtorchを使えば、PytorchをC++で使うことができるようになります。Python+PyTorchが遅い!!と感じている方はlibtorchを勉強してC++で高速化を目指してみてください。
ただ、正直、DeepLearningのモデルをC++で高速化したいのであれば、TensorRTを使ったほうが良いと思います。NVIDIA社が公開しているtorch2trtを使えば、PyTorchのモデルをとても簡単にTensorRTに変換することができます。
torch2trtという、とても便利なOSSが出てきた今、多分よっぽどのことがない限り、libtorchを選択することはないかもしれません。ですが、この記事でも述べましたが、libtorchはPytorchからテンソルをそのまま受け取ることができるので、TensorRTでは対応できない前処理とか後処理とかの処理をC++で高速化したいときには活躍してくれそうな気がします。
以上です。それでは皆様良いお年を。2021年も楽しい年であることを祈ります。