Julia（+ Flux.jl）でGPUを用いた深層学習を行う環境構築

# はじめに

Python での深層学習用の環境構築は日々簡単になっていますが、Juliaでの環境構築手順をメモしました。

# 環境について

* AWS上の Ubuntu 18.04 で試しています
* 下記の手順だけでも8GBほど使用するので、少なくとも16GBくらいのストレージがあると良いと思います。

# インストール手順

## 標準的なソフトウェア

仮想マシンはだいぶ空なので、下記をインストールしておきます。一部は割愛できるかもしれません。

```shell
sudo apt-get update && sudo apt-get install -y \
  build-essential cmake g++ gfortran git pkg-config \
  python-dev software-properties-common wget
```

## NVIDIA 関連のソフトウェア

NVIDIA のドライバーと関連ソフトウェアのインストール。

```shell
sudo add-apt-repository -y ppa:graphics-drivers/ppa && \
  sudo apt-get update && \
  sudo apt-get install -y nvidia-390 nvidia-cuda-toolkit
```

* `nvidia-390` の部分は、以前は `nvidia-370` だった。徐々にバージョンがあがっていくのでしょう。
* `nvidia-cuda-toolkit` は、Pythonでは必要なかった気がするが、`julia` では `nvcc` というソフトウェアが必要なようなので、加える。


## 再起動

ここで一度、再起動する。AWSなら、Reboot, もしくは一度 stopしてから start。


## NVIDIA 関連のソフトウェアを確認

```shell
cat /proc/driver/nvidia/version
# NVRM version: ... などと表示

nvcc -V
# nvcc: NVIDIA (R) Cuda compiler driver ... release 9.1 ... などと表示

nvidia-smi
# GPU の使用状況が表示される
```


## Miniconda を導入

[Miniconda](https://docs.conda.io/en/latest/miniconda.html) をインストールします。一番の目的は、Anaconda Cloud で管理されている [cudnn](https://anaconda.org/anaconda/cudnn) を使うことです。自分で導入するより楽です。また Jupyter notebook をインストールしたり、Python環境を合わせて作るのにも使えます。

```shell
wget -v https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# ほぼデフォルト設定で良いが、`.bashrc` への設定追加のみ "yes" としておく
source ~/.bashrc
conda create -y -n cudnn cudnn=7.3.1=cuda9.0_0
```

* Minicondaの設定は、基本的にはデフォルト通りが良いと思います。一点だけ、`.bashrc` にconda用の設定を付け加えるかについては、"yes" とすると、ログイン時にすぐに `conda` が使えて良いと思います。
* `source ~/.bashrc` ... その設定を読み込む
* `conda create ...` "cudnn" という仮想環境を作って、そこに `cudnn` ライブラリをインストールしています。仮想環境は作らなくても良いですが、後に色々なパッケージを追加するときにバージョンが変わってしまうことを避けるのに有効です。

`cudnn` のバージョンについては、現在 10.0 まであるようです（`conda search` で確認可）が、インストールした CUDAのバージョンが 9.1 だったので（`nvcc -V` で確認）、１つ前の `cuda9.0`というBuildを選びました（試しに別のBuildを試したところ、9.2, 10.0 ではエラーが出ました）。

```shell 
$ conda search cudnn
# Loading channels: done
# # Name                  Version           Build  Channel             
# ...
# cudnn                     7.3.1      cuda10.0_0  pkgs/main           
# cudnn                     7.3.1       cuda9.0_0  pkgs/main           
# cudnn                     7.3.1       cuda9.2_0  pkgs/main
```

念の為、`libcudnn.so` がきちんと入っていることを確認します。

```shell
find ~/miniconda3 -name "libcudnn*"
# /home/ubuntu/miniconda3/envs/cudnn/lib/libcudnn.so
# /home/ubuntu/miniconda3/envs/cudnn/lib/libcudnn.so.7
# /home/ubuntu/miniconda3/envs/cudnn/lib/libcudnn.so.7.3.1
# /home/ubuntu/miniconda3/envs/cudnn/lib/libcudnn_static.a
# /home/ubuntu/miniconda3/pkgs/cudnn-7.3.1-cuda9.0_0/lib/libcudnn.so
# /home/ubuntu/miniconda3/pkgs/cudnn-7.3.1-cuda9.0_0/lib/libcudnn.so.7
# /home/ubuntu/miniconda3/pkgs/cudnn-7.3.1-cuda9.0_0/lib/libcudnn.so.7.3.1
# /home/ubuntu/miniconda3/pkgs/cudnn-7.3.1-cuda9.0_0/lib/libcudnn_static.a
```

現在、2つの `lib/` ディレクトリに同じファイルが存在しますが、どちらも同じもの（シンボリックリンク）でしょう。


## Julia のインストール

Julia は、最新のものを公式ページから持ってくるのが良いようです。
パスを通して、`julia` コマンドが使えるようにします。
ファイル名は、必要に応じて読み替えます。

```shell
wget https://julialang-s3.julialang.org/bin/linux/x64/1.1/julia-1.1.0-linux-x86_64.tar.gz
tar -xzvf julia-1.1.0-linux-x86_64.tar.gz
echo 'export PATH="$HOME/julia-1.1.0/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
```

## Julia で GPUを使う設定

インストールした`cudnn`のライブラリパスを `LD_LIBRARY_PATH` に加えます。そうすると、julia がこれを見つけてくれます。
フォルダの名前は、必要に応じて読み替えます。

```shell 
echo 'export LD_LIBRARY_PATH="$HOME/miniconda3/envs/cudnn/lib:$LD_LIBRARY_PATH"' >> ~/.bashrc
source ~/.bashrc
echo $LD_LIBRARY_PATH  # check
```

Julia 側では、`Flux`, `CuArrays`, `BSON` パッケージをインストールしておきます。

* `Flux` ... 機械学習パッケージ
* `CuArrays` ... GPU演算用
* `BSON` ... モデルの保存用

```shell
julia -e 'import Pkg; Pkg.add("Flux"); Pkg.add("CuArrays"); Pkg.add("BSON")'
```

## Julia での GPU テスト

Julia がきちんとGPUを使用していることを確認するために、下記の演算を行います。
GPU が使用されている場合、`CuArray` というタイプの配列が作られます。

```julia
julia
> using Flux, CuArrays
> m = Dense(10,5) |> gpu
> x = rand(10,3) |> gpu
# 10x3 CuArray{Float32,2} ... 
> m(x) |> softmax
# Tracked 5x3 CuArray{Float32,2} ...
> exit()
```

最後に、畳み込みニューラルネットの例を行ってみます。[FluxML/model-zoo](https://github.com/FluxML/model-zoo) というレポジトリが、モデルの実装例を公開しています。この内の一つを実行して動作確認を試みます。
スクリプト実行時には学習したモデルが保存されるので、念の為テスト用のフォルダを作って実行します。

このスクリプトは、GPUを使用しない設定で書かれています。GPUを使用するために、`sed` コマンドを使って、1行目に `using CuArrays` を加えています。

```shell
mkdir -p gputest && cd gputest
wget https://raw.githubusercontent.com/FluxML/model-zoo/master/vision/mnist/conv.jl

sed -i '1i using CuArrays' conv.jl  # to run with GPU
julia conv.jl
```


上記が問題なく動いていれば、ひとまず設定完了と思われます。実行時のGPUの使用状況を、`nvidia-smi` などで確認しても良いでしょう。