PyTorch C++（LibTorch）環境構築

**Deep Learningフレームワークの一種であるPyTorchには，Python版だけではなくC++版がリリースされています！**

今回は，「PyTorch C++」またの名を「LibTorch」のUbuntuでの環境構築の手順を解説していきます．
Deep Learningが好きな人，主要言語がC++の人，組み込み系システムを扱う人は，もしかしたら一度通る道かもしれませんね！
また，C++を使ったことがない人でもある程度わかる内容になっているはずです．

最初に簡単なソースコードの作成と実行の手順を説明し，次に実際のソースコード（私が書いたソースコード）を用いた実行の手順を説明していきます．
**<font color="Red">私が書いたソースコードについては，以下のGitHubにアップロードしています！</font>**
https://github.com/koba-jon/pytorch_cpp

それでは，具体的な手順を説明していきます．

# 事前準備

- 必須
 - Ubuntuのインストール([18.04](https://qiita.com/koba-jon/items/240e6b9a9bee6a0b28cc), [20.04](https://qiita.com/koba-jon/items/019a3b4eac4f60ca89c9))

- GPUを使用する場合
 - NVIDIAドライバーのインストール([18.04](https://qiita.com/koba-jon/items/a7c5239fb5c05172c1b3#1-nvidia%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%83%BC%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB))
 - CUDAのインストール([18.04](https://qiita.com/koba-jon/items/a7c5239fb5c05172c1b3#2-cuda%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB))
 - cuDNNのインストール([18.04](https://qiita.com/koba-jon/items/a7c5239fb5c05172c1b3#3-cudnn%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB))


# LibTorchのダウンロード

PyTorch公式：https://pytorch.org/
リンクにアクセスし，以下の手順に沿ってLibTorchをダウンロードします．

![libtorch1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/8b49b1c8-3304-85f2-7ecb-f70c4fac98d5.png)

- PyTorchBuild<br>
安定版を使いたい場合は**「Stable」**，最新版を使いたい場合は**「Preview」**を選択
- Your OS<br>
今回はUbuntuを使うので**「Linux」**を選択
- Package<br>
今回はLibTorchを使うので**「LibTorch」**を選択
- Language<br>
今回はC++を使うので**「C++/Java」**を選択
- CUDA<br>
GPUを使う場合は**「バージョン名」**，使わない場合は**「None」**を選択
- Run this Command<br>
C++11以後のコンパイラを使うので**「Download here (cxx11 ABI)」**を選択

LibTorch自体のダウンロードはこれで終了です．
ディレクトリ自体は，わかりやすい場所（ホームディレクトリ直下など）に配置するのがおすすめです．


# 動作確認

まず，**仮のソースコード**を作成します．

```
$ mkdir libtorch_test
$ cd libtorch_test
$ vi main.cpp
```

以下のコードを追加し，保存します．
これは，Float型の要素1.5を持つ3x3の行列（2階テンソル）を作成し，それをコンソール画面に表示するプログラムです．

```C++:main.cpp
#include <iostream>
#include <torch/torch.h>

int main(void){
    torch::Tensor x = torch::full({3, 3}, 1.5, torch::TensorOptions().dtype(torch::kFloat));
    std::cout << x << std::endl;
    return 0;
}
```

今回は，簡単にソースコードをビルドするために，「Makefile」を使用します．
また，その「Makefile」を作成するために「CMakeLists.txt」を作成します．

```
$ vi CMakeLists.txt
```

最も簡単なCMakeLists.txtは以下のとおりです．
これを**コピーして保存**します．

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)

# Project Name
project(test CXX)

# Find Package
find_package(Torch REQUIRED)

# Create Executable File
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${TORCH_LIBRARIES})
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 17)
```

簡単に解説すると，最初の行が「cmakeのバージョン指定」，次が「プロジェクト名とC++コンパイラの指定」，次が「探索するライブラリの指定」，次が「コンパイルするファイルの指定」，次が「ライブラリの指定」，最後が「C++コンパイラのバージョンの指定」を意味しています．

次に，ビルド用のディレクトリを作成し，cmakeを実行します．
ここで，**先程ダウンロードしたlibtorchディレクトリのパスを一緒に入力**します．
（「~/libtorch」はホームディレクトリ直下にある場合）

```
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_PREFIX_PATH=~/libtorch
```

これで，Makefileが作られるため，makeを実行してビルドします．

```
$ make
```

最後に，実行ファイルを実行します．

```
$ ./test
```

以下のように表示されたら成功です．

```
 1.5000  1.5000  1.5000
 1.5000  1.5000  1.5000
 1.5000  1.5000  1.5000
[ CPUFloatType{3,3} ]
```

**<font color="Red">LibTorch自体の環境構築はこれで終了</font>**です！
あとは，依存ライブラリをインストールし，サンプルコードが実行できるように設定します．


# 依存ライブラリのインストール

## OpenCV

OpenCVは，**画像データの入出力**に使用します．

インストール方法は，[こちらの記事](https://qiita.com/usk81/items/98e54e2463e9d8a11415#ubuntu)が非常にわかりやすかったです．
cv::Matクラスベースで実装したので，**<font color="Red">バージョン3.0.0以上</font>**をインストールしてください．

## Boost

Boostは，**コマンドライン引数やファイル名の取得など**に使用します．

以下のコマンドを実行して，インストールします．

```
$ sudo apt install libboost-dev libboost-all-dev
```

## Gnuplot

Gnuplotは，**損失のグラフを描画するため**に使用します．

以下のコマンドを実行して，インストールします．

```
$ sudo apt install gnuplot
```

## libpng/png++/zlib

libpng/png++/zlibは，**セマンティックセグメンテーション用のインデックスカラー画像を読み込むため**に使用します．

以下のコマンドを実行して，インストールします．

```
$ sudo apt install libpng-dev libpng++-dev zlib1g-dev
```


# サンプルコードの実行

まず，以下のコマンドを実行し，サンプルコードをクローンします．

```
$ cd ~
$ git clone https://github.com/koba-jon/pytorch_cpp.git
$ cd pytorch_cpp
```

## パス設定

LibTorchのパスを設定するために，「CMakeLists.txt」を編集します．

```
$ vi utils/CMakeLists.txt
```

先程は，cmakeの引数に「-DCMAKE_PREFIX_PATH=~/libtorch」を追加して実行していましたが，毎回これを入力するのは面倒です．
そこで，「CMakeLists.txt」にそのパスを直接埋め込みます．

**4行目の「$ENV{HOME}/libtorch」**をダウンロードした**「libtorch」ディレクトリのパス**に設定してください．
**<font color="Red">ホームディレクトリ直下に「libtorch」がある場合は，変更しなくて大丈夫です．</font>**

```cmake:CMakeLists.txt
3: # LibTorch
4: set(LIBTORCH_DIR $ENV{HOME}/libtorch)
5: list(APPEND CMAKE_PREFIX_PATH ${LIBTORCH_DIR})
```

これで，ビルドするときに自動的にlibtorchのファイルを見つけてくれます．
ちなみに，私のサンプルコードのcmakeは，「utils」の「CMakeLists.txt」と各モデルディレクトリの「CMakeLists.txt」の2つで1セットになっています．
（cmakeの使い方は，[こちらの記事](https://qiita.com/shohirose/items/45fb49c6b429e8b204ac)が非常にわかりやすかったです．）

## ビルド

今回は，畳み込みオートエンコーダを使おうと思うので，そのディレクトリに移動します．
以下のコマンドを実行することで，ソースコードをビルドします．

```
$ cd Dimensionality_Reduction/AE2d
$ mkdir build
$ cd build
$ cmake ..
$ make -j4
$ cd ..
```

## データセットの設定

今回は，「CelebA」の顔画像データを使用します．
以下のサイトにアクセスし，**「Downloads > In-The-Wild Images → Img → img_align_celeba.zip」**の順にクリックしてダウンロードします．
http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html

また，元のデータセットを訓練データとテストデータに分けようと思うので，それを設定します．
まず，**<font color="Red">\<dataset_path>を「ダウンロードしたcelebAデータセットのパス」に当てはめて</font>**，以下のコマンドを実行します．

```
$ cd datasets
$ ln -s <dataset_path> ./celebA_org
```

次に，以下のコマンドを実行して，訓練とテストにデータを分けます．（デフォルトでは「訓練：テスト＝9：1」）

```
$ sudo apt install python3 python3-pip
$ pip3 install natsort
$ sh ../../../scripts/hold_out.sh
$ mv ./celebA/valid ./celebA/test
$ cd ..
```

## モデルの訓練

訓練のためのファイルを設定します．

```
$ vi scripts/train.sh
```

**epochsでエポック数，sizeで画像サイズ，gpu_idで使用するGPUの番号（GPUがない場合は自動的にCPUモードに切り替わる），ncで画像のチャンネル数**を選べます．
（他のオプションは，引数に「--help」を追加するか，「main.cpp」を参照してください．）

今回はサンプルコードのテストなので，**「epochs」を「1」，「size」を「64」に変更**しましょう．
ちなみに，この設定は，[こちらの記事](https://qiita.com/koba-jon/items/274e5e4970da72216f73)で書いた訓練の設定と比較して，損失関数以外は全く同一です．

```bash:train.sh
#!/bin/bash

DATA='celebA'

./AE2d \
    --train true \
    --epochs 300 \ # 1 に変更
    --dataset ${DATA} \
    --size 256 \ # 64 に変更
    --batch_size 16 \
    --gpu_id 0 \
    --nc 3
```

以下のコマンドを実行し，訓練を開始します．

```
$ sh scripts/train.sh
```

訓練の途中段階はこのような感じです．
![libtorch2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/26e004b5-58af-c177-fc5c-e9c88278955e.png)

損失の値が下がっていなければ問題ですが，そうでなければ特に問題ないと思います．

## モデルのテスト

テストのためのファイルを設定します．

```
$ vi scripts/test.sh
```

今回はサンプルコードのテストなので，**「size」を「64」に変更**しましょう．
また，「test_in_dir」を入力画像，「test_out_dir」を出力画像のGround Truthのパスに設定することで，デノイジングの性能評価にも使えますが，今回はそこは変更しません．

```bash:test.sh
#!/bin/bash

DATA='celebA'

./AE2d \
    --test true \
    --dataset ${DATA} \
    --test_in_dir "test" \
    --test_out_dir "test" \
    --size 256 \ # 64 に変更
    --gpu_id 0 \
    --nc 3
```

以下のコマンドを実行し，テストを開始します．

```
$ sh scripts/test.sh
```

テストの様子です．
左側の損失が出力画像と入力画像の誤差，右側の損失が出力画像とそのGround Truthの誤差です．
![libtorch3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/55f98adc-621f-a285-3103-7e6b9d33a35a.png)
最後に，損失の平均が出てきて終わりです．
![libtorch4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/76e29c2d-19e4-1864-8767-40b2210136e2.png)


# ソースコードの公開

**<font color="Red">今回のソースコードは，以下のGitHubに公開しています！！！</font>**
https://github.com/koba-jon/pytorch_cpp

オートエンコーダだけではなく，**VAE，U-Net，pix2pix**などいろいろ書いたので，ぜひ試してみてください！
アドバイスや修正希望の箇所などがありましたら，気軽にコメントしてくださると非常に助かります！

その他，以下の損失のグラフのように，デバッグに役立つツールも実装したので，ぜひ活用してください！
（表示には，Gnuplotを使用しています．）

訓練時に追加されるディレクトリ「checkpoints」の中に作られます．
![libtorch6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/09ad6a67-0966-a15f-3c18-df1420a4d5e5.png)


ちなみに，今回の処理の流れは以下のようになっています．

![libtorch5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374669/fd6373c2-3364-5827-f15c-e16aa94bf6f1.png)


# おわりに

今回は，PyTorch C++（LibTorch）のUbuntuでの環境構築をしました．
開発者や研究者，あるいはDeep Learningに興味があって勉強しているみなさまのお役に立てればと思います．

それでは，最後まで記事を見ていただきありがとうございました！
**Let's have a good development and research life!**